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 = constants.Dir; | 24 var Dir = AutomationUtil.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 */ | |
158 getLineStart: function(lineIndex) { | 144 getLineStart: function(lineIndex) { |
159 if (!this.multiline || lineIndex == 0) | 145 if (!this.multiline || lineIndex == 0) |
160 return 0; | 146 return 0; |
161 var breaks = this.getLineBreaks_(); | 147 var breaks = this.getLineBreaks_(); |
162 return breaks[lineIndex - 1] || this.node_.value.length; | 148 return breaks[lineIndex - 1] || this.node_.value.length; |
163 }, | 149 }, |
164 | 150 |
165 /** @override */ | 151 /** @override */ |
166 getLineEnd: function(lineIndex) { | 152 getLineEnd: function(lineIndex) { |
167 var breaks = this.getLineBreaks_(); | 153 var breaks = this.getLineBreaks_(); |
(...skipping 12 matching lines...) Expand all Loading... |
180 * @private | 166 * @private |
181 */ | 167 */ |
182 getLineBreaks_: function() { | 168 getLineBreaks_: function() { |
183 // node.lineBreaks is undefined when the multiline field has no line | 169 // node.lineBreaks is undefined when the multiline field has no line |
184 // breaks. | 170 // breaks. |
185 return this.node_.lineBreaks || []; | 171 return this.node_.lineBreaks || []; |
186 }, | 172 }, |
187 | 173 |
188 /** @private */ | 174 /** @private */ |
189 outputBraille_: function() { | 175 outputBraille_: function() { |
190 // First line in a multiline field. | 176 var isFirstLine = false; // First line in a multiline field. |
191 var lineIndex = this.getLineIndex(this.start); | |
192 var isFirstLine = this.multiline && lineIndex == 0; | |
193 var output = new Output(); | 177 var output = new Output(); |
194 var range; | 178 var range; |
195 if (this.node_.state.richlyEditable) { | 179 if (this.multiline) { |
196 range = this.getSelectedAutomationLine_(); | 180 var lineIndex = this.getLineIndex(this.start); |
197 } else if (this.multiline) { | 181 if (lineIndex == 0) { |
198 if (isFirstLine) | 182 isFirstLine = true; |
199 output.formatForBraille('$name', this.node_); | 183 output.formatForBraille('$name', this.node_); |
| 184 } |
200 range = new Range( | 185 range = new Range( |
201 new Cursor(this.node_, this.getLineStart(lineIndex)), | 186 new Cursor(this.node_, this.getLineStart(lineIndex)), |
202 new Cursor(this.node_, this.getLineEnd(lineIndex))); | 187 new Cursor(this.node_, this.getLineEnd(lineIndex))); |
203 } else { | 188 } else { |
204 range = Range.fromNode(this.node_); | 189 range = Range.fromNode(this.node_); |
205 } | 190 } |
206 if (range) | 191 output.withBraille(range, null, Output.EventType.NAVIGATE); |
207 output.withBraille(range, null, Output.EventType.NAVIGATE); | |
208 if (isFirstLine) | 192 if (isFirstLine) |
209 output.formatForBraille('@tag_textarea'); | 193 output.formatForBraille('@tag_textarea'); |
210 output.go(); | 194 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); | |
226 } | 195 } |
227 }; | 196 }; |
228 | 197 |
229 /** | 198 /** |
230 * @param {!AutomationNode} node The root editable node, i.e. the root of a | 199 * @param {!AutomationNode} node The root editable node, i.e. the root of a |
231 * contenteditable subtree or a text field. | 200 * contenteditable subtree or a text field. |
232 * @return {editing.TextEditHandler} | 201 * @return {editing.TextEditHandler} |
233 */ | 202 */ |
234 editing.TextEditHandler.createForNode = function(node) { | 203 editing.TextEditHandler.createForNode = function(node) { |
235 var rootFocusedEditable = null; | 204 var rootFocusedEditable = null; |
236 var testNode = node; | 205 var testNode = node; |
237 | 206 |
238 do { | 207 do { |
239 if (testNode.state.focused && testNode.state.editable) | 208 if (testNode.state.focused && testNode.state.editable) |
240 rootFocusedEditable = testNode; | 209 rootFocusedEditable = testNode; |
241 testNode = testNode.parent; | 210 testNode = testNode.parent; |
242 } while (testNode); | 211 } while (testNode); |
243 | 212 |
244 if (rootFocusedEditable) | 213 if (rootFocusedEditable) |
245 return new TextFieldTextEditHandler(rootFocusedEditable); | 214 return new TextFieldTextEditHandler(rootFocusedEditable); |
246 | 215 |
247 return null; | 216 return null; |
248 }; | 217 }; |
249 | 218 |
250 }); | 219 }); |
OLD | NEW |