| 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 |