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 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
80 * @private | 80 * @private |
81 */ | 81 */ |
82 this.speechCategory_ = cvox.TtsCategory.NAV; | 82 this.speechCategory_ = cvox.TtsCategory.NAV; |
83 | 83 |
84 /** | 84 /** |
85 * The speech queue mode for the generated speech utterance. | 85 * The speech queue mode for the generated speech utterance. |
86 * @type {cvox.QueueMode} | 86 * @type {cvox.QueueMode} |
87 * @private | 87 * @private |
88 */ | 88 */ |
89 this.queueMode_ = cvox.QueueMode.QUEUE; | 89 this.queueMode_ = cvox.QueueMode.QUEUE; |
| 90 |
| 91 /** |
| 92 * @type {boolean} |
| 93 * @private |
| 94 */ |
| 95 this.outputContextFirst_ = localStorage['outputContextFirst'] == 'true'; |
90 }; | 96 }; |
91 | 97 |
92 /** | 98 /** |
93 * Delimiter to use between output values. | 99 * Delimiter to use between output values. |
94 * @type {string} | 100 * @type {string} |
95 */ | 101 */ |
96 Output.SPACE = ' '; | 102 Output.SPACE = ' '; |
97 | 103 |
98 /** | 104 /** |
99 * Metadata about supported automation roles. | 105 * Metadata about supported automation roles. |
(...skipping 28 matching lines...) Expand all Loading... |
128 inherits: 'abstractContainer' | 134 inherits: 'abstractContainer' |
129 }, | 135 }, |
130 button: { | 136 button: { |
131 msgId: 'role_button', | 137 msgId: 'role_button', |
132 earconId: 'BUTTON' | 138 earconId: 'BUTTON' |
133 }, | 139 }, |
134 buttonDropDown: { | 140 buttonDropDown: { |
135 msgId: 'role_button', | 141 msgId: 'role_button', |
136 earconId: 'BUTTON' | 142 earconId: 'BUTTON' |
137 }, | 143 }, |
138 cell: { | |
139 msgId: 'role_gridcell' | |
140 }, | |
141 checkBox: { | 144 checkBox: { |
142 msgId: 'role_checkbox' | 145 msgId: 'role_checkbox' |
143 }, | 146 }, |
144 columnHeader: { | 147 columnHeader: { |
145 msgId: 'role_columnheader', | 148 msgId: 'role_columnheader', |
146 inherits: 'abstractContainer' | 149 inherits: 'cell' |
147 }, | 150 }, |
148 comboBox: { | 151 comboBox: { |
149 msgId: 'role_combobox' | 152 msgId: 'role_combobox' |
150 }, | 153 }, |
151 complementary: { | 154 complementary: { |
152 msgId: 'role_complementary', | 155 msgId: 'role_complementary', |
153 inherits: 'abstractContainer' | 156 inherits: 'abstractContainer' |
154 }, | 157 }, |
155 contentInfo: { | 158 contentInfo: { |
156 msgId: 'role_contentinfo', | 159 msgId: 'role_contentinfo', |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 popUpButton: { | 264 popUpButton: { |
262 msgId: 'role_button', | 265 msgId: 'role_button', |
263 earconId: 'POP_UP_BUTTON' | 266 earconId: 'POP_UP_BUTTON' |
264 }, | 267 }, |
265 radioButton: { | 268 radioButton: { |
266 msgId: 'role_radio' | 269 msgId: 'role_radio' |
267 }, | 270 }, |
268 radioGroup: { | 271 radioGroup: { |
269 msgId: 'role_radiogroup', | 272 msgId: 'role_radiogroup', |
270 }, | 273 }, |
| 274 row: { |
| 275 msgId: 'role_row', |
| 276 inherits: 'abstractContainer' |
| 277 }, |
271 rowHeader: { | 278 rowHeader: { |
272 msgId: 'role_rowheader', | 279 msgId: 'role_rowheader', |
273 inherits: 'abstractContainer' | 280 inherits: 'cell' |
274 }, | 281 }, |
275 scrollBar: { | 282 scrollBar: { |
276 msgId: 'role_scrollbar', | 283 msgId: 'role_scrollbar', |
277 }, | 284 }, |
278 search: { | 285 search: { |
279 msgId: 'role_search', | 286 msgId: 'role_search', |
280 inherits: 'abstractContainer' | 287 inherits: 'abstractContainer' |
281 }, | 288 }, |
282 separator: { | 289 separator: { |
283 msgId: 'role_separator', | 290 msgId: 'role_separator', |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
415 leave: '@exited_container($role)' | 422 leave: '@exited_container($role)' |
416 }, | 423 }, |
417 alert: { | 424 alert: { |
418 speak: '!doNotInterrupt $role $descendants' | 425 speak: '!doNotInterrupt $role $descendants' |
419 }, | 426 }, |
420 alertDialog: { | 427 alertDialog: { |
421 enter: '$nameFromNode $role $description', | 428 enter: '$nameFromNode $role $description', |
422 speak: '$name $role $descendants' | 429 speak: '$name $role $descendants' |
423 }, | 430 }, |
424 cell: { | 431 cell: { |
425 enter: '@column_granularity $tableCellColumnIndex' | 432 enter: '@cell_summary($tableCellRowIndex, $tableCellColumnIndex) ' + |
| 433 '$node(tableColumnHeader)', |
| 434 speak: '@cell_summary($tableCellRowIndex, $tableCellColumnIndex) ' + |
| 435 '$node(tableColumnHeader)' |
426 }, | 436 }, |
427 checkBox: { | 437 checkBox: { |
428 speak: '$if($checked, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + | 438 speak: '$if($checked, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + |
429 '$name $role $checked $description' | 439 '$name $role $checked $description' |
430 }, | 440 }, |
431 dialog: { | 441 dialog: { |
432 enter: '$nameFromNode $role $description' | 442 enter: '$nameFromNode $role $description' |
433 }, | 443 }, |
434 div: { | 444 div: { |
435 enter: '$nameFromNode', | 445 enter: '$nameFromNode', |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
507 enter: '$name $role $description' | 517 enter: '$name $role $description' |
508 }, | 518 }, |
509 rootWebArea: { | 519 rootWebArea: { |
510 enter: '$name', | 520 enter: '$name', |
511 speak: '$if($name, $name, $docUrl)' | 521 speak: '$if($name, $name, $docUrl)' |
512 }, | 522 }, |
513 region: { | 523 region: { |
514 speak: '$descendants' | 524 speak: '$descendants' |
515 }, | 525 }, |
516 row: { | 526 row: { |
517 enter: '@row_granularity $tableRowIndex' | 527 enter: '$node(tableRowHeader)' |
| 528 }, |
| 529 rowHeader: { |
| 530 speak: '$descendants' |
518 }, | 531 }, |
519 slider: { | 532 slider: { |
520 speak: '$earcon(SLIDER) @describe_slider($value, $name) $description' | 533 speak: '$earcon(SLIDER) @describe_slider($value, $name) $description' |
521 }, | 534 }, |
522 staticText: { | 535 staticText: { |
523 speak: '$name=' | 536 speak: '$name=' |
524 }, | 537 }, |
525 tab: { | 538 tab: { |
526 speak: '@describe_tab($name)' | 539 speak: '@describe_tab($name)' |
527 }, | 540 }, |
| 541 table: { |
| 542 enter: '@table_summary($name, $tableRowCount, $tableColumnCount) ' + |
| 543 '$node(tableHeader)' |
| 544 }, |
| 545 tableHeaderContainer: { |
| 546 speak: '$descendants' |
| 547 }, |
528 textField: { | 548 textField: { |
529 speak: '$name $value $if($multiline, @tag_textarea, $if(' + | 549 speak: '$name $value $if($multiline, @tag_textarea, $if(' + |
530 '$inputType, $inputType, $role)) $description', | 550 '$inputType, $inputType, $role)) $description', |
531 braille: '' | 551 braille: '' |
532 }, | 552 }, |
533 toggleButton: { | 553 toggleButton: { |
534 speak: '$if($pressed, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + | 554 speak: '$if($pressed, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + |
535 '$name $role $pressed $description' | 555 '$name $role $pressed $description' |
536 }, | 556 }, |
537 toolbar: { | 557 toolbar: { |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
799 * Output a string literal. | 819 * Output a string literal. |
800 * @param {string} value | 820 * @param {string} value |
801 * @return {!Output} | 821 * @return {!Output} |
802 */ | 822 */ |
803 withString: function(value) { | 823 withString: function(value) { |
804 this.append_(this.speechBuffer_, value); | 824 this.append_(this.speechBuffer_, value); |
805 this.append_(this.brailleBuffer_, value); | 825 this.append_(this.brailleBuffer_, value); |
806 return this; | 826 return this; |
807 }, | 827 }, |
808 | 828 |
| 829 |
| 830 /** |
| 831 * Outputs formatting nodes after this will contain context first. |
| 832 * @return {!Output} |
| 833 */ |
| 834 withContextFirst: function() { |
| 835 this.outputContextFirst_ = true; |
| 836 return this; |
| 837 }, |
| 838 |
809 /** | 839 /** |
810 * Apply a format string directly to the output buffer. This lets you | 840 * Apply a format string directly to the output buffer. This lets you |
811 * output a message directly to the buffer using the format syntax. | 841 * output a message directly to the buffer using the format syntax. |
812 * @param {string} formatStr | 842 * @param {string} formatStr |
813 * @param {!AutomationNode=} opt_node An optional node to apply the | 843 * @param {!AutomationNode=} opt_node An optional node to apply the |
814 * formatting to. | 844 * formatting to. |
815 * @return {!Output} |this| for chaining | 845 * @return {!Output} |this| for chaining |
816 */ | 846 */ |
817 format: function(formatStr, opt_node) { | 847 format: function(formatStr, opt_node) { |
818 return this | 848 return this |
(...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1126 this.append_(buff, msg, options); | 1156 this.append_(buff, msg, options); |
1127 } else if (token == 'inputType') { | 1157 } else if (token == 'inputType') { |
1128 if (!node.inputType) | 1158 if (!node.inputType) |
1129 return; | 1159 return; |
1130 options.annotation.push(token); | 1160 options.annotation.push(token); |
1131 var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[node.inputType] || | 1161 var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[node.inputType] || |
1132 'input_type_text'; | 1162 'input_type_text'; |
1133 if (this.formatOptions_.braille) | 1163 if (this.formatOptions_.braille) |
1134 msgId = msgId + '_brl'; | 1164 msgId = msgId + '_brl'; |
1135 this.append_(buff, Msgs.getMsg(msgId), options); | 1165 this.append_(buff, Msgs.getMsg(msgId), options); |
1136 } else if (token == 'tableRowIndex' || | 1166 } else if (token == 'tableCellRowIndex' || |
1137 token == 'tableCellColumnIndex') { | 1167 token == 'tableCellColumnIndex') { |
1138 var value = node[token]; | 1168 var value = node[token]; |
1139 if (!value) | 1169 if (value == undefined) |
1140 return; | 1170 return; |
1141 value = String(value + 1); | 1171 value = String(value + 1); |
1142 options.annotation.push(token); | 1172 options.annotation.push(token); |
1143 this.append_(buff, value, options); | 1173 this.append_(buff, value, options); |
| 1174 } else if (token == 'node') { |
| 1175 if (!tree.firstChild || !node[tree.firstChild.value]) |
| 1176 return; |
| 1177 var related = node[tree.firstChild.value]; |
| 1178 this.node_(related, related, Output.EventType.NAVIGATE, buff); |
1144 } else if (node[token] !== undefined) { | 1179 } else if (node[token] !== undefined) { |
1145 options.annotation.push(token); | 1180 options.annotation.push(token); |
1146 var value = node[token]; | 1181 var value = node[token]; |
1147 if (typeof value == 'number') | 1182 if (typeof value == 'number') |
1148 value = String(value); | 1183 value = String(value); |
1149 this.append_(buff, value, options); | 1184 this.append_(buff, value, options); |
1150 } else if (Output.STATE_INFO_[token]) { | 1185 } else if (Output.STATE_INFO_[token]) { |
1151 options.annotation.push('state'); | 1186 options.annotation.push('state'); |
1152 var stateInfo = Output.STATE_INFO_[token]; | 1187 var stateInfo = Output.STATE_INFO_[token]; |
1153 var resolvedInfo = {}; | 1188 var resolvedInfo = {}; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1211 var msgArgs = []; | 1246 var msgArgs = []; |
1212 if (!isPluralized) { | 1247 if (!isPluralized) { |
1213 var curArg = tree.firstChild; | 1248 var curArg = tree.firstChild; |
1214 while (curArg) { | 1249 while (curArg) { |
1215 if (curArg.value[0] != '$') { | 1250 if (curArg.value[0] != '$') { |
1216 console.error('Unexpected value: ' + curArg.value); | 1251 console.error('Unexpected value: ' + curArg.value); |
1217 return; | 1252 return; |
1218 } | 1253 } |
1219 var msgBuff = []; | 1254 var msgBuff = []; |
1220 this.format_(node, curArg, msgBuff); | 1255 this.format_(node, curArg, msgBuff); |
| 1256 // Fill in empty string if nothing was formatted. |
| 1257 if (!msgBuff.length) |
| 1258 msgBuff = ['']; |
1221 msgArgs = msgArgs.concat(msgBuff); | 1259 msgArgs = msgArgs.concat(msgBuff); |
1222 curArg = curArg.nextSibling; | 1260 curArg = curArg.nextSibling; |
1223 } | 1261 } |
1224 } | 1262 } |
1225 var msg = Msgs.getMsg(msgId, msgArgs); | 1263 var msg = Msgs.getMsg(msgId, msgArgs); |
1226 try { | 1264 try { |
1227 if (this.formatOptions_.braille) | 1265 if (this.formatOptions_.braille) |
1228 msg = Msgs.getMsg(msgId + '_brl', msgArgs) || msg; | 1266 msg = Msgs.getMsg(msgId + '_brl', msgArgs) || msg; |
1229 } catch(e) {} | 1267 } catch(e) {} |
1230 | 1268 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1293 if (!range.start.node || !range.end.node) | 1331 if (!range.start.node || !range.end.node) |
1294 return; | 1332 return; |
1295 | 1333 |
1296 if (!prevRange && range.start.node.root) | 1334 if (!prevRange && range.start.node.root) |
1297 prevRange = cursors.Range.fromNode(range.start.node.root); | 1335 prevRange = cursors.Range.fromNode(range.start.node.root); |
1298 var cursor = cursors.Cursor.fromNode(range.start.node); | 1336 var cursor = cursors.Cursor.fromNode(range.start.node); |
1299 var prevNode = prevRange.start.node; | 1337 var prevNode = prevRange.start.node; |
1300 | 1338 |
1301 var formatNodeAndAncestors = function(node, prevNode) { | 1339 var formatNodeAndAncestors = function(node, prevNode) { |
1302 var buff = []; | 1340 var buff = []; |
1303 var outputContextFirst = localStorage['outputContextFirst'] == 'true'; | 1341 |
1304 if (outputContextFirst) | 1342 if (this.outputContextFirst_) |
1305 this.ancestry_(node, prevNode, type, buff); | 1343 this.ancestry_(node, prevNode, type, buff); |
1306 this.node_(node, prevNode, type, buff); | 1344 this.node_(node, prevNode, type, buff); |
1307 if (!outputContextFirst) | 1345 if (!this.outputContextFirst_) |
1308 this.ancestry_(node, prevNode, type, buff); | 1346 this.ancestry_(node, prevNode, type, buff); |
1309 if (node.location) | 1347 if (node.location) |
1310 this.locations_.push(node.location); | 1348 this.locations_.push(node.location); |
1311 return buff; | 1349 return buff; |
1312 }.bind(this); | 1350 }.bind(this); |
1313 | 1351 |
1314 while (cursor.node != range.end.node) { | 1352 while (cursor.node != range.end.node) { |
1315 var node = cursor.node; | 1353 var node = cursor.node; |
1316 rangeBuff.push.apply(rangeBuff, formatNodeAndAncestors(node, prevNode)); | 1354 rangeBuff.push.apply(rangeBuff, formatNodeAndAncestors(node, prevNode)); |
1317 prevNode = node; | 1355 prevNode = node; |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1441 var selStart = node.textSelStart; | 1479 var selStart = node.textSelStart; |
1442 var selEnd = node.textSelEnd; | 1480 var selEnd = node.textSelEnd; |
1443 if (selStart !== undefined && | 1481 if (selStart !== undefined && |
1444 (selEnd >= startIndex && selStart <= endIndex)) { | 1482 (selEnd >= startIndex && selStart <= endIndex)) { |
1445 options.annotation.push(new Output.SelectionSpan( | 1483 options.annotation.push(new Output.SelectionSpan( |
1446 selStart - startIndex, | 1484 selStart - startIndex, |
1447 selEnd - startIndex, | 1485 selEnd - startIndex, |
1448 startIndex)); | 1486 startIndex)); |
1449 } | 1487 } |
1450 } | 1488 } |
1451 var outputContextFirst = localStorage['outputContextFirst'] == 'true'; | 1489 |
1452 if (outputContextFirst) | 1490 if (this.outputContextFirst_) |
1453 this.ancestry_(node, prevNode, type, buff); | 1491 this.ancestry_(node, prevNode, type, buff); |
1454 var earcon = this.findEarcon_(node, prevNode); | 1492 var earcon = this.findEarcon_(node, prevNode); |
1455 if (earcon) | 1493 if (earcon) |
1456 options.annotation.push(earcon); | 1494 options.annotation.push(earcon); |
1457 this.append_(buff, range.start.getText().substring(startIndex, endIndex), | 1495 this.append_(buff, range.start.getText().substring(startIndex, endIndex), |
1458 options); | 1496 options); |
1459 if (!outputContextFirst) | 1497 if (!this.outputContextFirst_) |
1460 this.ancestry_(node, prevNode, type, buff); | 1498 this.ancestry_(node, prevNode, type, buff); |
1461 | 1499 |
1462 var loc = | 1500 var loc = |
1463 range.start.node.boundsForRange(startIndex, endIndex); | 1501 range.start.node.boundsForRange(startIndex, endIndex); |
1464 if (loc) | 1502 if (loc) |
1465 this.locations_.push(loc); | 1503 this.locations_.push(loc); |
1466 }, | 1504 }, |
1467 | 1505 |
1468 /** | 1506 /** |
1469 * Appends output to the |buff|. | 1507 * Appends output to the |buff|. |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1643 break; | 1681 break; |
1644 } | 1682 } |
1645 earconFinder = earconFinder.parent; | 1683 earconFinder = earconFinder.parent; |
1646 } | 1684 } |
1647 } | 1685 } |
1648 return null; | 1686 return null; |
1649 } | 1687 } |
1650 }; | 1688 }; |
1651 | 1689 |
1652 }); // goog.scope | 1690 }); // goog.scope |
OLD | NEW |