OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 Processes events related to editing text and emits the | 6 * @fileoverview Processes events related to editing text and emits the |
7 * appropriate spoken and braille feedback. | 7 * appropriate spoken and braille feedback. |
8 */ | 8 */ |
9 | 9 |
10 goog.provide('editing.TextEditHandler'); | 10 goog.provide('editing.TextEditHandler'); |
11 | 11 |
12 goog.require('AutomationTreeWalker'); | 12 goog.require('AutomationTreeWalker'); |
13 goog.require('AutomationUtil'); | 13 goog.require('AutomationUtil'); |
14 goog.require('Output'); | 14 goog.require('Output'); |
15 goog.require('Output.EventType'); | 15 goog.require('Output.EventType'); |
16 goog.require('cursors.Cursor'); | 16 goog.require('cursors.Cursor'); |
17 goog.require('cursors.Range'); | 17 goog.require('cursors.Range'); |
18 goog.require('cvox.ChromeVoxEditableTextBase'); | 18 goog.require('cvox.ChromeVoxEditableTextBase'); |
19 | 19 |
20 goog.scope(function() { | 20 goog.scope(function() { |
21 var AutomationEvent = chrome.automation.AutomationEvent; | 21 var AutomationEvent = chrome.automation.AutomationEvent; |
22 var AutomationNode = chrome.automation.AutomationNode; | 22 var AutomationNode = chrome.automation.AutomationNode; |
23 var Cursor = cursors.Cursor; | 23 var Cursor = cursors.Cursor; |
24 var Dir = AutomationUtil.Dir; | 24 var Dir = constants.Dir; |
25 var EventType = chrome.automation.EventType; | 25 var EventType = chrome.automation.EventType; |
26 var Range = cursors.Range; | 26 var Range = cursors.Range; |
27 var RoleType = chrome.automation.RoleType; | 27 var RoleType = chrome.automation.RoleType; |
28 var Movement = cursors.Movement; | 28 var Movement = cursors.Movement; |
29 var Unit = cursors.Unit; | 29 var Unit = cursors.Unit; |
30 | 30 |
31 /** | 31 /** |
32 * A handler for automation events in a focused text field or editable root | 32 * A handler for automation events in a focused text field or editable root |
33 * such as a |contenteditable| subtree. | 33 * such as a |contenteditable| subtree. |
34 * @constructor | 34 * @constructor |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 if (!this.multiline) | 134 if (!this.multiline) |
135 return 0; | 135 return 0; |
136 var breaks = this.node_.lineBreaks || []; | 136 var breaks = this.node_.lineBreaks || []; |
137 var index = 0; | 137 var index = 0; |
138 while (index < breaks.length && breaks[index] <= charIndex) | 138 while (index < breaks.length && breaks[index] <= charIndex) |
139 ++index; | 139 ++index; |
140 return index; | 140 return index; |
141 }, | 141 }, |
142 | 142 |
143 /** @override */ | 143 /** @override */ |
| 144 describeLine: function(lineIndex, triggeredByUser) { |
| 145 if (!this.node_.state.richlyEditable) { |
| 146 cvox.ChromeVoxEditableTextBase.prototype.describeLine.call( |
| 147 this, lineIndex, triggeredByUser); |
| 148 return; |
| 149 } |
| 150 var line = this.getSelectedAutomationLine_(); |
| 151 if (line) { |
| 152 new Output().withRichSpeech(line, null, Output.EventType.NAVIGATE) |
| 153 .go(); |
| 154 } |
| 155 }, |
| 156 |
| 157 /** @override */ |
144 getLineStart: function(lineIndex) { | 158 getLineStart: function(lineIndex) { |
145 if (!this.multiline || lineIndex == 0) | 159 if (!this.multiline || lineIndex == 0) |
146 return 0; | 160 return 0; |
147 var breaks = this.getLineBreaks_(); | 161 var breaks = this.getLineBreaks_(); |
148 return breaks[lineIndex - 1] || this.node_.value.length; | 162 return breaks[lineIndex - 1] || this.node_.value.length; |
149 }, | 163 }, |
150 | 164 |
151 /** @override */ | 165 /** @override */ |
152 getLineEnd: function(lineIndex) { | 166 getLineEnd: function(lineIndex) { |
153 var breaks = this.getLineBreaks_(); | 167 var breaks = this.getLineBreaks_(); |
(...skipping 12 matching lines...) Expand all Loading... |
166 * @private | 180 * @private |
167 */ | 181 */ |
168 getLineBreaks_: function() { | 182 getLineBreaks_: function() { |
169 // node.lineBreaks is undefined when the multiline field has no line | 183 // node.lineBreaks is undefined when the multiline field has no line |
170 // breaks. | 184 // breaks. |
171 return this.node_.lineBreaks || []; | 185 return this.node_.lineBreaks || []; |
172 }, | 186 }, |
173 | 187 |
174 /** @private */ | 188 /** @private */ |
175 outputBraille_: function() { | 189 outputBraille_: function() { |
176 var isFirstLine = false; // First line in a multiline field. | 190 // First line in a multiline field. |
| 191 var lineIndex = this.getLineIndex(this.start); |
| 192 var isFirstLine = this.multiline && lineIndex == 0; |
177 var output = new Output(); | 193 var output = new Output(); |
178 var range; | 194 var range; |
179 if (this.multiline) { | 195 if (this.node_.state.richlyEditable) { |
180 var lineIndex = this.getLineIndex(this.start); | 196 range = this.getSelectedAutomationLine_(); |
181 if (lineIndex == 0) { | 197 } else if (this.multiline) { |
182 isFirstLine = true; | 198 if (isFirstLine) |
183 output.formatForBraille('$name', this.node_); | 199 output.formatForBraille('$name', this.node_); |
184 } | |
185 range = new Range( | 200 range = new Range( |
186 new Cursor(this.node_, this.getLineStart(lineIndex)), | 201 new Cursor(this.node_, this.getLineStart(lineIndex)), |
187 new Cursor(this.node_, this.getLineEnd(lineIndex))); | 202 new Cursor(this.node_, this.getLineEnd(lineIndex))); |
188 } else { | 203 } else { |
189 range = Range.fromNode(this.node_); | 204 range = Range.fromNode(this.node_); |
190 } | 205 } |
191 output.withBraille(range, null, Output.EventType.NAVIGATE); | 206 if (range) |
| 207 output.withBraille(range, null, Output.EventType.NAVIGATE); |
192 if (isFirstLine) | 208 if (isFirstLine) |
193 output.formatForBraille('@tag_textarea'); | 209 output.formatForBraille('@tag_textarea'); |
194 output.go(); | 210 output.go(); |
| 211 }, |
| 212 |
| 213 /** |
| 214 * @return {Range} |
| 215 * @private |
| 216 */ |
| 217 getSelectedAutomationLine_: function() { |
| 218 if (!this.node_.root.anchorObject || !this.node_.root.focusObject) |
| 219 return null; |
| 220 |
| 221 var start = Cursor.fromNode(this.node_.root.anchorObject); |
| 222 start = start.move(Unit.LINE, Movement.BOUND, Dir.BACKWARD); |
| 223 var end = Cursor.fromNode(this.node_.root.focusObject); |
| 224 end = end.move(Unit.LINE, Movement.BOUND, Dir.FORWARD); |
| 225 return new Range(start, end); |
195 } | 226 } |
196 }; | 227 }; |
197 | 228 |
198 /** | 229 /** |
199 * @param {!AutomationNode} node The root editable node, i.e. the root of a | 230 * @param {!AutomationNode} node The root editable node, i.e. the root of a |
200 * contenteditable subtree or a text field. | 231 * contenteditable subtree or a text field. |
201 * @return {editing.TextEditHandler} | 232 * @return {editing.TextEditHandler} |
202 */ | 233 */ |
203 editing.TextEditHandler.createForNode = function(node) { | 234 editing.TextEditHandler.createForNode = function(node) { |
204 var rootFocusedEditable = null; | 235 var rootFocusedEditable = null; |
205 var testNode = node; | 236 var testNode = node; |
206 | 237 |
207 do { | 238 do { |
208 if (testNode.state.focused && testNode.state.editable) | 239 if (testNode.state.focused && testNode.state.editable) |
209 rootFocusedEditable = testNode; | 240 rootFocusedEditable = testNode; |
210 testNode = testNode.parent; | 241 testNode = testNode.parent; |
211 } while (testNode); | 242 } while (testNode); |
212 | 243 |
213 if (rootFocusedEditable) | 244 if (rootFocusedEditable) |
214 return new TextFieldTextEditHandler(rootFocusedEditable); | 245 return new TextFieldTextEditHandler(rootFocusedEditable); |
215 | 246 |
216 return null; | 247 return null; |
217 }; | 248 }; |
218 | 249 |
219 }); | 250 }); |
OLD | NEW |