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 * @fileoverview Classes related to cursors that point to and select parts of | 6 * @fileoverview Classes related to cursors that point to and select parts of |
7 * the automation tree. | 7 * the automation tree. |
8 */ | 8 */ |
9 | 9 |
10 goog.provide('cursors.Cursor'); | 10 goog.provide('cursors.Cursor'); |
11 goog.provide('cursors.Movement'); | 11 goog.provide('cursors.Movement'); |
| 12 goog.provide('cursors.Range'); |
12 goog.provide('cursors.Unit'); | 13 goog.provide('cursors.Unit'); |
13 | 14 |
14 goog.require('AutomationUtil'); | 15 goog.require('AutomationUtil'); |
15 | 16 |
16 /** | 17 /** |
17 * The special index that represents a cursor pointing to a node without | 18 * The special index that represents a cursor pointing to a node without |
18 * pointing to any part of its accessible text. | 19 * pointing to any part of its accessible text. |
19 */ | 20 */ |
20 cursors.NODE_INDEX = -1; | 21 cursors.NODE_INDEX = -1; |
21 | 22 |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 | 124 |
124 /** | 125 /** |
125 * Makes a Cursor which has been moved from this cursor by the unit in the | 126 * Makes a Cursor which has been moved from this cursor by the unit in the |
126 * given direction using the given movement type. | 127 * given direction using the given movement type. |
127 * @param {Unit} unit | 128 * @param {Unit} unit |
128 * @param {Movement} movement | 129 * @param {Movement} movement |
129 * @param {Dir} dir | 130 * @param {Dir} dir |
130 * @return {!cursors.Cursor} The moved cursor. | 131 * @return {!cursors.Cursor} The moved cursor. |
131 */ | 132 */ |
132 move: function(unit, movement, dir) { | 133 move: function(unit, movement, dir) { |
133 var newNode, newIndex; | 134 var newNode = this.node_; |
| 135 var newIndex = this.index_; |
| 136 |
| 137 if (unit != Unit.NODE && newIndex === cursors.NODE_INDEX) |
| 138 newIndex = 0; |
| 139 |
134 switch (unit) { | 140 switch (unit) { |
135 case Unit.CHARACTER: | 141 case Unit.CHARACTER: |
136 // BOUND and DIRECTIONAL are the same for characters. | 142 // BOUND and DIRECTIONAL are the same for characters. |
137 var node = this.node_; | 143 newIndex = dir == Dir.FORWARD ? newIndex + 1 : newIndex - 1; |
138 var nextIndex = dir == Dir.FORWARD ? this.index_ + 1 : this.index_ - 1; | 144 if (newIndex < 0 || newIndex >= this.getText().length) { |
139 if (nextIndex < 0 || nextIndex >= this.getText().length) { | 145 newNode = AutomationUtil.findNextNode( |
140 node = AutomationUtil.findNextNode( | 146 newNode, dir, AutomationPredicate.leafWithText); |
141 node, dir, AutomationPredicate.leaf); | 147 if (newNode) { |
142 if (node) { | 148 newIndex = |
143 nextIndex = | 149 dir == Dir.FORWARD ? 0 : this.getText(newNode).length - 1; |
144 dir == Dir.FORWARD ? 0 : this.getText(node).length - 1; | 150 newIndex = newIndex == -1 ? 0 : newIndex; |
145 } else { | 151 } else { |
146 node = this.node_; | 152 newIndex = this.index_; |
147 nextIndex = this.index_; | |
148 } | 153 } |
149 } | 154 } |
150 newNode = node; | |
151 newIndex = nextIndex; | |
152 break; | 155 break; |
153 case Unit.WORD: | 156 case Unit.WORD: |
154 switch (movement) { | 157 switch (movement) { |
155 case Movement.BOUND: | 158 case Movement.BOUND: |
156 if (this.node_.role == Role.inlineTextBox) { | 159 if (newNode.role == Role.inlineTextBox) { |
157 var start, end; | 160 var start, end; |
158 for (var i = 0; | 161 for (var i = 0; i < newNode.attributes.wordStarts.length; i++) { |
159 i < this.node_.attributes.wordStarts.length; | 162 if (newIndex >= newNode.attributes.wordStarts[i] && |
160 i++) { | 163 newIndex <= newNode.attributes.wordEnds[i]) { |
161 if (this.index_ >= this.node_.attributes.wordStarts[i] && | 164 start = newNode.attributes.wordStarts[i]; |
162 this.index_ <= this.node_.attributes.wordEnds[i]) { | 165 end = newNode.attributes.wordEnds[i]; |
163 start = this.node_.attributes.wordStarts[i]; | |
164 end = this.node_.attributes.wordEnds[i]; | |
165 break; | 166 break; |
166 } | 167 } |
167 } | 168 } |
168 if (goog.isDef(start) && goog.isDef(end)) | 169 if (goog.isDef(start) && goog.isDef(end)) |
169 newIndex = dir == Dir.FORWARD ? end : start; | 170 newIndex = dir == Dir.FORWARD ? end : start; |
170 } else { | 171 } else { |
171 // TODO(dtseng): Figure out what to do in this case. | 172 // TODO(dtseng): Figure out what to do in this case. |
172 } | 173 } |
173 break; | 174 break; |
174 case Movement.DIRECTIONAL: | 175 case Movement.DIRECTIONAL: |
175 if (this.node_.role == Role.inlineTextBox) { | 176 if (newNode.role == Role.inlineTextBox) { |
176 var start, end; | 177 var start, end; |
177 for (var i = 0; | 178 for (var i = 0; i < newNode.attributes.wordStarts.length; i++) { |
178 i < this.node_.attributes.wordStarts.length; | 179 if (newIndex >= newNode.attributes.wordStarts[i] && |
179 i++) { | 180 newIndex <= newNode.attributes.wordEnds[i]) { |
180 if (this.index_ >= this.node_.attributes.wordStarts[i] && | |
181 this.index_ <= this.node_.attributes.wordEnds[i]) { | |
182 var nextIndex = dir == Dir.FORWARD ? i + 1 : i - 1; | 181 var nextIndex = dir == Dir.FORWARD ? i + 1 : i - 1; |
183 start = this.node_.attributes.wordStarts[nextIndex]; | 182 start = newNode.attributes.wordStarts[nextIndex]; |
184 end = this.node_.attributes.wordEnds[nextIndex]; | 183 end = newNode.attributes.wordEnds[nextIndex]; |
185 break; | 184 break; |
186 } | 185 } |
187 } | 186 } |
188 if (goog.isDef(start)) { | 187 if (goog.isDef(start)) { |
189 newIndex = start; | 188 newIndex = start; |
190 } else { | 189 } else { |
191 // The backward case is special at the beginning of nodes. | 190 // The backward case is special at the beginning of nodes. |
192 if (dir == Dir.BACKWARD && this.index_ != 0) { | 191 if (dir == Dir.BACKWARD && newIndex != 0) { |
193 this.index_ = 0; | 192 newIndex = 0; |
194 } else { | 193 } else { |
195 var node = AutomationUtil.findNextNode(this.node_, dir, | 194 newNode = AutomationUtil.findNextNode(newNode, dir, |
196 AutomationPredicate.leaf); | 195 AutomationPredicate.leaf); |
197 if (node) { | 196 if (newNode) { |
198 newNode = node; | |
199 newIndex = 0; | 197 newIndex = 0; |
200 if (dir == Dir.BACKWARD && | 198 if (dir == Dir.BACKWARD && |
201 node.role == Role.inlineTextBox) { | 199 newNode.role == Role.inlineTextBox) { |
202 var starts = node.attributes.wordStarts; | 200 var starts = newNode.attributes.wordStarts; |
203 newIndex = starts[starts.length - 1] || 0; | 201 newIndex = starts[starts.length - 1] || 0; |
204 } else { | 202 } else { |
205 // TODO(dtseng): Figure out what to do for general nodes. | 203 // TODO(dtseng): Figure out what to do for general nodes. |
206 } | 204 } |
207 } | 205 } |
208 } | 206 } |
209 } | 207 } |
210 } else { | 208 } else { |
211 // TODO(dtseng): Figure out what to do in this case. | 209 // TODO(dtseng): Figure out what to do in this case. |
212 } | 210 } |
213 } | 211 } |
214 break; | 212 break; |
215 case Unit.NODE: | 213 case Unit.NODE: |
216 switch (movement) { | 214 switch (movement) { |
217 case Movement.BOUND: | 215 case Movement.BOUND: |
218 newIndex = dir == Dir.FORWARD ? this.getText().length - 1 : 0; | 216 newIndex = dir == Dir.FORWARD ? this.getText().length - 1 : 0; |
219 break; | 217 break; |
220 case Movement.DIRECTIONAL: | 218 case Movement.DIRECTIONAL: |
221 newNode = AutomationUtil.findNextNode( | 219 newNode = AutomationUtil.findNextNode( |
222 this.node_, dir, AutomationPredicate.leaf) || this.node_; | 220 newNode, dir, AutomationPredicate.leaf) || this.node_; |
223 newIndex = cursors.NODE_INDEX; | 221 newIndex = cursors.NODE_INDEX; |
224 break; | 222 break; |
225 } | 223 } |
226 break; | 224 break; |
227 case Unit.LINE: | 225 case Unit.LINE: |
228 newIndex = 0; | 226 newIndex = 0; |
229 switch (movement) { | 227 switch (movement) { |
230 case Movement.BOUND: | 228 case Movement.BOUND: |
231 newNode = AutomationUtil.findNodeUntil(this.node_, dir, | 229 newNode = AutomationUtil.findNodeUntil(newNode, dir, |
232 AutomationPredicate.linebreak, {before: true}); | 230 AutomationPredicate.linebreak, {before: true}); |
233 newNode = newNode || this.node_; | 231 newNode = newNode || this.node_; |
234 newIndex = | 232 newIndex = |
235 dir == Dir.FORWARD ? this.getText(newNode).length - 1 : 0; | 233 dir == Dir.FORWARD ? this.getText(newNode).length : 0; |
236 break; | 234 break; |
237 case Movement.DIRECTIONAL: | 235 case Movement.DIRECTIONAL: |
238 newNode = AutomationUtil.findNodeUntil( | 236 newNode = AutomationUtil.findNodeUntil( |
239 this.node_, dir, AutomationPredicate.linebreak); | 237 newNode, dir, AutomationPredicate.linebreak); |
240 | |
241 // We stick to the beginning of lines out of convention. | |
242 if (newNode && dir == Dir.BACKWARD) { | |
243 newNode = AutomationUtil.findNodeUntil(newNode, dir, | |
244 AutomationPredicate.linebreak, {before: true}) || node; | |
245 } | |
246 break; | 238 break; |
247 } | 239 } |
248 break; | 240 break; |
249 default: | 241 default: |
250 throw 'Unrecognized unit: ' + unit; | 242 throw 'Unrecognized unit: ' + unit; |
251 } | 243 } |
252 newNode = newNode || this.node_; | 244 newNode = newNode || this.node_; |
253 newIndex = goog.isDef(newIndex) ? newIndex : this.index_; | 245 newIndex = goog.isDef(newIndex) ? newIndex : this.index_; |
254 return new cursors.Cursor(newNode, newIndex); | 246 return new cursors.Cursor(newNode, newIndex); |
255 } | 247 } |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 }, | 301 }, |
310 | 302 |
311 /** | 303 /** |
312 * @return {!cursors.Cursor} | 304 * @return {!cursors.Cursor} |
313 */ | 305 */ |
314 getEnd: function() { | 306 getEnd: function() { |
315 return this.end_; | 307 return this.end_; |
316 }, | 308 }, |
317 | 309 |
318 /** | 310 /** |
| 311 * Returns whether true if this range covers less than a node. |
| 312 * @return {boolean} |
| 313 */ |
| 314 isSubNode: function() { |
| 315 return this.getStart().getNode() === this.getEnd().getNode() && |
| 316 this.getStart().getIndex() > -1 && |
| 317 this.getEnd().getIndex() > -1; |
| 318 }, |
| 319 |
| 320 /** |
319 * Makes a Range which has been moved from this range by the given unit and | 321 * Makes a Range which has been moved from this range by the given unit and |
320 * direction. | 322 * direction. |
321 * @param {Unit} unit | 323 * @param {Unit} unit |
322 * @param {Dir} dir | 324 * @param {Dir} dir |
323 * @return {cursors.Range} | 325 * @return {cursors.Range} |
324 */ | 326 */ |
325 move: function(unit, dir) { | 327 move: function(unit, dir) { |
326 var newStart = this.start_; | 328 var newStart = this.start_; |
327 var newEnd = newStart; | 329 var newEnd = newStart; |
328 switch (unit) { | 330 switch (unit) { |
329 case Unit.CHARACTER: | 331 case Unit.CHARACTER: |
330 newStart = newStart.move(unit, Movement.BOUND, dir); | 332 newStart = newStart.move(unit, Movement.BOUND, dir); |
331 newEnd = newStart.move(unit, Movement.BOUND, Dir.FORWARD); | 333 newEnd = newStart.move(unit, Movement.BOUND, Dir.FORWARD); |
332 // Character crossed a node; collapses to the end of the node. | 334 // Character crossed a node; collapses to the end of the node. |
333 if (newStart.getNode() !== newEnd.getNode()) | 335 if (newStart.getNode() !== newEnd.getNode()) |
334 newEnd = newStart; | 336 newEnd = newStart; |
335 break; | 337 break; |
336 case Unit.WORD: | 338 case Unit.WORD: |
337 case Unit.LINE: | 339 case Unit.LINE: |
| 340 newStart = newStart.move(unit, Movement.DIRECTIONAL, dir); |
| 341 newStart = newStart.move(unit, Movement.BOUND, Dir.BACKWARD); |
| 342 newEnd = newStart.move(unit, Movement.BOUND, Dir.FORWARD); |
| 343 break; |
338 case Unit.NODE: | 344 case Unit.NODE: |
339 newEnd = newEnd.move(unit, Movement.DIRECTIONAL, dir); | 345 newStart = newStart.move(unit, Movement.DIRECTIONAL, dir); |
340 newStart = newEnd; | 346 newEnd = newStart; |
341 newEnd = newEnd.move(unit, Movement.BOUND, Dir.FORWARD); | |
342 break; | 347 break; |
343 } | 348 } |
344 return new cursors.Range(newStart, newEnd); | 349 return new cursors.Range(newStart, newEnd); |
345 } | 350 } |
346 }; | 351 }; |
347 | 352 |
348 }); // goog.scope | 353 }); // goog.scope |
OLD | NEW |