1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file. 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @fileoverview JavaScript shim for the liblouis Native Client wrapper. 7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.provide('cvox.LibLouis'); 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Encapsulates a liblouis Native Client instance in the page. 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} nmfPath Path to .nmf file for the module. 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string=} opt_tablesDir Path to tables directory. 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis = function(nmfPath, opt_tablesDir) { 19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Path to .nmf file for the module. 21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private {string} 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.nmfPath_ = nmfPath; 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Path to translation tables. 27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private {?string} 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.tablesDir_ = goog.isDef(opt_tablesDir) ? opt_tablesDir : null; 30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Native Client <embed> element. 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * {@code null} when no <embed> is attached to the DOM. 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private {NaClEmbedElement} 35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.embedElement_ = null; 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The state of the instance. 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private {cvox.LibLouis.InstanceState} 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.instanceState_ = 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.LibLouis.InstanceState.NOT_LOADED; 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Pending requests to construct translators. 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private {!Array.<{tableName: string, 48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * callback: function(cvox.LibLouis.Translator)}>} 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTranslators_ = []; 51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Pending RPC callbacks. Maps from message IDs to callbacks. 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private {!Object.<string, function(!Object)>} 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingRpcCallbacks_ = {}; 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Next message ID to be used. Incremented with each sent message. 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private {number} 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.nextMessageId_ = 1; 63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Describes the loading state of the instance. 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @enum {number} 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.InstanceState = { 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NOT_LOADED: 0, 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) LOADING: 1, 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) LOADED: 2, 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ERROR: -1 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Attaches the Native Client wrapper to the DOM as a child of the provided 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * element, assumed to already be in the document. 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Element} elem Desired parent element of the instance. 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.prototype.attachToElement = function(elem) { 84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.isAttached()) { 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) throw Error('instance already attached'); 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var embed = document.createElement('embed'); 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) embed.src = this.nmfPath_; 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) embed.type = 'application/x-nacl'; 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) embed.width = 0; 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) embed.height = 0; 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!goog.isNull(this.tablesDir_)) { 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) embed.setAttribute('tablesdir', this.tablesDir_); 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) embed.addEventListener('load', goog.bind(this.onInstanceLoad_, this), 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) false /* useCapture */); 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) embed.addEventListener('error', goog.bind(this.onInstanceError_, this), 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) false /* useCapture */); 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) embed.addEventListener('message', goog.bind(this.onInstanceMessage_, this), 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) false /* useCapture */); 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) elem.appendChild(embed); 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.embedElement_ = /** @type {!NaClEmbedElement} */ (embed); 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.instanceState_ = cvox.LibLouis.InstanceState.LOADING; 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Detaches the Native Client instance from the DOM. 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.prototype.detach = function() { 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!this.isAttached()) { 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) throw Error('cannot detach unattached instance'); 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.embedElement_.parentNode.removeChild(this.embedElement_); 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.embedElement_ = null; 119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.instanceState_ = 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.LibLouis.InstanceState.NOT_LOADED; 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Determines whether the Native Client instance is attached. 126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} {@code true} if the <embed> element is attached to the DOM. 127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.prototype.isAttached = function() { 129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.embedElement_ !== null; 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Returns a translator for the desired table, asynchronously. 1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {string} tableNames Comma separated list of braille table names for 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * liblouis. 137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {function(cvox.LibLouis.Translator)} callback 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Callback which will receive the translator, or {@code null} on failure. 139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.prototype.getTranslator = 1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci function(tableNames, callback) { 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) switch (this.instanceState_) { 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case cvox.LibLouis.InstanceState.NOT_LOADED: 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case cvox.LibLouis.InstanceState.LOADING: 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTranslators_.push( 1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci { tableNames: tableNames, callback: callback }); 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case cvox.LibLouis.InstanceState.ERROR: 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) callback(null /* translator */); 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case cvox.LibLouis.InstanceState.LOADED: 1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.rpc_('CheckTable', { 'table_names': tableNames }, 153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.bind(function(reply) { 154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (reply['success']) { 155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var translator = new cvox.LibLouis.Translator( 1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this, tableNames); 157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) callback(translator); 158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) callback(null /* translator */); 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }, this)); 162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Dispatches a message to the remote end and returns the reply asynchronously. 169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * A message ID will be automatically assigned (as a side-effect). 170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} command Command name to be sent. 171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} message JSONable message to be sent. 172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {function(!Object)} callback Callback to receive the reply. 173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.prototype.rpc_ = 176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) function(command, message, callback) { 177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.instanceState_ !== 178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cvox.LibLouis.InstanceState.LOADED) { 179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) throw Error('cannot send RPC: liblouis instance not loaded'); 180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var messageId = '' + this.nextMessageId_++; 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) message['message_id'] = messageId; 183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) message['command'] = command; 184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var json = JSON.stringify(message); 185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.DEBUG) { 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) window.console.debug('RPC -> ' + json); 187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.embedElement_.postMessage(json); 189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingRpcCallbacks_[messageId] = callback; 190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Invoked when the Native Client instance successfully loads. 195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {Event} e Event dispatched after loading. 196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 198cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.prototype.onInstanceLoad_ = function(e) { 199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) window.console.info('loaded liblouis Native Client instance'); 200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.instanceState_ = cvox.LibLouis.InstanceState.LOADED; 201cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTranslators_.forEach(goog.bind(function(record) { 2021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.getTranslator(record.tableNames, record.callback); 203cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }, this)); 204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTranslators_.length = 0; 205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Invoked when the Native Client instance fails to load. 210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {Event} e Event dispatched after loading failure. 211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.prototype.onInstanceError_ = function(e) { 214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) window.console.error('failed to load liblouis Native Client instance'); 215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.instanceState_ = cvox.LibLouis.InstanceState.ERROR; 216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTranslators_.forEach(goog.bind(function(record) { 2171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.getTranslator(record.tableNames, record.callback); 218cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }, this)); 219cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTranslators_.length = 0; 220cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 221cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 222cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 224cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Invoked when the Native Client instance posts a message. 225cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {Event} e Event dispatched after the message was posted. 226cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 227cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 228cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.prototype.onInstanceMessage_ = function(e) { 229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.DEBUG) { 230cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) window.console.debug('RPC <- ' + e.data); 231cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 232cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var message = /** @type {!Object} */ (JSON.parse(e.data)); 233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var messageId = message['in_reply_to']; 234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!goog.isDef(messageId)) { 235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) window.console.warn('liblouis Native Client module sent message with no ID', 236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) message); 237cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.isDef(message['error'])) { 240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) window.console.error('liblouis Native Client error', message['error']); 241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var callback = this.pendingRpcCallbacks_[messageId]; 243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.isDef(callback)) { 244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) delete this.pendingRpcCallbacks_[messageId]; 245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) callback(message); 246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 250cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Braille translator which uses a Native Client instance of liblouis. 252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor 253cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!cvox.LibLouis} instance The instance wrapper. 2541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {string} tableNames Comma separated list of Table names to be passed 2551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * to liblouis. 256cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 2571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccicvox.LibLouis.Translator = function(instance, tableNames) { 258cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 259cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The instance wrapper. 260cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private {!cvox.LibLouis} 261cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 262cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.instance_ = instance; 263cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 265cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * The table name. 266cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private {string} 267cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 2681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.tableNames_ = tableNames; 269cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 270cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 271cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 272cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 273cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Translates text into braille cells. 274cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} text Text to be translated. 275cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {function(ArrayBuffer, Array.<number>, Array.<number>)} callback 276cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Callback for result. Takes 3 parameters: the resulting cells, 277cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * mapping from text to braille positions and mapping from braille to 278cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * text positions. If translation fails for any reason, all parameters are 279cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * {@code null}. 280cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 281cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.Translator.prototype.translate = function(text, callback) { 2821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var message = { 'table_names': this.tableNames_, 'text': text }; 283cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.instance_.rpc_('Translate', message, function(reply) { 284cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var cells = null; 285cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var textToBraille = null; 286cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var brailleToText = null; 287cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (reply['success'] && goog.isString(reply['cells'])) { 288cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) cells = cvox.LibLouis.Translator.decodeHexString_(reply['cells']); 289cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.isDef(reply['text_to_braille'])) { 290cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) textToBraille = reply['text_to_braille']; 291cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 292cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.isDef(reply['braille_to_text'])) { 293cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) brailleToText = reply['braille_to_text']; 294cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 295cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (text.length > 0) { 296cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO(plundblad): The nacl wrapper currently returns an error 297cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // when translating an empty string. Address that and always log here. 298cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) console.error('Braille translation error for ' + JSON.stringify(message)); 299cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 300cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) callback(cells, textToBraille, brailleToText); 301cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 302cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 303cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 304cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 305cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 306cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Translates braille cells into text. 307cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!ArrayBuffer} cells Cells to be translated. 308cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {function(?string)} callback Callback for result. 309cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 310cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.Translator.prototype.backTranslate = 311cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) function(cells, callback) { 312116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (cells.byteLength == 0) { 313116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // liblouis doesn't handle empty input, so handle that trivially 314116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // here. 315116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch callback(''); 316116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return; 317116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 318cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var message = { 3191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 'table_names': this.tableNames_, 320cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'cells': cvox.LibLouis.Translator.encodeHexString_(cells) 321cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }; 322cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.instance_.rpc_('BackTranslate', message, function(reply) { 323cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (reply['success'] && goog.isString(reply['text'])) { 324cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) callback(reply['text']); 325cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 326cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) callback(null /* text */); 327cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 328cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 329cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 330cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 331cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 332cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 333cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Decodes a hexadecimal string to an {@code ArrayBuffer}. 334cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} hex Hexadecimal string. 335cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {!ArrayBuffer} Decoded binary data. 336cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 337cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 338cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.Translator.decodeHexString_ = function(hex) { 339cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!/^([0-9a-f]{2})*$/i.test(hex)) { 340cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) throw Error('invalid hexadecimal string'); 341cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var array = new Uint8Array(hex.length / 2); 343cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var idx = 0; 344cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < hex.length; i += 2) { 345cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) array[idx++] = parseInt(hex.substring(i, i + 2), 16); 346cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 347cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return array.buffer; 348cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 349cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 350cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 351cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 352cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Encodes an {@code ArrayBuffer} in hexadecimal. 353cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!ArrayBuffer} arrayBuffer Binary data. 354cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {string} Hexadecimal string. 355cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 356cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 357cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)cvox.LibLouis.Translator.encodeHexString_ = function(arrayBuffer) { 358cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var array = new Uint8Array(arrayBuffer); 359cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var hex = ''; 360cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < array.length; i++) { 361cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var b = array[i]; 362cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) hex += (b < 0x10 ? '0' : '') + b.toString(16); 363cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 364cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return hex; 365cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 366