| 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 378 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 389 * Rules specifying format of AutomationNodes for output. | 389 * Rules specifying format of AutomationNodes for output. |
| 390 * @type {!Object<Object<Object<string>>>} | 390 * @type {!Object<Object<Object<string>>>} |
| 391 */ | 391 */ |
| 392 Output.RULES = { | 392 Output.RULES = { |
| 393 navigate: { | 393 navigate: { |
| 394 'default': { | 394 'default': { |
| 395 speak: '$name $value $role $description', | 395 speak: '$name $value $role $description', |
| 396 braille: '' | 396 braille: '' |
| 397 }, | 397 }, |
| 398 abstractContainer: { | 398 abstractContainer: { |
| 399 enter: '$name $role $description', | 399 enter: '$nameFromNode $role $description', |
| 400 leave: '@exited_container($role)' | 400 leave: '@exited_container($role)' |
| 401 }, | 401 }, |
| 402 alert: { | 402 alert: { |
| 403 speak: '!doNotInterrupt $role $descendants' | 403 speak: '!doNotInterrupt $role $descendants' |
| 404 }, | 404 }, |
| 405 alertDialog: { | 405 alertDialog: { |
| 406 enter: '$name $role $description $descendants' | 406 enter: '$nameFromNode $role $description $descendants' |
| 407 }, | 407 }, |
| 408 cell: { | 408 cell: { |
| 409 enter: '@column_granularity $tableCellColumnIndex' | 409 enter: '@column_granularity $tableCellColumnIndex' |
| 410 }, | 410 }, |
| 411 checkBox: { | 411 checkBox: { |
| 412 speak: '$if($checked, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + | 412 speak: '$if($checked, $earcon(CHECK_ON), $earcon(CHECK_OFF)) ' + |
| 413 '$name $role $checked $description' | 413 '$name $role $checked $description' |
| 414 }, | 414 }, |
| 415 dialog: { | 415 dialog: { |
| 416 enter: '$name $role $description' | 416 enter: '$nameFromNode $role $description' |
| 417 }, | 417 }, |
| 418 div: { | 418 div: { |
| 419 enter: '$name', | 419 enter: '$nameFromNode', |
| 420 speak: '$name $description' | 420 speak: '$name $description' |
| 421 }, | 421 }, |
| 422 grid: { | 422 grid: { |
| 423 enter: '$name $role $description' | 423 enter: '$nameFromNode $role $description' |
| 424 }, | 424 }, |
| 425 heading: { | 425 heading: { |
| 426 enter: '@tag_h+$hierarchicalLevel', | 426 enter: '@tag_h+$hierarchicalLevel', |
| 427 speak: '!relativePitch(hierarchicalLevel)' + | 427 speak: '!relativePitch(hierarchicalLevel)' + |
| 428 ' $nameOrDescendants= @tag_h+$hierarchicalLevel' | 428 ' $nameOrDescendants= @tag_h+$hierarchicalLevel' |
| 429 }, | 429 }, |
| 430 inlineTextBox: { | 430 inlineTextBox: { |
| 431 speak: '$name=' | 431 speak: '$name=' |
| 432 }, | 432 }, |
| 433 link: { | 433 link: { |
| 434 enter: '$name= $if($visited, @visited_link, $role)', | 434 enter: '$nameFromNode $if($visited, @visited_link, $role)', |
| 435 speak: '$name= $if($visited, @visited_link, $role) $description' | 435 speak: '$name= $if($visited, @visited_link, $role) $description' |
| 436 }, | 436 }, |
| 437 list: { | 437 list: { |
| 438 enter: '$role @@list_with_items($countChildren(listItem))' | 438 enter: '$role @@list_with_items($countChildren(listItem))' |
| 439 }, | 439 }, |
| 440 listBox: { | 440 listBox: { |
| 441 enter: '$name $role @@list_with_items($countChildren(listBoxOption)) ' + | 441 enter: '$nameFromNode' + |
| 442 '$role @@list_with_items($countChildren(listBoxOption)) ' + |
| 442 '$description' | 443 '$description' |
| 443 }, | 444 }, |
| 444 listBoxOption: { | 445 listBoxOption: { |
| 445 speak: '$name $role @describe_index($indexInParent, $parentChildCount) ' + | 446 speak: '$name $role @describe_index($indexInParent, $parentChildCount) ' + |
| 446 '$description' | 447 '$description' |
| 447 }, | 448 }, |
| 448 listItem: { | 449 listItem: { |
| 449 enter: '$role' | 450 enter: '$role' |
| 450 }, | 451 }, |
| 451 menu: { | 452 menu: { |
| (...skipping 464 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 916 else | 917 else |
| 917 this.range_(range, prevRange, type, buff); | 918 this.range_(range, prevRange, type, buff); |
| 918 }, | 919 }, |
| 919 | 920 |
| 920 /** | 921 /** |
| 921 * Format the node given the format specifier. | 922 * Format the node given the format specifier. |
| 922 * @param {AutomationNode} node | 923 * @param {AutomationNode} node |
| 923 * @param {string|!Object} format The output format either specified as an | 924 * @param {string|!Object} format The output format either specified as an |
| 924 * output template string or a parsed output format tree. | 925 * output template string or a parsed output format tree. |
| 925 * @param {!Array<Spannable>} buff Buffer to receive rendered output. | 926 * @param {!Array<Spannable>} buff Buffer to receive rendered output. |
| 927 * @param {!AutomationNode=} opt_prevNode |
| 926 * @private | 928 * @private |
| 927 */ | 929 */ |
| 928 format_: function(node, format, buff) { | 930 format_: function(node, format, buff, opt_prevNode) { |
| 929 var tokens = []; | 931 var tokens = []; |
| 930 var args = null; | 932 var args = null; |
| 931 | 933 |
| 932 // Hacky way to support args. | 934 // Hacky way to support args. |
| 933 if (typeof(format) == 'string') { | 935 if (typeof(format) == 'string') { |
| 934 format = format.replace(/([,:])\W/g, '$1'); | 936 format = format.replace(/([,:])\W/g, '$1'); |
| 935 tokens = format.split(' '); | 937 tokens = format.split(' '); |
| 936 } else { | 938 } else { |
| 937 tokens = [format]; | 939 tokens = [format]; |
| 938 } | 940 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 977 if (node.textSelStart !== undefined) { | 979 if (node.textSelStart !== undefined) { |
| 978 options.annotation.push(new Output.SelectionSpan( | 980 options.annotation.push(new Output.SelectionSpan( |
| 979 node.textSelStart, | 981 node.textSelStart, |
| 980 node.textSelEnd)); | 982 node.textSelEnd)); |
| 981 } | 983 } |
| 982 } | 984 } |
| 983 options.annotation.push(token); | 985 options.annotation.push(token); |
| 984 this.append_(buff, text, options); | 986 this.append_(buff, text, options); |
| 985 } else if (token == 'name') { | 987 } else if (token == 'name') { |
| 986 options.annotation.push(token); | 988 options.annotation.push(token); |
| 987 var earcon = node ? this.findEarcon_(node) : null; | 989 var earcon = node ? this.findEarcon_(node, opt_prevNode) : null; |
| 988 if (earcon) | 990 if (earcon) |
| 989 options.annotation.push(earcon); | 991 options.annotation.push(earcon); |
| 990 this.append_(buff, node.name, options); | 992 this.append_(buff, node.name, options); |
| 993 } else if (token == 'nameFromNode') { |
| 994 if (chrome.automation.NameFromType[node.nameFrom] == |
| 995 'nameFromContents') |
| 996 return; |
| 997 |
| 998 options.annotation.push(token); |
| 999 this.append_(buff, node.name, options); |
| 991 } else if (token == 'nameOrDescendants') { | 1000 } else if (token == 'nameOrDescendants') { |
| 992 options.annotation.push(token); | 1001 options.annotation.push(token); |
| 993 if (node.name) | 1002 if (node.name) |
| 994 this.append_(buff, node.name, options); | 1003 this.append_(buff, node.name, options); |
| 995 else | 1004 else |
| 996 this.format_(node, '$descendants', buff); | 1005 this.format_(node, '$descendants', buff); |
| 997 } else if (token == 'indexInParent') { | 1006 } else if (token == 'indexInParent') { |
| 998 options.annotation.push(token); | 1007 options.annotation.push(token); |
| 999 this.append_(buff, String(node.indexInParent + 1)); | 1008 this.append_(buff, String(node.indexInParent + 1)); |
| 1000 } else if (token == 'parentChildCount') { | 1009 } else if (token == 'parentChildCount') { |
| (...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1297 for (var i = 0, formatPrevNode; | 1306 for (var i = 0, formatPrevNode; |
| 1298 (formatPrevNode = prevUniqueAncestors[i]); | 1307 (formatPrevNode = prevUniqueAncestors[i]); |
| 1299 i++) { | 1308 i++) { |
| 1300 // This prevents very repetitive announcements. | 1309 // This prevents very repetitive announcements. |
| 1301 if (enteredRoleSet[formatPrevNode.role] || | 1310 if (enteredRoleSet[formatPrevNode.role] || |
| 1302 localStorage['useVerboseMode'] == 'false') | 1311 localStorage['useVerboseMode'] == 'false') |
| 1303 continue; | 1312 continue; |
| 1304 | 1313 |
| 1305 var roleBlock = getMergedRoleBlock(formatPrevNode.role); | 1314 var roleBlock = getMergedRoleBlock(formatPrevNode.role); |
| 1306 if (roleBlock.leave && localStorage['useVerboseMode'] == 'true') | 1315 if (roleBlock.leave && localStorage['useVerboseMode'] == 'true') |
| 1307 this.format_(formatPrevNode, roleBlock.leave, buff); | 1316 this.format_(formatPrevNode, roleBlock.leave, buff, prevNode); |
| 1308 } | 1317 } |
| 1309 | 1318 |
| 1310 var enterOutputs = []; | 1319 var enterOutputs = []; |
| 1311 var enterRole = {}; | 1320 var enterRole = {}; |
| 1312 for (var j = uniqueAncestors.length - 2, formatNode; | 1321 for (var j = uniqueAncestors.length - 2, formatNode; |
| 1313 (formatNode = uniqueAncestors[j]); | 1322 (formatNode = uniqueAncestors[j]); |
| 1314 j--) { | 1323 j--) { |
| 1315 var roleBlock = getMergedRoleBlock(formatNode.role); | 1324 var roleBlock = getMergedRoleBlock(formatNode.role); |
| 1316 if (roleBlock.enter) { | 1325 if (roleBlock.enter) { |
| 1317 if (enterRole[formatNode.role]) | 1326 if (enterRole[formatNode.role]) |
| 1318 continue; | 1327 continue; |
| 1319 enterRole[formatNode.role] = true; | 1328 enterRole[formatNode.role] = true; |
| 1320 this.format_(formatNode, roleBlock.enter, buff); | 1329 this.format_(formatNode, roleBlock.enter, buff, prevNode); |
| 1321 } | 1330 } |
| 1322 if (formatNode.role == 'window') | 1331 if (formatNode.role == 'window') |
| 1323 break; | 1332 break; |
| 1324 } | 1333 } |
| 1325 }, | 1334 }, |
| 1326 | 1335 |
| 1327 /** | 1336 /** |
| 1328 * @param {!AutomationNode} node | 1337 * @param {!AutomationNode} node |
| 1329 * @param {!AutomationNode} prevNode | 1338 * @param {!AutomationNode} prevNode |
| 1330 * @param {EventType|Output.EventType} type | 1339 * @param {EventType|Output.EventType} type |
| 1331 * @param {!Array<Spannable>} buff | 1340 * @param {!Array<Spannable>} buff |
| 1332 * @private | 1341 * @private |
| 1333 */ | 1342 */ |
| 1334 node_: function(node, prevNode, type, buff) { | 1343 node_: function(node, prevNode, type, buff) { |
| 1335 // Navigate is the default event. | 1344 // Navigate is the default event. |
| 1336 var eventBlock = Output.RULES[type] || Output.RULES['navigate']; | 1345 var eventBlock = Output.RULES[type] || Output.RULES['navigate']; |
| 1337 var roleBlock = eventBlock[node.role] || {}; | 1346 var roleBlock = eventBlock[node.role] || {}; |
| 1338 var parentRole = (Output.ROLE_INFO_[node.role] || {}).inherits; | 1347 var parentRole = (Output.ROLE_INFO_[node.role] || {}).inherits; |
| 1339 var parentRoleBlock = eventBlock[parentRole || ''] || {}; | 1348 var parentRoleBlock = eventBlock[parentRole || ''] || {}; |
| 1340 var speakFormat = roleBlock.speak || | 1349 var speakFormat = roleBlock.speak || |
| 1341 parentRoleBlock.speak || | 1350 parentRoleBlock.speak || |
| 1342 eventBlock['default'].speak; | 1351 eventBlock['default'].speak; |
| 1343 | 1352 |
| 1344 this.format_(node, speakFormat, buff); | 1353 this.format_(node, speakFormat, buff, prevNode); |
| 1345 }, | 1354 }, |
| 1346 | 1355 |
| 1347 /** | 1356 /** |
| 1348 * @param {!cursors.Range} range | 1357 * @param {!cursors.Range} range |
| 1349 * @param {cursors.Range} prevRange | 1358 * @param {cursors.Range} prevRange |
| 1350 * @param {EventType|Output.EventType} type | 1359 * @param {EventType|Output.EventType} type |
| 1351 * @param {!Array<Spannable>} buff | 1360 * @param {!Array<Spannable>} buff |
| 1352 * @private | 1361 * @private |
| 1353 */ | 1362 */ |
| 1354 subNode_: function(range, prevRange, type, buff) { | 1363 subNode_: function(range, prevRange, type, buff) { |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1537 return result; | 1546 return result; |
| 1538 }, | 1547 }, |
| 1539 | 1548 |
| 1540 /** | 1549 /** |
| 1541 * Find the earcon for a given node (including ancestry). | 1550 * Find the earcon for a given node (including ancestry). |
| 1542 * @param {!AutomationNode} node | 1551 * @param {!AutomationNode} node |
| 1543 * @param {!AutomationNode=} opt_prevNode | 1552 * @param {!AutomationNode=} opt_prevNode |
| 1544 * @return {Output.Action} | 1553 * @return {Output.Action} |
| 1545 */ | 1554 */ |
| 1546 findEarcon_: function(node, opt_prevNode) { | 1555 findEarcon_: function(node, opt_prevNode) { |
| 1556 if (node === opt_prevNode) |
| 1557 return null; |
| 1558 |
| 1547 if (this.formatOptions_.speech) { | 1559 if (this.formatOptions_.speech) { |
| 1548 var earconFinder = node; | 1560 var earconFinder = node; |
| 1549 var ancestors; | 1561 var ancestors; |
| 1550 if (opt_prevNode) { | 1562 if (opt_prevNode) |
| 1551 // Don't include the node itself. | |
| 1552 ancestors = AutomationUtil.getUniqueAncestors(opt_prevNode, node); | 1563 ancestors = AutomationUtil.getUniqueAncestors(opt_prevNode, node); |
| 1553 ancestors.pop(); | 1564 else |
| 1554 } else { | |
| 1555 ancestors = AutomationUtil.getAncestors(node); | 1565 ancestors = AutomationUtil.getAncestors(node); |
| 1556 } | |
| 1557 | 1566 |
| 1558 while (earconFinder = ancestors.pop()) { | 1567 while (earconFinder = ancestors.pop()) { |
| 1559 var info = Output.ROLE_INFO_[earconFinder.role]; | 1568 var info = Output.ROLE_INFO_[earconFinder.role]; |
| 1560 if (info && info.earconId) { | 1569 if (info && info.earconId) { |
| 1561 return new Output.EarconAction(info.earconId); | 1570 return new Output.EarconAction(info.earconId); |
| 1562 break; | 1571 break; |
| 1563 } | 1572 } |
| 1564 earconFinder = earconFinder.parent; | 1573 earconFinder = earconFinder.parent; |
| 1565 } | 1574 } |
| 1566 } | 1575 } |
| 1567 return null; | 1576 return null; |
| 1568 } | 1577 } |
| 1569 }; | 1578 }; |
| 1570 | 1579 |
| 1571 }); // goog.scope | 1580 }); // goog.scope |
| OLD | NEW |