1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file. 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * TODO(stoarca): This class has become obsolete except for the shadow table. 7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Chop most of it away. 8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @fileoverview A DOM traversal interface for navigating data in tables. 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.provide('cvox.TraverseTable'); 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.require('cvox.DomPredicates'); 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.require('cvox.DomUtil'); 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.require('cvox.SelectionUtil'); 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.require('cvox.TableUtil'); 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.require('cvox.TraverseUtil'); 18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * An object that represents an active table cell inside the shadow table. 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)function ShadowTableNode() {} 26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Whether or not the active cell is spanned by a preceding cell. 30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {boolean} 31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ShadowTableNode.prototype.spanned; 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Whether or not this cell is spanned by a rowSpan. 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {?boolean} 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ShadowTableNode.prototype.rowSpan; 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Whether or not this cell is spanned by a colspan 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {?boolean} 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ShadowTableNode.prototype.colSpan; 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The row index of the corresponding active table cell 51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {?number} 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ShadowTableNode.prototype.i; 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The column index of the corresponding active table cell 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {?number} 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ShadowTableNode.prototype.j; 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The corresponding <TD> or <TH> node in the active table. 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {?Node} 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ShadowTableNode.prototype.activeCell; 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The cells that are row headers of the corresponding active table cell 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {!Array} 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ShadowTableNode.prototype.rowHeaderCells = []; 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The cells that are column headers of the corresponding active table cell 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {!Array} 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ShadowTableNode.prototype.colHeaderCells = []; 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Initializes the traversal with the provided table node. 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {Node} tableNode The table to be traversed. 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable = function(tableNode) { 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The active table <TABLE> node. In this context, "active" means that this is 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the table the TraverseTable object is navigating. 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {Node} 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.activeTable_ = null; 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * A 2D array "shadow table" that contains pointers to nodes in the active 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * table. More specifically, each cell of the shadow table contains a special 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * object ShadowTableNode that has as one of its member variables the 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * corresponding cell in the active table. 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The shadow table will allow us efficient navigation of tables with 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * rowspans and colspans without needing to repeatedly scan the table. For 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * example, if someone requests a cell at (1,3), predecessor cells with 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * rowspans/colspans mean the cell you eventually return could actually be 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * one located at (0,2) that spans out to (1,3). 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * This shadow table will contain a ShadowTableNode with the (0, 2) index at 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the (1,3) position, eliminating the need to check for predecessor cells 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * with rowspan/colspan every time we traverse the table. 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {!Array.<Array.<ShadowTableNode>>} 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_ = []; 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * An array of shadow table nodes that have been determined to contain header 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * cells or information about header cells. This array is collected at 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * initialization and then only recalculated if the table changes. 126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * This array is used by findHeaderCells() to determine table row headers 127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * and column headers. 128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {Array.<ShadowTableNode>} 129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.candidateHeaders_ = []; 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * An array that associates cell IDs with their corresponding shadow nodes. 135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * If there are two shadow nodes for the same cell (i.e. when a cell spans 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * other cells) then the first one will be associated with the ID. This means 137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * that shadow nodes that have spanned set to true will not be included in 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * this array. 139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {Array.<ShadowTableNode>} 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.idToShadowNode_ = []; 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.initialize(tableNode); 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The cell cursor, represented by an array that stores the row and 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * column location [i, j] of the active cell. These numbers are 0-based. 151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * In this context, "active" means that this is the cell the user is 152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * currently looking at. 153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {Array} 154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.currentCellCursor; 156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The number of columns in the active table. This is calculated at 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * initialization and then only recalculated if the table changes. 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Please Note: We have chosen to use the number of columns in the shadow 163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * table as the canonical column count. This is important for tables that 164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * have colspans - the number of columns in the active table will always be 165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * less than the true number of columns. 166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {?number} 167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.colCount = null; 169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The number of rows in the active table. This is calculated at 173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * initialization and then only recalculated if the table changes. 174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {?number} 175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.rowCount = null; 177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The row headers in the active table. This is calculated at 181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * initialization and then only recalculated if the table changes. 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Please Note: 184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Row headers are defined here as <TH> or <TD> elements. <TD> elements when 185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * serving as header cells must have either: 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * - The scope attribute defined 187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * - Their IDs referenced in the header content attribute of another <TD> or 188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * <TH> element. 189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The HTML5 spec specifies that only header <TH> elements can be row headers 191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * ( http://dev.w3.org/html5/spec/tabular-data.html#row-header ) but the 192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * HTML4 spec says that <TD> elements can act as both 193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * ( http://www.w3.org/TR/html401/struct/tables.html#h-11.2.6 ). In the 194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * interest of providing meaningful header information for all tables, here 195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * we take the position that <TD> elements can act as both. 196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {Array} 198cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.tableRowHeaders = null; 200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 201cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 202cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 203cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The column headers in the active table. This is calculated at 204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * initialization and then only recalculated if the table changes. 205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Please Note: see comment for tableRowHeaders. 207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {Array} 209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.tableColHeaders = null; 211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// TODO (stoarca): tighten up interface to {!Node} 214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Initializes the class member variables. 216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {Node} tableNode The table to be traversed. 217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 218cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.initialize = function(tableNode) { 219cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!tableNode) { 220cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 221cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 222cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (tableNode == this.activeTable_) { 223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 224cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 225cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.activeTable_ = tableNode; 226cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = null; 227cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 228cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.tableRowHeaders = []; 229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.tableColHeaders = []; 230cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 231cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.buildShadowTable_(); 232cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.colCount = this.shadowColCount_(); 234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.rowCount = this.countRows_(); 235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.findHeaderCells_(); 237cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Listen for changes to the active table. If the active table changes, 239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // rebuild the shadow table. 240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO (stoarca): Is this safe? When this object goes away, doesn't the 241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // eventListener stay on the node? Someone with better knowledge of js 242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // please confirm. If so, this is a leak. 243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.activeTable_.addEventListener('DOMSubtreeModified', 244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.bind(function() { 245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.buildShadowTable_(); 246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.colCount = this.shadowColCount_(); 247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.rowCount = this.countRows_(); 248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.tableRowHeaders = []; 250cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.tableColHeaders = []; 251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.findHeaderCells_(); 252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 253cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.colCount == 0 && this.rowCount == 0) { 254cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 255cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 256cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 257cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.getCell() == null) { 258cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.attachCursorToNearestCell_(); 259cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 260cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }, this), false); 261cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 262cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 263cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 265cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Finds the cell cursor containing the specified node within the table. 266cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Returns null if there is no close cell. 267cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Node} node The node for which to find the cursor. 268cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {Array.<number>} The table index for the node. 269cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 270cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.findNearestCursor = function(node) { 271cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO (stoarca): The current structure for representing the 272cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // shadow table is not optimal for this query, but it's not urgent 273cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // since this only gets executed at most once per user action. 274cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 275cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // In case node is in a table but above any individual cell, we go down as 276cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // deep as we can, being careful to avoid going into nested tables. 277cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var n = node; 278cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 279cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (n.firstElementChild && 280cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) !(n.firstElementChild.tagName == 'TABLE' || 281cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.AriaUtil.isGrid(n.firstElementChild))) { 282cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) n = n.firstElementChild; 283cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 284cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (!cvox.DomPredicates.cellPredicate(cvox.DomUtil.getAncestors(n))) { 285cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) n = cvox.DomUtil.directedNextLeafNode(n); 286cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO(stoarca): Ugly logic. Captions should be part of tables. 287cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // There have been a bunch of bugs as a result of 288cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // DomUtil.findTableNodeInList excluding captions from tables because 289cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // it makes them non-contiguous. 290cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!cvox.DomUtil.getContainingTable(n, {allowCaptions: true})) { 291cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return null; 292cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 293cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 294cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < this.rowCount; ++i) { 295cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var j = 0; j < this.colCount; ++j) { 296cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[i][j]) { 297cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (cvox.DomUtil.isDescendantOfNode( 298cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) n, this.shadowTable_[i][j].activeCell)) { 299cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return [i, j]; 300cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 301cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 302cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 303cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 304cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return null; 305cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 306cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 307cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 308cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Finds the valid cell nearest to the current cell cursor and moves the cell 309cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * cursor there. To be used when the table has changed and the current cell 310cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * cursor is now invalid (doesn't exist anymore). 311cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 312cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 313cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.attachCursorToNearestCell_ = function() { 314cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!this.currentCellCursor) { 315cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We have no idea. Just go 'somewhere'. Other code paths in this 316cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // function go to the last cell, so let's do that! 317cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.goToLastCell(); 318cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 319cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 320cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 321cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentCursor = this.currentCellCursor; 322cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 323cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Does the current row still exist in the table? 324cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentRow = this.shadowTable_[currentCursor[0]]; 325cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (currentRow) { 326cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Try last cell of current row 327cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = [currentCursor[0], (currentRow.length - 1)]; 328cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 329cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Current row does not exist anymore. Does current column still exist? 330cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Try last cell of current column 331cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var numRows = this.shadowTable_.length; 332cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (numRows == 0) { 333cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Table has been deleted! 334cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = null; 335cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 336cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 337cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var aboveCell = 338cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[numRows - 1][currentCursor[1]]; 339cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (aboveCell) { 340cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = [(numRows - 1), currentCursor[1]]; 341cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Current column does not exist anymore either. 343cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Move cursor to last cell in table. 344cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.goToLastCell(); 345cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 346cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 347cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 348cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 349cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 350cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 351cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Builds or rebuilds the shadow table by iterating through all of the cells 352cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * ( <TD> or <TH> or role='gridcell' nodes) of the active table. 353cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {!Array} The shadow table. 354cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 355cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 356cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.buildShadowTable_ = function() { 357cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Clear shadow table 358cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_ = []; 359cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 360cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Build shadow table structure. Initialize it as a 2D array. 361cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var allRows = cvox.TableUtil.getChildRows(this.activeTable_); 362cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentRowParent = null; 363cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentRowGroup = null; 364cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 365cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var colGroups = cvox.TableUtil.getColGroups(this.activeTable_); 366cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var colToColGroup = cvox.TableUtil.determineColGroups(colGroups); 367cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 368cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var ctr = 0; ctr < allRows.length; ctr++) { 369cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_.push([]); 370cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 371cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 372cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Iterate through active table by row 373cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < allRows.length; i++) { 374cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var childCells = cvox.TableUtil.getChildCells(allRows[i]); 375cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 376cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Keep track of position in active table 377cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var activeTableCol = 0; 378cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Keep track of position in shadow table 379cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowTableCol = 0; 380cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 381cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (activeTableCol < childCells.length) { 382cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 383cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Check to make sure we haven't already filled this cell. 384cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[i][shadowTableCol] == null) { 385cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 386cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var activeTableCell = childCells[activeTableCol]; 387cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 388cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Default value for colspan and rowspan is 1 389cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var colsSpanned = 1; 390cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var rowsSpanned = 1; 391cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 392cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (activeTableCell.hasAttribute('colspan')) { 393cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 394cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) colsSpanned = 395cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) parseInt(activeTableCell.getAttribute('colspan'), 10); 396cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 397cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((isNaN(colsSpanned)) || (colsSpanned <= 0)) { 398cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // The HTML5 spec defines colspan MUST be greater than 0: 399cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-colspan 400cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // 401cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This is a change from the HTML4 spec: 402cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // http://www.w3.org/TR/html401/struct/tables.html#adef-colspan 403cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // 404cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We will degrade gracefully by treating a colspan=0 as 405cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // equivalent to a colspan=1. 406cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Tested in method testColSpan0 in rowColSpanTable_test.js 407cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) colsSpanned = 1; 408cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 409cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 410cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (activeTableCell.hasAttribute('rowspan')) { 411cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rowsSpanned = 412cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) parseInt(activeTableCell.getAttribute('rowspan'), 10); 413cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 414cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((isNaN(rowsSpanned)) || (rowsSpanned <= 0)) { 415cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // The HTML5 spec defines that rowspan can be any non-negative 416cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // integer, including 0: 417cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-rowspan 418cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // 419cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // However, Chromium treats rowspan=0 as rowspan=1. This appears 420cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // to be a bug from WebKit: 421cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // https://bugs.webkit.org/show_bug.cgi?id=10300 422cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Inherited from a bug (since fixed) in KDE: 423cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // http://bugs.kde.org/show_bug.cgi?id=41063 424cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // 425cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We will follow Chromium and treat rowspan=0 as equivalent to 426cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // rowspan=1. 427cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // 428cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Tested in method testRowSpan0 in rowColSpanTable_test.js 429cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // 430cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Filed as a bug in Chromium: http://crbug.com/58223 431cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rowsSpanned = 1; 432cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 433cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 434cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var r = 0; r < rowsSpanned; r++) { 435cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var c = 0; c < colsSpanned; c++) { 436cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowNode = new ShadowTableNode(); 437cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((r == 0) && (c == 0)) { 438cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This position is not spanned. 439cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.spanned = false; 440cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.rowSpan = false; 441cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.colSpan = false; 442cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.i = i; 443cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.j = shadowTableCol; 444cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.activeCell = activeTableCell; 445cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.rowHeaderCells = []; 446cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.colHeaderCells = []; 447cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.isRowHeader = false; 448cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.isColHeader = false; 449cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 450cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This position is spanned. 451cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.spanned = true; 452cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.rowSpan = (rowsSpanned > 1); 453cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.colSpan = (colsSpanned > 1); 454cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.i = i; 455cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.j = shadowTableCol; 456cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.activeCell = activeTableCell; 457cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.rowHeaderCells = []; 458cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.colHeaderCells = []; 459cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.isRowHeader = false; 460cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.isColHeader = false; 461cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 462cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Check this shadowNode to see if it is a candidate header cell 463cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (cvox.TableUtil.checkIfHeader(shadowNode.activeCell)) { 464cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.candidateHeaders_.push(shadowNode); 465cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (shadowNode.activeCell.hasAttribute('headers')) { 466cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This shadowNode has information about other header cells 467cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.candidateHeaders_.push(shadowNode); 468cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 469cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 470cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Check and update row group status. 471cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (currentRowParent == null) { 472cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This is the first row 473cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentRowParent = allRows[i].parentNode; 474cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentRowGroup = 0; 475cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 476cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (allRows[i].parentNode != currentRowParent) { 477cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We're in a different row group now 478cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentRowParent = allRows[i].parentNode; 479cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentRowGroup = currentRowGroup + 1; 480cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 481cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 482cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.rowGroup = currentRowGroup; 483cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 484cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Check and update col group status 485cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (colToColGroup.length > 0) { 486cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.colGroup = colToColGroup[shadowTableCol]; 487cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 488cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowNode.colGroup = 0; 489cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 490cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 491cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (! shadowNode.spanned) { 492cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (activeTableCell.id != null) { 493cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.idToShadowNode_[activeTableCell.id] = shadowNode; 494cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 495cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 496cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 497cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[i + r][shadowTableCol + c] = shadowNode; 498cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 499cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 500cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowTableCol += colsSpanned; 501cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) activeTableCol++; 502cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 503cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This position has already been filled (by a previous cell that has 504cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // a colspan or a rowspan) 505cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowTableCol += 1; 506cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 507cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 508cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 509cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.shadowTable_; 510cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 511cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 512cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 513cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 514cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Finds header cells from the list of candidate headers and classifies them 515cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * in two ways: 516cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * -- Identifies them for the entire table by adding them to 517cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * this.tableRowHeaders and this.tableColHeaders. 518cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * -- Identifies them for each shadow table node by adding them to the node's 519cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * rowHeaderCells or colHeaderCells arrays. 520cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 521cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 522cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 523cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.findHeaderCells_ = function() { 524cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Forming relationships between data cells and header cells: 525cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // http://dev.w3.org/html5/spec/tabular-data.html 526cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // #header-and-data-cell-semantics 527cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 528cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < this.candidateHeaders_.length; i++) { 529cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 530cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentShadowNode = this.candidateHeaders_[i]; 531cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentCell = currentShadowNode.activeCell; 532cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 533cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var assumedScope = null; 534cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var specifiedScope = null; 535cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 536cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (currentShadowNode.spanned) { 537cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) continue; 538cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 539cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 540cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((currentCell.tagName == 'TH') && 541cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) !(currentCell.hasAttribute('scope'))) { 542cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // No scope specified - compute scope ourselves. 543cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Go left/right - if there's a header node, then this is a column 544cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // header 545cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (currentShadowNode.j > 0) { 546cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j - 1]. 547cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) activeCell.tagName == 'TH') { 548cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) assumedScope = 'col'; 549cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 550cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (currentShadowNode.j < this.shadowTable_[currentShadowNode.i]. 551cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) length - 1) { 552cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j + 1]. 553cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) activeCell.tagName == 'TH') { 554cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) assumedScope = 'col'; 555cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 556cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 557cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This row has a width of 1 cell, just assume this is a colum header 558cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) assumedScope = 'col'; 559cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 560cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 561cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (assumedScope == null) { 562cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Go up/down - if there's a header node, then this is a row header 563cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (currentShadowNode.i > 0) { 564cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[currentShadowNode.i - 1][currentShadowNode.j]. 565cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) activeCell.tagName == 'TH') { 566cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) assumedScope = 'row'; 567cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 568cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (currentShadowNode.i < this.shadowTable_.length - 1) { 569cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[currentShadowNode.i + 1][currentShadowNode.j]. 570cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) activeCell.tagName == 'TH') { 571cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) assumedScope = 'row'; 572cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 573cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 574cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This column has a height of 1 cell, just assume that this is 575cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // a row header 576cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) assumedScope = 'row'; 577cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 578cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 579cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (currentCell.hasAttribute('scope')) { 580cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) specifiedScope = currentCell.getAttribute('scope'); 581cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (currentCell.hasAttribute('role') && 582cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) (currentCell.getAttribute('role') == 'rowheader')) { 583cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) specifiedScope = 'row'; 584cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (currentCell.hasAttribute('role') && 585cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) (currentCell.getAttribute('role') == 'columnheader')) { 586cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) specifiedScope = 'col'; 587cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 588cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 589cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((specifiedScope == 'row') || (assumedScope == 'row')) { 590cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentShadowNode.isRowHeader = true; 591cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 592cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Go right until you hit the edge of the table or a data 593cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // cell after another header cell. 594cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Add this cell to each shadowNode.rowHeaderCells attribute as you go. 595cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var rightCtr = currentShadowNode.j; 596cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rightCtr < this.shadowTable_[currentShadowNode.i].length; 597cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rightCtr++) { 598cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 599cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var rightShadowNode = this.shadowTable_[currentShadowNode.i][rightCtr]; 600cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var rightCell = rightShadowNode.activeCell; 601cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 602cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((rightCell.tagName == 'TH') || 603cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) (rightCell.hasAttribute('scope'))) { 604cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 605cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (rightCtr < this.shadowTable_[currentShadowNode.i].length - 1) { 606cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var checkDataCell = 607cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[currentShadowNode.i][rightCtr + 1]; 608cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 609cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 610cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rightShadowNode.rowHeaderCells.push(currentCell); 611cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 612cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.tableRowHeaders.push(currentCell); 613cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if ((specifiedScope == 'col') || (assumedScope == 'col')) { 614cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentShadowNode.isColHeader = true; 615cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 616cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Go down until you hit the edge of the table or a data cell 617cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // after another header cell. 618cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Add this cell to each shadowNode.colHeaders attribute as you go. 619cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 620cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var downCtr = currentShadowNode.i; 621cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) downCtr < this.shadowTable_.length; 622cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) downCtr++) { 623cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 624cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var downShadowNode = this.shadowTable_[downCtr][currentShadowNode.j]; 625cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (downShadowNode == null) { 626cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 627cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 628cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var downCell = downShadowNode.activeCell; 629cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 630cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((downCell.tagName == 'TH') || 631cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) (downCell.hasAttribute('scope'))) { 632cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 633cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (downCtr < this.shadowTable_.length - 1) { 634cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var checkDataCell = 635cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[downCtr + 1][currentShadowNode.j]; 636cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 637cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 638cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) downShadowNode.colHeaderCells.push(currentCell); 639cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 640cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.tableColHeaders.push(currentCell); 641cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (specifiedScope == 'rowgroup') { 642cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentShadowNode.isRowHeader = true; 643cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 644cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This cell is a row header for the rest of the cells in this row group. 645cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentRowGroup = currentShadowNode.rowGroup; 646cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 647cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Get the rest of the cells in this row first 648cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var cellsInRow = currentShadowNode.j + 1; 649cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cellsInRow < this.shadowTable_[currentShadowNode.i].length; 650cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cellsInRow++) { 651cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[currentShadowNode.i][cellsInRow]. 652cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rowHeaderCells.push(currentCell); 653cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 654cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 655cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Now propagate to rest of row group 656cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var downCtr = currentShadowNode.i + 1; 657cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) downCtr < this.shadowTable_.length; 658cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) downCtr++) { 659cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 660cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[downCtr][0].rowGroup != currentRowGroup) { 661cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 662cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 663cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 664cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var rightCtr = 0; 665cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rightCtr < this.shadowTable_[downCtr].length; 666cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rightCtr++) { 667cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 668cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[downCtr][rightCtr]. 669cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rowHeaderCells.push(currentCell); 670cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 671cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 672cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.tableRowHeaders.push(currentCell); 673cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 674cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (specifiedScope == 'colgroup') { 675cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentShadowNode.isColHeader = true; 676cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 677cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This cell is a col header for the rest of the cells in this col group. 678cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentColGroup = currentShadowNode.colGroup; 679cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 680cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Get the rest of the cells in this colgroup first 681cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var cellsInCol = currentShadowNode.j + 1; 682cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cellsInCol < this.shadowTable_[currentShadowNode.i].length; 683cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cellsInCol++) { 684cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[currentShadowNode.i][cellsInCol].colGroup == 685cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentColGroup) { 686cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[currentShadowNode.i][cellsInCol]. 687cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) colHeaderCells.push(currentCell); 688cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 689cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 690cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 691cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Now propagate to rest of col group 692cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var downCtr = currentShadowNode.i + 1; 693cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) downCtr < this.shadowTable_.length; 694cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) downCtr++) { 695cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 696cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var rightCtr = 0; 697cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rightCtr < this.shadowTable_[downCtr].length; 698cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) rightCtr++) { 699cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 700cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[downCtr][rightCtr].colGroup == 701cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentColGroup) { 702cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[downCtr][rightCtr]. 703cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) colHeaderCells.push(currentCell); 704cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 705cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 706cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 707cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.tableColHeaders.push(currentCell); 708cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 709cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (currentCell.hasAttribute('headers')) { 710cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.findAttrbHeaders_(currentShadowNode); 711cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 712cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (currentCell.hasAttribute('aria-describedby')) { 713cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.findAttrbDescribedBy_(currentShadowNode); 714cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 715cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 716cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 717cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 718cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 719cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 720cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Finds header cells from the 'headers' attribute of a given shadow node's 721cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * active cell and classifies them in two ways: 722cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * -- Identifies them for the entire table by adding them to 723cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * this.tableRowHeaders and this.tableColHeaders. 724cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * -- Identifies them for the shadow table node by adding them to the node's 725cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * rowHeaderCells or colHeaderCells arrays. 726cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Please note that header cells found through the 'headers' attribute are 727cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * difficult to attribute to being either row or column headers because a 728cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * table cell can declare arbitrary cells as its headers. A guess is made here 729cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * based on which axis the header cell is closest to. 730cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 731cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {ShadowTableNode} currentShadowNode A shadow node with an active cell 732cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * that has a 'headers' attribute. 733cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 734cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 735cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 736cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.findAttrbHeaders_ = function(currentShadowNode) { 737cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var activeTableCell = currentShadowNode.activeCell; 738cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 739cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var idList = activeTableCell.getAttribute('headers').split(' '); 740cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var idToken = 0; idToken < idList.length; idToken++) { 741cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Find cell(s) with this ID, add to header list 742cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var idCellArray = cvox.TableUtil.getCellWithID(this.activeTable_, 743cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) idList[idToken]); 744cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 745cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) { 746cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (idCellArray[idCtr].id == activeTableCell.id) { 747cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Skip if the ID is the same as the current cell's ID 748cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 749cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 750cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Check if this list of candidate headers contains a 751cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // shadowNode with an active cell with this ID already 752cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var possibleHeaderNode = 753cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.idToShadowNode_[idCellArray[idCtr].id]; 754cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (! cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) { 755cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This listed header cell will not be handled later. 756cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Determine whether this is a row or col header for 757cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // the active table cell 758cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 759cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var iDiff = Math.abs(possibleHeaderNode.i - currentShadowNode.i); 760cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var jDiff = Math.abs(possibleHeaderNode.j - currentShadowNode.j); 761cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((iDiff == 0) || (iDiff < jDiff)) { 762cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.TableUtil.pushIfNotContained(currentShadowNode.rowHeaderCells, 763cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) possibleHeaderNode.activeCell); 764cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.TableUtil.pushIfNotContained(this.tableRowHeaders, 765cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) possibleHeaderNode.activeCell); 766cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 767cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This is a column header 768cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.TableUtil.pushIfNotContained(currentShadowNode.colHeaderCells, 769cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) possibleHeaderNode.activeCell); 770cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.TableUtil.pushIfNotContained(this.tableColHeaders, 771cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) possibleHeaderNode.activeCell); 772cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 773cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 774cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 775cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 776cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 777cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 778cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 779cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 780cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Finds header cells from the 'aria-describedby' attribute of a given shadow 781cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * node's active cell and classifies them in two ways: 782cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * -- Identifies them for the entire table by adding them to 783cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * this.tableRowHeaders and this.tableColHeaders. 784cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * -- Identifies them for the shadow table node by adding them to the node's 785cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * rowHeaderCells or colHeaderCells arrays. 786cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 787cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Please note that header cells found through the 'aria-describedby' attribute 788cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * must have the role='rowheader' or role='columnheader' attributes in order to 789cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * be considered header cells. 790cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 791cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {ShadowTableNode} currentShadowNode A shadow node with an active cell 792cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * that has an 'aria-describedby' attribute. 793cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 794cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 795cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 796cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.findAttrbDescribedBy_ = 797cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) function(currentShadowNode) { 798cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var activeTableCell = currentShadowNode.activeCell; 799cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 800cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var idList = activeTableCell.getAttribute('aria-describedby').split(' '); 801cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var idToken = 0; idToken < idList.length; idToken++) { 802cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Find cell(s) with this ID, add to header list 803cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var idCellArray = cvox.TableUtil.getCellWithID(this.activeTable_, 804cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) idList[idToken]); 805cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 806cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) { 807cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (idCellArray[idCtr].id == activeTableCell.id) { 808cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Skip if the ID is the same as the current cell's ID 809cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 810cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 811cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Check if this list of candidate headers contains a 812cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // shadowNode with an active cell with this ID already 813cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var possibleHeaderNode = 814cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.idToShadowNode_[idCellArray[idCtr].id]; 815cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (! cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) { 816cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // This listed header cell will not be handled later. 817cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Determine whether this is a row or col header for 818cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // the active table cell 819cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 820cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (possibleHeaderNode.activeCell.hasAttribute('role') && 821cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) (possibleHeaderNode.activeCell.getAttribute('role') == 822cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'rowheader')) { 823cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.TableUtil.pushIfNotContained(currentShadowNode.rowHeaderCells, 824cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) possibleHeaderNode.activeCell); 825cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.TableUtil.pushIfNotContained(this.tableRowHeaders, 826cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) possibleHeaderNode.activeCell); 827cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (possibleHeaderNode.activeCell.hasAttribute('role') && 828cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) (possibleHeaderNode.activeCell.getAttribute('role') == 829cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'columnheader')) { 830cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.TableUtil.pushIfNotContained(currentShadowNode.colHeaderCells, 831cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) possibleHeaderNode.activeCell); 832cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.TableUtil.pushIfNotContained(this.tableColHeaders, 833cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) possibleHeaderNode.activeCell); 834cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 835cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 836cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 837cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 838cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 839cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 840cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 841cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 842cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Gets the current cell or null if there is no current cell. 843cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {?Node} The cell <TD> or <TH> or role='gridcell' node. 844cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 845cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.getCell = function() { 846cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!this.currentCellCursor || !this.shadowTable_) { 847cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return null; 848cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 849cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 850cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowEntry = 851cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; 852cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 853cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return shadowEntry && shadowEntry.activeCell; 854cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 855cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 856cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 857cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 858cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Gets the cell at the specified location. 859cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {Array.<number>} index The index <i, j> of the required cell. 860cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {?Node} The cell <TD> or <TH> or role='gridcell' node at the 861cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * specified location. Null if that cell does not exist. 862cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 863cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.getCellAt = function(index) { 864cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (((index[0] < this.rowCount) && (index[0] >= 0)) && 865cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ((index[1] < this.colCount) && (index[1] >= 0))) { 866cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowEntry = this.shadowTable_[index[0]][index[1]]; 867cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (shadowEntry != null) { 868cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return shadowEntry.activeCell; 869cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 870cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 871cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return null; 872cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 873cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 874cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 875cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 876cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Gets the cells that are row headers of the current cell. 877cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {!Array} The cells that are row headers of the current cell. Empty if 878cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the current cell does not have row headers. 879cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 880cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.getCellRowHeaders = function() { 881cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowEntry = 882cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; 883cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 884cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return shadowEntry.rowHeaderCells; 885cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 886cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 887cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 888cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 889cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Gets the cells that are col headers of the current cell. 890cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {!Array} The cells that are col headers of the current cell. Empty if 891cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the current cell does not have col headers. 892cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 893cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.getCellColHeaders = function() { 894cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowEntry = 895cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; 896cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 897cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return shadowEntry.colHeaderCells; 898cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 899cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 900cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 901cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 902cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Whether or not the current cell is spanned by another cell. 903cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Whether or not the current cell is spanned by another cell. 904cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 905cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.isSpanned = function() { 906cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowEntry = 907cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; 908cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 909cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return shadowEntry.spanned; 910cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 911cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 912cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 913cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 914cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Whether or not the current cell is a row header cell. 915cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Whether or not the current cell is a row header cell. 916cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 917cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.isRowHeader = function() { 918cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowEntry = 919cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; 920cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 921cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return shadowEntry.isRowHeader; 922cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 923cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 924cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 925cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 926cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Whether or not the current cell is a col header cell. 927cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Whether or not the current cell is a col header cell. 928cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 929cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.isColHeader = function() { 930cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowEntry = 931cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; 932cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 933cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return shadowEntry.isColHeader; 934cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 935cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 936cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 937cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 938cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Gets the active column, represented as an array of <TH> or <TD> nodes that 939cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * make up a column. In this context, "active" means that this is the column 940cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * that contains the cell the user is currently looking at. 941cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {Array} An array of <TH> or <TD> or role='gridcell' nodes. 942cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 943cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.getCol = function() { 944cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var colArray = []; 945cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < this.shadowTable_.length; i++) { 946cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 947cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[i][this.currentCellCursor[1]]) { 948cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var shadowEntry = this.shadowTable_[i][this.currentCellCursor[1]]; 949cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 950cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (shadowEntry.colSpan && shadowEntry.rowSpan) { 951cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Look at the last element in the column cell aray. 952cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var prev = colArray[colArray.length - 1]; 953cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (prev != 954cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowEntry.activeCell) { 955cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Watch out for positions spanned by a cell with rowspan and 956cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // colspan. We don't want the same cell showing up multiple times 957cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // in per-column cell lists. 958cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) colArray.push( 959cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowEntry.activeCell); 960cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 961cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if ((shadowEntry.colSpan) || (!shadowEntry.rowSpan)) { 962cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) colArray.push( 963cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) shadowEntry.activeCell); 964cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 965cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 966cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 967cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return colArray; 968cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 969cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 970cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 971cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 972cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Gets the active row <TR> node. In this context, "active" means that this is 973cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the row that contains the cell the user is currently looking at. 974cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {Node} The active row node. 975cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 976cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.getRow = function() { 977cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var childRows = cvox.TableUtil.getChildRows(this.activeTable_); 978cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return childRows[this.currentCellCursor[0]]; 979cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 980cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 981cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 982cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 983cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Gets the table summary text. 984cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 985cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {?string} Either: 986cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) The table summary text 987cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) Null if the table does not contain a summary attribute. 988cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 989cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.summaryText = function() { 990cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // see http://code.google.com/p/chromium/issues/detail?id=46567 991cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // for information why this is necessary 992cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!this.activeTable_.hasAttribute('summary')) { 993cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return null; 994cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 995cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.activeTable_.getAttribute('summary'); 996cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 997cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 998cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 999cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1000cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Gets the table caption text. 1001cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1002cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {?string} Either: 1003cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) The table caption text 1004cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) Null if the table does not include a caption tag. 1005cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1006cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.captionText = function() { 1007cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If there's more than one outer <caption> element, choose the first one. 1008cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var captionNodes = cvox.XpathUtil.evalXPath('caption\[1]', 1009cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.activeTable_); 1010cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (captionNodes.length > 0) { 1011cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return captionNodes[0].innerHTML; 1012cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1013cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return null; 1014cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1015cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1016cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1017cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1018cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1019cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Calculates the number of columns in the shadow table. 1020cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {number} The number of columns in the shadow table. 1021cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 1022cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1023cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.shadowColCount_ = function() { 1024cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // As the shadow table is a 2D array, the number of columns is the 1025cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // max number of elements in the second-level arrays. 1026cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var max = 0; 1027cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < this.shadowTable_.length; i++) { 1028cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[i].length > max) { 1029cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) max = this.shadowTable_[i].length; 1030cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1031cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1032cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return max; 1033cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1034cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1035cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1036cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1037cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Calculates the number of rows in the table. 1038cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {number} The number of rows in the table. 1039cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 1040cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1041cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.countRows_ = function() { 1042cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Number of rows in a table is equal to the number of TR elements contained 1043cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // by the (outer) TBODY elements. 1044cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var rowCount = cvox.TableUtil.getChildRows(this.activeTable_); 1045cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return rowCount.length; 1046cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1047cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1048cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1049cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1050cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Calculates the number of columns in the table. 1051cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * This uses the W3C recommended algorithm for calculating number of 1052cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * columns, but it does not take rowspans or colspans into account. This means 1053cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * that the number of columns calculated here might be lower than the actual 1054cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * number of columns in the table if columns are indicated by colspans. 1055cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {number} The number of columns in the table. 1056cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 1057cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1058cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.getW3CColCount_ = function() { 1059cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // See http://www.w3.org/TR/html401/struct/tables.html#h-11.2.4.3 1060cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1061cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var colgroupNodes = cvox.XpathUtil.evalXPath('child::colgroup', 1062cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.activeTable_); 1063cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var colNodes = cvox.XpathUtil.evalXPath('child::col', this.activeTable_); 1064cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1065cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((colgroupNodes.length == 0) && (colNodes.length == 0)) { 1066cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var maxcols = 0; 1067cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var outerChildren = cvox.TableUtil.getChildRows(this.activeTable_); 1068cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < outerChildren.length; i++) { 1069cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var childrenCount = cvox.TableUtil.getChildCells(outerChildren[i]); 1070cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (childrenCount.length > maxcols) { 1071cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) maxcols = childrenCount.length; 1072cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1073cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1074cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return maxcols; 1075cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1076cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var sum = 0; 1077cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < colNodes.length; i++) { 1078cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (colNodes[i].hasAttribute('span')) { 1079cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) sum += colNodes[i].getAttribute('span'); 1080cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1081cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) sum += 1; 1082cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1083cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1084cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (i = 0; i < colgroupNodes.length; i++) { 1085cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var colChildren = cvox.XpathUtil.evalXPath('child::col', 1086cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) colgroupNodes[i]); 1087cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (colChildren.length == 0) { 1088cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (colgroupNodes[i].hasAttribute('span')) { 1089cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) sum += colgroupNodes[i].getAttribute('span'); 1090cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1091cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) sum += 1; 1092cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1093cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1094cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1095cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1096cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return sum; 1097cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1098cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1099cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the next row in the table. Updates the cell cursor. 1102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the update has been made. 1105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the end of the table has been reached and the update has not 1106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * happened. 1107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.nextRow = function() { 1109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!this.currentCellCursor) { 1110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We have not started moving through the table yet 1111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.goToRow(0); 1112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.goToRow(this.currentCellCursor[0] + 1); 1114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the previous row in the table. Updates the cell cursor. 1121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the update has been made. 1124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the end of the table has been reached and the update has not 1125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * happened. 1126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.prevRow = function() { 1128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!this.currentCellCursor) { 1129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We have not started moving through the table yet 1130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.goToRow(this.rowCount - 1); 1131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.goToRow(this.currentCellCursor[0] - 1); 1133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the next column in the table. Updates the cell cursor. 1139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the update has been made. 1142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the end of the table has been reached and the update has not 1143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * happened. 1144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.nextCol = function() { 1146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!this.currentCellCursor) { 1147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We have not started moving through the table yet 1148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.goToCol(0); 1149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.goToCol(this.currentCellCursor[1] + 1); 1151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the previous column in the table. Updates the cell cursor. 1157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the update has been made. 1160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the end of the table has been reached and the update has not 1161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * happened. 1162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.prevCol = function() { 1164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!this.currentCellCursor) { 1165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We have not started moving through the table yet 1166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.goToCol(this.shadowColCount_() - 1); 1167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.goToCol(this.currentCellCursor[1] - 1); 1169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the row at the specified index in the table. Updates the cell 1175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * cursor. 1176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {number} index The index of the required row. 1177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the index is valid and the update has been made. 1179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the index is not valid (either less than 0 or greater than 1180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the number of rows in the table). 1181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.goToRow = function(index) { 1183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.shadowTable_[index] != null) { 1184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.currentCellCursor == null) { 1185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We haven't started moving through the table yet 1186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = [index, 0]; 1187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = [index, this.currentCellCursor[1]]; 1189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return true; 1191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 1193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1198cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the column at the specified index in the table. Updates the cell 1199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * cursor. 1200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {number} index The index of the required column. 1201cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1202cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the index is valid and the update has been made. 1203cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the index is not valid (either less than 0 or greater than 1204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the number of rows in the table). 1205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.goToCol = function(index) { 1207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (index < 0 || index >= this.colCount) { 1208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 1209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.currentCellCursor == null) { 1211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We haven't started moving through the table yet 1212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = [0, index]; 1213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 1214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = [this.currentCellCursor[0], index]; 1215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return true; 1217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1218cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1219cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1220cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1221cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the cell at the specified index <i, j> in the table. Updates the 1222cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * cell cursor. 1223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {Array.<number>} index The index <i, j> of the required cell. 1224cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1225cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the index is valid and the update has been made. 1226cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the index is not valid (either less than 0, greater than 1227cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the number of rows or columns in the table, or there is no cell 1228cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * at that location). 1229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1230cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.goToCell = function(index) { 1231cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (((index[0] < this.rowCount) && (index[0] >= 0)) && 1232cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ((index[1] < this.colCount) && (index[1] >= 0))) { 1233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var cell = this.shadowTable_[index[0]][index[1]]; 1234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (cell != null) { 1235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = index; 1236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return true; 1237cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 1240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the cell at the last index in the table. Updates the cell cursor. 1245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the index is valid and the update has been made. 1247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the index is not valid (there is no cell at that location). 1248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.goToLastCell = function() { 1250cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var numRows = this.shadowTable_.length; 1251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (numRows == 0) { 1252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 1253cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1254cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var lastRow = this.shadowTable_[numRows - 1]; 1255cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var lastIndex = [(numRows - 1), (lastRow.length - 1)]; 1256cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var cell = 1257cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[lastIndex[0]][lastIndex[1]]; 1258cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (cell != null) { 1259cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = lastIndex; 1260cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return true; 1261cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1262cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 1263cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1265cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1266cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1267cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the cell at the last index in the current row of the table. Update 1268cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the cell cursor. 1269cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1270cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the index is valid and the update has been made. 1271cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the index is not valid (there is no cell at that location). 1272cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1273cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.goToRowLastCell = function() { 1274cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentRow = this.currentCellCursor[0]; 1275cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var lastIndex = [currentRow, (this.shadowTable_[currentRow].length - 1)]; 1276cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var cell = 1277cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[lastIndex[0]][lastIndex[1]]; 1278cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (cell != null) { 1279cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = lastIndex; 1280cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return true; 1281cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1282cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 1283cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1284cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1285cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1286cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1287cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Moves to the cell at the last index in the current column of the table. 1288cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Update the cell cursor. 1289cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Either: 1290cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1) True if the index is valid and the update has been made. 1291cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 2) False if the index is not valid (there is no cell at that location). 1292cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1293cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.goToColLastCell = function() { 1294cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var currentCol = this.getCol(); 1295cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var lastIndex = [(currentCol.length - 1), this.currentCellCursor[1]]; 1296cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var cell = 1297cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.shadowTable_[lastIndex[0]][lastIndex[1]]; 1298cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (cell != null) { 1299cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = lastIndex; 1300cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return true; 1301cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1302cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 1303cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1304cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1305cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1306cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 1307cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Resets the table cursors. 1308cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1309cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 1310cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.TraverseTable.prototype.resetCursor = function() { 1311cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.currentCellCursor = null; 1312cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 1313