navigation_speaker.js revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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