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 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 dialog: { | 99 dialog: { |
100 msgId: 'dialog' | 100 msgId: 'dialog' |
101 }, | 101 }, |
102 heading: { | 102 heading: { |
103 msgId: 'aria_role_heading', | 103 msgId: 'aria_role_heading', |
104 }, | 104 }, |
105 link: { | 105 link: { |
106 msgId: 'tag_link', | 106 msgId: 'tag_link', |
107 earcon: 'LINK' | 107 earcon: 'LINK' |
108 }, | 108 }, |
| 109 listBox: { |
| 110 msgId: 'aria_role_listbox', |
| 111 earcon: 'LISTBOX' |
| 112 }, |
| 113 listBoxOption: { |
| 114 msgId: 'aria_role_listitem', |
| 115 earcon: 'LIST_ITEM' |
| 116 }, |
109 listItem: { | 117 listItem: { |
110 msgId: 'ARIA_ROLE_LISTITEM', | 118 msgId: 'ARIA_ROLE_LISTITEM', |
111 earcon: 'list_item' | 119 earcon: 'list_item' |
112 }, | 120 }, |
| 121 menu: { |
| 122 msgId: 'aria_role_menu' |
| 123 }, |
| 124 menuItem: { |
| 125 msgId: 'aria_role_menuitem' |
| 126 }, |
113 menuListOption: { | 127 menuListOption: { |
114 msgId: 'aria_role_menuitem' | 128 msgId: 'aria_role_menuitem' |
115 }, | 129 }, |
| 130 menuListPopup: { |
| 131 msgId: 'aria_role_menu' |
| 132 }, |
116 popUpButton: { | 133 popUpButton: { |
117 msgId: 'tag_button' | 134 msgId: 'tag_button' |
118 }, | 135 }, |
119 radioButton: { | 136 radioButton: { |
120 msgId: 'input_type_radio' | 137 msgId: 'input_type_radio' |
121 }, | 138 }, |
122 spinButton: { | 139 spinButton: { |
123 msgId: 'aria_role_combobox', | 140 msgId: 'aria_role_combobox', |
124 earcon: 'LISTBOX' | 141 earcon: 'LISTBOX' |
125 }, | 142 }, |
126 textBox: { | 143 textBox: { |
127 msgId: 'input_type_text', | 144 msgId: 'input_type_text', |
128 earcon: 'EDITABLE_TEXT' | 145 earcon: 'EDITABLE_TEXT' |
129 }, | 146 }, |
130 textField: { | 147 textField: { |
131 msgId: 'input_type_text', | 148 msgId: 'input_type_text', |
132 earcon: 'EDITABLE_TEXT' | 149 earcon: 'EDITABLE_TEXT' |
133 }, | 150 }, |
134 time: { | 151 time: { |
135 msgId: 'tag_time' | 152 msgId: 'tag_time' |
136 }, | 153 }, |
137 toolbar: { | 154 toolbar: { |
138 msgId: 'aria_role_toolbar' | 155 msgId: 'aria_role_toolbar' |
| 156 }, |
| 157 tree: { |
| 158 msgId: 'aria_role_tree' |
| 159 }, |
| 160 treeItem: { |
| 161 msgId: 'aria_role_treeitem' |
139 } | 162 } |
140 }; | 163 }; |
141 | 164 |
142 /** | 165 /** |
143 * Metadata about supported automation states. | 166 * Metadata about supported automation states. |
144 * @const {!Object<string, | 167 * @const {!Object<string, |
145 * {on: {msgId: string, earconId: string}, | 168 * {on: {msgId: string, earconId: string}, |
146 * off: {msgId: string, earconId: string}}>} | 169 * off: {msgId: string, earconId: string}, |
| 170 * omitted: {msgId: string, earconId: string}}>} |
| 171 * on: info used to describe a state that is set to true. |
| 172 * off: info used to describe a state that is set to false. |
| 173 * omitted: info used to describe a state that is undefined. |
147 * @private | 174 * @private |
148 */ | 175 */ |
149 Output.STATE_INFO_ = { | 176 Output.STATE_INFO_ = { |
150 checked: { | 177 checked: { |
151 on: { | 178 on: { |
152 earconId: 'CHECK_ON', | 179 earconId: 'CHECK_ON', |
153 msgId: 'checkbox_checked_state' | 180 msgId: 'checkbox_checked_state' |
154 }, | 181 }, |
155 off: { | 182 off: { |
156 earconId: 'CHECK_OFF', | 183 earconId: 'CHECK_OFF', |
157 msgId: 'checkbox_unchecked_state' | 184 msgId: 'checkbox_unchecked_state' |
| 185 }, |
| 186 omitted: { |
| 187 earconId: 'CHECK_OFF', |
| 188 msgId: 'checkbox_unchecked_state' |
| 189 } |
| 190 }, |
| 191 collapsed: { |
| 192 on: { |
| 193 msgId: 'aria_expanded_false' |
| 194 }, |
| 195 off: { |
| 196 msgId: 'aria_expanded_true' |
| 197 } |
| 198 }, |
| 199 expanded: { |
| 200 on: { |
| 201 msgId: 'aria_expanded_true' |
| 202 }, |
| 203 off: { |
| 204 msgId: 'aria_expanded_false' |
158 } | 205 } |
159 } | 206 } |
160 }; | 207 }; |
161 | 208 |
162 /** | 209 /** |
163 * Rules specifying format of AutomationNodes for output. | 210 * Rules specifying format of AutomationNodes for output. |
164 * @type {!Object<string, Object<string, Object<string, string>>>} | 211 * @type {!Object<string, Object<string, Object<string, string>>>} |
165 */ | 212 */ |
166 Output.RULES = { | 213 Output.RULES = { |
167 navigate: { | 214 navigate: { |
(...skipping 20 matching lines...) Expand all Loading... |
188 }, | 235 }, |
189 inlineTextBox: { | 236 inlineTextBox: { |
190 speak: '$value=' | 237 speak: '$value=' |
191 }, | 238 }, |
192 link: { | 239 link: { |
193 enter: '$name $visited $role', | 240 enter: '$name $visited $role', |
194 stay: '$name= $visited $role', | 241 stay: '$name= $visited $role', |
195 speak: '$name= $visited $role' | 242 speak: '$name= $visited $role' |
196 }, | 243 }, |
197 list: { | 244 list: { |
198 enter: '@aria_role_list @list_with_items($parentChildCount)' | 245 enter: '$role @list_with_items($countChildren(listItem))' |
| 246 }, |
| 247 listBox: { |
| 248 enter: '$name $role @list_with_items($countChildren(listBoxOption))' |
| 249 }, |
| 250 listBoxOption: { |
| 251 speak: '$name $role @describe_index($indexInParent, $parentChildCount)' |
199 }, | 252 }, |
200 listItem: { | 253 listItem: { |
201 enter: '$role' | 254 enter: '$role' |
202 }, | 255 }, |
| 256 menu: { |
| 257 enter: '$name $role @list_with_items($countChildren(menuItem))' |
| 258 }, |
203 menuItem: { | 259 menuItem: { |
204 speak: '$if($haspopup, @describe_menu_item_with_submenu($name), ' + | 260 speak: '$if($haspopup, @describe_menu_item_with_submenu($name), ' + |
205 '@describe_menu_item($name)) ' + | 261 '@describe_menu_item($name)) ' + |
206 '@describe_index($indexInParent, $parentChildCount)' | 262 '@describe_index($indexInParent, $parentChildCount)' |
207 }, | 263 }, |
208 menuListOption: { | 264 menuListOption: { |
209 speak: '$name $value @aria_role_menuitem ' + | 265 speak: '$name $value @aria_role_menuitem ' + |
210 '@describe_index($indexInParent, $parentChildCount)' | 266 '@describe_index($indexInParent, $parentChildCount)' |
211 }, | 267 }, |
212 paragraph: { | 268 paragraph: { |
(...skipping 22 matching lines...) Expand all Loading... |
235 '$earcon(EDITABLE_TEXT)', | 291 '$earcon(EDITABLE_TEXT)', |
236 braille: '' | 292 braille: '' |
237 }, | 293 }, |
238 time: { | 294 time: { |
239 enter: '$name $role', | 295 enter: '$name $role', |
240 leave: '@exited_container($role)' | 296 leave: '@exited_container($role)' |
241 }, | 297 }, |
242 toolbar: { | 298 toolbar: { |
243 enter: '$name $role' | 299 enter: '$name $role' |
244 }, | 300 }, |
| 301 tree: { |
| 302 enter: '$name $role @list_with_items($countChildren(treeItem))' |
| 303 }, |
| 304 treeItem: { |
| 305 enter: '$role $expanded $collapsed ' + |
| 306 '@describe_index($indexInParent, $parentChildCount) ' + |
| 307 '@describe_depth($hierarchicalLevel)' |
| 308 }, |
245 window: { | 309 window: { |
246 enter: '$name', | 310 enter: '$name', |
247 speak: '@describe_window($name) $earcon(OBJECT_OPEN)' | 311 speak: '@describe_window($name) $earcon(OBJECT_OPEN)' |
248 } | 312 } |
249 }, | 313 }, |
250 menuStart: { | 314 menuStart: { |
251 'default': { | 315 'default': { |
252 speak: '@chrome_menu_opened($name) $earcon(OBJECT_OPEN)' | 316 speak: '@chrome_menu_opened($name) $earcon(OBJECT_OPEN)' |
253 } | 317 } |
254 }, | 318 }, |
(...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
621 msg = cvox.ChromeVox.msgs.getMsg(info.msgId); | 685 msg = cvox.ChromeVox.msgs.getMsg(info.msgId); |
622 earconId = info.earcon; | 686 earconId = info.earcon; |
623 } else { | 687 } else { |
624 console.error('Missing role info for ' + node.role); | 688 console.error('Missing role info for ' + node.role); |
625 } | 689 } |
626 if (earconId) | 690 if (earconId) |
627 options.annotation.push(new Output.EarconAction(earconId)); | 691 options.annotation.push(new Output.EarconAction(earconId)); |
628 this.append_(buff, msg, options); | 692 this.append_(buff, msg, options); |
629 } else if (node.attributes[token] !== undefined) { | 693 } else if (node.attributes[token] !== undefined) { |
630 options.annotation.push(token); | 694 options.annotation.push(token); |
631 this.append_(buff, node.attributes[token], options); | 695 var value = node.attributes[token]; |
| 696 if (typeof value == 'number') |
| 697 value = String(value); |
| 698 this.append_(buff, value, options); |
632 } else if (Output.STATE_INFO_[token]) { | 699 } else if (Output.STATE_INFO_[token]) { |
633 options.annotation.push('state'); | 700 options.annotation.push('state'); |
634 var stateInfo = Output.STATE_INFO_[token]; | 701 var stateInfo = Output.STATE_INFO_[token]; |
635 var resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off; | 702 var resolvedInfo = {}; |
636 options.annotation.push( | 703 if (node.state[token] === undefined) |
637 new Output.EarconAction(resolvedInfo.earconId)); | 704 resolvedInfo = stateInfo.omitted; |
| 705 else |
| 706 resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off; |
| 707 if (!resolvedInfo) |
| 708 return; |
| 709 if (resolvedInfo.earconId) { |
| 710 options.annotation.push( |
| 711 new Output.EarconAction(resolvedInfo.earconId)); |
| 712 } |
638 var msgId = | 713 var msgId = |
639 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' : | 714 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' : |
640 resolvedInfo.msgId; | 715 resolvedInfo.msgId; |
641 var msg = cvox.ChromeVox.msgs.getMsg(msgId); | 716 var msg = cvox.ChromeVox.msgs.getMsg(msgId); |
642 this.append_(buff, msg, options); | 717 this.append_(buff, msg, options); |
643 } else if (tree.firstChild) { | 718 } else if (tree.firstChild) { |
644 // Custom functions. | 719 // Custom functions. |
645 if (token == 'if') { | 720 if (token == 'if') { |
646 var cond = tree.firstChild; | 721 var cond = tree.firstChild; |
647 var attrib = cond.value.slice(1); | 722 var attrib = cond.value.slice(1); |
648 if (node.attributes[attrib] || node.state[attrib]) | 723 if (node.attributes[attrib] || node.state[attrib]) |
649 this.format_(node, cond.nextSibling, buff); | 724 this.format_(node, cond.nextSibling, buff); |
650 else | 725 else |
651 this.format_(node, cond.nextSibling.nextSibling, buff); | 726 this.format_(node, cond.nextSibling.nextSibling, buff); |
652 } else if (token == 'earcon') { | 727 } else if (token == 'earcon') { |
653 // Assumes there's existing output in our buffer. | 728 // Assumes there's existing output in our buffer. |
654 var lastBuff = buff[buff.length - 1]; | 729 var lastBuff = buff[buff.length - 1]; |
655 if (!lastBuff) | 730 if (!lastBuff) |
656 return; | 731 return; |
657 | 732 |
658 lastBuff.setSpan( | 733 lastBuff.setSpan( |
659 new Output.EarconAction(tree.firstChild.value), 0, 0); | 734 new Output.EarconAction(tree.firstChild.value), 0, 0); |
| 735 } else if (token == 'countChildren') { |
| 736 var role = tree.firstChild.value; |
| 737 var count = node.children.filter(function(e) { |
| 738 return e.role == role; |
| 739 }).length; |
| 740 this.append_(buff, String(count)); |
660 } | 741 } |
661 } | 742 } |
662 } else if (prefix == '@') { | 743 } else if (prefix == '@') { |
663 // Tokens can have substitutions. | 744 // Tokens can have substitutions. |
664 var pieces = token.split('+'); | 745 var pieces = token.split('+'); |
665 token = pieces.reduce(function(prev, cur) { | 746 token = pieces.reduce(function(prev, cur) { |
666 var lookup = cur; | 747 var lookup = cur; |
667 if (cur[0] == '$') | 748 if (cur[0] == '$') |
668 lookup = node.attributes[cur.slice(1)]; | 749 lookup = node.attributes[cur.slice(1)]; |
669 return prev + lookup; | 750 return prev + lookup; |
670 }.bind(this), ''); | 751 }.bind(this), ''); |
671 var msgId = token; | 752 var msgId = token; |
672 var msgArgs = []; | 753 var msgArgs = []; |
673 var curMsg = tree.firstChild; | 754 var curMsg = tree.firstChild; |
674 | 755 |
675 while (curMsg) { | 756 while (curMsg) { |
676 var arg = curMsg.value; | 757 var arg = curMsg.value; |
677 if (arg[0] != '$') { | 758 if (arg[0] != '$') { |
678 console.error('Unexpected value: ' + arg); | 759 console.error('Unexpected value: ' + arg); |
679 return; | 760 return; |
680 } | 761 } |
681 var msgBuff = []; | 762 var msgBuff = []; |
682 this.format_(node, arg, msgBuff); | 763 this.format_(node, curMsg, msgBuff); |
683 msgArgs = msgArgs.concat(msgBuff); | 764 msgArgs = msgArgs.concat(msgBuff); |
684 curMsg = curMsg.nextSibling; | 765 curMsg = curMsg.nextSibling; |
685 } | 766 } |
686 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs); | 767 var msg = cvox.ChromeVox.msgs.getMsg(msgId, msgArgs); |
687 try { | 768 try { |
688 if (this.formatOptions_.braille) | 769 if (this.formatOptions_.braille) |
689 msg = cvox.ChromeVox.msgs.getMsg(msgId + '_brl', msgArgs) || msg; | 770 msg = cvox.ChromeVox.msgs.getMsg(msgId + '_brl', msgArgs) || msg; |
690 } catch(e) {} | 771 } catch(e) {} |
691 | 772 |
692 if (msg) { | 773 if (msg) { |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
904 } | 985 } |
905 | 986 |
906 if (currentNode != root) | 987 if (currentNode != root) |
907 throw 'Unbalanced parenthesis.'; | 988 throw 'Unbalanced parenthesis.'; |
908 | 989 |
909 return root; | 990 return root; |
910 } | 991 } |
911 }; | 992 }; |
912 | 993 |
913 }); // goog.scope | 994 }); // goog.scope |
OLD | NEW |