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 |