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 3// found in the LICENSE file. 4 5/** 6 * @fileoverview Sends Braille commands to the Braille API. 7 */ 8 9goog.provide('cvox.BrailleBackground'); 10 11goog.require('cvox.AbstractBraille'); 12goog.require('cvox.BrailleDisplayManager'); 13goog.require('cvox.BrailleInputHandler'); 14goog.require('cvox.BrailleKeyEvent'); 15goog.require('cvox.BrailleTable'); 16goog.require('cvox.ChromeVox'); 17goog.require('cvox.LibLouis'); 18 19 20/** 21 * @constructor 22 * @param {cvox.BrailleDisplayManager=} opt_displayManagerForTest 23 * Display manager (for mocking in tests). 24 * @param {cvox.BrailleInputHandler=} opt_inputHandlerForTest Input handler 25 * (for mocking in tests). 26 * @extends {cvox.AbstractBraille} 27 */ 28cvox.BrailleBackground = function(opt_displayManagerForTest, 29 opt_inputHandlerForTest) { 30 goog.base(this); 31 /** 32 * @type {cvox.BrailleDisplayManager} 33 * @private 34 */ 35 this.displayManager_ = opt_displayManagerForTest || 36 new cvox.BrailleDisplayManager(); 37 cvox.BrailleTable.getAll(goog.bind(function(tables) { 38 /** 39 * @type {!Array.<cvox.BrailleTable.Table>} 40 * @private 41 */ 42 this.tables_ = tables; 43 this.initialize_(0); 44 }, this)); 45 this.displayManager_.setCommandListener( 46 goog.bind(this.onBrailleKeyEvent_, this)); 47 /** 48 * @type {cvox.NavBraille} 49 * @private 50 */ 51 this.lastContent_ = null; 52 /** 53 * @type {?string} 54 * @private 55 */ 56 this.lastContentId_ = null; 57 /** 58 * @type {!cvox.BrailleInputHandler} 59 * @private 60 */ 61 this.inputHandler_ = opt_inputHandlerForTest || 62 new cvox.BrailleInputHandler(); 63 this.inputHandler_.init(); 64}; 65goog.inherits(cvox.BrailleBackground, cvox.AbstractBraille); 66 67 68/** @override */ 69cvox.BrailleBackground.prototype.write = function(params) { 70 this.lastContentId_ = null; 71 this.lastContent_ = null; 72 this.inputHandler_.onDisplayContentChanged(params.text); 73 this.displayManager_.setContent( 74 params, this.inputHandler_.getExpansionType()); 75}; 76 77 78/** @override */ 79cvox.BrailleBackground.prototype.setCommandListener = function(func) { 80 // TODO(plundblad): Implement when the background page handles commands 81 // as well. 82}; 83 84 85/** 86 * Refreshes the braille translator used for output. This should be 87 * called when something changed (such as a preference) to make sure that 88 * the correct translator is used. 89 */ 90cvox.BrailleBackground.prototype.refreshTranslator = function() { 91 if (!this.liblouis_) { 92 return; 93 } 94 // First, see if we have a braille table set previously. 95 var tables = this.tables_; 96 var table = cvox.BrailleTable.forId(tables, localStorage['brailleTable']); 97 if (!table) { 98 // Match table against current locale. 99 var currentLocale = chrome.i18n.getMessage('@@ui_locale').split(/[_-]/); 100 var major = currentLocale[0]; 101 var minor = currentLocale[1]; 102 var firstPass = tables.filter(function(table) { 103 return table.locale.split(/[_-]/)[0] == major; 104 }); 105 if (firstPass.length > 0) { 106 table = firstPass[0]; 107 if (minor) { 108 var secondPass = firstPass.filter(function(table) { 109 return table.locale.split(/[_-]/)[1] == minor; 110 }); 111 if (secondPass.length > 0) { 112 table = secondPass[0]; 113 } 114 } 115 } 116 } 117 if (!table) { 118 table = cvox.BrailleTable.forId(tables, 'en-US-comp8'); 119 } 120 // TODO(plundblad): ONly update when user explicitly chooses a table 121 // so that switching locales changes table by default. 122 localStorage['brailleTable'] = table.id; 123 if (table.dots == '6') { 124 localStorage['brailleTableType'] = 'brailleTable6'; 125 localStorage['brailleTable6'] = table.id; 126 } else { 127 localStorage['brailleTableType'] = 'brailleTable8'; 128 localStorage['brailleTable8'] = table.id; 129 } 130 131 // Initialize all other defaults. 132 // TODO(plundblad): Stop doing this here. 133 if (!localStorage['brailleTable6']) { 134 localStorage['brailleTable6'] = 'en-US-g1'; 135 } 136 if (!localStorage['brailleTable8']) { 137 localStorage['brailleTable8'] = 'en-US-comp8'; 138 } 139 140 // If the user explicitly set an 8 dot table, use that when looking 141 // for an uncontracted table. Otherwise, use the current table and let 142 // getUncontracted find an appropriate corresponding table. 143 var table8Dot = cvox.BrailleTable.forId(tables, 144 localStorage['brailleTable8']); 145 var uncontractedTable = cvox.BrailleTable.getUncontracted( 146 tables, 147 table8Dot ? table8Dot : table); 148 this.liblouis_.getTranslator(table.fileNames, goog.bind( 149 function(translator) { 150 if (uncontractedTable.id === table.id) { 151 this.displayManager_.setTranslator(translator); 152 this.inputHandler_.setTranslator(translator); 153 } else { 154 this.liblouis_.getTranslator(uncontractedTable.fileNames, goog.bind( 155 function(uncontractedTranslator) { 156 this.displayManager_.setTranslator( 157 translator, uncontractedTranslator); 158 this.inputHandler_.setTranslator( 159 translator, uncontractedTranslator); 160 }, this)); 161 } 162 }, this)); 163}; 164 165 166/** 167 * Called when a Braille message is received from a page content script. 168 * @param {Object} msg The Braille message. 169 */ 170cvox.BrailleBackground.prototype.onBrailleMessage = function(msg) { 171 if (msg['action'] == 'write') { 172 this.lastContentId_ = msg['contentId']; 173 this.lastContent_ = cvox.NavBraille.fromJson(msg['params']); 174 this.inputHandler_.onDisplayContentChanged(this.lastContent_.text); 175 this.displayManager_.setContent( 176 this.lastContent_, this.inputHandler_.getExpansionType()); 177 } 178}; 179 180 181/** 182 * @return {cvox.LibLouis} The liblouis instance used by this object, or null 183 * if not initialized yet. 184 */ 185cvox.BrailleBackground.prototype.getLibLouisForTest = function() { 186 return this.liblouis_; 187}; 188 189 190/** 191 * Initialization to be done after part of the background page's DOM has been 192 * constructed. Currently only used on ChromeOS. 193 * @param {number} retries Number of retries. 194 * @private 195 */ 196cvox.BrailleBackground.prototype.initialize_ = function(retries) { 197 if (retries > 5) { 198 console.error( 199 'Timeout waiting for document.body; not initializing braille.'); 200 return; 201 } 202 if (!document.body) { 203 window.setTimeout(goog.bind(this.initialize_, this, ++retries), 500); 204 } else { 205 /** 206 * @type {cvox.LibLouis} 207 * @private 208 */ 209 this.liblouis_ = new cvox.LibLouis( 210 chrome.extension.getURL( 211 'chromevox/background/braille/liblouis_nacl.nmf'), 212 chrome.extension.getURL( 213 'chromevox/background/braille/tables')); 214 this.liblouis_.attachToElement(document.body); 215 this.refreshTranslator(); 216 } 217}; 218 219 220/** 221 * Handles braille key events by dispatching either to the input handler or 222 * a content script. 223 * @param {!cvox.BrailleKeyEvent} brailleEvt The event. 224 * @param {cvox.NavBraille} content Content of display when event fired. 225 * @private 226 */ 227cvox.BrailleBackground.prototype.onBrailleKeyEvent_ = function( 228 brailleEvt, content) { 229 if (this.inputHandler_.onBrailleKeyEvent(brailleEvt)) { 230 return; 231 } 232 this.sendCommand_(brailleEvt, content); 233}; 234 235 236/** 237 * Dispatches braille input commands to the content script. 238 * @param {!cvox.BrailleKeyEvent} brailleEvt The event. 239 * @param {cvox.NavBraille} content Content of display when event fired. 240 * @private 241 */ 242cvox.BrailleBackground.prototype.sendCommand_ = 243 function(brailleEvt, content) { 244 var msg = { 245 'message': 'BRAILLE', 246 'args': brailleEvt 247 }; 248 if (content === this.lastContent_) { 249 msg.contentId = this.lastContentId_; 250 } 251 cvox.ExtensionBridge.send(msg); 252}; 253