Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js

Issue 2650733002: Revert of Update json_schema_compiler to handle the Automation extension API (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 12 matching lines...) Expand all
23 goog.require('cvox.TtsCategory'); 23 goog.require('cvox.TtsCategory');
24 goog.require('cvox.ValueSelectionSpan'); 24 goog.require('cvox.ValueSelectionSpan');
25 goog.require('cvox.ValueSpan'); 25 goog.require('cvox.ValueSpan');
26 goog.require('goog.i18n.MessageFormat'); 26 goog.require('goog.i18n.MessageFormat');
27 27
28 goog.scope(function() { 28 goog.scope(function() {
29 var AutomationNode = chrome.automation.AutomationNode; 29 var AutomationNode = chrome.automation.AutomationNode;
30 var Dir = constants.Dir; 30 var Dir = constants.Dir;
31 var EventType = chrome.automation.EventType; 31 var EventType = chrome.automation.EventType;
32 var RoleType = chrome.automation.RoleType; 32 var RoleType = chrome.automation.RoleType;
33 var StateType = chrome.automation.StateType;
34 33
35 /** 34 /**
36 * An Output object formats a cursors.Range into speech, braille, or both 35 * An Output object formats a cursors.Range into speech, braille, or both
37 * representations. This is typically a |Spannable|. 36 * representations. This is typically a |Spannable|.
38 * 37 *
39 * The translation from Range to these output representations rely upon format 38 * The translation from Range to these output representations rely upon format
40 * rules which specify how to convert AutomationNode objects into annotated 39 * rules which specify how to convert AutomationNode objects into annotated
41 * strings. 40 * strings.
42 * The format of these rules is as follows. 41 * The format of these rules is as follows.
43 * 42 *
(...skipping 935 matching lines...) Expand 10 before | Expand all | Expand 10 after
979 if (prevRange && !prevRange.isValid()) 978 if (prevRange && !prevRange.isValid())
980 prevRange = null; 979 prevRange = null;
981 980
982 // Scan unique ancestors to get the value of |outputContextFirst|. 981 // Scan unique ancestors to get the value of |outputContextFirst|.
983 var parent = range.start.node; 982 var parent = range.start.node;
984 var prevParent = prevRange ? prevRange.start.node : parent; 983 var prevParent = prevRange ? prevRange.start.node : parent;
985 if (!parent || !prevParent) 984 if (!parent || !prevParent)
986 return; 985 return;
987 var uniqueAncestors = AutomationUtil.getUniqueAncestors(prevParent, parent); 986 var uniqueAncestors = AutomationUtil.getUniqueAncestors(prevParent, parent);
988 for (var i = 0; parent = uniqueAncestors[i]; i++) { 987 for (var i = 0; parent = uniqueAncestors[i]; i++) {
989 if (parent.role == RoleType.WINDOW) 988 if (parent.role == RoleType.window)
990 break; 989 break;
991 if (Output.ROLE_INFO_[parent.role] && 990 if (Output.ROLE_INFO_[parent.role] &&
992 Output.ROLE_INFO_[parent.role].outputContextFirst) { 991 Output.ROLE_INFO_[parent.role].outputContextFirst) {
993 this.outputContextFirst_ = true; 992 this.outputContextFirst_ = true;
994 break; 993 break;
995 } 994 }
996 } 995 }
997 996
998 if (range.isSubNode()) 997 if (range.isSubNode())
999 this.subNode_(range, prevRange, type, buff); 998 this.subNode_(range, prevRange, type, buff);
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
1045 if (options.isUnique) 1044 if (options.isUnique)
1046 token = token.substring(0, token.length - 1); 1045 token = token.substring(0, token.length - 1);
1047 1046
1048 // Process token based on prefix. 1047 // Process token based on prefix.
1049 var prefix = token[0]; 1048 var prefix = token[0];
1050 token = token.slice(1); 1049 token = token.slice(1);
1051 1050
1052 // All possible tokens based on prefix. 1051 // All possible tokens based on prefix.
1053 if (prefix == '$') { 1052 if (prefix == '$') {
1054 if (token == 'value') { 1053 if (token == 'value') {
1055 var text = node.value || ''; 1054 var text = node.value;
1056 if (!node.state[StateType.EDITABLE] && node.name == text) 1055 if (!node.state.editable && node.name == text)
1057 return; 1056 return;
1058 1057
1059 var selectedText = ''; 1058 var selectedText = '';
1060 if (node.textSelStart !== undefined) { 1059 if (text !== undefined) {
1061 options.annotation.push(new Output.SelectionSpan( 1060 if (node.textSelStart !== undefined) {
1062 node.textSelStart || 0, 1061 options.annotation.push(new Output.SelectionSpan(
1063 node.textSelEnd || 0)); 1062 node.textSelStart,
1063 node.textSelEnd));
1064 1064
1065 selectedText = 1065 selectedText =
1066 node.value.substring(node.textSelStart || 0, 1066 node.value.substring(node.textSelStart, node.textSelEnd);
1067 node.textSelEnd || 0); 1067 }
1068 } 1068 }
1069 options.annotation.push(token); 1069 options.annotation.push(token);
1070 if (selectedText && !this.formatOptions_.braille) { 1070 if (selectedText && !this.formatOptions_.braille) {
1071 this.append_(buff, selectedText, options); 1071 this.append_(buff, selectedText, options);
1072 this.append_(buff, Msgs.getMsg('selected')); 1072 this.append_(buff, Msgs.getMsg('selected'));
1073 } else { 1073 } else {
1074 this.append_(buff, text, options); 1074 this.append_(buff, text, options);
1075 } 1075 }
1076 } else if (token == 'name') { 1076 } else if (token == 'name') {
1077 options.annotation.push(token); 1077 options.annotation.push(token);
1078 var earcon = node ? this.findEarcon_(node, opt_prevNode) : null; 1078 var earcon = node ? this.findEarcon_(node, opt_prevNode) : null;
1079 if (earcon) 1079 if (earcon)
1080 options.annotation.push(earcon); 1080 options.annotation.push(earcon);
1081 this.append_(buff, node.name || '', options); 1081 this.append_(buff, node.name, options);
1082 } else if (token == 'urlFilename') { 1082 } else if (token == 'urlFilename') {
1083 options.annotation.push('name'); 1083 options.annotation.push('name');
1084 var url = node.url || ''; 1084 var url = node.url;
1085 var filename = ''; 1085 var filename = '';
1086 if (url.substring(0, 4) != 'data') { 1086 if (url.substring(0, 4) != 'data') {
1087 filename = 1087 filename =
1088 url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.')); 1088 url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.'));
1089 1089
1090 // Hack to not speak the filename if it's ridiculously long. 1090 // Hack to not speak the filename if it's ridiculously long.
1091 if (filename.length >= 30) 1091 if (filename.length >= 30)
1092 filename = filename.substring(0, 16) + '...'; 1092 filename = filename.substring(0, 16) + '...';
1093 } 1093 }
1094 this.append_(buff, filename, options); 1094 this.append_(buff, filename, options);
1095 } else if (token == 'nameFromNode') { 1095 } else if (token == 'nameFromNode') {
1096 if (node.nameFrom == chrome.automation.NameFromType.CONTENTS) 1096 if (chrome.automation.NameFromType[node.nameFrom] ==
1097 'contents')
1097 return; 1098 return;
1098 1099
1099 options.annotation.push('name'); 1100 options.annotation.push('name');
1100 this.append_(buff, node.name || '', options); 1101 this.append_(buff, node.name, options);
1101 } else if (token == 'nameOrDescendants') { 1102 } else if (token == 'nameOrDescendants') {
1102 options.annotation.push(token); 1103 options.annotation.push(token);
1103 if (node.name) 1104 if (node.name)
1104 this.append_(buff, node.name || '', options); 1105 this.append_(buff, node.name, options);
1105 else 1106 else
1106 this.format_(node, '$descendants', buff); 1107 this.format_(node, '$descendants', buff);
1107 } else if (token == 'description') { 1108 } else if (token == 'description') {
1108 if (node.name == node.description || node.value == node.description) 1109 if (node.name == node.description || node.value == node.description)
1109 return; 1110 return;
1110 options.annotation.push(token); 1111 options.annotation.push(token);
1111 this.append_(buff, node.description || '', options); 1112 this.append_(buff, node.description, options);
1112 } else if (token == 'indexInParent') { 1113 } else if (token == 'indexInParent') {
1113 if (node.parent) { 1114 if (node.parent) {
1114 options.annotation.push(token); 1115 options.annotation.push(token);
1115 var count = 0; 1116 var count = 0;
1116 for (var i = 0, child; child = node.parent.children[i]; i++) { 1117 for (var i = 0, child; child = node.parent.children[i]; i++) {
1117 if (node.role == child.role) 1118 if (node.role == child.role)
1118 count++; 1119 count++;
1119 if (node === child) 1120 if (node === child)
1120 break; 1121 break;
1121 } 1122 }
(...skipping 14 matching lines...) Expand all
1136 case 'mixed': 1137 case 'mixed':
1137 msg = 'aria_checked_mixed'; 1138 msg = 'aria_checked_mixed';
1138 break; 1139 break;
1139 case 'true': 1140 case 'true':
1140 msg = 'aria_checked_true'; 1141 msg = 'aria_checked_true';
1141 break; 1142 break;
1142 case 'false': 1143 case 'false':
1143 msg = 'aria_checked_false'; 1144 msg = 'aria_checked_false';
1144 break; 1145 break;
1145 default: 1146 default:
1146 msg = node.state[StateType.CHECKED] ? 1147 msg =
1147 'aria_checked_true' : 'aria_checked_false'; 1148 node.state.checked ? 'aria_checked_true' : 'aria_checked_false';
1148 } 1149 }
1149 this.format_(node, '@' + msg, buff); 1150 this.format_(node, '@' + msg, buff);
1150 } else if (token == 'state') { 1151 } else if (token == 'state') {
1151 if (node.state) { 1152 Object.getOwnPropertyNames(node.state).forEach(function(s) {
1152 Object.getOwnPropertyNames(node.state).forEach(function(s) { 1153 var stateInfo = Output.STATE_INFO_[s];
1153 var stateInfo = Output.STATE_INFO_[s]; 1154 if (stateInfo && !stateInfo.isRoleSpecific && stateInfo.on)
1154 if (stateInfo && !stateInfo.isRoleSpecific && stateInfo.on)
1155 this.format_(node, '@' + stateInfo.on.msgId, buff); 1155 this.format_(node, '@' + stateInfo.on.msgId, buff);
1156 }.bind(this)); 1156 }.bind(this));
1157 }
1158 } else if (token == 'find') { 1157 } else if (token == 'find') {
1159 // Find takes two arguments: JSON query string and format string. 1158 // Find takes two arguments: JSON query string and format string.
1160 if (tree.firstChild) { 1159 if (tree.firstChild) {
1161 var jsonQuery = tree.firstChild.value; 1160 var jsonQuery = tree.firstChild.value;
1162 node = node.find( 1161 node = node.find(
1163 /** @type {chrome.automation.FindParams}*/( 1162 /** @type {Object}*/(JSON.parse(jsonQuery)));
1164 JSON.parse(jsonQuery)));
1165 var formatString = tree.firstChild.nextSibling; 1163 var formatString = tree.firstChild.nextSibling;
1166 if (node) 1164 if (node)
1167 this.format_(node, formatString, buff); 1165 this.format_(node, formatString, buff);
1168 } 1166 }
1169 } else if (token == 'descendants') { 1167 } else if (token == 'descendants') {
1170 if (!node || AutomationPredicate.leafOrStaticText(node)) 1168 if (!node || AutomationPredicate.leafOrStaticText(node))
1171 return; 1169 return;
1172 1170
1173 // Construct a range to the leftmost and rightmost leaves. 1171 // Construct a range to the leftmost and rightmost leaves.
1174 var leftmost = AutomationUtil.findNodePre( 1172 var leftmost = AutomationUtil.findNodePre(
(...skipping 26 matching lines...) Expand all
1201 var msg = node.role; 1199 var msg = node.role;
1202 var info = Output.ROLE_INFO_[node.role]; 1200 var info = Output.ROLE_INFO_[node.role];
1203 if (info) { 1201 if (info) {
1204 if (this.formatOptions_.braille) 1202 if (this.formatOptions_.braille)
1205 msg = Msgs.getMsg(info.msgId + '_brl'); 1203 msg = Msgs.getMsg(info.msgId + '_brl');
1206 else 1204 else
1207 msg = Msgs.getMsg(info.msgId); 1205 msg = Msgs.getMsg(info.msgId);
1208 } else { 1206 } else {
1209 console.error('Missing role info for ' + node.role); 1207 console.error('Missing role info for ' + node.role);
1210 } 1208 }
1211 this.append_(buff, msg || '', options); 1209 this.append_(buff, msg, options);
1212 } else if (token == 'inputType') { 1210 } else if (token == 'inputType') {
1213 if (!node.inputType) 1211 if (!node.inputType)
1214 return; 1212 return;
1215 options.annotation.push(token); 1213 options.annotation.push(token);
1216 var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[node.inputType] || 1214 var msgId = Output.INPUT_TYPE_MESSAGE_IDS_[node.inputType] ||
1217 'input_type_text'; 1215 'input_type_text';
1218 if (this.formatOptions_.braille) 1216 if (this.formatOptions_.braille)
1219 msgId = msgId + '_brl'; 1217 msgId = msgId + '_brl';
1220 this.append_(buff, Msgs.getMsg(msgId), options); 1218 this.append_(buff, Msgs.getMsg(msgId), options);
1221 } else if (token == 'tableCellRowIndex' || 1219 } else if (token == 'tableCellRowIndex' ||
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
1256 this.append_(buff, value, options); 1254 this.append_(buff, value, options);
1257 } else if (Output.STATE_INFO_[token]) { 1255 } else if (Output.STATE_INFO_[token]) {
1258 options.annotation.push('state'); 1256 options.annotation.push('state');
1259 var stateInfo = Output.STATE_INFO_[token]; 1257 var stateInfo = Output.STATE_INFO_[token];
1260 var resolvedInfo = {}; 1258 var resolvedInfo = {};
1261 resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off; 1259 resolvedInfo = node.state[token] ? stateInfo.on : stateInfo.off;
1262 if (!resolvedInfo) 1260 if (!resolvedInfo)
1263 return; 1261 return;
1264 if (this.formatOptions_.speech && resolvedInfo.earconId) { 1262 if (this.formatOptions_.speech && resolvedInfo.earconId) {
1265 options.annotation.push( 1263 options.annotation.push(
1266 new Output.EarconAction(resolvedInfo.earconId), 1264 new Output.EarconAction(resolvedInfo.earconId), node.location);
1267 node.location || undefined);
1268 } 1265 }
1269 var msgId = 1266 var msgId =
1270 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' : 1267 this.formatOptions_.braille ? resolvedInfo.msgId + '_brl' :
1271 resolvedInfo.msgId; 1268 resolvedInfo.msgId;
1272 var msg = Msgs.getMsg(msgId); 1269 var msg = Msgs.getMsg(msgId);
1273 this.append_(buff, msg, options); 1270 this.append_(buff, msg, options);
1274 } else if (tree.firstChild) { 1271 } else if (tree.firstChild) {
1275 // Custom functions. 1272 // Custom functions.
1276 if (token == 'if') { 1273 if (token == 'if') {
1277 var cond = tree.firstChild; 1274 var cond = tree.firstChild;
1278 var attrib = cond.value.slice(1); 1275 var attrib = cond.value.slice(1);
1279 if (node[attrib] || node.state[attrib]) 1276 if (node[attrib] || node.state[attrib])
1280 this.format_(node, cond.nextSibling, buff); 1277 this.format_(node, cond.nextSibling, buff);
1281 else 1278 else
1282 this.format_(node, cond.nextSibling.nextSibling, buff); 1279 this.format_(node, cond.nextSibling.nextSibling, buff);
1283 } else if (token == 'earcon') { 1280 } else if (token == 'earcon') {
1284 // Ignore unless we're generating speech output. 1281 // Ignore unless we're generating speech output.
1285 if (!this.formatOptions_.speech) 1282 if (!this.formatOptions_.speech)
1286 return; 1283 return;
1287 1284
1288 options.annotation.push( 1285 options.annotation.push(
1289 new Output.EarconAction(tree.firstChild.value, 1286 new Output.EarconAction(tree.firstChild.value, node.location));
1290 node.location || undefined));
1291 this.append_(buff, '', options); 1287 this.append_(buff, '', options);
1292 } else if (token == 'countChildren') { 1288 } else if (token == 'countChildren') {
1293 var role = tree.firstChild.value; 1289 var role = tree.firstChild.value;
1294 var count = node.children.filter(function(e) { 1290 var count = node.children.filter(function(e) {
1295 return e.role == role; 1291 return e.role == role;
1296 }).length; 1292 }).length;
1297 this.append_(buff, String(count)); 1293 this.append_(buff, String(count));
1298 } 1294 }
1299 } 1295 }
1300 } else if (prefix == '@') { 1296 } else if (prefix == '@') {
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
1447 */ 1443 */
1448 ancestry_: function(node, prevNode, type, buff) { 1444 ancestry_: function(node, prevNode, type, buff) {
1449 // Expects |ancestors| to be ordered from root down to leaf. Outputs in 1445 // Expects |ancestors| to be ordered from root down to leaf. Outputs in
1450 // reverse; place context first nodes at the end. 1446 // reverse; place context first nodes at the end.
1451 function byContextFirst(ancestors) { 1447 function byContextFirst(ancestors) {
1452 var contextFirst = []; 1448 var contextFirst = [];
1453 var rest = []; 1449 var rest = [];
1454 for (i = 0; i < ancestors.length - 1; i++) { 1450 for (i = 0; i < ancestors.length - 1; i++) {
1455 var node = ancestors[i]; 1451 var node = ancestors[i];
1456 // Discard ancestors of deepest window. 1452 // Discard ancestors of deepest window.
1457 if (node.role == RoleType.WINDOW) { 1453 if (node.role == RoleType.window) {
1458 contextFirst = []; 1454 contextFirst = [];
1459 rest = []; 1455 rest = [];
1460 } 1456 }
1461 if ((Output.ROLE_INFO_[node.role] || {}).outputContextFirst) 1457 if ((Output.ROLE_INFO_[node.role] || {}).outputContextFirst)
1462 contextFirst.push(node); 1458 contextFirst.push(node);
1463 else 1459 else
1464 rest.push(node); 1460 rest.push(node);
1465 } 1461 }
1466 return rest.concat(contextFirst.reverse()); 1462 return rest.concat(contextFirst.reverse());
1467 } 1463 }
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after
1611 } 1607 }
1612 } 1608 }
1613 1609
1614 if (this.outputContextFirst_) 1610 if (this.outputContextFirst_)
1615 this.ancestry_(node, prevNode, type, buff); 1611 this.ancestry_(node, prevNode, type, buff);
1616 var earcon = this.findEarcon_(node, prevNode); 1612 var earcon = this.findEarcon_(node, prevNode);
1617 if (earcon) 1613 if (earcon)
1618 options.annotation.push(earcon); 1614 options.annotation.push(earcon);
1619 var text = ''; 1615 var text = '';
1620 1616
1621 if (this.formatOptions_.braille && !node.state[StateType.EDITABLE]) { 1617 if (this.formatOptions_.braille && !node.state.editable) {
1622 // In braille, we almost always want to show the entire contents and 1618 // In braille, we almost always want to show the entire contents and
1623 // simply place the cursor under the SelectionSpan we set above. 1619 // simply place the cursor under the SelectionSpan we set above.
1624 text = range.start.getText(); 1620 text = range.start.getText();
1625 } else { 1621 } else {
1626 // This is output for speech or editable braille. 1622 // This is output for speech or editable braille.
1627 text = range.start.getText().substring(rangeStart, rangeEnd); 1623 text = range.start.getText().substring(rangeStart, rangeEnd);
1628 } 1624 }
1629 1625
1630 this.append_(buff, text, options); 1626 this.append_(buff, text, options);
1631 1627
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
1754 return result; 1750 return result;
1755 } 1751 }
1756 1752
1757 // Keep track of if there's an inline node associated with 1753 // Keep track of if there's an inline node associated with
1758 // |cur|. 1754 // |cur|.
1759 var hasInlineNode = cur.getSpansInstanceOf(Output.NodeSpan) 1755 var hasInlineNode = cur.getSpansInstanceOf(Output.NodeSpan)
1760 .some(function(s) { 1756 .some(function(s) {
1761 if (!s.node) 1757 if (!s.node)
1762 return false; 1758 return false;
1763 return s.node.display == 'inline' || 1759 return s.node.display == 'inline' ||
1764 s.node.role == RoleType.INLINE_TEXT_BOX; 1760 s.node.role == RoleType.inlineTextBox;
1765 }); 1761 });
1766 1762
1767 var isName = cur.hasSpan('name'); 1763 var isName = cur.hasSpan('name');
1768 1764
1769 // Now, decide whether we should include separators between the previous 1765 // Now, decide whether we should include separators between the previous
1770 // span and |cur|. 1766 // span and |cur|.
1771 // Never separate chunks without something already there at this point. 1767 // Never separate chunks without something already there at this point.
1772 1768
1773 // The only case where we know for certain that a separator is not needed 1769 // The only case where we know for certain that a separator is not needed
1774 // is when the previous and current values are in-lined and part of the 1770 // is when the previous and current values are in-lined and part of the
(...skipping 30 matching lines...) Expand all
1805 var earconFinder = node; 1801 var earconFinder = node;
1806 var ancestors; 1802 var ancestors;
1807 if (opt_prevNode) 1803 if (opt_prevNode)
1808 ancestors = AutomationUtil.getUniqueAncestors(opt_prevNode, node); 1804 ancestors = AutomationUtil.getUniqueAncestors(opt_prevNode, node);
1809 else 1805 else
1810 ancestors = AutomationUtil.getAncestors(node); 1806 ancestors = AutomationUtil.getAncestors(node);
1811 1807
1812 while (earconFinder = ancestors.pop()) { 1808 while (earconFinder = ancestors.pop()) {
1813 var info = Output.ROLE_INFO_[earconFinder.role]; 1809 var info = Output.ROLE_INFO_[earconFinder.role];
1814 if (info && info.earconId) { 1810 if (info && info.earconId) {
1815 return new Output.EarconAction(info.earconId, 1811 return new Output.EarconAction(info.earconId, node.location);
1816 node.location || undefined);
1817 break; 1812 break;
1818 } 1813 }
1819 earconFinder = earconFinder.parent; 1814 earconFinder = earconFinder.parent;
1820 } 1815 }
1821 } 1816 }
1822 return null; 1817 return null;
1823 }, 1818 },
1824 1819
1825 /** 1820 /**
1826 * Gets a human friendly string with the contents of output. 1821 * Gets a human friendly string with the contents of output.
(...skipping 25 matching lines...) Expand all
1852 /** 1847 /**
1853 * Gets the output buffer for braille. 1848 * Gets the output buffer for braille.
1854 * @return {!Spannable} 1849 * @return {!Spannable}
1855 */ 1850 */
1856 get brailleOutputForTest() { 1851 get brailleOutputForTest() {
1857 return this.mergeBraille_(this.brailleBuffer_); 1852 return this.mergeBraille_(this.brailleBuffer_);
1858 } 1853 }
1859 }; 1854 };
1860 1855
1861 }); // goog.scope 1856 }); // goog.scope
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698