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 goog.provide('cvox.ChromeVoxEditableContentEditable'); | 5 goog.provide('cvox.ChromeVoxEditableContentEditable'); |
6 goog.provide('cvox.ChromeVoxEditableElement'); | 6 goog.provide('cvox.ChromeVoxEditableElement'); |
7 goog.provide('cvox.ChromeVoxEditableHTMLInput'); | 7 goog.provide('cvox.ChromeVoxEditableHTMLInput'); |
8 goog.provide('cvox.ChromeVoxEditableTextArea'); | 8 goog.provide('cvox.ChromeVoxEditableTextArea'); |
9 goog.provide('cvox.TextHandlerInterface'); | 9 goog.provide('cvox.TextHandlerInterface'); |
10 | 10 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
53 * and EditableTextArea, but that might not apply to a non-DOM text box. | 53 * and EditableTextArea, but that might not apply to a non-DOM text box. |
54 * @param {Element} node A DOM node which allows text input. | 54 * @param {Element} node A DOM node which allows text input. |
55 * @param {string} value The string value of the editable text control. | 55 * @param {string} value The string value of the editable text control. |
56 * @param {number} start The 0-based start cursor/selection index. | 56 * @param {number} start The 0-based start cursor/selection index. |
57 * @param {number} end The 0-based end cursor/selection index. | 57 * @param {number} end The 0-based end cursor/selection index. |
58 * @param {boolean} isPassword Whether the text control if a password field. | 58 * @param {boolean} isPassword Whether the text control if a password field. |
59 * @param {cvox.TtsInterface} tts A TTS object. | 59 * @param {cvox.TtsInterface} tts A TTS object. |
60 * @extends {cvox.ChromeVoxEditableTextBase} | 60 * @extends {cvox.ChromeVoxEditableTextBase} |
61 * @constructor | 61 * @constructor |
62 */ | 62 */ |
63 cvox.ChromeVoxEditableElement = function(node, value, start, end, isPassword, | 63 cvox.ChromeVoxEditableElement = function( |
64 tts) { | 64 node, value, start, end, isPassword, tts) { |
65 goog.base(this, value, start, end, isPassword, tts); | 65 goog.base(this, value, start, end, isPassword, tts); |
66 | 66 |
67 /** | 67 /** |
68 * An optional handler for braille output. | 68 * An optional handler for braille output. |
69 * @type {cvox.BrailleTextHandler|undefined} | 69 * @type {cvox.BrailleTextHandler|undefined} |
70 * @private | 70 * @private |
71 */ | 71 */ |
72 this.brailleHandler_ = cvox.ChromeVox.braille ? | 72 this.brailleHandler_ = cvox.ChromeVox.braille ? |
73 new cvox.BrailleTextHandler(cvox.ChromeVox.braille) : undefined; | 73 new cvox.BrailleTextHandler(cvox.ChromeVox.braille) : |
| 74 undefined; |
74 | 75 |
75 /** | 76 /** |
76 * The DOM node which allows text input. | 77 * The DOM node which allows text input. |
77 * @type {Element} | 78 * @type {Element} |
78 * @protected | 79 * @protected |
79 */ | 80 */ |
80 this.node = node; | 81 this.node = node; |
81 | 82 |
82 /** | 83 /** |
83 * True if the description was just spoken. | 84 * True if the description was just spoken. |
84 * @type {boolean} | 85 * @type {boolean} |
85 * @private | 86 * @private |
86 */ | 87 */ |
87 this.justSpokeDescription_ = false; | 88 this.justSpokeDescription_ = false; |
88 }; | 89 }; |
89 goog.inherits(cvox.ChromeVoxEditableElement, | 90 goog.inherits(cvox.ChromeVoxEditableElement, cvox.ChromeVoxEditableTextBase); |
90 cvox.ChromeVoxEditableTextBase); | |
91 | 91 |
92 | 92 |
93 /** @override */ | 93 /** @override */ |
94 cvox.ChromeVoxEditableElement.prototype.changed = function(evt) { | 94 cvox.ChromeVoxEditableElement.prototype.changed = function(evt) { |
95 // Ignore changes to the cursor and selection if they happen immediately | 95 // Ignore changes to the cursor and selection if they happen immediately |
96 // after the description was just spoken. This avoid double-speaking when, | 96 // after the description was just spoken. This avoid double-speaking when, |
97 // for example, a text field is focused and then a moment later the | 97 // for example, a text field is focused and then a moment later the |
98 // contents are selected. If the value changes, though, this change will | 98 // contents are selected. If the value changes, though, this change will |
99 // not be ignored. | 99 // not be ignored. |
100 if (this.justSpokeDescription_ && this.value == evt.value) { | 100 if (this.justSpokeDescription_ && this.value == evt.value) { |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
174 index = re.lastIndex; | 174 index = re.lastIndex; |
175 } | 175 } |
176 } | 176 } |
177 node.selectionStart = node.selectionEnd = index; | 177 node.selectionStart = node.selectionEnd = index; |
178 cvox.ChromeVoxEventWatcher.handleTextChanged(true); | 178 cvox.ChromeVoxEventWatcher.handleTextChanged(true); |
179 return true; | 179 return true; |
180 }; | 180 }; |
181 | 181 |
182 | 182 |
183 /** @override */ | 183 /** @override */ |
184 cvox.ChromeVoxEditableElement.prototype.moveCursorToNextParagraph = | 184 cvox.ChromeVoxEditableElement.prototype.moveCursorToNextParagraph = function() { |
185 function() { | |
186 var node = this.node; | 185 var node = this.node; |
187 var length = node.value.length; | 186 var length = node.value.length; |
188 var index = node.selectionEnd >= length ? length : | 187 var index = node.selectionEnd >= length ? |
| 188 length : |
189 node.value.indexOf('\n', node.selectionEnd); | 189 node.value.indexOf('\n', node.selectionEnd); |
190 if (index < 0) { | 190 if (index < 0) { |
191 index = length; | 191 index = length; |
192 } | 192 } |
193 node.selectionStart = node.selectionEnd = index + 1; | 193 node.selectionStart = node.selectionEnd = index + 1; |
194 cvox.ChromeVoxEventWatcher.handleTextChanged(true); | 194 cvox.ChromeVoxEventWatcher.handleTextChanged(true); |
195 return true; | 195 return true; |
196 }; | 196 }; |
197 | 197 |
198 | 198 |
199 /** @override */ | 199 /** @override */ |
200 cvox.ChromeVoxEditableElement.prototype.moveCursorToPreviousParagraph = | 200 cvox.ChromeVoxEditableElement.prototype.moveCursorToPreviousParagraph = |
201 function() { | 201 function() { |
202 var node = this.node; | 202 var node = this.node; |
203 var index = node.selectionStart <= 0 ? 0 : | 203 var index = node.selectionStart <= 0 ? |
| 204 0 : |
204 node.value.lastIndexOf('\n', node.selectionStart - 2) + 1; | 205 node.value.lastIndexOf('\n', node.selectionStart - 2) + 1; |
205 if (index < 0) { | 206 if (index < 0) { |
206 index = 0; | 207 index = 0; |
207 } | 208 } |
208 node.selectionStart = node.selectionEnd = index; | 209 node.selectionStart = node.selectionEnd = index; |
209 cvox.ChromeVoxEventWatcher.handleTextChanged(true); | 210 cvox.ChromeVoxEventWatcher.handleTextChanged(true); |
210 return true; | 211 return true; |
211 }; | 212 }; |
212 | 213 |
213 /** | 214 /** |
214 * Shows the current line on the braille display. | 215 * Shows the current line on the braille display. |
215 * @private | 216 * @private |
216 */ | 217 */ |
217 cvox.ChromeVoxEditableElement.prototype.brailleCurrentLine_ = function() { | 218 cvox.ChromeVoxEditableElement.prototype.brailleCurrentLine_ = function() { |
218 if (this.brailleHandler_) { | 219 if (this.brailleHandler_) { |
219 var lineIndex = this.getLineIndex(this.start); | 220 var lineIndex = this.getLineIndex(this.start); |
220 var line = this.getLine(lineIndex); | 221 var line = this.getLine(lineIndex); |
221 // Collapsable whitespace inside the contenteditable is represented | 222 // Collapsable whitespace inside the contenteditable is represented |
222 // as non-breaking spaces. This confuses braille input (which relies on | 223 // as non-breaking spaces. This confuses braille input (which relies on |
223 // the text being added to be the same as the text in the input field). | 224 // the text being added to be the same as the text in the input field). |
224 // Since the non-breaking spaces are just an artifact of how | 225 // Since the non-breaking spaces are just an artifact of how |
225 // contenteditable is implemented, normalize to normal spaces instead. | 226 // contenteditable is implemented, normalize to normal spaces instead. |
226 if (this instanceof cvox.ChromeVoxEditableContentEditable) { | 227 if (this instanceof cvox.ChromeVoxEditableContentEditable) { |
227 line = line.replace(/\u00A0/g, ' '); | 228 line = line.replace(/\u00A0/g, ' '); |
228 } | 229 } |
229 var lineStart = this.getLineStart(lineIndex); | 230 var lineStart = this.getLineStart(lineIndex); |
230 var start = this.start - lineStart; | 231 var start = this.start - lineStart; |
231 var end = Math.min(this.end - lineStart, line.length); | 232 var end = Math.min(this.end - lineStart, line.length); |
232 this.brailleHandler_.changed(line, start, end, this.multiline, this.node, | 233 this.brailleHandler_.changed( |
233 lineStart); | 234 line, start, end, this.multiline, this.node, lineStart); |
234 } | 235 } |
235 }; | 236 }; |
236 | 237 |
237 /******************************************/ | 238 /******************************************/ |
238 | 239 |
239 | 240 |
240 /** | 241 /** |
241 * A subclass of ChromeVoxEditableElement for an HTMLInputElement. | 242 * A subclass of ChromeVoxEditableElement for an HTMLInputElement. |
242 * @param {HTMLInputElement} node The HTMLInputElement node. | 243 * @param {HTMLInputElement} node The HTMLInputElement node. |
243 * @param {cvox.TtsInterface} tts A TTS object. | 244 * @param {cvox.TtsInterface} tts A TTS object. |
244 * @extends {cvox.ChromeVoxEditableElement} | 245 * @extends {cvox.ChromeVoxEditableElement} |
245 * @implements {cvox.TextHandlerInterface} | 246 * @implements {cvox.TextHandlerInterface} |
246 * @constructor | 247 * @constructor |
247 */ | 248 */ |
248 cvox.ChromeVoxEditableHTMLInput = function(node, tts) { | 249 cvox.ChromeVoxEditableHTMLInput = function(node, tts) { |
249 this.node = node; | 250 this.node = node; |
250 this.setup(); | 251 this.setup(); |
251 goog.base(this, | 252 goog.base( |
252 node, | 253 this, node, node.value, node.selectionStart, node.selectionEnd, |
253 node.value, | 254 node.type === 'password', tts); |
254 node.selectionStart, | |
255 node.selectionEnd, | |
256 node.type === 'password', | |
257 tts); | |
258 }; | 255 }; |
259 goog.inherits(cvox.ChromeVoxEditableHTMLInput, | 256 goog.inherits(cvox.ChromeVoxEditableHTMLInput, cvox.ChromeVoxEditableElement); |
260 cvox.ChromeVoxEditableElement); | |
261 | 257 |
262 | 258 |
263 /** | 259 /** |
264 * Performs setup for this input node. | 260 * Performs setup for this input node. |
265 * This accounts for exception-throwing behavior introduced by crbug.com/324360. | 261 * This accounts for exception-throwing behavior introduced by crbug.com/324360. |
266 * @override | 262 * @override |
267 */ | 263 */ |
268 cvox.ChromeVoxEditableHTMLInput.prototype.setup = function() { | 264 cvox.ChromeVoxEditableHTMLInput.prototype.setup = function() { |
269 if (!this.node) { | 265 if (!this.node) { |
270 return; | 266 return; |
(...skipping 18 matching lines...) Expand all Loading... |
289 | 285 |
290 | 286 |
291 /** | 287 /** |
292 * Update the state of the text and selection and describe any changes as | 288 * Update the state of the text and selection and describe any changes as |
293 * appropriate. | 289 * appropriate. |
294 * | 290 * |
295 * @param {boolean} triggeredByUser True if this was triggered by a user action. | 291 * @param {boolean} triggeredByUser True if this was triggered by a user action. |
296 */ | 292 */ |
297 cvox.ChromeVoxEditableHTMLInput.prototype.update = function(triggeredByUser) { | 293 cvox.ChromeVoxEditableHTMLInput.prototype.update = function(triggeredByUser) { |
298 var newValue = this.node.value; | 294 var newValue = this.node.value; |
299 var textChangeEvent = new cvox.TextChangeEvent(newValue, | 295 var textChangeEvent = new cvox.TextChangeEvent( |
300 this.node.selectionStart, | 296 newValue, this.node.selectionStart, this.node.selectionEnd, |
301 this.node.selectionEnd, | 297 triggeredByUser); |
302 triggeredByUser); | |
303 this.changed(textChangeEvent); | 298 this.changed(textChangeEvent); |
304 }; | 299 }; |
305 | 300 |
306 | 301 |
307 /******************************************/ | 302 /******************************************/ |
308 | 303 |
309 | 304 |
310 /** | 305 /** |
311 * A subclass of ChromeVoxEditableElement for an HTMLTextAreaElement. | 306 * A subclass of ChromeVoxEditableElement for an HTMLTextAreaElement. |
312 * @param {HTMLTextAreaElement} node The HTMLTextAreaElement node. | 307 * @param {HTMLTextAreaElement} node The HTMLTextAreaElement node. |
313 * @param {cvox.TtsInterface} tts A TTS object. | 308 * @param {cvox.TtsInterface} tts A TTS object. |
314 * @extends {cvox.ChromeVoxEditableElement} | 309 * @extends {cvox.ChromeVoxEditableElement} |
315 * @implements {cvox.TextHandlerInterface} | 310 * @implements {cvox.TextHandlerInterface} |
316 * @constructor | 311 * @constructor |
317 */ | 312 */ |
318 cvox.ChromeVoxEditableTextArea = function(node, tts) { | 313 cvox.ChromeVoxEditableTextArea = function(node, tts) { |
319 goog.base(this, node, node.value, node.selectionStart, node.selectionEnd, | 314 goog.base( |
| 315 this, node, node.value, node.selectionStart, node.selectionEnd, |
320 false /* isPassword */, tts); | 316 false /* isPassword */, tts); |
321 this.multiline = true; | 317 this.multiline = true; |
322 | 318 |
323 /** | 319 /** |
324 * True if the shadow is up to date with the current value of this text area. | 320 * True if the shadow is up to date with the current value of this text area. |
325 * @type {boolean} | 321 * @type {boolean} |
326 * @private | 322 * @private |
327 */ | 323 */ |
328 this.shadowIsCurrent_ = false; | 324 this.shadowIsCurrent_ = false; |
329 }; | 325 }; |
330 goog.inherits(cvox.ChromeVoxEditableTextArea, | 326 goog.inherits(cvox.ChromeVoxEditableTextArea, cvox.ChromeVoxEditableElement); |
331 cvox.ChromeVoxEditableElement); | |
332 | 327 |
333 | 328 |
334 /** | 329 /** |
335 * An offscreen div used to compute the line numbers. A single div is | 330 * An offscreen div used to compute the line numbers. A single div is |
336 * shared by all instances of the class. | 331 * shared by all instances of the class. |
337 * @type {!cvox.EditableTextAreaShadow|undefined} | 332 * @type {!cvox.EditableTextAreaShadow|undefined} |
338 * @private | 333 * @private |
339 */ | 334 */ |
340 cvox.ChromeVoxEditableTextArea.shadow_; | 335 cvox.ChromeVoxEditableTextArea.shadow_; |
341 | 336 |
342 | 337 |
343 /** | 338 /** |
344 * Update the state of the text and selection and describe any changes as | 339 * Update the state of the text and selection and describe any changes as |
345 * appropriate. | 340 * appropriate. |
346 * | 341 * |
347 * @param {boolean} triggeredByUser True if this was triggered by a user action. | 342 * @param {boolean} triggeredByUser True if this was triggered by a user action. |
348 */ | 343 */ |
349 cvox.ChromeVoxEditableTextArea.prototype.update = function(triggeredByUser) { | 344 cvox.ChromeVoxEditableTextArea.prototype.update = function(triggeredByUser) { |
350 if (this.node.value != this.value) { | 345 if (this.node.value != this.value) { |
351 this.shadowIsCurrent_ = false; | 346 this.shadowIsCurrent_ = false; |
352 } | 347 } |
353 var textChangeEvent = new cvox.TextChangeEvent(this.node.value, | 348 var textChangeEvent = new cvox.TextChangeEvent( |
354 this.node.selectionStart, this.node.selectionEnd, triggeredByUser); | 349 this.node.value, this.node.selectionStart, this.node.selectionEnd, |
| 350 triggeredByUser); |
355 this.changed(textChangeEvent); | 351 this.changed(textChangeEvent); |
356 }; | 352 }; |
357 | 353 |
358 | 354 |
359 /** | 355 /** |
360 * Get the line number corresponding to a particular index. | 356 * Get the line number corresponding to a particular index. |
361 * @param {number} index The 0-based character index. | 357 * @param {number} index The 0-based character index. |
362 * @return {number} The 0-based line number corresponding to that character. | 358 * @return {number} The 0-based line number corresponding to that character. |
363 */ | 359 */ |
364 cvox.ChromeVoxEditableTextArea.prototype.getLineIndex = function(index) { | 360 cvox.ChromeVoxEditableTextArea.prototype.getLineIndex = function(index) { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
409 var node = this.node; | 405 var node = this.node; |
410 var length = node.value.length; | 406 var length = node.value.length; |
411 if (node.selectionEnd >= length) { | 407 if (node.selectionEnd >= length) { |
412 return false; | 408 return false; |
413 } | 409 } |
414 var shadow = this.getShadow(); | 410 var shadow = this.getShadow(); |
415 var lineIndex = shadow.getLineIndex(node.selectionEnd); | 411 var lineIndex = shadow.getLineIndex(node.selectionEnd); |
416 var lineStart = shadow.getLineStart(lineIndex); | 412 var lineStart = shadow.getLineStart(lineIndex); |
417 var offset = node.selectionEnd - lineStart; | 413 var offset = node.selectionEnd - lineStart; |
418 var lastLine = (length == 0) ? 0 : shadow.getLineIndex(length - 1); | 414 var lastLine = (length == 0) ? 0 : shadow.getLineIndex(length - 1); |
419 var newCursorPosition = (lineIndex >= lastLine) ? length : | 415 var newCursorPosition = (lineIndex >= lastLine) ? |
420 Math.min(shadow.getLineStart(lineIndex + 1) + offset, | 416 length : |
| 417 Math.min( |
| 418 shadow.getLineStart(lineIndex + 1) + offset, |
421 shadow.getLineEnd(lineIndex + 1)); | 419 shadow.getLineEnd(lineIndex + 1)); |
422 node.selectionStart = node.selectionEnd = newCursorPosition; | 420 node.selectionStart = node.selectionEnd = newCursorPosition; |
423 cvox.ChromeVoxEventWatcher.handleTextChanged(true); | 421 cvox.ChromeVoxEventWatcher.handleTextChanged(true); |
424 return true; | 422 return true; |
425 }; | 423 }; |
426 | 424 |
427 | 425 |
428 /** @override */ | 426 /** @override */ |
429 cvox.ChromeVoxEditableTextArea.prototype.moveCursorToPreviousLine = function() { | 427 cvox.ChromeVoxEditableTextArea.prototype.moveCursorToPreviousLine = function() { |
430 var node = this.node; | 428 var node = this.node; |
431 if (node.selectionStart <= 0) { | 429 if (node.selectionStart <= 0) { |
432 return false; | 430 return false; |
433 } | 431 } |
434 var shadow = this.getShadow(); | 432 var shadow = this.getShadow(); |
435 var lineIndex = shadow.getLineIndex(node.selectionStart); | 433 var lineIndex = shadow.getLineIndex(node.selectionStart); |
436 var lineStart = shadow.getLineStart(lineIndex); | 434 var lineStart = shadow.getLineStart(lineIndex); |
437 var offset = node.selectionStart - lineStart; | 435 var offset = node.selectionStart - lineStart; |
438 var newCursorPosition = (lineIndex <= 0) ? 0 : | 436 var newCursorPosition = (lineIndex <= 0) ? |
439 Math.min(shadow.getLineStart(lineIndex - 1) + offset, | 437 0 : |
| 438 Math.min( |
| 439 shadow.getLineStart(lineIndex - 1) + offset, |
440 shadow.getLineEnd(lineIndex - 1)); | 440 shadow.getLineEnd(lineIndex - 1)); |
441 node.selectionStart = node.selectionEnd = newCursorPosition; | 441 node.selectionStart = node.selectionEnd = newCursorPosition; |
442 cvox.ChromeVoxEventWatcher.handleTextChanged(true); | 442 cvox.ChromeVoxEventWatcher.handleTextChanged(true); |
443 return true; | 443 return true; |
444 }; | 444 }; |
445 | 445 |
446 | 446 |
447 /******************************************/ | 447 /******************************************/ |
448 | 448 |
449 | 449 |
(...skipping 19 matching lines...) Expand all Loading... |
469 * @private | 469 * @private |
470 */ | 470 */ |
471 this.extractorIsCurrent_ = false; | 471 this.extractorIsCurrent_ = false; |
472 | 472 |
473 var extractor = this.getExtractor(); | 473 var extractor = this.getExtractor(); |
474 this.value = extractor.getText(); | 474 this.value = extractor.getText(); |
475 this.start = extractor.getStartIndex(); | 475 this.start = extractor.getStartIndex(); |
476 this.end = extractor.getEndIndex(); | 476 this.end = extractor.getEndIndex(); |
477 this.multiline = true; | 477 this.multiline = true; |
478 }; | 478 }; |
479 goog.inherits(cvox.ChromeVoxEditableContentEditable, | 479 goog.inherits( |
480 cvox.ChromeVoxEditableElement); | 480 cvox.ChromeVoxEditableContentEditable, cvox.ChromeVoxEditableElement); |
481 | 481 |
482 /** | 482 /** |
483 * A helper used to compute the line numbers. A single object is | 483 * A helper used to compute the line numbers. A single object is |
484 * shared by all instances of the class. | 484 * shared by all instances of the class. |
485 * @type {!cvox.ContentEditableExtractor|undefined} | 485 * @type {!cvox.ContentEditableExtractor|undefined} |
486 * @private | 486 * @private |
487 */ | 487 */ |
488 cvox.ChromeVoxEditableContentEditable.extractor_; | 488 cvox.ChromeVoxEditableContentEditable.extractor_; |
489 | 489 |
490 | 490 |
491 /** | 491 /** |
492 * Update the state of the text and selection and describe any changes as | 492 * Update the state of the text and selection and describe any changes as |
493 * appropriate. | 493 * appropriate. |
494 * | 494 * |
495 * @param {boolean} triggeredByUser True if this was triggered by a user action. | 495 * @param {boolean} triggeredByUser True if this was triggered by a user action. |
496 */ | 496 */ |
497 cvox.ChromeVoxEditableContentEditable.prototype.update = | 497 cvox.ChromeVoxEditableContentEditable.prototype.update = function( |
498 function(triggeredByUser) { | 498 triggeredByUser) { |
499 this.extractorIsCurrent_ = false; | 499 this.extractorIsCurrent_ = false; |
500 var textChangeEvent = new cvox.TextChangeEvent( | 500 var textChangeEvent = new cvox.TextChangeEvent( |
501 this.getExtractor().getText(), | 501 this.getExtractor().getText(), this.getExtractor().getStartIndex(), |
502 this.getExtractor().getStartIndex(), | 502 this.getExtractor().getEndIndex(), triggeredByUser); |
503 this.getExtractor().getEndIndex(), | |
504 triggeredByUser); | |
505 this.changed(textChangeEvent); | 503 this.changed(textChangeEvent); |
506 }; | 504 }; |
507 | 505 |
508 | 506 |
509 /** | 507 /** |
510 * Get the line number corresponding to a particular index. | 508 * Get the line number corresponding to a particular index. |
511 * @param {number} index The 0-based character index. | 509 * @param {number} index The 0-based character index. |
512 * @return {number} The 0-based line number corresponding to that character. | 510 * @return {number} The 0-based line number corresponding to that character. |
513 */ | 511 */ |
514 cvox.ChromeVoxEditableContentEditable.prototype.getLineIndex = function(index) { | 512 cvox.ChromeVoxEditableContentEditable.prototype.getLineIndex = function(index) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
548 } | 546 } |
549 if (!this.extractorIsCurrent_) { | 547 if (!this.extractorIsCurrent_) { |
550 extractor.update(this.node); | 548 extractor.update(this.node); |
551 this.extractorIsCurrent_ = true; | 549 this.extractorIsCurrent_ = true; |
552 } | 550 } |
553 return extractor; | 551 return extractor; |
554 }; | 552 }; |
555 | 553 |
556 | 554 |
557 /** @override */ | 555 /** @override */ |
558 cvox.ChromeVoxEditableContentEditable.prototype.changed = | 556 cvox.ChromeVoxEditableContentEditable.prototype.changed = function(evt) { |
559 function(evt) { | |
560 if (!evt.triggeredByUser) { | 557 if (!evt.triggeredByUser) { |
561 return; | 558 return; |
562 } | 559 } |
563 // Take over here if we can't describe a change; assume it's a blank line. | 560 // Take over here if we can't describe a change; assume it's a blank line. |
564 if (!this.shouldDescribeChange(evt)) { | 561 if (!this.shouldDescribeChange(evt)) { |
565 this.speak(Msgs.getMsg('text_box_blank'), true); | 562 this.speak(Msgs.getMsg('text_box_blank'), true); |
566 if (this.brailleHandler_) { | 563 if (this.brailleHandler_) { |
567 this.brailleHandler_.changed('' /*line*/, 0 /*start*/, 0 /*end*/, | 564 this.brailleHandler_.changed( |
568 true /*multiline*/, null /*element*/, | 565 '' /*line*/, 0 /*start*/, 0 /*end*/, true /*multiline*/, |
569 evt.start /*lineStart*/); | 566 null /*element*/, evt.start /*lineStart*/); |
570 } | 567 } |
571 } else { | 568 } else { |
572 goog.base(this, 'changed', evt); | 569 goog.base(this, 'changed', evt); |
573 } | 570 } |
574 }; | 571 }; |
575 | 572 |
576 | 573 |
577 /** @override */ | 574 /** @override */ |
578 cvox.ChromeVoxEditableContentEditable.prototype.moveCursorToNextCharacter = | 575 cvox.ChromeVoxEditableContentEditable.prototype.moveCursorToNextCharacter = |
579 function() { | 576 function() { |
(...skipping 25 matching lines...) Expand all Loading... |
605 function() { | 602 function() { |
606 window.getSelection().modify('move', 'backward', 'paragraph'); | 603 window.getSelection().modify('move', 'backward', 'paragraph'); |
607 cvox.ChromeVoxEventWatcher.handleTextChanged(true); | 604 cvox.ChromeVoxEventWatcher.handleTextChanged(true); |
608 return true; | 605 return true; |
609 }; | 606 }; |
610 | 607 |
611 | 608 |
612 /** | 609 /** |
613 * @override | 610 * @override |
614 */ | 611 */ |
615 cvox.ChromeVoxEditableContentEditable.prototype.shouldDescribeChange = | 612 cvox.ChromeVoxEditableContentEditable.prototype.shouldDescribeChange = function( |
616 function(evt) { | 613 evt) { |
617 var sel = window.getSelection(); | 614 var sel = window.getSelection(); |
618 var cursor = new cvox.Cursor(sel.baseNode, sel.baseOffset, ''); | 615 var cursor = new cvox.Cursor(sel.baseNode, sel.baseOffset, ''); |
619 | 616 |
620 // This is a very specific work around because of our buggy content editable | 617 // This is a very specific work around because of our buggy content editable |
621 // support. Blank new lines are not captured in the line indexing data | 618 // support. Blank new lines are not captured in the line indexing data |
622 // structures. | 619 // structures. |
623 // Scenario: given a piece of text like: | 620 // Scenario: given a piece of text like: |
624 // | 621 // |
625 // Some Title | 622 // Some Title |
626 // | 623 // |
627 // Description | 624 // Description |
628 // Footer | 625 // Footer |
629 // | 626 // |
630 // The new lines after Title are not traversed to by TraverseUtil. A root fix | 627 // The new lines after Title are not traversed to by TraverseUtil. A root fix |
631 // would make changes there. However, considering the fickle nature of that | 628 // would make changes there. However, considering the fickle nature of that |
632 // code, we specifically detect for new lines here. | 629 // code, we specifically detect for new lines here. |
633 if (Math.abs(this.start - evt.start) != 1 && | 630 if (Math.abs(this.start - evt.start) != 1 && this.start == this.end && |
634 this.start == this.end && | 631 evt.start == evt.end && sel.baseNode == sel.extentNode && |
635 evt.start == evt.end && | |
636 sel.baseNode == sel.extentNode && | |
637 sel.baseOffset == sel.extentOffset && | 632 sel.baseOffset == sel.extentOffset && |
638 sel.baseNode.nodeType == Node.ELEMENT_NODE && | 633 sel.baseNode.nodeType == Node.ELEMENT_NODE && |
639 sel.baseNode.querySelector('BR') && | 634 sel.baseNode.querySelector('BR') && |
640 cvox.TraverseUtil.forwardsChar(cursor, [], [])) { | 635 cvox.TraverseUtil.forwardsChar(cursor, [], [])) { |
641 // This case detects if the range selection surrounds a new line, | 636 // This case detects if the range selection surrounds a new line, |
642 // but there is still content after the new line (like the example | 637 // but there is still content after the new line (like the example |
643 // above after "Title"). In these cases, we "pretend" we're the | 638 // above after "Title"). In these cases, we "pretend" we're the |
644 // last character so we speak "blank". | 639 // last character so we speak "blank". |
645 return false; | 640 return false; |
646 } | 641 } |
647 | 642 |
648 // Otherwise, we should never speak "blank" no matter what (even if | 643 // Otherwise, we should never speak "blank" no matter what (even if |
649 // we're at the end of a content editable). | 644 // we're at the end of a content editable). |
650 return true; | 645 return true; |
651 }; | 646 }; |
OLD | NEW |