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 Useful abstraction when speaking messages. 7 * 8 * Usage: 9 * $m('aria_role_link').withCount(document.links.length) 10 * .andPause() 11 * .andMessage('aria_role_forms') 12 * .withCount(document.forms.length) 13 * .andEnd() 14 * .speakFlush(); 15 * 16 */ 17 18 goog.provide('cvox.SpokenMessages'); 19 20 goog.require('cvox.AbstractTts'); 21 goog.require('cvox.ChromeVox'); 22 goog.require('cvox.SpokenMessage'); 23 24 /** 25 * @type {Array} 26 */ 27 cvox.SpokenMessages.messages = []; 28 29 /** 30 * Speaks the message chain and interrupts any on-going speech. 31 */ 32 cvox.SpokenMessages.speakFlush = function() { 33 cvox.SpokenMessages.speak(cvox.AbstractTts.QUEUE_MODE_FLUSH); 34 }; 35 36 /** 37 * Speaks the message chain after on-going speech finishes. 38 */ 39 cvox.SpokenMessages.speakQueued = function() { 40 cvox.SpokenMessages.speak(cvox.AbstractTts.QUEUE_MODE_QUEUE); 41 }; 42 43 /** 44 * Speak the message chain. 45 * @param {number} mode The speech queue mode. 46 */ 47 cvox.SpokenMessages.speak = function(mode) { 48 for (var i = 0; i < cvox.SpokenMessages.messages.length; ++i) { 49 var message = cvox.SpokenMessages.messages[i]; 50 51 // An invalid message format. 52 if (!message || (!message.id && !message.raw)) 53 throw 'Invalid message received.'; 54 55 var finalText = ''; 56 if (message.count != null) { 57 if (message.count <= 0) { 58 try { 59 finalText += 60 cvox.ChromeVox.msgs.getMsg(message.id[0] + '_optional_default'); 61 } catch(e) { 62 // The message doesn't exist. 63 continue; 64 } 65 } else if (message.count == 1) { 66 finalText += cvox.ChromeVox.msgs.getMsg(message.id[0] + '_singular'); 67 } else { 68 finalText += cvox.ChromeVox.msgs.getMsg(message.id[0] + '_plural', 69 [message.count]); 70 } 71 } else { 72 if (message.raw) { 73 finalText += message.raw; 74 } else { 75 finalText += 76 cvox.ChromeVox.msgs.getMsg.apply(cvox.ChromeVox.msgs, message.id); 77 } 78 } 79 80 cvox.ChromeVox.tts.speak(finalText, mode, 81 cvox.AbstractTts.PERSONALITY_ANNOUNCEMENT); 82 83 // Always queue after the first message. 84 mode = cvox.AbstractTts.QUEUE_MODE_QUEUE; 85 } 86 87 cvox.SpokenMessages.messages = []; 88 } 89 90 /** 91 * The newest message. 92 * @return {cvox.SpokenMessage} The newest (current) message. 93 */ 94 cvox.SpokenMessages.currentMessage = function() { 95 if (cvox.SpokenMessages.messages.length == 0) 96 throw 'Invalid usage of SpokenMessages; start the chain using $m()'; 97 return cvox.SpokenMessages.messages[cvox.SpokenMessages.messages.length - 1]; 98 }; 99 100 /** 101 * Quantifies the current message. 102 * This will modify the way the message gets read. 103 * For example, if the count is 2, the message becomes pluralized according 104 * to our i18n resources. The message "2 links" is a possible output. 105 * @param {Number} count Quantifies current message. 106 * @return {Object} This object, useful for chaining. 107 */ 108 cvox.SpokenMessages.withCount = function(count) { 109 cvox.SpokenMessages.currentMessage().count = count; 110 return cvox.SpokenMessages; 111 } 112 113 /** 114 * Quantifies the current message. 115 * Modifies the message with a current index/total description (commonly seen 116 * in lists). 117 * @param {number} index The current item. 118 * @param {number} total The total number of items. 119 * @return {Object} This object, useful for chaining. 120 */ 121 cvox.SpokenMessages.andIndexTotal = function(index, total) { 122 var newMessage = new cvox.SpokenMessage(); 123 newMessage.raw = cvox.ChromeVox.msgs.getMsg('index_total', [index, total]); 124 cvox.SpokenMessages.messages.push(newMessage); 125 return cvox.SpokenMessages; 126 } 127 128 /** 129 * Ends a message. with an appropriate marker. 130 * @return {Object} This object, useful for chaining. 131 */ 132 cvox.SpokenMessages.andEnd = function() { 133 return cvox.SpokenMessages.andMessage('end'); 134 }; 135 136 /** 137 * Adds a message. 138 * @param {string|Array} messageId The id of the message. 139 * @return {Object} This object, useful for chaining. 140 */ 141 cvox.SpokenMessages.andMessage = function(messageId) { 142 var newMessage = new cvox.SpokenMessage(); 143 newMessage.id = typeof(messageId) == 'string' ? [messageId] : messageId; 144 cvox.SpokenMessages.messages.push(newMessage); 145 return cvox.SpokenMessages; 146 }; 147 148 149 /** 150 * Adds a string as a message. 151 * @param {string} message An already localized string. 152 * @return {Object} This object, useful for chaining. 153 */ 154 cvox.SpokenMessages.andRawMessage = function(message) { 155 var newMessage = new cvox.SpokenMessage(); 156 newMessage.raw = message; 157 cvox.SpokenMessages.messages.push(newMessage); 158 return cvox.SpokenMessages; 159 } 160 161 /** 162 * Pauses after the message, with an appropriate marker. 163 * @return {Object} This object, useful for chaining. 164 */ 165 cvox.SpokenMessages.andPause = function() { 166 return cvox.SpokenMessages.andMessage('pause'); 167 }; 168 169 cvox.$m = cvox.SpokenMessages.andMessage; 170