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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
44 */ | 44 */ |
45 Output = function() { | 45 Output = function() { |
46 // TODO(dtseng): Include braille specific rules. | 46 // TODO(dtseng): Include braille specific rules. |
47 /** @type {!cvox.Spannable} */ | 47 /** @type {!cvox.Spannable} */ |
48 this.buffer_ = new cvox.Spannable(); | 48 this.buffer_ = new cvox.Spannable(); |
49 /** @type {!cvox.Spannable} */ | 49 /** @type {!cvox.Spannable} */ |
50 this.brailleBuffer_ = new cvox.Spannable(); | 50 this.brailleBuffer_ = new cvox.Spannable(); |
51 /** @type {!Array.<Object>} */ | 51 /** @type {!Array.<Object>} */ |
52 this.locations_ = []; | 52 this.locations_ = []; |
53 /** @type {function()} */ | 53 /** @type {function()} */ |
54 this.speechStartCallback_ = function() {}; | 54 this.speechStartCallback_; |
55 /** @type {function()} */ | 55 /** @type {function()} */ |
56 this.speechEndCallback_ = function() {}; | 56 this.speechEndCallback_; |
57 /** @type {function()} */ | 57 /** @type {function()} */ |
58 this.speechInterruptedCallback_ = function() {}; | 58 this.speechInterruptedCallback_; |
59 | 59 |
60 /** | 60 /** |
61 * Current global options. | 61 * Current global options. |
62 * @type {{speech: boolean, braille: boolean, location: boolean}} | 62 * @type {{speech: boolean, braille: boolean, location: boolean}} |
63 */ | 63 */ |
64 this.formatOptions_ = {speech: true, braille: false, location: true}; | 64 this.formatOptions_ = {speech: true, braille: false, location: true}; |
65 | |
66 /** | |
67 * Speech properties to apply to the entire output. | |
68 * @type {!Object.<string, *>} | |
69 */ | |
70 this.speechProperties_ = {}; | |
65 }; | 71 }; |
66 | 72 |
67 /** | 73 /** |
68 * Delimiter to use between output values. | 74 * Delimiter to use between output values. |
69 * @type {string} | 75 * @type {string} |
70 */ | 76 */ |
71 Output.SPACE = ' '; | 77 Output.SPACE = ' '; |
72 | 78 |
73 /** | 79 /** |
74 * Rules specifying format of AutomationNodes for output. | 80 * Rules specifying format of AutomationNodes for output. |
75 * @type {!Object.<string, Object.<string, Object.<string, string>>>} | 81 * @type {!Object.<string, Object.<string, Object.<string, string>>>} |
76 */ | 82 */ |
77 Output.RULES = { | 83 Output.RULES = { |
78 navigate: { | 84 navigate: { |
79 'default': { | 85 'default': { |
80 speak: '$name $role $value', | 86 speak: '$name $role $value', |
81 braille: '' | 87 braille: '' |
82 }, | 88 }, |
83 alert: { | 89 alert: { |
84 speak: '@aria_role_alert $name $earcon(ALERT_NONMODAL)' | 90 speak: '!doNotInterrupt' + |
dmazzoni
2015/02/04 08:50:16
is there supposed to be a space after the !doNotIn
David Tseng
2015/02/04 16:57:13
Good catch! The parser splits expressions on space
| |
91 '@aria_role_alert $name $earcon(ALERT_NONMODAL) $descendants' | |
85 }, | 92 }, |
86 button: { | 93 button: { |
87 speak: '$name $earcon(BUTTON, @tag_button)' | 94 speak: '$name $earcon(BUTTON, @tag_button)' |
88 }, | 95 }, |
89 checkBox: { | 96 checkBox: { |
90 speak: '$or($checked, @describe_checkbox_checked($name), ' + | 97 speak: '$or($checked, @describe_checkbox_checked($name), ' + |
91 '@describe_checkbox_unchecked($name)) ' + | 98 '@describe_checkbox_unchecked($name)) ' + |
92 '$or($checked, ' + | 99 '$or($checked, ' + |
93 '$earcon(CHECK_ON, @input_type_checkbox), ' + | 100 '$earcon(CHECK_ON, @input_type_checkbox), ' + |
94 '$earcon(CHECK_OFF, @input_type_checkbox))' | 101 '$earcon(CHECK_OFF, @input_type_checkbox))' |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
300 | 307 |
301 /** | 308 /** |
302 * Executes all specified output. | 309 * Executes all specified output. |
303 */ | 310 */ |
304 go: function() { | 311 go: function() { |
305 // Speech. | 312 // Speech. |
306 var buff = this.buffer_; | 313 var buff = this.buffer_; |
307 | 314 |
308 var onEvent = function(evt) { | 315 var onEvent = function(evt) { |
309 switch (evt.type) { | 316 switch (evt.type) { |
310 case 'start': this.speechStartCallback_(); break; | 317 case 'start': |
311 case 'end': this.speechEndCallback_(); break; | 318 this.speechStartCallback_(); |
312 case 'interrupted': this.speechInterruptedCallback_(); break; | 319 break; |
320 case 'end': | |
321 this.speechEndCallback_(); | |
322 break; | |
323 case 'interrupted': | |
324 this.speechInterruptedCallback_ && this.speechInterruptedCallback_(); | |
325 break; | |
313 } | 326 } |
314 }.bind(this); | 327 }.bind(this); |
315 | 328 |
316 if (buff.toString()) { | 329 if (buff.toString()) { |
330 if (this.speechStartCallback_ || | |
331 this.speechEndCallback_ || | |
332 this.speechInterruptedCallback_) | |
333 this.speechProperties_['onEvent'] = onEvent; | |
334 | |
317 cvox.ChromeVox.tts.speak( | 335 cvox.ChromeVox.tts.speak( |
318 buff.toString(), cvox.QueueMode.FLUSH, {onEvent: onEvent}); | 336 buff.toString(), cvox.QueueMode.FLUSH, this.speechProperties_); |
319 } | 337 } |
320 | 338 |
321 var actions = buff.getSpansInstanceOf(Output.Action); | 339 var actions = buff.getSpansInstanceOf(Output.Action); |
322 if (actions) { | 340 if (actions) { |
323 actions.forEach(function(a) { | 341 actions.forEach(function(a) { |
324 a.run(); | 342 a.run(); |
325 }); | 343 }); |
326 } | 344 } |
327 | 345 |
328 // Braille. | 346 // Braille. |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
450 } else if (token == 'find') { | 468 } else if (token == 'find') { |
451 // Find takes two arguments: JSON query string and format string. | 469 // Find takes two arguments: JSON query string and format string. |
452 if (tree.firstChild) { | 470 if (tree.firstChild) { |
453 var jsonQuery = tree.firstChild.value; | 471 var jsonQuery = tree.firstChild.value; |
454 node = node.find( | 472 node = node.find( |
455 /** @type {Object}*/(JSON.parse(jsonQuery))); | 473 /** @type {Object}*/(JSON.parse(jsonQuery))); |
456 var formatString = tree.firstChild.nextSibling; | 474 var formatString = tree.firstChild.nextSibling; |
457 if (node) | 475 if (node) |
458 this.format_(node, formatString, buff); | 476 this.format_(node, formatString, buff); |
459 } | 477 } |
478 } else if (token == 'descendants') { | |
479 // Construct a range to the leftmost and rightmost leaves. | |
480 var leftmost = AutomationUtil.findNodePre( | |
481 node, Dir.FORWARD, AutomationPredicate.leaf); | |
482 var rightmost = AutomationUtil.findNodePre( | |
483 node, Dir.BACKWARD, AutomationPredicate.leaf); | |
484 if (!leftmost || !rightmost) | |
485 return; | |
486 | |
487 var subrange = new cursors.Range( | |
488 new cursors.Cursor(leftmost, 0), | |
489 new cursors.Cursor(rightmost, 0)); | |
490 this.range_(subrange, null, 'navigate', buff); | |
460 } else if (node.attributes[token]) { | 491 } else if (node.attributes[token]) { |
461 this.addToSpannable_(buff, node.attributes[token], options); | 492 this.addToSpannable_(buff, node.attributes[token], options); |
462 } else if (node.state[token]) { | 493 } else if (node.state[token]) { |
463 this.addToSpannable_(buff, token, options); | 494 this.addToSpannable_(buff, token, options); |
464 } else if (tree.firstChild) { | 495 } else if (tree.firstChild) { |
465 // Custom functions. | 496 // Custom functions. |
466 if (token == 'or') { | 497 if (token == 'or') { |
467 var cond = tree.firstChild; | 498 var cond = tree.firstChild; |
468 var attrib = cond.value.slice(1); | 499 var attrib = cond.value.slice(1); |
469 if (node.attributes[attrib] || node.state[attrib]) | 500 if (node.attributes[attrib] || node.state[attrib]) |
(...skipping 29 matching lines...) Expand all Loading... | |
499 } | 530 } |
500 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs); | 531 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs); |
501 try { | 532 try { |
502 if (this.formatOptions_.braille) | 533 if (this.formatOptions_.braille) |
503 msg = cvox.ChromeVox.msgs.getMsg(msgId + '_brl', msgArgs) || msg; | 534 msg = cvox.ChromeVox.msgs.getMsg(msgId + '_brl', msgArgs) || msg; |
504 } catch(e) {} | 535 } catch(e) {} |
505 | 536 |
506 if (msg) { | 537 if (msg) { |
507 this.addToSpannable_(buff, msg, options); | 538 this.addToSpannable_(buff, msg, options); |
508 } | 539 } |
540 } else if (prefix == '!') { | |
541 this.speechProperties_[token] = true; | |
509 } | 542 } |
510 }.bind(this)); | 543 }.bind(this)); |
511 }, | 544 }, |
512 | 545 |
513 /** | 546 /** |
514 * @param {!cursors.Range} range | 547 * @param {!cursors.Range} range |
515 * @param {cursors.Range} prevRange | 548 * @param {cursors.Range} prevRange |
516 * @param {chrome.automation.EventType|string} type | 549 * @param {chrome.automation.EventType|string} type |
517 * @param {!cvox.Spannable} rangeBuff | 550 * @param {!cvox.Spannable} rangeBuff |
518 * @private | 551 * @private |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
706 } | 739 } |
707 | 740 |
708 if (currentNode != root) | 741 if (currentNode != root) |
709 throw 'Unbalanced parenthesis.'; | 742 throw 'Unbalanced parenthesis.'; |
710 | 743 |
711 return root; | 744 return root; |
712 } | 745 } |
713 }; | 746 }; |
714 | 747 |
715 }); // goog.scope | 748 }); // goog.scope |
OLD | NEW |