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.
6 * @fileoverview A JavaScript class for walking lines consisting of one or more
11 goog
.provide('cvox.LayoutLineWalker');
13 goog
.require('cvox.AbstractWalker');
14 goog
.require('cvox.StructuralLineWalker');
19 * @extends {cvox.AbstractWalker}
21 cvox
.LayoutLineWalker = function() {
22 this.subWalker_
= new cvox
.StructuralLineWalker();
24 goog
.inherits(cvox
.LayoutLineWalker
, cvox
.AbstractWalker
);
30 cvox
.LayoutLineWalker
.prototype.next = function(sel
) {
31 // Collapse selection to the directed end.
32 var endSel
= new cvox
.CursorSelection(sel
.end
, sel
.end
, sel
.isReversed());
35 var sync
= this.subWalker_
.sync(endSel
);
40 // Compute the next selection.
41 var start
= this.subWalker_
.next(endSel
);
45 start
.setReversed(sel
.isReversed());
46 return this.extend_(start
).setReversed(false);
53 cvox
.LayoutLineWalker
.prototype.sync = function(sel
) {
54 var line
= this.subWalker_
.sync(sel
);
59 // Extend to both line breaks (in each direction).
60 var end
= this.extend_(line
);
61 var start
= this.extend_(line
.setReversed(!line
.isReversed()));
63 return new cvox
.CursorSelection(start
.end
, end
.end
, sel
.isReversed());
70 cvox
.LayoutLineWalker
.prototype.getDescription = function(prevSel
, sel
) {
71 var descriptions
= [];
73 var absSel
= sel
.clone().setReversed(false);
74 var cur
= new cvox
.CursorSelection(absSel
.start
, absSel
.start
);
75 cur
= this.subWalker_
.sync(cur
);
80 // No need to accumulate descriptions.
81 if (absSel
.start
.node
== absSel
.end
.node
) {
82 return this.subWalker_
.getDescription(prevSel
, sel
);
85 // Walk through and collect descriptions for each line.
86 while (cur
&& !cur
.end
.equals(absSel
.end
)) {
87 descriptions
.push
.apply(
88 descriptions
, this.subWalker_
.getDescription(prev
, cur
));
90 cur
= this.subWalker_
.next(cur
);
93 descriptions
.push
.apply(
94 descriptions
, this.subWalker_
.getDescription(prev
, cur
));
103 cvox
.LayoutLineWalker
.prototype.getBraille = function(prevSel
, sel
) {
104 var braille
= new cvox
.NavBraille({});
105 var absSel
= this.subWalker_
.sync(sel
.clone().setReversed(false));
106 var layoutSel
= this.sync(sel
).setReversed(false);
107 if (!layoutSel
|| !absSel
) {
110 var cur
= new cvox
.CursorSelection(layoutSel
.start
, layoutSel
.start
);
111 cur
= this.subWalker_
.sync(cur
);
116 // Walk through and collect braille for each line.
117 while (cur
&& !cur
.end
.equals(layoutSel
.end
)) {
118 this.appendBraille_(prevSel
, absSel
, cur
, braille
);
120 cur
= this.subWalker_
.next(cur
);
123 this.appendBraille_(prevSel
, absSel
, cur
, braille
);
132 cvox
.LayoutLineWalker
.prototype.getGranularityMsg = function() {
133 return cvox
.ChromeVox
.msgs
.getMsg('layout_line');
138 * Compares two selections and determines if the lie on the same horizontal
139 * line as determined by their bounding rectangles.
140 * @param {!cvox.CursorSelection} lSel Left selection.
141 * @param {!cvox.CursorSelection} rSel Right selection.
142 * @return {boolean} Whether lSel and rSel are on different visual lines.
145 cvox
.LayoutLineWalker
.prototype.isVisualLineBreak_ = function(lSel
, rSel
) {
146 if (this.wantsOwnLine_(lSel
.end
.node
) ||
147 this.wantsOwnLine_(rSel
.start
.node
)) {
150 var lRect
= lSel
.getRange().getBoundingClientRect();
151 var rRect
= rSel
.getRange().getBoundingClientRect();
153 // Some ranges from the browser give us 0-sized rects (such as in the case of
154 // select's). Detect these cases and use a more reliable method (take the
155 // bounding rect of the actual element rather than the range).
156 if (lRect
.width
== 0 &&
158 lSel
.end
.node
.nodeType
== Node
.ELEMENT_NODE
) {
159 lRect
= lSel
.end
.node
.getBoundingClientRect();
162 if (rRect
.width
== 0 &&
164 rSel
.start
.node
.nodeType
== Node
.ELEMENT_NODE
) {
165 rRect
= rSel
.start
.node
.getBoundingClientRect();
167 return lRect
.bottom
!= rRect
.bottom
;
172 * Determines if node should force a line break.
173 * This is used for elements with unusual semantics, such as multi-line
174 * text fields, where the behaviour would otherwise be confusing.
175 * @param {!Node} node Node.
176 * @return {boolean} True if the node should appear next to a line break.
179 cvox
.LayoutLineWalker
.prototype.wantsOwnLine_ = function(node
) {
180 return node
instanceof HTMLTextAreaElement
||
181 node
.parentNode
instanceof HTMLTextAreaElement
;
186 * Extends a given cursor selection up to the next visual line break.
187 * @param {!cvox.CursorSelection} start The selection.
188 * @return {!cvox.CursorSelection} The resulting selection.
191 cvox
.LayoutLineWalker
.prototype.extend_ = function(start
) {
192 // Extend the selection up to just before a new visual line break.
198 next
= this.subWalker_
.next(end
);
199 } while (next
&& !this.isVisualLineBreak_(end
, next
));
200 return new cvox
.CursorSelection(start
.start
, end
.end
, start
.isReversed());
205 * Private routine to append braille given three selections.
206 * @param {!cvox.CursorSelection} prevSel A previous selection in walker
208 * @param {!cvox.CursorSelection} sel A selection that represents the location
209 * of the braille cursor.
210 * @param {!cvox.CursorSelection} cur The specific selection to append.
211 * @param {!cvox.NavBraille} braille Braille on which to append.
214 cvox
.LayoutLineWalker
.prototype.appendBraille_ = function(
215 prevSel
, sel
, cur
, braille
) {
216 var item
= this.subWalker_
.getBraille(prevSel
, cur
).text
;
217 var valueSelectionSpan
= item
.getSpanInstanceOf(cvox
.ValueSelectionSpan
);
219 if (braille
.text
.getLength() > 0) {
220 braille
.text
.append(cvox
.BrailleUtil
.ITEM_SEPARATOR
);
223 // Find the surrounding logical "leaf node".
224 // This prevents us from labelling the braille output with the wrong node,
225 // such as a text node child of a <textarea>.
226 var node
= cur
.start
.node
;
227 while (node
.parentNode
&& cvox
.DomUtil
.isLeafNode(node
.parentNode
)) {
228 node
= node
.parentNode
;
231 var nodeStart
= braille
.text
.getLength();
232 var nodeEnd
= nodeStart
+ item
.getLength();
233 braille
.text
.append(item
);
234 braille
.text
.setSpan(node
, nodeStart
, nodeEnd
);
236 if (sel
&& cur
.absEquals(sel
)) {
237 if (valueSelectionSpan
) {
238 braille
.startIndex
= nodeStart
+ item
.getSpanStart(valueSelectionSpan
);
239 braille
.endIndex
= nodeStart
+ item
.getSpanEnd(valueSelectionSpan
);
241 braille
.startIndex
= nodeStart
;
242 braille
.endIndex
= nodeStart
+ 1;