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 29 matching lines...) Expand all Loading... | |
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: { | 144 cell: { |
139 msgId: 'role_gridcell' | 145 msgId: 'role_column' |
dmazzoni
2016/07/13 23:38:28
Why is "cell" speaking the "column" message?
David Tseng
2016/07/14 20:24:41
This is unused by the cell speech rule...removed.
| |
140 }, | 146 }, |
141 checkBox: { | 147 checkBox: { |
142 msgId: 'role_checkbox' | 148 msgId: 'role_checkbox' |
143 }, | 149 }, |
144 columnHeader: { | 150 columnHeader: { |
145 msgId: 'role_columnheader', | 151 msgId: 'role_columnheader', |
146 inherits: 'abstractContainer' | 152 inherits: 'cell' |
147 }, | 153 }, |
148 comboBox: { | 154 comboBox: { |
149 msgId: 'role_combobox' | 155 msgId: 'role_combobox' |
150 }, | 156 }, |
151 complementary: { | 157 complementary: { |
152 msgId: 'role_complementary', | 158 msgId: 'role_complementary', |
153 inherits: 'abstractContainer' | 159 inherits: 'abstractContainer' |
154 }, | 160 }, |
155 contentInfo: { | 161 contentInfo: { |
156 msgId: 'role_contentinfo', | 162 msgId: 'role_contentinfo', |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
261 popUpButton: { | 267 popUpButton: { |
262 msgId: 'role_button', | 268 msgId: 'role_button', |
263 earconId: 'POP_UP_BUTTON' | 269 earconId: 'POP_UP_BUTTON' |
264 }, | 270 }, |
265 radioButton: { | 271 radioButton: { |
266 msgId: 'role_radio' | 272 msgId: 'role_radio' |
267 }, | 273 }, |
268 radioGroup: { | 274 radioGroup: { |
269 msgId: 'role_radiogroup', | 275 msgId: 'role_radiogroup', |
270 }, | 276 }, |
277 row: { | |
278 msgId: 'role_row', | |
279 inherits: 'abstractContainer' | |
280 }, | |
271 rowHeader: { | 281 rowHeader: { |
272 msgId: 'role_rowheader', | 282 msgId: 'role_rowheader', |
273 inherits: 'abstractContainer' | 283 inherits: 'cell' |
274 }, | 284 }, |
275 scrollBar: { | 285 scrollBar: { |
276 msgId: 'role_scrollbar', | 286 msgId: 'role_scrollbar', |
277 }, | 287 }, |
278 search: { | 288 search: { |
279 msgId: 'role_search', | 289 msgId: 'role_search', |
280 inherits: 'abstractContainer' | 290 inherits: 'abstractContainer' |
281 }, | 291 }, |
282 separator: { | 292 separator: { |
283 msgId: 'role_separator', | 293 msgId: 'role_separator', |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
404 * Rules specifying format of AutomationNodes for output. | 414 * Rules specifying format of AutomationNodes for output. |
405 * @type {!Object<Object<Object<string>>>} | 415 * @type {!Object<Object<Object<string>>>} |
406 */ | 416 */ |
407 Output.RULES = { | 417 Output.RULES = { |
408 navigate: { | 418 navigate: { |
409 'default': { | 419 'default': { |
410 speak: '$name $value $state $role $description', | 420 speak: '$name $value $state $role $description', |
411 braille: '' | 421 braille: '' |
412 }, | 422 }, |
413 abstractContainer: { | 423 abstractContainer: { |
414 enter: '$nameFromNode $role $description', | 424 enter: '$nameFromNode $role $description' |
415 leave: '@exited_container($role)' | |
dmazzoni
2016/07/13 23:38:28
I think it's particularly useful to have exited_co
David Tseng
2016/07/14 20:24:41
Leaving for another cl where I can do an audit on
| |
416 }, | 425 }, |
417 alert: { | 426 alert: { |
418 speak: '!doNotInterrupt $role $descendants' | 427 speak: '!doNotInterrupt $role $descendants' |
419 }, | 428 }, |
420 alertDialog: { | 429 alertDialog: { |
421 enter: '$nameFromNode $role $description', | 430 enter: '$nameFromNode $role $description', |
422 speak: '$name $role $descendants' | 431 speak: '$name $role $descendants' |
423 }, | 432 }, |
424 cell: { | 433 cell: { |
425 enter: '@column_granularity $tableCellColumnIndex' | 434 enter: '@cell_summary($tableCellRowIndex, $tableCellColumnIndex) ' + |
435 '$node(tableColumnHeader)', | |
436 speak: '@cell_summary($tableCellRowIndex, $tableCellColumnIndex) ' + | |
437 '$node(tableColumnHeader)' | |
426 }, | 438 }, |
427 checkBox: { | 439 checkBox: { |
428 speak: '$if($checked, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + | 440 speak: '$if($checked, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + |
429 '$name $role $checked $description' | 441 '$name $role $checked $description' |
430 }, | 442 }, |
431 dialog: { | 443 dialog: { |
432 enter: '$nameFromNode $role $description' | 444 enter: '$nameFromNode $role $description' |
433 }, | 445 }, |
434 div: { | 446 div: { |
435 enter: '$nameFromNode', | 447 enter: '$nameFromNode', |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
507 enter: '$name $role $description' | 519 enter: '$name $role $description' |
508 }, | 520 }, |
509 rootWebArea: { | 521 rootWebArea: { |
510 enter: '$name', | 522 enter: '$name', |
511 speak: '$if($name, $name, $docUrl)' | 523 speak: '$if($name, $name, $docUrl)' |
512 }, | 524 }, |
513 region: { | 525 region: { |
514 speak: '$descendants' | 526 speak: '$descendants' |
515 }, | 527 }, |
516 row: { | 528 row: { |
517 enter: '@row_granularity $tableRowIndex' | 529 enter: '$node(tableRowHeader)' |
530 }, | |
531 rowHeader: { | |
532 speak: '$descendants' | |
518 }, | 533 }, |
519 slider: { | 534 slider: { |
520 speak: '$earcon(SLIDER) @describe_slider($value, $name) $description' | 535 speak: '$earcon(SLIDER) @describe_slider($value, $name) $description' |
521 }, | 536 }, |
522 staticText: { | 537 staticText: { |
523 speak: '$name=' | 538 speak: '$name=' |
524 }, | 539 }, |
525 tab: { | 540 tab: { |
526 speak: '@describe_tab($name)' | 541 speak: '@describe_tab($name)' |
527 }, | 542 }, |
543 table: { | |
544 enter: '@table_summary($name, $tableRowCount, $tableColumnCount) ' + | |
545 '$node(tableHeader)' | |
546 }, | |
547 tableHeaderContainer: { | |
548 speak: '$descendants' | |
549 }, | |
528 textField: { | 550 textField: { |
529 speak: '$name $value $if($multiline, @tag_textarea, $if(' + | 551 speak: '$name $value $if($multiline, @tag_textarea, $if(' + |
530 '$inputType, $inputType, $role)) $description', | 552 '$inputType, $inputType, $role)) $description', |
531 braille: '' | 553 braille: '' |
532 }, | 554 }, |
533 toggleButton: { | 555 toggleButton: { |
534 speak: '$if($pressed, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + | 556 speak: '$if($pressed, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + |
535 '$name $role $pressed $description' | 557 '$name $role $pressed $description' |
536 }, | 558 }, |
537 toolbar: { | 559 toolbar: { |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
799 * Output a string literal. | 821 * Output a string literal. |
800 * @param {string} value | 822 * @param {string} value |
801 * @return {!Output} | 823 * @return {!Output} |
802 */ | 824 */ |
803 withString: function(value) { | 825 withString: function(value) { |
804 this.append_(this.speechBuffer_, value); | 826 this.append_(this.speechBuffer_, value); |
805 this.append_(this.brailleBuffer_, value); | 827 this.append_(this.brailleBuffer_, value); |
806 return this; | 828 return this; |
807 }, | 829 }, |
808 | 830 |
831 | |
832 /** | |
833 * Outputs formatting nodes after this will contain context first. | |
834 * @return {!Output} | |
835 */ | |
836 withContextFirst: function() { | |
837 this.outputContextFirst_ = true; | |
838 return this; | |
839 }, | |
840 | |
809 /** | 841 /** |
810 * Apply a format string directly to the output buffer. This lets you | 842 * Apply a format string directly to the output buffer. This lets you |
811 * output a message directly to the buffer using the format syntax. | 843 * output a message directly to the buffer using the format syntax. |
812 * @param {string} formatStr | 844 * @param {string} formatStr |
813 * @param {!AutomationNode=} opt_node An optional node to apply the | 845 * @param {!AutomationNode=} opt_node An optional node to apply the |
814 * formatting to. | 846 * formatting to. |
815 * @return {!Output} |this| for chaining | 847 * @return {!Output} |this| for chaining |
816 */ | 848 */ |
817 format: function(formatStr, opt_node) { | 849 format: function(formatStr, opt_node) { |
818 return this | 850 return this |
(...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1126 this.append_(buff, msg, options); | 1158 this.append_(buff, msg, options); |
1127 } else if (token == 'inputType') { | 1159 } else if (token == 'inputType') { |
1128 if (!node.inputType) | 1160 if (!node.inputType) |
1129 return; | 1161 return; |
1130 options.annotation.push(token); | 1162 options.annotation.push(token); |
1131 var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[node.inputType] || | 1163 var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[node.inputType] || |
1132 'input_type_text'; | 1164 'input_type_text'; |
1133 if (this.formatOptions_.braille) | 1165 if (this.formatOptions_.braille) |
1134 msgId = msgId + '_brl'; | 1166 msgId = msgId + '_brl'; |
1135 this.append_(buff, Msgs.getMsg(msgId), options); | 1167 this.append_(buff, Msgs.getMsg(msgId), options); |
1136 } else if (token == 'tableRowIndex' || | 1168 } else if (token == 'tableCellRowIndex' || |
1137 token == 'tableCellColumnIndex') { | 1169 token == 'tableCellColumnIndex') { |
1138 var value = node[token]; | 1170 var value = node[token]; |
1139 if (!value) | 1171 if (value == undefined) |
1140 return; | 1172 return; |
1141 value = String(value + 1); | 1173 value = String(value + 1); |
1142 options.annotation.push(token); | 1174 options.annotation.push(token); |
1143 this.append_(buff, value, options); | 1175 this.append_(buff, value, options); |
1176 } else if (token == 'node') { | |
1177 if (!tree.firstChild || !node[tree.firstChild.value]) | |
1178 return; | |
1179 var related = node[tree.firstChild.value]; | |
1180 this.node_(related, related, Output.EventType.NAVIGATE, buff); | |
1144 } else if (node[token] !== undefined) { | 1181 } else if (node[token] !== undefined) { |
1145 options.annotation.push(token); | 1182 options.annotation.push(token); |
1146 var value = node[token]; | 1183 var value = node[token]; |
1147 if (typeof value == 'number') | 1184 if (typeof value == 'number') |
1148 value = String(value); | 1185 value = String(value); |
1149 this.append_(buff, value, options); | 1186 this.append_(buff, value, options); |
1150 } else if (Output.STATE_INFO_[token]) { | 1187 } else if (Output.STATE_INFO_[token]) { |
1151 options.annotation.push('state'); | 1188 options.annotation.push('state'); |
1152 var stateInfo = Output.STATE_INFO_[token]; | 1189 var stateInfo = Output.STATE_INFO_[token]; |
1153 var resolvedInfo = {}; | 1190 var resolvedInfo = {}; |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1293 if (!range.start.node || !range.end.node) | 1330 if (!range.start.node || !range.end.node) |
1294 return; | 1331 return; |
1295 | 1332 |
1296 if (!prevRange && range.start.node.root) | 1333 if (!prevRange && range.start.node.root) |
1297 prevRange = cursors.Range.fromNode(range.start.node.root); | 1334 prevRange = cursors.Range.fromNode(range.start.node.root); |
1298 var cursor = cursors.Cursor.fromNode(range.start.node); | 1335 var cursor = cursors.Cursor.fromNode(range.start.node); |
1299 var prevNode = prevRange.start.node; | 1336 var prevNode = prevRange.start.node; |
1300 | 1337 |
1301 var formatNodeAndAncestors = function(node, prevNode) { | 1338 var formatNodeAndAncestors = function(node, prevNode) { |
1302 var buff = []; | 1339 var buff = []; |
1303 var outputContextFirst = localStorage['outputContextFirst'] == 'true'; | 1340 |
1304 if (outputContextFirst) | 1341 if (this.outputContextFirst_) |
1305 this.ancestry_(node, prevNode, type, buff); | 1342 this.ancestry_(node, prevNode, type, buff); |
1306 this.node_(node, prevNode, type, buff); | 1343 this.node_(node, prevNode, type, buff); |
1307 if (!outputContextFirst) | 1344 if (!this.outputContextFirst_) |
1308 this.ancestry_(node, prevNode, type, buff); | 1345 this.ancestry_(node, prevNode, type, buff); |
1309 if (node.location) | 1346 if (node.location) |
1310 this.locations_.push(node.location); | 1347 this.locations_.push(node.location); |
1311 return buff; | 1348 return buff; |
1312 }.bind(this); | 1349 }.bind(this); |
1313 | 1350 |
1314 while (cursor.node != range.end.node) { | 1351 while (cursor.node != range.end.node) { |
1315 var node = cursor.node; | 1352 var node = cursor.node; |
1316 rangeBuff.push.apply(rangeBuff, formatNodeAndAncestors(node, prevNode)); | 1353 rangeBuff.push.apply(rangeBuff, formatNodeAndAncestors(node, prevNode)); |
1317 prevNode = node; | 1354 prevNode = node; |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1441 var selStart = node.textSelStart; | 1478 var selStart = node.textSelStart; |
1442 var selEnd = node.textSelEnd; | 1479 var selEnd = node.textSelEnd; |
1443 if (selStart !== undefined && | 1480 if (selStart !== undefined && |
1444 (selEnd >= startIndex && selStart <= endIndex)) { | 1481 (selEnd >= startIndex && selStart <= endIndex)) { |
1445 options.annotation.push(new Output.SelectionSpan( | 1482 options.annotation.push(new Output.SelectionSpan( |
1446 selStart - startIndex, | 1483 selStart - startIndex, |
1447 selEnd - startIndex, | 1484 selEnd - startIndex, |
1448 startIndex)); | 1485 startIndex)); |
1449 } | 1486 } |
1450 } | 1487 } |
1451 var outputContextFirst = localStorage['outputContextFirst'] == 'true'; | 1488 |
1452 if (outputContextFirst) | 1489 if (this.outputContextFirst_) |
1453 this.ancestry_(node, prevNode, type, buff); | 1490 this.ancestry_(node, prevNode, type, buff); |
1454 var earcon = this.findEarcon_(node, prevNode); | 1491 var earcon = this.findEarcon_(node, prevNode); |
1455 if (earcon) | 1492 if (earcon) |
1456 options.annotation.push(earcon); | 1493 options.annotation.push(earcon); |
1457 this.append_(buff, range.start.getText().substring(startIndex, endIndex), | 1494 this.append_(buff, range.start.getText().substring(startIndex, endIndex), |
1458 options); | 1495 options); |
1459 if (!outputContextFirst) | 1496 if (!this.outputContextFirst_) |
1460 this.ancestry_(node, prevNode, type, buff); | 1497 this.ancestry_(node, prevNode, type, buff); |
1461 | 1498 |
1462 var loc = | 1499 var loc = |
1463 range.start.node.boundsForRange(startIndex, endIndex); | 1500 range.start.node.boundsForRange(startIndex, endIndex); |
1464 if (loc) | 1501 if (loc) |
1465 this.locations_.push(loc); | 1502 this.locations_.push(loc); |
1466 }, | 1503 }, |
1467 | 1504 |
1468 /** | 1505 /** |
1469 * Appends output to the |buff|. | 1506 * Appends output to the |buff|. |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1643 break; | 1680 break; |
1644 } | 1681 } |
1645 earconFinder = earconFinder.parent; | 1682 earconFinder = earconFinder.parent; |
1646 } | 1683 } |
1647 } | 1684 } |
1648 return null; | 1685 return null; |
1649 } | 1686 } |
1650 }; | 1687 }; |
1651 | 1688 |
1652 }); // goog.scope | 1689 }); // goog.scope |
OLD | NEW |