abstract_selection_walker.js revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 3// found in the LICENSE file. 4 5/** 6 * @fileoverview An abstract class for walking at the sub-element level. 7 * For example, walking at the sentence, word, or character level. 8 * This class is an adapter around TraverseContent which exposes the interface 9 * required by walkers. Subclasses must override the this.grain attribute 10 * on initialization. 11 */ 12 13 14goog.provide('cvox.AbstractSelectionWalker'); 15 16goog.require('cvox.AbstractWalker'); 17goog.require('cvox.BareObjectWalker'); 18goog.require('cvox.DescriptionUtil'); 19goog.require('cvox.DomUtil'); 20goog.require('cvox.Spannable'); 21goog.require('cvox.TraverseContent'); 22 23/** 24 * @constructor 25 * @extends {cvox.AbstractWalker} 26 */ 27cvox.AbstractSelectionWalker = function() { 28 cvox.AbstractWalker.call(this); 29 this.objWalker_ = new cvox.BareObjectWalker(); 30 this.tc_ = cvox.TraverseContent.getInstance(); 31 this.grain /** @protected */ = ''; // child must override 32}; 33goog.inherits(cvox.AbstractSelectionWalker, cvox.AbstractWalker); 34 35/** 36 * @override 37 */ 38cvox.AbstractSelectionWalker.prototype.next = function(sel) { 39 var r = sel.isReversed(); 40 this.tc_.syncToCursorSelection(sel.clone().setReversed(false)); 41 var ret = r ? this.tc_.prevElement(this.grain) : 42 this.tc_.nextElement(this.grain); 43 if (ret == null) { 44 // Unfortunately, we can't trust TraverseContent; fall back to ObjectWalker. 45 return this.objWalker_.next(sel); 46 } 47 var retSel = this.tc_.getCurrentCursorSelection().setReversed(r); 48 var objSel = this.objWalker_.next(sel); 49 objSel = objSel ? objSel.setReversed(r) : null; 50 51 // ObjectWalker wins when there's a discrepancy between it and 52 // TraverseContent. The only exception is with an end cursor on a text node. 53 // In all other cases, this makes sure we visit the same selections as 54 // object walker. 55 if (objSel && 56 (retSel.end.node.constructor.name != 'Text' || 57 objSel.end.node.constructor.name != 'Text') && 58 !cvox.DomUtil.isDescendantOfNode(retSel.end.node, sel.end.node) && 59 !cvox.DomUtil.isDescendantOfNode(retSel.end.node, objSel.end.node)) { 60 return objSel; 61 } 62 return retSel; 63}; 64 65/** 66 * @override 67 */ 68cvox.AbstractSelectionWalker.prototype.sync = function(sel) { 69 var r = sel.isReversed(); 70 var newSel = null; 71 if (sel.start.equals(sel.end) && sel.start.node.constructor.name != 'Text') { 72 var node = sel.start.node; 73 74 // Find the deepest visible node; written specifically here because we want 75 // to move across siblings if necessary and take the deepest node which can 76 // be BODY. 77 while (node && 78 cvox.DomUtil.directedFirstChild(node, r) && 79 !cvox.TraverseUtil.treatAsLeafNode(node)) { 80 var child = cvox.DomUtil.directedFirstChild(node, r); 81 82 // Find the first visible child. 83 while (child) { 84 if (cvox.DomUtil.isVisible(child, 85 {checkAncestors: false, checkDescendants: false})) { 86 node = child; 87 break; 88 } else { 89 child = cvox.DomUtil.directedNextSibling(child, r); 90 } 91 } 92 93 // node has no visible children; it's therefore the deepest visible node. 94 if (!child) { 95 break; 96 } 97 } 98 newSel = cvox.CursorSelection.fromNode(node); 99 } else { 100 newSel = sel.clone(); 101 if (r) { 102 newSel.start = newSel.end; 103 } else { 104 newSel.end = newSel.start; 105 } 106 } 107 108 // This.next places us at the correct initial position (except below). 109 newSel = this.next(newSel.setReversed(false)); 110 111 // ObjectWalker wins when there's a discrepancy between it and 112 // TraverseContent. The only exception is with an end cursor on a text node. 113 // In all other cases, this makes sure we visit the same selections as 114 // object walker. 115 var objSel = this.objWalker_.sync(sel); 116 objSel = objSel ? objSel.setReversed(r) : null; 117 118 if (!newSel) { 119 return objSel; 120 } 121 122 newSel.setReversed(r); 123 124 if (objSel && 125 (newSel.end.node.constructor.name != 'Text' || 126 objSel.end.node.constructor.name != 'Text') && 127 !cvox.DomUtil.isDescendantOfNode(newSel.end.node, sel.end.node) && 128 !cvox.DomUtil.isDescendantOfNode(newSel.end.node, objSel.end.node)) { 129 return objSel; 130 } 131 return newSel; 132}; 133 134/** 135 * @override 136 */ 137cvox.AbstractSelectionWalker.prototype.getDescription = function(prevSel, sel) { 138 var description = cvox.DescriptionUtil.getDescriptionFromAncestors( 139 cvox.DomUtil.getUniqueAncestors(prevSel.end.node, sel.start.node), 140 true, 141 cvox.ChromeVox.verbosity); 142 description.text = sel.getText() || description.text; 143 return [description]; 144}; 145 146/** 147 * @override 148 */ 149cvox.AbstractSelectionWalker.prototype.getBraille = function(prevSel, sel) { 150 var node = sel.absStart().node; 151 var text = cvox.TraverseUtil.getNodeText(node); 152 var spannable = new cvox.Spannable(text); 153 spannable.setSpan(node, 0, text.length); 154 return new cvox.NavBraille({ 155 text: spannable, 156 startIndex: sel.absStart().index, 157 endIndex: sel.absEnd().index 158 }); 159}; 160