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 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
70 this.speechProperties_ = {}; | 70 this.speechProperties_ = {}; |
71 }; | 71 }; |
72 | 72 |
73 /** | 73 /** |
74 * Delimiter to use between output values. | 74 * Delimiter to use between output values. |
75 * @type {string} | 75 * @type {string} |
76 */ | 76 */ |
77 Output.SPACE = ' '; | 77 Output.SPACE = ' '; |
78 | 78 |
79 /** | 79 /** |
| 80 * Metadata about supported automation roles. |
| 81 * @const {Object<string, {msgId: string, earcon: (string|undefined)}>} |
| 82 * @private |
| 83 */ |
| 84 Output.ROLE_INFO_ = { |
| 85 alert: { |
| 86 msgId: 'aria_role_alert', |
| 87 earcon: 'ALERT_NONMODAL', |
| 88 }, |
| 89 button: { |
| 90 msgId: 'tag_button', |
| 91 earcon: 'BUTTON' |
| 92 }, |
| 93 checkbox: { |
| 94 msgId: 'input_type_checkbox' |
| 95 }, |
| 96 heading: { |
| 97 msgId: 'aria_role_heading', |
| 98 }, |
| 99 link: { |
| 100 msgId: 'tag_link', |
| 101 earcon: 'LINK' |
| 102 }, |
| 103 listItem: { |
| 104 msgId: 'ARIA_ROLE_LISTITEM', |
| 105 earcon: 'list_item' |
| 106 }, |
| 107 menuListOption: { |
| 108 msgId: 'aria_role_menuitem' |
| 109 }, |
| 110 popUpButton: { |
| 111 msgId: 'tag_button' |
| 112 }, |
| 113 radioButton: { |
| 114 msgId: 'input_type_radio' |
| 115 }, |
| 116 textBox: { |
| 117 msgId: 'input_type_text', |
| 118 earcon: 'EDITABLE_TEXT' |
| 119 }, |
| 120 textField: { |
| 121 msgId: 'input_type_text', |
| 122 earcon: 'EDITABLE_TEXT' |
| 123 }, |
| 124 toolbar: { |
| 125 msgId: 'aria_role_toolbar' |
| 126 } |
| 127 }; |
| 128 |
| 129 /** |
80 * Rules specifying format of AutomationNodes for output. | 130 * Rules specifying format of AutomationNodes for output. |
81 * @type {!Object<string, Object<string, Object<string, string>>>} | 131 * @type {!Object<string, Object<string, Object<string, string>>>} |
82 */ | 132 */ |
83 Output.RULES = { | 133 Output.RULES = { |
84 navigate: { | 134 navigate: { |
85 'default': { | 135 'default': { |
86 speak: '$name $role $value', | 136 speak: '$name $role $value', |
87 braille: '' | 137 braille: '' |
88 }, | 138 }, |
89 alert: { | 139 alert: { |
90 speak: '!doNotInterrupt ' + | 140 speak: '!doNotInterrupt ' + |
91 '@aria_role_alert $name $earcon(ALERT_NONMODAL) $descendants' | 141 '@aria_role_alert $name $earcon(ALERT_NONMODAL) $descendants' |
92 }, | 142 }, |
93 button: { | |
94 speak: '$name $earcon(BUTTON, @tag_button)' | |
95 }, | |
96 checkBox: { | 143 checkBox: { |
97 speak: '$if($checked, @describe_checkbox_checked($name), ' + | 144 speak: '$if($checked, @describe_checkbox_checked($name), ' + |
98 '@describe_checkbox_unchecked($name)) ' + | 145 '@describe_checkbox_unchecked($name)) ' + |
99 '$if($checked, ' + | 146 '$if($checked, ' + |
100 '$earcon(CHECK_ON, @input_type_checkbox), ' + | 147 '$earcon(CHECK_ON, @input_type_checkbox), ' + |
101 '$earcon(CHECK_OFF, @input_type_checkbox))' | 148 '$earcon(CHECK_OFF, @input_type_checkbox))' |
102 }, | 149 }, |
103 dialog: { | 150 dialog: { |
104 enter: '$name $role' | 151 enter: '$name $role' |
105 }, | 152 }, |
106 heading: { | 153 heading: { |
107 enter: '@aria_role_heading', | 154 enter: '@aria_role_heading', |
108 speak: '@aria_role_heading $name=' | 155 speak: '@aria_role_heading $name=' |
109 }, | 156 }, |
110 inlineTextBox: { | 157 inlineTextBox: { |
111 speak: '$value=' | 158 speak: '$value=' |
112 }, | 159 }, |
113 link: { | 160 link: { |
114 enter: '$name= $visited $earcon(LINK, @tag_link)=', | 161 enter: '$name= $visited $earcon(LINK, @tag_link)=', |
115 stay: '$name= $visited @tag_link', | 162 stay: '$name= $visited @tag_link', |
116 speak: '$name= $visited $earcon(LINK, @tag_link)=' | 163 speak: '$name= $visited $earcon(LINK, @tag_link)=' |
117 }, | 164 }, |
118 list: { | 165 list: { |
119 enter: '$role' | 166 enter: '@aria_role_list @list_with_items($parentChildCount)' |
120 }, | 167 }, |
121 listItem: { | 168 listItem: { |
122 enter: '$role' | 169 enter: '$role' |
123 }, | 170 }, |
124 menuItem: { | 171 menuItem: { |
125 speak: '$if($haspopup, @describe_menu_item_with_submenu($name), ' + | 172 speak: '$if($haspopup, @describe_menu_item_with_submenu($name), ' + |
126 '@describe_menu_item($name)) ' + | 173 '@describe_menu_item($name)) ' + |
127 '@describe_index($indexInParent, $parentChildCount)' | 174 '@describe_index($indexInParent, $parentChildCount)' |
128 }, | 175 }, |
129 menuListOption: { | 176 menuListOption: { |
(...skipping 13 matching lines...) Expand all Loading... |
143 '$if($checked, ' + | 190 '$if($checked, ' + |
144 '$earcon(CHECK_ON, @input_type_radio), ' + | 191 '$earcon(CHECK_ON, @input_type_radio), ' + |
145 '$earcon(CHECK_OFF, @input_type_radio))' | 192 '$earcon(CHECK_OFF, @input_type_radio))' |
146 }, | 193 }, |
147 slider: { | 194 slider: { |
148 speak: '@describe_slider($value, $name)' | 195 speak: '@describe_slider($value, $name)' |
149 }, | 196 }, |
150 staticText: { | 197 staticText: { |
151 speak: '$value' | 198 speak: '$value' |
152 }, | 199 }, |
153 textBox: { | |
154 speak: '$name $value $earcon(EDITABLE_TEXT, @input_type_text)' | |
155 }, | |
156 tab: { | 200 tab: { |
157 speak: '@describe_tab($name)' | 201 speak: '@describe_tab($name)' |
158 }, | 202 }, |
159 textField: { | |
160 speak: '$name $value $earcon(EDITABLE_TEXT, @input_type_text) $protected' | |
161 }, | |
162 toolbar: { | 203 toolbar: { |
163 enter: '$name $role' | 204 enter: '$name $role' |
164 }, | 205 }, |
165 window: { | 206 window: { |
166 enter: '$name', | 207 enter: '$name', |
167 speak: '@describe_window($name) $earcon(OBJECT_OPEN)' | 208 speak: '@describe_window($name) $earcon(OBJECT_OPEN)' |
168 } | 209 } |
169 }, | 210 }, |
170 menuStart: { | 211 menuStart: { |
171 'default': { | 212 'default': { |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 // Process token based on prefix. | 459 // Process token based on prefix. |
419 var prefix = token[0]; | 460 var prefix = token[0]; |
420 token = token.slice(1); | 461 token = token.slice(1); |
421 | 462 |
422 if (opt_exclude[token]) | 463 if (opt_exclude[token]) |
423 return; | 464 return; |
424 | 465 |
425 // All possible tokens based on prefix. | 466 // All possible tokens based on prefix. |
426 if (prefix == '$') { | 467 if (prefix == '$') { |
427 options.annotation = token; | 468 options.annotation = token; |
428 if (token == 'role') { | 469 if (token == 'value') { |
429 // Non-localized role and state obtained by default. | |
430 this.addToSpannable_(buff, node.role, options); | |
431 } else if (token == 'value') { | |
432 var text = node.attributes.value; | 470 var text = node.attributes.value; |
433 if (text !== undefined) { | 471 if (text !== undefined) { |
434 var offset = buff.getLength(); | 472 var offset = buff.getLength(); |
435 if (node.attributes.textSelStart !== undefined) { | 473 if (node.attributes.textSelStart !== undefined) { |
436 options.annotation = new Output.SelectionSpan( | 474 options.annotation = new Output.SelectionSpan( |
437 node.attributes.textSelStart, | 475 node.attributes.textSelStart, |
438 node.attributes.textSelEnd); | 476 node.attributes.textSelEnd); |
439 } | 477 } |
440 } else if (node.role == chrome.automation.RoleType.staticText) { | 478 } else if (node.role == chrome.automation.RoleType.staticText) { |
441 // TODO(dtseng): Remove once Blink treats staticText values as | 479 // TODO(dtseng): Remove once Blink treats staticText values as |
(...skipping 29 matching lines...) Expand all Loading... |
471 node, Dir.FORWARD, AutomationPredicate.leaf); | 509 node, Dir.FORWARD, AutomationPredicate.leaf); |
472 var rightmost = AutomationUtil.findNodePre( | 510 var rightmost = AutomationUtil.findNodePre( |
473 node, Dir.BACKWARD, AutomationPredicate.leaf); | 511 node, Dir.BACKWARD, AutomationPredicate.leaf); |
474 if (!leftmost || !rightmost) | 512 if (!leftmost || !rightmost) |
475 return; | 513 return; |
476 | 514 |
477 var subrange = new cursors.Range( | 515 var subrange = new cursors.Range( |
478 new cursors.Cursor(leftmost, 0), | 516 new cursors.Cursor(leftmost, 0), |
479 new cursors.Cursor(rightmost, 0)); | 517 new cursors.Cursor(rightmost, 0)); |
480 this.range_(subrange, null, 'navigate', buff); | 518 this.range_(subrange, null, 'navigate', buff); |
| 519 } else if (token == 'role') { |
| 520 var msg = node.role; |
| 521 var earconId = null; |
| 522 var info = Output.ROLE_INFO_[node.role]; |
| 523 if (info) { |
| 524 if (this.formatOptions_.braille) |
| 525 msg = cvox.ChromeVox.msgs.getMsg(info.msgId + '_brl'); |
| 526 else |
| 527 msg = cvox.ChromeVox.msgs.getMsg(info.msgId); |
| 528 earconId = info.earcon; |
| 529 } else { |
| 530 console.error('Missing role info for ' + node.role); |
| 531 } |
| 532 if (earconId) { |
| 533 options.annotation = new Output.Action(function() { |
| 534 cvox.ChromeVox.earcons.playEarcon( |
| 535 cvox.AbstractEarcons[earconId]); |
| 536 }); |
| 537 } |
| 538 this.addToSpannable_(buff, msg, options); |
481 } else if (node.attributes[token]) { | 539 } else if (node.attributes[token]) { |
482 this.addToSpannable_(buff, node.attributes[token], options); | 540 this.addToSpannable_(buff, node.attributes[token], options); |
483 } else if (node.state[token]) { | 541 } else if (node.state[token]) { |
484 this.addToSpannable_(buff, token, options); | 542 this.addToSpannable_(buff, token, options); |
485 } else if (tree.firstChild) { | 543 } else if (tree.firstChild) { |
486 // Custom functions. | 544 // Custom functions. |
487 if (token == 'if') { | 545 if (token == 'if') { |
488 var cond = tree.firstChild; | 546 var cond = tree.firstChild; |
489 var attrib = cond.value.slice(1); | 547 var attrib = cond.value.slice(1); |
490 if (node.attributes[attrib] || node.state[attrib]) | 548 if (node.attributes[attrib] || node.state[attrib]) |
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
729 } | 787 } |
730 | 788 |
731 if (currentNode != root) | 789 if (currentNode != root) |
732 throw 'Unbalanced parenthesis.'; | 790 throw 'Unbalanced parenthesis.'; |
733 | 791 |
734 return root; | 792 return root; |
735 } | 793 } |
736 }; | 794 }; |
737 | 795 |
738 }); // goog.scope | 796 }); // goog.scope |
OLD | NEW |