Chromium Code Reviews| 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 Provides output services for ChromeVox. | 6 * @fileoverview Provides output services for ChromeVox. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 goog.provide('Output'); | 9 goog.provide('Output'); |
| 10 goog.provide('Output.EventType'); | 10 goog.provide('Output.EventType'); |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 86 msgId: 'aria_role_alert', | 86 msgId: 'aria_role_alert', |
| 87 earcon: 'ALERT_NONMODAL', | 87 earcon: 'ALERT_NONMODAL', |
| 88 }, | 88 }, |
| 89 button: { | 89 button: { |
| 90 msgId: 'tag_button', | 90 msgId: 'tag_button', |
| 91 earcon: 'BUTTON' | 91 earcon: 'BUTTON' |
| 92 }, | 92 }, |
| 93 checkbox: { | 93 checkbox: { |
| 94 msgId: 'input_type_checkbox' | 94 msgId: 'input_type_checkbox' |
| 95 }, | 95 }, |
| 96 dialog: { | |
| 97 msgId: 'dialog' | |
| 98 }, | |
| 96 heading: { | 99 heading: { |
| 97 msgId: 'aria_role_heading', | 100 msgId: 'aria_role_heading', |
| 98 }, | 101 }, |
| 99 link: { | 102 link: { |
| 100 msgId: 'tag_link', | 103 msgId: 'tag_link', |
| 101 earcon: 'LINK' | 104 earcon: 'LINK' |
| 102 }, | 105 }, |
| 103 listItem: { | 106 listItem: { |
| 104 msgId: 'ARIA_ROLE_LISTITEM', | 107 msgId: 'ARIA_ROLE_LISTITEM', |
| 105 earcon: 'list_item' | 108 earcon: 'list_item' |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 203 toolbar: { | 206 toolbar: { |
| 204 enter: '$name $role' | 207 enter: '$name $role' |
| 205 }, | 208 }, |
| 206 window: { | 209 window: { |
| 207 enter: '$name', | 210 enter: '$name', |
| 208 speak: '@describe_window($name) $earcon(OBJECT_OPEN)' | 211 speak: '@describe_window($name) $earcon(OBJECT_OPEN)' |
| 209 } | 212 } |
| 210 }, | 213 }, |
| 211 menuStart: { | 214 menuStart: { |
| 212 'default': { | 215 'default': { |
| 213 speak: '@chrome_menu_opened($name) $role $earcon(OBJECT_OPEN)' | 216 speak: '@chrome_menu_opened($name) $earcon(OBJECT_OPEN)' |
| 214 } | 217 } |
| 215 }, | 218 }, |
| 216 menuEnd: { | 219 menuEnd: { |
| 217 'default': { | 220 'default': { |
| 218 speak: '$earcon(OBJECT_CLOSE)' | 221 speak: '$earcon(OBJECT_CLOSE)' |
| 219 } | 222 } |
| 220 }, | 223 }, |
| 221 menuListValueChanged: { | 224 menuListValueChanged: { |
| 222 'default': { | 225 'default': { |
| 223 speak: '$value $name ' + | 226 speak: '$value $name ' + |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 251 this.action_ = action; | 254 this.action_ = action; |
| 252 }; | 255 }; |
| 253 | 256 |
| 254 Output.Action.prototype = { | 257 Output.Action.prototype = { |
| 255 run: function() { | 258 run: function() { |
| 256 this.action_(); | 259 this.action_(); |
| 257 } | 260 } |
| 258 }; | 261 }; |
| 259 | 262 |
| 260 /** | 263 /** |
| 264 * Annotation for string splitting. | |
| 265 * @constructor | |
| 266 */ | |
| 267 Output.StringSplit = function() {}; | |
| 268 | |
| 269 /** | |
| 261 * Annotation for selection. | 270 * Annotation for selection. |
| 262 * @param {number} startIndex | 271 * @param {number} startIndex |
| 263 * @param {number} endIndex | 272 * @param {number} endIndex |
| 264 * @constructor | 273 * @constructor |
| 265 */ | 274 */ |
| 266 Output.SelectionSpan = function(startIndex, endIndex) { | 275 Output.SelectionSpan = function(startIndex, endIndex) { |
| 267 // TODO(dtseng): Direction lost below; should preserve for braille panning. | 276 // TODO(dtseng): Direction lost below; should preserve for braille panning. |
| 268 this.startIndex = startIndex < endIndex ? startIndex : endIndex; | 277 this.startIndex = startIndex < endIndex ? startIndex : endIndex; |
| 269 this.endIndex = endIndex > startIndex ? endIndex : startIndex; | 278 this.endIndex = endIndex > startIndex ? endIndex : startIndex; |
| 270 }; | 279 }; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 343 return this; | 352 return this; |
| 344 }, | 353 }, |
| 345 | 354 |
| 346 /** | 355 /** |
| 347 * Executes all specified output. | 356 * Executes all specified output. |
| 348 */ | 357 */ |
| 349 go: function() { | 358 go: function() { |
| 350 // Speech. | 359 // Speech. |
| 351 var buff = this.buffer_; | 360 var buff = this.buffer_; |
| 352 if (buff.toString()) { | 361 if (buff.toString()) { |
| 353 if (this.speechStartCallback_) | 362 var splits = |
| 354 this.speechProperties_['startCallback'] = this.speechStartCallback_; | 363 buff.getSpansInstanceOf(Output.StringSplit).map(function(split) { |
| 355 if (this.speechEndCallback_) { | 364 return buff.getSpanStart(split); |
| 356 this.speechProperties_['endCallback'] = this.speechEndCallback_; | 365 }); |
| 366 | |
| 367 if (splits.length == 0) { | |
| 368 cvox.ChromeVox.tts.speak(buff.toString(), | |
| 369 cvox.QueueMode.FLUSH, this.speechProperties_); | |
| 370 } else { | |
| 371 var start = -1; | |
| 372 var seen = {}; | |
| 373 splits = splits.filter(function(i) { | |
|
Peter Lundblad
2015/03/30 15:27:46
When are we going to have duplicates here?
| |
| 374 if (seen[i]) | |
| 375 return false; | |
| 376 seen[i] = true; | |
| 377 return true; | |
| 378 }); | |
| 379 var queueMode = cvox.QueueMode.FLUSH; | |
| 380 do { | |
| 381 start++; | |
| 382 if (this.speechStartCallback_ && start == 0) | |
| 383 this.speechProperties_['startCallback'] = this.speechStartCallback_; | |
| 384 else | |
| 385 this.speechProperties_['startCallback'] = null; | |
| 386 if (this.speechEndCallback_ && start == splits.length - 1) | |
| 387 this.speechProperties_['endCallback'] = this.speechEndCallback_; | |
| 388 else | |
| 389 this.speechProperties_['endCallback'] = null; | |
| 390 var startIndex = splits[start]; | |
| 391 var endIndex = splits[start + 1] || buff.getLength(); | |
| 392 | |
| 393 cvox.ChromeVox.tts.speak(buff.substring(startIndex, | |
| 394 endIndex).toString(), queueMode, this.speechProperties_); | |
| 395 queueMode = cvox.QueueMode.QUEUE; | |
| 396 } while (start < splits.length - 1); | |
| 357 } | 397 } |
| 358 | |
| 359 cvox.ChromeVox.tts.speak( | |
| 360 buff.toString(), cvox.QueueMode.FLUSH, this.speechProperties_); | |
| 361 } | 398 } |
| 362 | 399 |
| 363 var actions = buff.getSpansInstanceOf(Output.Action); | 400 var actions = buff.getSpansInstanceOf(Output.Action); |
| 364 if (actions) { | 401 if (actions) { |
| 365 actions.forEach(function(a) { | 402 actions.forEach(function(a) { |
| 366 a.run(); | 403 a.run(); |
| 367 }); | 404 }); |
| 368 } | 405 } |
| 369 | 406 |
| 370 // Braille. | 407 // Braille. |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 429 | 466 |
| 430 // Hacky way to support args. | 467 // Hacky way to support args. |
| 431 if (typeof(format) == 'string') { | 468 if (typeof(format) == 'string') { |
| 432 format = format.replace(/([,:])\W/g, '$1'); | 469 format = format.replace(/([,:])\W/g, '$1'); |
| 433 tokens = format.split(' '); | 470 tokens = format.split(' '); |
| 434 } else { | 471 } else { |
| 435 tokens = [format]; | 472 tokens = [format]; |
| 436 } | 473 } |
| 437 | 474 |
| 438 tokens.forEach(function(token) { | 475 tokens.forEach(function(token) { |
| 476 // Always split at the beginning of each token for speech. | |
| 477 var splitOptions = {}; | |
| 478 splitOptions.annotation = new Output.StringSplit(); | |
| 479 this.addToSpannable_(buff, new cvox.Spannable(), splitOptions); | |
| 480 | |
| 439 // Ignore empty tokens. | 481 // Ignore empty tokens. |
| 440 if (!token) | 482 if (!token) |
| 441 return; | 483 return; |
| 442 | 484 |
| 443 // Parse the token. | 485 // Parse the token. |
| 444 var tree; | 486 var tree; |
| 445 if (typeof(token) == 'string') | 487 if (typeof(token) == 'string') |
| 446 tree = this.createParseTree(token); | 488 tree = this.createParseTree(token); |
| 447 else | 489 else |
| 448 tree = token; | 490 tree = token; |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 615 }.bind(this); | 657 }.bind(this); |
| 616 | 658 |
| 617 while (cursor.getNode() != range.getEnd().getNode()) { | 659 while (cursor.getNode() != range.getEnd().getNode()) { |
| 618 var node = cursor.getNode(); | 660 var node = cursor.getNode(); |
| 619 this.addToSpannable_( | 661 this.addToSpannable_( |
| 620 rangeBuff, formatNodeAndAncestors(node, prevNode)); | 662 rangeBuff, formatNodeAndAncestors(node, prevNode)); |
| 621 prevNode = node; | 663 prevNode = node; |
| 622 cursor = cursor.move(cursors.Unit.NODE, | 664 cursor = cursor.move(cursors.Unit.NODE, |
| 623 cursors.Movement.DIRECTIONAL, | 665 cursors.Movement.DIRECTIONAL, |
| 624 Dir.FORWARD); | 666 Dir.FORWARD); |
| 667 var options = {}; | |
| 668 options.annotation = new Output.StringSplit(); | |
| 669 this.addToSpannable_(rangeBuff, new cvox.Spannable(), options); | |
| 625 } | 670 } |
| 626 var lastNode = range.getEnd().getNode(); | 671 var lastNode = range.getEnd().getNode(); |
| 627 this.addToSpannable_(rangeBuff, formatNodeAndAncestors(lastNode, prevNode)); | 672 this.addToSpannable_(rangeBuff, formatNodeAndAncestors(lastNode, prevNode)); |
| 628 }, | 673 }, |
| 629 | 674 |
| 630 /** | 675 /** |
| 631 * @param {!chrome.automation.AutomationNode} node | 676 * @param {!chrome.automation.AutomationNode} node |
| 632 * @param {!chrome.automation.AutomationNode} prevNode | 677 * @param {!chrome.automation.AutomationNode} prevNode |
| 633 * @param {chrome.automation.EventType|string} type | 678 * @param {chrome.automation.EventType|string} type |
| 634 * @param {!cvox.Spannable} buff | 679 * @param {!cvox.Spannable} buff |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 720 if (startIndex === endIndex) | 765 if (startIndex === endIndex) |
| 721 endIndex++; | 766 endIndex++; |
| 722 this.addToSpannable_( | 767 this.addToSpannable_( |
| 723 buff, range.getStart().getText().substring(startIndex, endIndex)); | 768 buff, range.getStart().getText().substring(startIndex, endIndex)); |
| 724 }, | 769 }, |
| 725 | 770 |
| 726 /** | 771 /** |
| 727 * Adds to the given buffer with proper delimiters added. | 772 * Adds to the given buffer with proper delimiters added. |
| 728 * @param {!cvox.Spannable} spannable | 773 * @param {!cvox.Spannable} spannable |
| 729 * @param {string|!cvox.Spannable} value | 774 * @param {string|!cvox.Spannable} value |
| 730 * @param {{ifEmpty: boolean, | 775 * @param {{ifEmpty: (boolean|undefined), |
| 731 * annotation: (string|Output.Action|undefined)}=} opt_options | 776 * annotation: *}=} opt_options |
| 732 */ | 777 */ |
| 733 addToSpannable_: function(spannable, value, opt_options) { | 778 addToSpannable_: function(spannable, value, opt_options) { |
| 734 opt_options = opt_options || {ifEmpty: false, annotation: undefined}; | 779 opt_options = opt_options || {ifEmpty: false, annotation: undefined}; |
| 735 if ((!value || value.length == 0) && !opt_options.annotation) | 780 if ((!value || value.length == 0) && !opt_options.annotation) |
| 736 return; | 781 return; |
| 737 | 782 |
| 738 var spannableToAdd = new cvox.Spannable(value, opt_options.annotation); | 783 var spannableToAdd = new cvox.Spannable(value, opt_options.annotation); |
| 739 if (spannable.getLength() == 0) { | 784 if (spannable.getLength() == 0) { |
| 740 spannable.append(spannableToAdd); | 785 spannable.append(spannableToAdd); |
| 741 return; | 786 return; |
| 742 } | 787 } |
| 743 | 788 |
| 744 if (opt_options.ifEmpty && | 789 if (opt_options.ifEmpty && |
| 745 opt_options.annotation && | 790 opt_options.annotation && |
| 746 (spannable.getSpanStart(opt_options.annotation) != undefined || | 791 (spannable.getSpanStart(opt_options.annotation) != undefined || |
| 747 spannable.getSpanStart( | 792 spannable.getSpanStart( |
| 748 Output.ATTRIBUTE_ALIAS[opt_options.annotation]) != undefined)) | 793 Output.ATTRIBUTE_ALIAS[opt_options.annotation]) != undefined)) |
| 749 return; | 794 return; |
| 750 | 795 |
| 751 var prefixed = new cvox.Spannable(Output.SPACE); | 796 // Figure out if we need to add the spacing prefix. |
| 797 var needsPrefix = this.formatOptions_.braille; | |
| 798 if (needsPrefix) { | |
|
Peter Lundblad
2015/03/30 15:27:46
Indentation.
| |
| 799 needsPrefix = value instanceof cvox.Spannable ? | |
| 800 value.getLength() > 0 : value.length > 0; | |
| 801 } | |
| 802 var prefixed = new cvox.Spannable(needsPrefix ? Output.SPACE : ''); | |
| 752 prefixed.append(spannableToAdd); | 803 prefixed.append(spannableToAdd); |
| 753 spannable.append(prefixed); | 804 spannable.append(prefixed); |
| 754 }, | 805 }, |
| 755 | 806 |
| 756 /** | 807 /** |
| 757 * Parses the token containing a custom function and returns a tree. | 808 * Parses the token containing a custom function and returns a tree. |
| 758 * @param {string} inputStr | 809 * @param {string} inputStr |
| 759 * @return {Object} | 810 * @return {Object} |
| 760 */ | 811 */ |
| 761 createParseTree: function(inputStr) { | 812 createParseTree: function(inputStr) { |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 787 } | 838 } |
| 788 | 839 |
| 789 if (currentNode != root) | 840 if (currentNode != root) |
| 790 throw 'Unbalanced parenthesis.'; | 841 throw 'Unbalanced parenthesis.'; |
| 791 | 842 |
| 792 return root; | 843 return root; |
| 793 } | 844 } |
| 794 }; | 845 }; |
| 795 | 846 |
| 796 }); // goog.scope | 847 }); // goog.scope |
| OLD | NEW |