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 |