| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * TODO(stoarca): This class has become obsolete except for the shadow table. | 6 * TODO(stoarca): This class has become obsolete except for the shadow table. |
| 7 * Chop most of it away. | 7 * Chop most of it away. |
| 8 * @fileoverview A DOM traversal interface for navigating data in tables. | 8 * @fileoverview A DOM traversal interface for navigating data in tables. |
| 9 */ | 9 */ |
| 10 | 10 |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 this.colCount = this.shadowColCount_(); | 230 this.colCount = this.shadowColCount_(); |
| 231 this.rowCount = this.countRows_(); | 231 this.rowCount = this.countRows_(); |
| 232 | 232 |
| 233 this.findHeaderCells_(); | 233 this.findHeaderCells_(); |
| 234 | 234 |
| 235 // Listen for changes to the active table. If the active table changes, | 235 // Listen for changes to the active table. If the active table changes, |
| 236 // rebuild the shadow table. | 236 // rebuild the shadow table. |
| 237 // TODO (stoarca): Is this safe? When this object goes away, doesn't the | 237 // TODO (stoarca): Is this safe? When this object goes away, doesn't the |
| 238 // eventListener stay on the node? Someone with better knowledge of js | 238 // eventListener stay on the node? Someone with better knowledge of js |
| 239 // please confirm. If so, this is a leak. | 239 // please confirm. If so, this is a leak. |
| 240 this.activeTable_.addEventListener('DOMSubtreeModified', | 240 this.activeTable_.addEventListener( |
| 241 goog.bind(function() { | 241 'DOMSubtreeModified', goog.bind(function() { |
| 242 this.buildShadowTable_(); | 242 this.buildShadowTable_(); |
| 243 this.colCount = this.shadowColCount_(); | 243 this.colCount = this.shadowColCount_(); |
| 244 this.rowCount = this.countRows_(); | 244 this.rowCount = this.countRows_(); |
| 245 | 245 |
| 246 this.tableRowHeaders = []; | 246 this.tableRowHeaders = []; |
| 247 this.tableColHeaders = []; | 247 this.tableColHeaders = []; |
| 248 this.findHeaderCells_(); | 248 this.findHeaderCells_(); |
| 249 | 249 |
| 250 if (this.colCount == 0 && this.rowCount == 0) { | 250 if (this.colCount == 0 && this.rowCount == 0) { |
| 251 return; | 251 return; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 288 // DomUtil.findTableNodeInList excluding captions from tables because | 288 // DomUtil.findTableNodeInList excluding captions from tables because |
| 289 // it makes them non-contiguous. | 289 // it makes them non-contiguous. |
| 290 if (!cvox.DomUtil.getContainingTable(n, {allowCaptions: true})) { | 290 if (!cvox.DomUtil.getContainingTable(n, {allowCaptions: true})) { |
| 291 return null; | 291 return null; |
| 292 } | 292 } |
| 293 } | 293 } |
| 294 for (var i = 0; i < this.rowCount; ++i) { | 294 for (var i = 0; i < this.rowCount; ++i) { |
| 295 for (var j = 0; j < this.colCount; ++j) { | 295 for (var j = 0; j < this.colCount; ++j) { |
| 296 if (this.shadowTable_[i][j]) { | 296 if (this.shadowTable_[i][j]) { |
| 297 if (cvox.DomUtil.isDescendantOfNode( | 297 if (cvox.DomUtil.isDescendantOfNode( |
| 298 n, this.shadowTable_[i][j].activeCell)) { | 298 n, this.shadowTable_[i][j].activeCell)) { |
| 299 return [i, j]; | 299 return [i, j]; |
| 300 } | 300 } |
| 301 } | 301 } |
| 302 } | 302 } |
| 303 } | 303 } |
| 304 return null; | 304 return null; |
| 305 }; | 305 }; |
| 306 | 306 |
| 307 /** | 307 /** |
| 308 * Finds the valid cell nearest to the current cell cursor and moves the cell | 308 * Finds the valid cell nearest to the current cell cursor and moves the cell |
| (...skipping 18 matching lines...) Expand all Loading... |
| 327 this.currentCellCursor = [currentCursor[0], (currentRow.length - 1)]; | 327 this.currentCellCursor = [currentCursor[0], (currentRow.length - 1)]; |
| 328 } else { | 328 } else { |
| 329 // Current row does not exist anymore. Does current column still exist? | 329 // Current row does not exist anymore. Does current column still exist? |
| 330 // Try last cell of current column | 330 // Try last cell of current column |
| 331 var numRows = this.shadowTable_.length; | 331 var numRows = this.shadowTable_.length; |
| 332 if (numRows == 0) { | 332 if (numRows == 0) { |
| 333 // Table has been deleted! | 333 // Table has been deleted! |
| 334 this.currentCellCursor = null; | 334 this.currentCellCursor = null; |
| 335 return; | 335 return; |
| 336 } | 336 } |
| 337 var aboveCell = | 337 var aboveCell = this.shadowTable_[numRows - 1][currentCursor[1]]; |
| 338 this.shadowTable_[numRows - 1][currentCursor[1]]; | |
| 339 if (aboveCell) { | 338 if (aboveCell) { |
| 340 this.currentCellCursor = [(numRows - 1), currentCursor[1]]; | 339 this.currentCellCursor = [(numRows - 1), currentCursor[1]]; |
| 341 } else { | 340 } else { |
| 342 // Current column does not exist anymore either. | 341 // Current column does not exist anymore either. |
| 343 // Move cursor to last cell in table. | 342 // Move cursor to last cell in table. |
| 344 this.goToLastCell(); | 343 this.goToLastCell(); |
| 345 } | 344 } |
| 346 } | 345 } |
| 347 }; | 346 }; |
| 348 | 347 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 372 // Iterate through active table by row | 371 // Iterate through active table by row |
| 373 for (var i = 0; i < allRows.length; i++) { | 372 for (var i = 0; i < allRows.length; i++) { |
| 374 var childCells = cvox.TableUtil.getChildCells(allRows[i]); | 373 var childCells = cvox.TableUtil.getChildCells(allRows[i]); |
| 375 | 374 |
| 376 // Keep track of position in active table | 375 // Keep track of position in active table |
| 377 var activeTableCol = 0; | 376 var activeTableCol = 0; |
| 378 // Keep track of position in shadow table | 377 // Keep track of position in shadow table |
| 379 var shadowTableCol = 0; | 378 var shadowTableCol = 0; |
| 380 | 379 |
| 381 while (activeTableCol < childCells.length) { | 380 while (activeTableCol < childCells.length) { |
| 382 | |
| 383 // Check to make sure we haven't already filled this cell. | 381 // Check to make sure we haven't already filled this cell. |
| 384 if (this.shadowTable_[i][shadowTableCol] == null) { | 382 if (this.shadowTable_[i][shadowTableCol] == null) { |
| 385 | |
| 386 var activeTableCell = childCells[activeTableCol]; | 383 var activeTableCell = childCells[activeTableCol]; |
| 387 | 384 |
| 388 // Default value for colspan and rowspan is 1 | 385 // Default value for colspan and rowspan is 1 |
| 389 var colsSpanned = 1; | 386 var colsSpanned = 1; |
| 390 var rowsSpanned = 1; | 387 var rowsSpanned = 1; |
| 391 | 388 |
| 392 if (activeTableCell.hasAttribute('colspan')) { | 389 if (activeTableCell.hasAttribute('colspan')) { |
| 393 | 390 colsSpanned = parseInt(activeTableCell.getAttribute('colspan'), 10); |
| 394 colsSpanned = | |
| 395 parseInt(activeTableCell.getAttribute('colspan'), 10); | |
| 396 | 391 |
| 397 if ((isNaN(colsSpanned)) || (colsSpanned <= 0)) { | 392 if ((isNaN(colsSpanned)) || (colsSpanned <= 0)) { |
| 398 // The HTML5 spec defines colspan MUST be greater than 0: | 393 // The HTML5 spec defines colspan MUST be greater than 0: |
| 399 // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-colspan | 394 // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-colspan |
| 400 // | 395 // |
| 401 // This is a change from the HTML4 spec: | 396 // This is a change from the HTML4 spec: |
| 402 // http://www.w3.org/TR/html401/struct/tables.html#adef-colspan | 397 // http://www.w3.org/TR/html401/struct/tables.html#adef-colspan |
| 403 // | 398 // |
| 404 // We will degrade gracefully by treating a colspan=0 as | 399 // We will degrade gracefully by treating a colspan=0 as |
| 405 // equivalent to a colspan=1. | 400 // equivalent to a colspan=1. |
| 406 // Tested in method testColSpan0 in rowColSpanTable_test.js | 401 // Tested in method testColSpan0 in rowColSpanTable_test.js |
| 407 colsSpanned = 1; | 402 colsSpanned = 1; |
| 408 } | 403 } |
| 409 } | 404 } |
| 410 if (activeTableCell.hasAttribute('rowspan')) { | 405 if (activeTableCell.hasAttribute('rowspan')) { |
| 411 rowsSpanned = | 406 rowsSpanned = parseInt(activeTableCell.getAttribute('rowspan'), 10); |
| 412 parseInt(activeTableCell.getAttribute('rowspan'), 10); | |
| 413 | 407 |
| 414 if ((isNaN(rowsSpanned)) || (rowsSpanned <= 0)) { | 408 if ((isNaN(rowsSpanned)) || (rowsSpanned <= 0)) { |
| 415 // The HTML5 spec defines that rowspan can be any non-negative | 409 // The HTML5 spec defines that rowspan can be any non-negative |
| 416 // integer, including 0: | 410 // integer, including 0: |
| 417 // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-rowspan | 411 // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-rowspan |
| 418 // | 412 // |
| 419 // However, Chromium treats rowspan=0 as rowspan=1. This appears | 413 // However, Chromium treats rowspan=0 as rowspan=1. This appears |
| 420 // to be a bug from WebKit: | 414 // to be a bug from WebKit: |
| 421 // https://bugs.webkit.org/show_bug.cgi?id=10300 | 415 // https://bugs.webkit.org/show_bug.cgi?id=10300 |
| 422 // Inherited from a bug (since fixed) in KDE: | 416 // Inherited from a bug (since fixed) in KDE: |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 481 } | 475 } |
| 482 shadowNode.rowGroup = currentRowGroup; | 476 shadowNode.rowGroup = currentRowGroup; |
| 483 | 477 |
| 484 // Check and update col group status | 478 // Check and update col group status |
| 485 if (colToColGroup.length > 0) { | 479 if (colToColGroup.length > 0) { |
| 486 shadowNode.colGroup = colToColGroup[shadowTableCol]; | 480 shadowNode.colGroup = colToColGroup[shadowTableCol]; |
| 487 } else { | 481 } else { |
| 488 shadowNode.colGroup = 0; | 482 shadowNode.colGroup = 0; |
| 489 } | 483 } |
| 490 | 484 |
| 491 if (! shadowNode.spanned) { | 485 if (!shadowNode.spanned) { |
| 492 if (activeTableCell.id != null) { | 486 if (activeTableCell.id != null) { |
| 493 this.idToShadowNode_[activeTableCell.id] = shadowNode; | 487 this.idToShadowNode_[activeTableCell.id] = shadowNode; |
| 494 } | 488 } |
| 495 } | 489 } |
| 496 | 490 |
| 497 this.shadowTable_[i + r][shadowTableCol + c] = shadowNode; | 491 this.shadowTable_[i + r][shadowTableCol + c] = shadowNode; |
| 498 } | 492 } |
| 499 } | 493 } |
| 500 shadowTableCol += colsSpanned; | 494 shadowTableCol += colsSpanned; |
| 501 activeTableCol++; | 495 activeTableCol++; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 519 * rowHeaderCells or colHeaderCells arrays. | 513 * rowHeaderCells or colHeaderCells arrays. |
| 520 * | 514 * |
| 521 * @private | 515 * @private |
| 522 */ | 516 */ |
| 523 cvox.TraverseTable.prototype.findHeaderCells_ = function() { | 517 cvox.TraverseTable.prototype.findHeaderCells_ = function() { |
| 524 // Forming relationships between data cells and header cells: | 518 // Forming relationships between data cells and header cells: |
| 525 // http://dev.w3.org/html5/spec/tabular-data.html | 519 // http://dev.w3.org/html5/spec/tabular-data.html |
| 526 // #header-and-data-cell-semantics | 520 // #header-and-data-cell-semantics |
| 527 | 521 |
| 528 for (var i = 0; i < this.candidateHeaders_.length; i++) { | 522 for (var i = 0; i < this.candidateHeaders_.length; i++) { |
| 529 | |
| 530 var currentShadowNode = this.candidateHeaders_[i]; | 523 var currentShadowNode = this.candidateHeaders_[i]; |
| 531 var currentCell = currentShadowNode.activeCell; | 524 var currentCell = currentShadowNode.activeCell; |
| 532 | 525 |
| 533 var assumedScope = null; | 526 var assumedScope = null; |
| 534 var specifiedScope = null; | 527 var specifiedScope = null; |
| 535 | 528 |
| 536 if (currentShadowNode.spanned) { | 529 if (currentShadowNode.spanned) { |
| 537 continue; | 530 continue; |
| 538 } | 531 } |
| 539 | 532 |
| 540 if ((currentCell.tagName == 'TH') && | 533 if ((currentCell.tagName == 'TH') && !(currentCell.hasAttribute('scope'))) { |
| 541 !(currentCell.hasAttribute('scope'))) { | |
| 542 // No scope specified - compute scope ourselves. | 534 // No scope specified - compute scope ourselves. |
| 543 // Go left/right - if there's a header node, then this is a column | 535 // Go left/right - if there's a header node, then this is a column |
| 544 // header | 536 // header |
| 545 if (currentShadowNode.j > 0) { | 537 if (currentShadowNode.j > 0) { |
| 546 if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j - 1]. | 538 if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j - 1] |
| 547 activeCell.tagName == 'TH') { | 539 .activeCell.tagName == 'TH') { |
| 548 assumedScope = 'col'; | 540 assumedScope = 'col'; |
| 549 } | 541 } |
| 550 } else if (currentShadowNode.j < this.shadowTable_[currentShadowNode.i]. | 542 } else if ( |
| 551 length - 1) { | 543 currentShadowNode.j < |
| 552 if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j + 1]. | 544 this.shadowTable_[currentShadowNode.i].length - 1) { |
| 553 activeCell.tagName == 'TH') { | 545 if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j + 1] |
| 546 .activeCell.tagName == 'TH') { |
| 554 assumedScope = 'col'; | 547 assumedScope = 'col'; |
| 555 } | 548 } |
| 556 } else { | 549 } else { |
| 557 // This row has a width of 1 cell, just assume this is a colum header | 550 // This row has a width of 1 cell, just assume this is a colum header |
| 558 assumedScope = 'col'; | 551 assumedScope = 'col'; |
| 559 } | 552 } |
| 560 | 553 |
| 561 if (assumedScope == null) { | 554 if (assumedScope == null) { |
| 562 // Go up/down - if there's a header node, then this is a row header | 555 // Go up/down - if there's a header node, then this is a row header |
| 563 if (currentShadowNode.i > 0) { | 556 if (currentShadowNode.i > 0) { |
| 564 if (this.shadowTable_[currentShadowNode.i - 1][currentShadowNode.j]. | 557 if (this.shadowTable_[currentShadowNode.i - 1][currentShadowNode.j] |
| 565 activeCell.tagName == 'TH') { | 558 .activeCell.tagName == 'TH') { |
| 566 assumedScope = 'row'; | 559 assumedScope = 'row'; |
| 567 } | 560 } |
| 568 } else if (currentShadowNode.i < this.shadowTable_.length - 1) { | 561 } else if (currentShadowNode.i < this.shadowTable_.length - 1) { |
| 569 if (this.shadowTable_[currentShadowNode.i + 1][currentShadowNode.j]. | 562 if (this.shadowTable_[currentShadowNode.i + 1][currentShadowNode.j] |
| 570 activeCell.tagName == 'TH') { | 563 .activeCell.tagName == 'TH') { |
| 571 assumedScope = 'row'; | 564 assumedScope = 'row'; |
| 572 } | 565 } |
| 573 } else { | 566 } else { |
| 574 // This column has a height of 1 cell, just assume that this is | 567 // This column has a height of 1 cell, just assume that this is |
| 575 // a row header | 568 // a row header |
| 576 assumedScope = 'row'; | 569 assumedScope = 'row'; |
| 577 } | 570 } |
| 578 } | 571 } |
| 579 } else if (currentCell.hasAttribute('scope')) { | 572 } else if (currentCell.hasAttribute('scope')) { |
| 580 specifiedScope = currentCell.getAttribute('scope'); | 573 specifiedScope = currentCell.getAttribute('scope'); |
| 581 } else if (currentCell.hasAttribute('role') && | 574 } else if ( |
| 575 currentCell.hasAttribute('role') && |
| 582 (currentCell.getAttribute('role') == 'rowheader')) { | 576 (currentCell.getAttribute('role') == 'rowheader')) { |
| 583 specifiedScope = 'row'; | 577 specifiedScope = 'row'; |
| 584 } else if (currentCell.hasAttribute('role') && | 578 } else if ( |
| 579 currentCell.hasAttribute('role') && |
| 585 (currentCell.getAttribute('role') == 'columnheader')) { | 580 (currentCell.getAttribute('role') == 'columnheader')) { |
| 586 specifiedScope = 'col'; | 581 specifiedScope = 'col'; |
| 587 } | 582 } |
| 588 | 583 |
| 589 if ((specifiedScope == 'row') || (assumedScope == 'row')) { | 584 if ((specifiedScope == 'row') || (assumedScope == 'row')) { |
| 590 currentShadowNode.isRowHeader = true; | 585 currentShadowNode.isRowHeader = true; |
| 591 | 586 |
| 592 // Go right until you hit the edge of the table or a data | 587 // Go right until you hit the edge of the table or a data |
| 593 // cell after another header cell. | 588 // cell after another header cell. |
| 594 // Add this cell to each shadowNode.rowHeaderCells attribute as you go. | 589 // Add this cell to each shadowNode.rowHeaderCells attribute as you go. |
| 595 for (var rightCtr = currentShadowNode.j; | 590 for (var rightCtr = currentShadowNode.j; |
| 596 rightCtr < this.shadowTable_[currentShadowNode.i].length; | 591 rightCtr < this.shadowTable_[currentShadowNode.i].length; |
| 597 rightCtr++) { | 592 rightCtr++) { |
| 598 | |
| 599 var rightShadowNode = this.shadowTable_[currentShadowNode.i][rightCtr]; | 593 var rightShadowNode = this.shadowTable_[currentShadowNode.i][rightCtr]; |
| 600 var rightCell = rightShadowNode.activeCell; | 594 var rightCell = rightShadowNode.activeCell; |
| 601 | 595 |
| 602 if ((rightCell.tagName == 'TH') || | 596 if ((rightCell.tagName == 'TH') || (rightCell.hasAttribute('scope'))) { |
| 603 (rightCell.hasAttribute('scope'))) { | |
| 604 | |
| 605 if (rightCtr < this.shadowTable_[currentShadowNode.i].length - 1) { | 597 if (rightCtr < this.shadowTable_[currentShadowNode.i].length - 1) { |
| 606 var checkDataCell = | 598 var checkDataCell = |
| 607 this.shadowTable_[currentShadowNode.i][rightCtr + 1]; | 599 this.shadowTable_[currentShadowNode.i][rightCtr + 1]; |
| 608 } | 600 } |
| 609 } | 601 } |
| 610 rightShadowNode.rowHeaderCells.push(currentCell); | 602 rightShadowNode.rowHeaderCells.push(currentCell); |
| 611 } | 603 } |
| 612 this.tableRowHeaders.push(currentCell); | 604 this.tableRowHeaders.push(currentCell); |
| 613 } else if ((specifiedScope == 'col') || (assumedScope == 'col')) { | 605 } else if ((specifiedScope == 'col') || (assumedScope == 'col')) { |
| 614 currentShadowNode.isColHeader = true; | 606 currentShadowNode.isColHeader = true; |
| 615 | 607 |
| 616 // Go down until you hit the edge of the table or a data cell | 608 // Go down until you hit the edge of the table or a data cell |
| 617 // after another header cell. | 609 // after another header cell. |
| 618 // Add this cell to each shadowNode.colHeaders attribute as you go. | 610 // Add this cell to each shadowNode.colHeaders attribute as you go. |
| 619 | 611 |
| 620 for (var downCtr = currentShadowNode.i; | 612 for (var downCtr = currentShadowNode.i; |
| 621 downCtr < this.shadowTable_.length; | 613 downCtr < this.shadowTable_.length; downCtr++) { |
| 622 downCtr++) { | |
| 623 | |
| 624 var downShadowNode = this.shadowTable_[downCtr][currentShadowNode.j]; | 614 var downShadowNode = this.shadowTable_[downCtr][currentShadowNode.j]; |
| 625 if (downShadowNode == null) { | 615 if (downShadowNode == null) { |
| 626 break; | 616 break; |
| 627 } | 617 } |
| 628 var downCell = downShadowNode.activeCell; | 618 var downCell = downShadowNode.activeCell; |
| 629 | 619 |
| 630 if ((downCell.tagName == 'TH') || | 620 if ((downCell.tagName == 'TH') || (downCell.hasAttribute('scope'))) { |
| 631 (downCell.hasAttribute('scope'))) { | |
| 632 | |
| 633 if (downCtr < this.shadowTable_.length - 1) { | 621 if (downCtr < this.shadowTable_.length - 1) { |
| 634 var checkDataCell = | 622 var checkDataCell = |
| 635 this.shadowTable_[downCtr + 1][currentShadowNode.j]; | 623 this.shadowTable_[downCtr + 1][currentShadowNode.j]; |
| 636 } | 624 } |
| 637 } | 625 } |
| 638 downShadowNode.colHeaderCells.push(currentCell); | 626 downShadowNode.colHeaderCells.push(currentCell); |
| 639 } | 627 } |
| 640 this.tableColHeaders.push(currentCell); | 628 this.tableColHeaders.push(currentCell); |
| 641 } else if (specifiedScope == 'rowgroup') { | 629 } else if (specifiedScope == 'rowgroup') { |
| 642 currentShadowNode.isRowHeader = true; | 630 currentShadowNode.isRowHeader = true; |
| 643 | 631 |
| 644 // This cell is a row header for the rest of the cells in this row group. | 632 // This cell is a row header for the rest of the cells in this row group. |
| 645 var currentRowGroup = currentShadowNode.rowGroup; | 633 var currentRowGroup = currentShadowNode.rowGroup; |
| 646 | 634 |
| 647 // Get the rest of the cells in this row first | 635 // Get the rest of the cells in this row first |
| 648 for (var cellsInRow = currentShadowNode.j + 1; | 636 for (var cellsInRow = currentShadowNode.j + 1; |
| 649 cellsInRow < this.shadowTable_[currentShadowNode.i].length; | 637 cellsInRow < this.shadowTable_[currentShadowNode.i].length; |
| 650 cellsInRow++) { | 638 cellsInRow++) { |
| 651 this.shadowTable_[currentShadowNode.i][cellsInRow]. | 639 this.shadowTable_[currentShadowNode.i][cellsInRow].rowHeaderCells.push( |
| 652 rowHeaderCells.push(currentCell); | 640 currentCell); |
| 653 } | 641 } |
| 654 | 642 |
| 655 // Now propagate to rest of row group | 643 // Now propagate to rest of row group |
| 656 for (var downCtr = currentShadowNode.i + 1; | 644 for (var downCtr = currentShadowNode.i + 1; |
| 657 downCtr < this.shadowTable_.length; | 645 downCtr < this.shadowTable_.length; downCtr++) { |
| 658 downCtr++) { | |
| 659 | |
| 660 if (this.shadowTable_[downCtr][0].rowGroup != currentRowGroup) { | 646 if (this.shadowTable_[downCtr][0].rowGroup != currentRowGroup) { |
| 661 break; | 647 break; |
| 662 } | 648 } |
| 663 | 649 |
| 664 for (var rightCtr = 0; | 650 for (var rightCtr = 0; rightCtr < this.shadowTable_[downCtr].length; |
| 665 rightCtr < this.shadowTable_[downCtr].length; | |
| 666 rightCtr++) { | 651 rightCtr++) { |
| 667 | 652 this.shadowTable_[downCtr][rightCtr].rowHeaderCells.push(currentCell); |
| 668 this.shadowTable_[downCtr][rightCtr]. | |
| 669 rowHeaderCells.push(currentCell); | |
| 670 } | 653 } |
| 671 } | 654 } |
| 672 this.tableRowHeaders.push(currentCell); | 655 this.tableRowHeaders.push(currentCell); |
| 673 | 656 |
| 674 } else if (specifiedScope == 'colgroup') { | 657 } else if (specifiedScope == 'colgroup') { |
| 675 currentShadowNode.isColHeader = true; | 658 currentShadowNode.isColHeader = true; |
| 676 | 659 |
| 677 // This cell is a col header for the rest of the cells in this col group. | 660 // This cell is a col header for the rest of the cells in this col group. |
| 678 var currentColGroup = currentShadowNode.colGroup; | 661 var currentColGroup = currentShadowNode.colGroup; |
| 679 | 662 |
| 680 // Get the rest of the cells in this colgroup first | 663 // Get the rest of the cells in this colgroup first |
| 681 for (var cellsInCol = currentShadowNode.j + 1; | 664 for (var cellsInCol = currentShadowNode.j + 1; |
| 682 cellsInCol < this.shadowTable_[currentShadowNode.i].length; | 665 cellsInCol < this.shadowTable_[currentShadowNode.i].length; |
| 683 cellsInCol++) { | 666 cellsInCol++) { |
| 684 if (this.shadowTable_[currentShadowNode.i][cellsInCol].colGroup == | 667 if (this.shadowTable_[currentShadowNode.i][cellsInCol].colGroup == |
| 685 currentColGroup) { | 668 currentColGroup) { |
| 686 this.shadowTable_[currentShadowNode.i][cellsInCol]. | 669 this.shadowTable_[currentShadowNode.i][cellsInCol] |
| 687 colHeaderCells.push(currentCell); | 670 .colHeaderCells.push(currentCell); |
| 688 } | 671 } |
| 689 } | 672 } |
| 690 | 673 |
| 691 // Now propagate to rest of col group | 674 // Now propagate to rest of col group |
| 692 for (var downCtr = currentShadowNode.i + 1; | 675 for (var downCtr = currentShadowNode.i + 1; |
| 693 downCtr < this.shadowTable_.length; | 676 downCtr < this.shadowTable_.length; downCtr++) { |
| 694 downCtr++) { | 677 for (var rightCtr = 0; rightCtr < this.shadowTable_[downCtr].length; |
| 695 | |
| 696 for (var rightCtr = 0; | |
| 697 rightCtr < this.shadowTable_[downCtr].length; | |
| 698 rightCtr++) { | 678 rightCtr++) { |
| 699 | |
| 700 if (this.shadowTable_[downCtr][rightCtr].colGroup == | 679 if (this.shadowTable_[downCtr][rightCtr].colGroup == |
| 701 currentColGroup) { | 680 currentColGroup) { |
| 702 this.shadowTable_[downCtr][rightCtr]. | 681 this.shadowTable_[downCtr][rightCtr].colHeaderCells.push( |
| 703 colHeaderCells.push(currentCell); | 682 currentCell); |
| 704 } | 683 } |
| 705 } | 684 } |
| 706 } | 685 } |
| 707 this.tableColHeaders.push(currentCell); | 686 this.tableColHeaders.push(currentCell); |
| 708 } | 687 } |
| 709 if (currentCell.hasAttribute('headers')) { | 688 if (currentCell.hasAttribute('headers')) { |
| 710 this.findAttrbHeaders_(currentShadowNode); | 689 this.findAttrbHeaders_(currentShadowNode); |
| 711 } | 690 } |
| 712 if (currentCell.hasAttribute('aria-describedby')) { | 691 if (currentCell.hasAttribute('aria-describedby')) { |
| 713 this.findAttrbDescribedBy_(currentShadowNode); | 692 this.findAttrbDescribedBy_(currentShadowNode); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 732 * that has a 'headers' attribute. | 711 * that has a 'headers' attribute. |
| 733 * | 712 * |
| 734 * @private | 713 * @private |
| 735 */ | 714 */ |
| 736 cvox.TraverseTable.prototype.findAttrbHeaders_ = function(currentShadowNode) { | 715 cvox.TraverseTable.prototype.findAttrbHeaders_ = function(currentShadowNode) { |
| 737 var activeTableCell = currentShadowNode.activeCell; | 716 var activeTableCell = currentShadowNode.activeCell; |
| 738 | 717 |
| 739 var idList = activeTableCell.getAttribute('headers').split(' '); | 718 var idList = activeTableCell.getAttribute('headers').split(' '); |
| 740 for (var idToken = 0; idToken < idList.length; idToken++) { | 719 for (var idToken = 0; idToken < idList.length; idToken++) { |
| 741 // Find cell(s) with this ID, add to header list | 720 // Find cell(s) with this ID, add to header list |
| 742 var idCellArray = cvox.TableUtil.getCellWithID(this.activeTable_, | 721 var idCellArray = |
| 743 idList[idToken]); | 722 cvox.TableUtil.getCellWithID(this.activeTable_, idList[idToken]); |
| 744 | 723 |
| 745 for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) { | 724 for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) { |
| 746 if (idCellArray[idCtr].id == activeTableCell.id) { | 725 if (idCellArray[idCtr].id == activeTableCell.id) { |
| 747 // Skip if the ID is the same as the current cell's ID | 726 // Skip if the ID is the same as the current cell's ID |
| 748 break; | 727 break; |
| 749 } | 728 } |
| 750 // Check if this list of candidate headers contains a | 729 // Check if this list of candidate headers contains a |
| 751 // shadowNode with an active cell with this ID already | 730 // shadowNode with an active cell with this ID already |
| 752 var possibleHeaderNode = | 731 var possibleHeaderNode = this.idToShadowNode_[idCellArray[idCtr].id]; |
| 753 this.idToShadowNode_[idCellArray[idCtr].id]; | 732 if (!cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) { |
| 754 if (! cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) { | |
| 755 // This listed header cell will not be handled later. | 733 // This listed header cell will not be handled later. |
| 756 // Determine whether this is a row or col header for | 734 // Determine whether this is a row or col header for |
| 757 // the active table cell | 735 // the active table cell |
| 758 | 736 |
| 759 var iDiff = Math.abs(possibleHeaderNode.i - currentShadowNode.i); | 737 var iDiff = Math.abs(possibleHeaderNode.i - currentShadowNode.i); |
| 760 var jDiff = Math.abs(possibleHeaderNode.j - currentShadowNode.j); | 738 var jDiff = Math.abs(possibleHeaderNode.j - currentShadowNode.j); |
| 761 if ((iDiff == 0) || (iDiff < jDiff)) { | 739 if ((iDiff == 0) || (iDiff < jDiff)) { |
| 762 cvox.TableUtil.pushIfNotContained(currentShadowNode.rowHeaderCells, | 740 cvox.TableUtil.pushIfNotContained( |
| 763 possibleHeaderNode.activeCell); | 741 currentShadowNode.rowHeaderCells, possibleHeaderNode.activeCell); |
| 764 cvox.TableUtil.pushIfNotContained(this.tableRowHeaders, | 742 cvox.TableUtil.pushIfNotContained( |
| 765 possibleHeaderNode.activeCell); | 743 this.tableRowHeaders, possibleHeaderNode.activeCell); |
| 766 } else { | 744 } else { |
| 767 // This is a column header | 745 // This is a column header |
| 768 cvox.TableUtil.pushIfNotContained(currentShadowNode.colHeaderCells, | 746 cvox.TableUtil.pushIfNotContained( |
| 769 possibleHeaderNode.activeCell); | 747 currentShadowNode.colHeaderCells, possibleHeaderNode.activeCell); |
| 770 cvox.TableUtil.pushIfNotContained(this.tableColHeaders, | 748 cvox.TableUtil.pushIfNotContained( |
| 771 possibleHeaderNode.activeCell); | 749 this.tableColHeaders, possibleHeaderNode.activeCell); |
| 772 } | 750 } |
| 773 } | 751 } |
| 774 } | 752 } |
| 775 } | 753 } |
| 776 }; | 754 }; |
| 777 | 755 |
| 778 | 756 |
| 779 /** | 757 /** |
| 780 * Finds header cells from the 'aria-describedby' attribute of a given shadow | 758 * Finds header cells from the 'aria-describedby' attribute of a given shadow |
| 781 * node's active cell and classifies them in two ways: | 759 * node's active cell and classifies them in two ways: |
| 782 * -- Identifies them for the entire table by adding them to | 760 * -- Identifies them for the entire table by adding them to |
| 783 * this.tableRowHeaders and this.tableColHeaders. | 761 * this.tableRowHeaders and this.tableColHeaders. |
| 784 * -- Identifies them for the shadow table node by adding them to the node's | 762 * -- Identifies them for the shadow table node by adding them to the node's |
| 785 * rowHeaderCells or colHeaderCells arrays. | 763 * rowHeaderCells or colHeaderCells arrays. |
| 786 * | 764 * |
| 787 * Please note that header cells found through the 'aria-describedby' attribute | 765 * Please note that header cells found through the 'aria-describedby' attribute |
| 788 * must have the role='rowheader' or role='columnheader' attributes in order to | 766 * must have the role='rowheader' or role='columnheader' attributes in order to |
| 789 * be considered header cells. | 767 * be considered header cells. |
| 790 * | 768 * |
| 791 * @param {ShadowTableNode} currentShadowNode A shadow node with an active cell | 769 * @param {ShadowTableNode} currentShadowNode A shadow node with an active cell |
| 792 * that has an 'aria-describedby' attribute. | 770 * that has an 'aria-describedby' attribute. |
| 793 * | 771 * |
| 794 * @private | 772 * @private |
| 795 */ | 773 */ |
| 796 cvox.TraverseTable.prototype.findAttrbDescribedBy_ = | 774 cvox.TraverseTable.prototype.findAttrbDescribedBy_ = function( |
| 797 function(currentShadowNode) { | 775 currentShadowNode) { |
| 798 var activeTableCell = currentShadowNode.activeCell; | 776 var activeTableCell = currentShadowNode.activeCell; |
| 799 | 777 |
| 800 var idList = activeTableCell.getAttribute('aria-describedby').split(' '); | 778 var idList = activeTableCell.getAttribute('aria-describedby').split(' '); |
| 801 for (var idToken = 0; idToken < idList.length; idToken++) { | 779 for (var idToken = 0; idToken < idList.length; idToken++) { |
| 802 // Find cell(s) with this ID, add to header list | 780 // Find cell(s) with this ID, add to header list |
| 803 var idCellArray = cvox.TableUtil.getCellWithID(this.activeTable_, | 781 var idCellArray = |
| 804 idList[idToken]); | 782 cvox.TableUtil.getCellWithID(this.activeTable_, idList[idToken]); |
| 805 | 783 |
| 806 for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) { | 784 for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) { |
| 807 if (idCellArray[idCtr].id == activeTableCell.id) { | 785 if (idCellArray[idCtr].id == activeTableCell.id) { |
| 808 // Skip if the ID is the same as the current cell's ID | 786 // Skip if the ID is the same as the current cell's ID |
| 809 break; | 787 break; |
| 810 } | 788 } |
| 811 // Check if this list of candidate headers contains a | 789 // Check if this list of candidate headers contains a |
| 812 // shadowNode with an active cell with this ID already | 790 // shadowNode with an active cell with this ID already |
| 813 var possibleHeaderNode = | 791 var possibleHeaderNode = this.idToShadowNode_[idCellArray[idCtr].id]; |
| 814 this.idToShadowNode_[idCellArray[idCtr].id]; | 792 if (!cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) { |
| 815 if (! cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) { | |
| 816 // This listed header cell will not be handled later. | 793 // This listed header cell will not be handled later. |
| 817 // Determine whether this is a row or col header for | 794 // Determine whether this is a row or col header for |
| 818 // the active table cell | 795 // the active table cell |
| 819 | 796 |
| 820 if (possibleHeaderNode.activeCell.hasAttribute('role') && | 797 if (possibleHeaderNode.activeCell.hasAttribute('role') && |
| 821 (possibleHeaderNode.activeCell.getAttribute('role') == | 798 (possibleHeaderNode.activeCell.getAttribute('role') == |
| 822 'rowheader')) { | 799 'rowheader')) { |
| 823 cvox.TableUtil.pushIfNotContained(currentShadowNode.rowHeaderCells, | 800 cvox.TableUtil.pushIfNotContained( |
| 824 possibleHeaderNode.activeCell); | 801 currentShadowNode.rowHeaderCells, possibleHeaderNode.activeCell); |
| 825 cvox.TableUtil.pushIfNotContained(this.tableRowHeaders, | 802 cvox.TableUtil.pushIfNotContained( |
| 826 possibleHeaderNode.activeCell); | 803 this.tableRowHeaders, possibleHeaderNode.activeCell); |
| 827 } else if (possibleHeaderNode.activeCell.hasAttribute('role') && | 804 } else if ( |
| 805 possibleHeaderNode.activeCell.hasAttribute('role') && |
| 828 (possibleHeaderNode.activeCell.getAttribute('role') == | 806 (possibleHeaderNode.activeCell.getAttribute('role') == |
| 829 'columnheader')) { | 807 'columnheader')) { |
| 830 cvox.TableUtil.pushIfNotContained(currentShadowNode.colHeaderCells, | 808 cvox.TableUtil.pushIfNotContained( |
| 831 possibleHeaderNode.activeCell); | 809 currentShadowNode.colHeaderCells, possibleHeaderNode.activeCell); |
| 832 cvox.TableUtil.pushIfNotContained(this.tableColHeaders, | 810 cvox.TableUtil.pushIfNotContained( |
| 833 possibleHeaderNode.activeCell); | 811 this.tableColHeaders, possibleHeaderNode.activeCell); |
| 834 } | 812 } |
| 835 } | 813 } |
| 836 } | 814 } |
| 837 } | 815 } |
| 838 }; | 816 }; |
| 839 | 817 |
| 840 | 818 |
| 841 /** | 819 /** |
| 842 * Gets the current cell or null if there is no current cell. | 820 * Gets the current cell or null if there is no current cell. |
| 843 * @return {?Node} The cell <TD> or <TH> or role='gridcell' node. | 821 * @return {?Node} The cell <TD> or <TH> or role='gridcell' node. |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 936 | 914 |
| 937 /** | 915 /** |
| 938 * Gets the active column, represented as an array of <TH> or <TD> nodes that | 916 * Gets the active column, represented as an array of <TH> or <TD> nodes that |
| 939 * make up a column. In this context, "active" means that this is the column | 917 * make up a column. In this context, "active" means that this is the column |
| 940 * that contains the cell the user is currently looking at. | 918 * that contains the cell the user is currently looking at. |
| 941 * @return {Array} An array of <TH> or <TD> or role='gridcell' nodes. | 919 * @return {Array} An array of <TH> or <TD> or role='gridcell' nodes. |
| 942 */ | 920 */ |
| 943 cvox.TraverseTable.prototype.getCol = function() { | 921 cvox.TraverseTable.prototype.getCol = function() { |
| 944 var colArray = []; | 922 var colArray = []; |
| 945 for (var i = 0; i < this.shadowTable_.length; i++) { | 923 for (var i = 0; i < this.shadowTable_.length; i++) { |
| 946 | |
| 947 if (this.shadowTable_[i][this.currentCellCursor[1]]) { | 924 if (this.shadowTable_[i][this.currentCellCursor[1]]) { |
| 948 var shadowEntry = this.shadowTable_[i][this.currentCellCursor[1]]; | 925 var shadowEntry = this.shadowTable_[i][this.currentCellCursor[1]]; |
| 949 | 926 |
| 950 if (shadowEntry.colSpan && shadowEntry.rowSpan) { | 927 if (shadowEntry.colSpan && shadowEntry.rowSpan) { |
| 951 // Look at the last element in the column cell aray. | 928 // Look at the last element in the column cell aray. |
| 952 var prev = colArray[colArray.length - 1]; | 929 var prev = colArray[colArray.length - 1]; |
| 953 if (prev != | 930 if (prev != shadowEntry.activeCell) { |
| 954 shadowEntry.activeCell) { | |
| 955 // Watch out for positions spanned by a cell with rowspan and | 931 // Watch out for positions spanned by a cell with rowspan and |
| 956 // colspan. We don't want the same cell showing up multiple times | 932 // colspan. We don't want the same cell showing up multiple times |
| 957 // in per-column cell lists. | 933 // in per-column cell lists. |
| 958 colArray.push( | 934 colArray.push(shadowEntry.activeCell); |
| 959 shadowEntry.activeCell); | |
| 960 } | 935 } |
| 961 } else if ((shadowEntry.colSpan) || (!shadowEntry.rowSpan)) { | 936 } else if ((shadowEntry.colSpan) || (!shadowEntry.rowSpan)) { |
| 962 colArray.push( | 937 colArray.push(shadowEntry.activeCell); |
| 963 shadowEntry.activeCell); | |
| 964 } | 938 } |
| 965 } | 939 } |
| 966 } | 940 } |
| 967 return colArray; | 941 return colArray; |
| 968 }; | 942 }; |
| 969 | 943 |
| 970 | 944 |
| 971 /** | 945 /** |
| 972 * Gets the active row <TR> node. In this context, "active" means that this is | 946 * Gets the active row <TR> node. In this context, "active" means that this is |
| 973 * the row that contains the cell the user is currently looking at. | 947 * the row that contains the cell the user is currently looking at. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 998 | 972 |
| 999 /** | 973 /** |
| 1000 * Gets the table caption text. | 974 * Gets the table caption text. |
| 1001 * | 975 * |
| 1002 * @return {?string} Either: | 976 * @return {?string} Either: |
| 1003 * 1) The table caption text | 977 * 1) The table caption text |
| 1004 * 2) Null if the table does not include a caption tag. | 978 * 2) Null if the table does not include a caption tag. |
| 1005 */ | 979 */ |
| 1006 cvox.TraverseTable.prototype.captionText = function() { | 980 cvox.TraverseTable.prototype.captionText = function() { |
| 1007 // If there's more than one outer <caption> element, choose the first one. | 981 // If there's more than one outer <caption> element, choose the first one. |
| 1008 var captionNodes = cvox.XpathUtil.evalXPath('caption\[1]', | 982 var captionNodes = cvox.XpathUtil.evalXPath('caption\[1]', this.activeTable_); |
| 1009 this.activeTable_); | |
| 1010 if (captionNodes.length > 0) { | 983 if (captionNodes.length > 0) { |
| 1011 return captionNodes[0].innerHTML; | 984 return captionNodes[0].innerHTML; |
| 1012 } else { | 985 } else { |
| 1013 return null; | 986 return null; |
| 1014 } | 987 } |
| 1015 }; | 988 }; |
| 1016 | 989 |
| 1017 | 990 |
| 1018 /** | 991 /** |
| 1019 * Calculates the number of columns in the shadow table. | 992 * Calculates the number of columns in the shadow table. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1051 * This uses the W3C recommended algorithm for calculating number of | 1024 * This uses the W3C recommended algorithm for calculating number of |
| 1052 * columns, but it does not take rowspans or colspans into account. This means | 1025 * columns, but it does not take rowspans or colspans into account. This means |
| 1053 * that the number of columns calculated here might be lower than the actual | 1026 * that the number of columns calculated here might be lower than the actual |
| 1054 * number of columns in the table if columns are indicated by colspans. | 1027 * number of columns in the table if columns are indicated by colspans. |
| 1055 * @return {number} The number of columns in the table. | 1028 * @return {number} The number of columns in the table. |
| 1056 * @private | 1029 * @private |
| 1057 */ | 1030 */ |
| 1058 cvox.TraverseTable.prototype.getW3CColCount_ = function() { | 1031 cvox.TraverseTable.prototype.getW3CColCount_ = function() { |
| 1059 // See http://www.w3.org/TR/html401/struct/tables.html#h-11.2.4.3 | 1032 // See http://www.w3.org/TR/html401/struct/tables.html#h-11.2.4.3 |
| 1060 | 1033 |
| 1061 var colgroupNodes = cvox.XpathUtil.evalXPath('child::colgroup', | 1034 var colgroupNodes = |
| 1062 this.activeTable_); | 1035 cvox.XpathUtil.evalXPath('child::colgroup', this.activeTable_); |
| 1063 var colNodes = cvox.XpathUtil.evalXPath('child::col', this.activeTable_); | 1036 var colNodes = cvox.XpathUtil.evalXPath('child::col', this.activeTable_); |
| 1064 | 1037 |
| 1065 if ((colgroupNodes.length == 0) && (colNodes.length == 0)) { | 1038 if ((colgroupNodes.length == 0) && (colNodes.length == 0)) { |
| 1066 var maxcols = 0; | 1039 var maxcols = 0; |
| 1067 var outerChildren = cvox.TableUtil.getChildRows(this.activeTable_); | 1040 var outerChildren = cvox.TableUtil.getChildRows(this.activeTable_); |
| 1068 for (var i = 0; i < outerChildren.length; i++) { | 1041 for (var i = 0; i < outerChildren.length; i++) { |
| 1069 var childrenCount = cvox.TableUtil.getChildCells(outerChildren[i]); | 1042 var childrenCount = cvox.TableUtil.getChildCells(outerChildren[i]); |
| 1070 if (childrenCount.length > maxcols) { | 1043 if (childrenCount.length > maxcols) { |
| 1071 maxcols = childrenCount.length; | 1044 maxcols = childrenCount.length; |
| 1072 } | 1045 } |
| 1073 } | 1046 } |
| 1074 return maxcols; | 1047 return maxcols; |
| 1075 } else { | 1048 } else { |
| 1076 var sum = 0; | 1049 var sum = 0; |
| 1077 for (var i = 0; i < colNodes.length; i++) { | 1050 for (var i = 0; i < colNodes.length; i++) { |
| 1078 if (colNodes[i].hasAttribute('span')) { | 1051 if (colNodes[i].hasAttribute('span')) { |
| 1079 sum += colNodes[i].getAttribute('span'); | 1052 sum += colNodes[i].getAttribute('span'); |
| 1080 } else { | 1053 } else { |
| 1081 sum += 1; | 1054 sum += 1; |
| 1082 } | 1055 } |
| 1083 } | 1056 } |
| 1084 for (i = 0; i < colgroupNodes.length; i++) { | 1057 for (i = 0; i < colgroupNodes.length; i++) { |
| 1085 var colChildren = cvox.XpathUtil.evalXPath('child::col', | 1058 var colChildren = |
| 1086 colgroupNodes[i]); | 1059 cvox.XpathUtil.evalXPath('child::col', colgroupNodes[i]); |
| 1087 if (colChildren.length == 0) { | 1060 if (colChildren.length == 0) { |
| 1088 if (colgroupNodes[i].hasAttribute('span')) { | 1061 if (colgroupNodes[i].hasAttribute('span')) { |
| 1089 sum += colgroupNodes[i].getAttribute('span'); | 1062 sum += colgroupNodes[i].getAttribute('span'); |
| 1090 } else { | 1063 } else { |
| 1091 sum += 1; | 1064 sum += 1; |
| 1092 } | 1065 } |
| 1093 } | 1066 } |
| 1094 } | 1067 } |
| 1095 } | 1068 } |
| 1096 return sum; | 1069 return sum; |
| 1097 }; | 1070 }; |
| 1098 | 1071 |
| 1099 | 1072 |
| 1100 /** | 1073 /** |
| 1101 * Moves to the next row in the table. Updates the cell cursor. | 1074 * Moves to the next row in the table. Updates the cell cursor. |
| 1102 * | 1075 * |
| 1103 * @return {boolean} Either: | 1076 * @return {boolean} Either: |
| 1104 * 1) True if the update has been made. | 1077 * 1) True if the update has been made. |
| 1105 * 2) False if the end of the table has been reached and the update has not | 1078 * 2) False if the end of the table has been reached and the update has not |
| 1106 * happened. | 1079 * happened. |
| 1107 */ | 1080 */ |
| 1108 cvox.TraverseTable.prototype.nextRow = function() { | 1081 cvox.TraverseTable.prototype.nextRow = function() { |
| 1109 if (!this.currentCellCursor) { | 1082 if (!this.currentCellCursor) { |
| 1110 // We have not started moving through the table yet | 1083 // We have not started moving through the table yet |
| 1111 return this.goToRow(0); | 1084 return this.goToRow(0); |
| 1112 } else { | 1085 } else { |
| 1113 return this.goToRow(this.currentCellCursor[0] + 1); | 1086 return this.goToRow(this.currentCellCursor[0] + 1); |
| 1114 } | 1087 } |
| 1115 | 1088 |
| 1116 }; | 1089 }; |
| 1117 | 1090 |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1246 * 1) True if the index is valid and the update has been made. | 1219 * 1) True if the index is valid and the update has been made. |
| 1247 * 2) False if the index is not valid (there is no cell at that location). | 1220 * 2) False if the index is not valid (there is no cell at that location). |
| 1248 */ | 1221 */ |
| 1249 cvox.TraverseTable.prototype.goToLastCell = function() { | 1222 cvox.TraverseTable.prototype.goToLastCell = function() { |
| 1250 var numRows = this.shadowTable_.length; | 1223 var numRows = this.shadowTable_.length; |
| 1251 if (numRows == 0) { | 1224 if (numRows == 0) { |
| 1252 return false; | 1225 return false; |
| 1253 } | 1226 } |
| 1254 var lastRow = this.shadowTable_[numRows - 1]; | 1227 var lastRow = this.shadowTable_[numRows - 1]; |
| 1255 var lastIndex = [(numRows - 1), (lastRow.length - 1)]; | 1228 var lastIndex = [(numRows - 1), (lastRow.length - 1)]; |
| 1256 var cell = | 1229 var cell = this.shadowTable_[lastIndex[0]][lastIndex[1]]; |
| 1257 this.shadowTable_[lastIndex[0]][lastIndex[1]]; | |
| 1258 if (cell != null) { | 1230 if (cell != null) { |
| 1259 this.currentCellCursor = lastIndex; | 1231 this.currentCellCursor = lastIndex; |
| 1260 return true; | 1232 return true; |
| 1261 } | 1233 } |
| 1262 return false; | 1234 return false; |
| 1263 }; | 1235 }; |
| 1264 | 1236 |
| 1265 | 1237 |
| 1266 /** | 1238 /** |
| 1267 * Moves to the cell at the last index in the current row of the table. Update | 1239 * Moves to the cell at the last index in the current row of the table. Update |
| 1268 * the cell cursor. | 1240 * the cell cursor. |
| 1269 * @return {boolean} Either: | 1241 * @return {boolean} Either: |
| 1270 * 1) True if the index is valid and the update has been made. | 1242 * 1) True if the index is valid and the update has been made. |
| 1271 * 2) False if the index is not valid (there is no cell at that location). | 1243 * 2) False if the index is not valid (there is no cell at that location). |
| 1272 */ | 1244 */ |
| 1273 cvox.TraverseTable.prototype.goToRowLastCell = function() { | 1245 cvox.TraverseTable.prototype.goToRowLastCell = function() { |
| 1274 var currentRow = this.currentCellCursor[0]; | 1246 var currentRow = this.currentCellCursor[0]; |
| 1275 var lastIndex = [currentRow, (this.shadowTable_[currentRow].length - 1)]; | 1247 var lastIndex = [currentRow, (this.shadowTable_[currentRow].length - 1)]; |
| 1276 var cell = | 1248 var cell = this.shadowTable_[lastIndex[0]][lastIndex[1]]; |
| 1277 this.shadowTable_[lastIndex[0]][lastIndex[1]]; | |
| 1278 if (cell != null) { | 1249 if (cell != null) { |
| 1279 this.currentCellCursor = lastIndex; | 1250 this.currentCellCursor = lastIndex; |
| 1280 return true; | 1251 return true; |
| 1281 } | 1252 } |
| 1282 return false; | 1253 return false; |
| 1283 }; | 1254 }; |
| 1284 | 1255 |
| 1285 | 1256 |
| 1286 /** | 1257 /** |
| 1287 * Moves to the cell at the last index in the current column of the table. | 1258 * Moves to the cell at the last index in the current column of the table. |
| 1288 * Update the cell cursor. | 1259 * Update the cell cursor. |
| 1289 * @return {boolean} Either: | 1260 * @return {boolean} Either: |
| 1290 * 1) True if the index is valid and the update has been made. | 1261 * 1) True if the index is valid and the update has been made. |
| 1291 * 2) False if the index is not valid (there is no cell at that location). | 1262 * 2) False if the index is not valid (there is no cell at that location). |
| 1292 */ | 1263 */ |
| 1293 cvox.TraverseTable.prototype.goToColLastCell = function() { | 1264 cvox.TraverseTable.prototype.goToColLastCell = function() { |
| 1294 var currentCol = this.getCol(); | 1265 var currentCol = this.getCol(); |
| 1295 var lastIndex = [(currentCol.length - 1), this.currentCellCursor[1]]; | 1266 var lastIndex = [(currentCol.length - 1), this.currentCellCursor[1]]; |
| 1296 var cell = | 1267 var cell = this.shadowTable_[lastIndex[0]][lastIndex[1]]; |
| 1297 this.shadowTable_[lastIndex[0]][lastIndex[1]]; | |
| 1298 if (cell != null) { | 1268 if (cell != null) { |
| 1299 this.currentCellCursor = lastIndex; | 1269 this.currentCellCursor = lastIndex; |
| 1300 return true; | 1270 return true; |
| 1301 } | 1271 } |
| 1302 return false; | 1272 return false; |
| 1303 }; | 1273 }; |
| 1304 | 1274 |
| 1305 | 1275 |
| 1306 /** | 1276 /** |
| 1307 * Resets the table cursors. | 1277 * Resets the table cursors. |
| 1308 * | 1278 * |
| 1309 */ | 1279 */ |
| 1310 cvox.TraverseTable.prototype.resetCursor = function() { | 1280 cvox.TraverseTable.prototype.resetCursor = function() { |
| 1311 this.currentCellCursor = null; | 1281 this.currentCellCursor = null; |
| 1312 }; | 1282 }; |
| OLD | NEW |