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 A class for speaking navigation information. 7 */ 8 9 10goog.provide('cvox.NavigationSpeaker'); 11 12goog.require('cvox.NavDescription'); 13 14/** 15 * @constructor 16 */ 17cvox.NavigationSpeaker = function() { 18 /** 19 * This member indicates to this speaker to cancel any pending callbacks. 20 * This is needed primarily to support cancelling a chain of callbacks by an 21 * outside caller. There's currently no way to cancel a chain of callbacks in 22 * any other way. Consider removing this if we ever get silence at the tts 23 * layer. 24 * @type {boolean} 25 */ 26 this.stopReading = false; 27 28 /** 29 * An identifier that tracks the calls to speakDescriptionArray. Used to 30 * cancel a chain of callbacks that is stale. 31 * @type {number} 32 * @private 33 */ 34 this.id_ = 1; 35}; 36 37/** 38 * Speak all of the NavDescriptions in the given array (as returned by 39 * getDescription), including playing earcons. 40 * 41 * @param {Array.<cvox.NavDescription>} descriptionArray The array of 42 * NavDescriptions to speak. 43 * @param {number} initialQueueMode The initial queue mode. 44 * @param {Function} completionFunction Function to call when finished speaking. 45 */ 46cvox.NavigationSpeaker.prototype.speakDescriptionArray = function( 47 descriptionArray, initialQueueMode, completionFunction) { 48 descriptionArray = this.reorderAnnotations(descriptionArray); 49 50 this.stopReading = false; 51 this.id_ = (this.id_ + 1) % 10000; 52 53 // Using self rather than goog.bind in order to get debug symbols. 54 var self = this; 55 var speakDescriptionChain = function(i, queueMode, id) { 56 var description = descriptionArray[i]; 57 if (!description || self.stopReading || self.id_ != id) { 58 return; 59 } 60 var startCallback = function() { 61 for (var j = 0; j < description.earcons.length; j++) { 62 cvox.ChromeVox.earcons.playEarcon(description.earcons[j]); 63 } 64 }; 65 var endCallbackHelper = function() { 66 speakDescriptionChain(i + 1, cvox.AbstractTts.QUEUE_MODE_QUEUE, id); 67 }; 68 var endCallback = function() { 69 // We process content-script specific properties here for now. 70 if (description.personality && 71 description.personality[cvox.AbstractTts.PAUSE] && 72 typeof(description.personality[cvox.AbstractTts.PAUSE]) == 'number') { 73 setTimeout( 74 endCallbackHelper, description.personality[cvox.AbstractTts.PAUSE]); 75 } else { 76 endCallbackHelper(); 77 } 78 if ((i == descriptionArray.length - 1) && completionFunction) { 79 completionFunction(); 80 } 81 }; 82 if (!description.isEmpty()) { 83 description.speak(queueMode, startCallback, endCallback); 84 } else { 85 startCallback(); 86 endCallback(); 87 return; 88 } 89 if (!cvox.ChromeVox.host.hasTtsCallback()) { 90 startCallback(); 91 endCallback(); 92 } 93 }; 94 95 speakDescriptionChain(0, initialQueueMode, this.id_); 96 97 if ((descriptionArray.length == 0) && completionFunction) { 98 completionFunction(); 99 } 100}; 101 102 103/** 104 * Checks for an annotation of a structured elements. 105 * @param {string} annon The annotation. 106 * @return {boolean} True if annotating a structured element. 107 */ 108cvox.NavigationSpeaker.structuredElement = function(annon) { 109 // TODO(dtseng, sorge): This doesn't work for languages other than English. 110 switch (annon) { 111 case 'table': 112 case 'Math': 113 return true; 114 } 115 return false; 116}; 117 118 119/** 120 * Reorder special annotations for structured elements to be spoken first. 121 * @param {Array.<cvox.NavDescription>} descriptionArray The array of 122 * NavDescriptions to speak. 123 * @return {Array.<cvox.NavDescription>} The reordered array. 124 */ 125cvox.NavigationSpeaker.prototype.reorderAnnotations = function( 126 descriptionArray) { 127 var descs = new Array; 128 for (var i = 0; i < descriptionArray.length; i++) { 129 var descr = descriptionArray[i]; 130 if (cvox.NavigationSpeaker.structuredElement(descr.annotation)) { 131 descs.push(new cvox.NavDescription({ 132 text: '', 133 annotation: descr.annotation 134 })); 135 descr.annotation = ''; 136 } 137 descs.push(descr); 138 } 139 return descs; 140}; 141