abstract_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 interface (and partial implementation) for the basic 7 * traversal through some piece of the dom. 8 * For each different ordered (either in dom or by any other metric) set 9 * of "valid selections" (just set from now on), a new 10 * base class should be defined that implements this interface. For example, 11 * there are subclasses for words, sentences, and lowest-level dom nodes. 12 * These classes should all be stateless; this makes testing much more 13 * effective at pinpointing errors. 14 * For all of the operations in this interface, the position in the dom on 15 * which to operate is given by a CursorSelection, see that file for 16 * documentation. 17 * The two main operations that currently exist for walkers are sync and 18 * next. See the docs where those functions are defined. 19 * Since most operations are hard to even define if there is no root element, 20 * all operations may assume that the selection given is attached to the body 21 * node. The behavior is undefined if any part of the selection passed in 22 * is not attached to the body. As a user of this class, it is your 23 * responsibility to make sure the selection is attached. 24 * No operation may visibly modify any of its arguments. In particular, take 25 * care with CursorSelections, since setReversed modifies the selection. 26 * For all documentation, = refers to the method equals for CursorSelections 27 * comparison. 28 * Thinking of adding something in this class? Here are some good questions to 29 * ask: 30 * Is this an operation that applies to any element of any arbitrary set? 31 * If not, then it probably doesn't belong here. 32 * Does it need to know something other than the set that it operates on? 33 * If so, then it probably doesn't belong here. 34 * 35 * This interface resembles a C++ STL bidirectional iterator. Additions should 36 * keep this in mind. 37 * 38 */ 39 40 41goog.provide('cvox.AbstractWalker'); 42 43goog.require('cvox.CursorSelection'); 44goog.require('cvox.NavBraille'); 45 46/** 47 * @constructor 48 */ 49cvox.AbstractWalker = function() { 50}; 51 52 53/** 54 * This takes a valid CursorSelection and returns the directed-next 55 * valid CursorSelection in the dom, or null. For example, if the walker 56 * navigates across sentences, this would return the selection of the sentence 57 * following the selection passed in. If sel is at the "end" of a section, 58 * this method may return null. In the example above, if we try to next on 59 * the last sentence in the dom, we would return null. 60 * Note that sel must be a valid selection. Undefined behavior if it isn't. 61 * There are several invariants that must hold for any subclasses. There may 62 * not be explicit tests for these, but subclasses are responsible for ensuring 63 * them and callers may assume them: 64 * 1) next(next(sel).setReversed(!sel.isReversed())) = sel for all sel if sel 65 * is a valid CursorSelection and next(sel) != null. 66 * That is, the valid elements for this walker are totally ordered; going 67 * forward and then backward returns us to the same cell. 68 * 2) next(sel).isReversed() = sel.isReversed() for all sel if sel is a 69 * valid CursorSelection and next(sel) != null. 70 * That is, next preserves direction. 71 * @param {!cvox.CursorSelection} sel The valid selection to start moving from. 72 * @return {cvox.CursorSelection} Returns the valid selection the walker moves 73 * to. null if directed end of section is reached. 74 */ 75cvox.AbstractWalker.prototype.next = goog.abstractMethod; 76 77 78/** 79 * Syncs and returns the first or last valid, non-null selection in the 80 * this walker's linearization of the DOM. 81 * @param {{reversed: (undefined|boolean)}=} kwargs Extra arguments. 82 * reversed: If true, syncs to the end and returns a reversed selection. 83 * False by default. 84 * @return {!cvox.CursorSelection} The valid selection. 85 */ 86cvox.AbstractWalker.prototype.begin = function(kwargs) { 87 kwargs = kwargs || {reversed: false}; 88 89 return /** @type {!cvox.CursorSelection} */ (this.sync( 90 cvox.CursorSelection.fromBody().setReversed(kwargs.reversed))); 91}; 92 93 94/** 95 * This takes an arbitrary CursorSelection and returns a valid CursorSelection, 96 * or null. For example, if the walker navigates across 97 * text nodes, and the selection passed in is for a single character within a 98 * larger text node, this method should return a text node. No restrictions 99 * are made as to exactly what selection should be returned, but it should be 100 * something "reasonable", and from the user's point of view, "close" to the 101 * previous selection. If no such selection exists, null may be returned. 102 * Note that, since CursorSelection has a direction, syncing to a selection 103 * should make sense in either direction. 104 * Note also that, as mentioned in the file overview, this operation has 105 * undefined behavior if the input selection is not attached to the body. 106 * There are several invariants that must hold for any subclasses. While they 107 * may not all be tested for at the time, subclasses are responsible for 108 * making sure these hold, and any caller may assume these to be true: 109 * 1) sync(sel) = sel iff sel is a valid selection 110 * This defines the set of valid selections for this walker. 111 * Note, in particular, that this implies sync(sync(sel)) = sync(sel) 112 * whenever sync(sel) != null. 113 * 2) sync(sel).isReversed() = sel.isReversed() for all sel if sync(sel) != null 114 * That is, sync preserves direction. 115 * Why do these restrictions exist? Because it makes it much easier to reason 116 * about the effect (and intent) of an operation if we can make these 117 * assumptions. 118 * @param {!cvox.CursorSelection} sel The (possibly unsynched) selection. 119 * @return {cvox.CursorSelection} The synched selection. 120 */ 121cvox.AbstractWalker.prototype.sync = goog.abstractMethod; 122 123 124/** 125 * Returns an array of NavDescriptions that defines what should be said 126 * by the tts engine on traversal from prevSel to sel. While this is 127 * introducing knowledge (of NavDescriptions) into this class that 128 * it shouldn't know, this is currently the best place for this method 129 * to reside, as the set of valid CursorSelections must be known. 130 * sel must be valid CursorSelections for this walker, prevSel may be any 131 * selection. Undefined behavior otherwise. 132 * @param {!cvox.CursorSelection} prevSel The valid previous selection. 133 * @param {!cvox.CursorSelection} sel The valid current selection. 134 * @return {!Array.<!cvox.NavDescription>} The description array. 135 */ 136cvox.AbstractWalker.prototype.getDescription = goog.abstractMethod; 137 138 139/** 140 * Returns a NavBraille that defines what should be brailled on traversal from 141 * prevSel to sel. 142 * sel must be valid CursorSelections for this walker, prevSel may be any 143 * selection. Undefined behavior otherwise. 144 * @param {!cvox.CursorSelection} prevSel The valid previous selection. 145 * @param {!cvox.CursorSelection} sel The valid current selection. 146 * @return {!cvox.NavBraille} The braille description. 147 */ 148cvox.AbstractWalker.prototype.getBraille = goog.abstractMethod; 149 150 151/** 152 * Returns if this walker supports the given action. 153 * @param {string} name Action name. 154 * @return {boolean} True if action supported. 155 */ 156cvox.AbstractWalker.prototype.hasAction = function(name) { 157 return typeof(this[name]) == 'function'; 158}; 159 160/** 161 * Performs an action specific to the walker. 162 * @param {string} name Action name. 163 * @param {!cvox.CursorSelection} sel The current selection. 164 * @return {cvox.CursorSelection} Selection after action. 165 */ 166cvox.AbstractWalker.prototype.performAction = function(name, sel) { 167 if (this.hasAction(name)) { 168 return this[name](sel); 169 } 170 return null; 171}; 172 173/** 174 * Returns message string of the walker's granularity. 175 * @return {string} The message string. 176 */ 177cvox.AbstractWalker.prototype.getGranularityMsg = goog.abstractMethod; 178