Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / speech_rules / speech_rule.js
blobcf07c6bf502e2cfcf040cd393d6706d6896a2fe1
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.
5 /**
6  * @fileoverview An interface definition of a speech rule.
7  *
8  * A speech rule is a data structure along with supporting methods that
9  * stipulates how to transform a tree structure such as XML, a browser DOM, or
10  * HTML into a format (usually strings) suitable for rendering by a
11  * text-to-speech engine.
12  *
13  * Speech rules consists of a variable number of speech rule components. Each
14  * component describes how to construct a single utterance. Text-to-speech
15  * renders the components in order.
16  */
18 goog.provide('cvox.SpeechRule');
19 goog.provide('cvox.SpeechRule.Action');
20 goog.provide('cvox.SpeechRule.Component');
21 goog.provide('cvox.SpeechRule.DynamicCstr');
22 goog.provide('cvox.SpeechRule.Precondition');
23 goog.provide('cvox.SpeechRule.Type');
26 /**
27  * Creates a speech rule with precondition, actions and admin information.
28  * @constructor
29  * @param {string} name The name of the rule.
30  * @param {cvox.SpeechRule.DynamicCstr} dynamic Dynamic constraint annotations
31  *     of the rule.
32  * @param {cvox.SpeechRule.Precondition} prec Precondition of the rule.
33  * @param {cvox.SpeechRule.Action} action Action of the speech rule.
34  */
35 cvox.SpeechRule = function(name, dynamic, prec, action) {
36   /** @type {string} */
37   this.name = name;
38   /** @type {cvox.SpeechRule.DynamicCstr} */
39   this.dynamicCstr = dynamic;
40   /** @type {cvox.SpeechRule.Precondition} */
41   this.precondition = prec;
42   /** @type {cvox.SpeechRule.Action} */
43   this.action = action;
47 /**
48  *
49  * @override
50  */
51 cvox.SpeechRule.prototype.toString = function() {
52   var cstrStrings = [];
53   for (var key in this.dynamicCstr) {
54     cstrStrings.push(this.dynamicCstr[key]);
55   }
56   return this.name + ' | ' + cstrStrings.join('.') + ' | ' +
57     this.precondition.toString() + ' ==> ' +
58     this.action.toString();
62 /**
63  * Mapping for types of speech rule components.
64  * @enum {string}
65  */
66 cvox.SpeechRule.Type = {
67   NODE: 'NODE',
68   MULTI: 'MULTI',
69   TEXT: 'TEXT',
70   PERSONALITY: 'PERSONALITY'
74 /**
75  * Maps a string to a valid speech rule type.
76  * @param {string} str Input string.
77  * @return {cvox.SpeechRule.Type}
78  */
79 cvox.SpeechRule.Type.fromString = function(str) {
80   switch (str) {
81     case '[n]': return cvox.SpeechRule.Type.NODE;
82     case '[m]': return cvox.SpeechRule.Type.MULTI;
83     case '[t]': return cvox.SpeechRule.Type.TEXT;
84     case '[p]': return cvox.SpeechRule.Type.PERSONALITY;
85     default: throw 'Parse error: ' + str;
86   }
90 /**
91  * Maps a speech rule type to a human-readable string.
92  * @param {cvox.SpeechRule.Type} speechType
93  * @return {string} Output string.
94  */
95 cvox.SpeechRule.Type.toString = function(speechType) {
96   switch (speechType) {
97     case cvox.SpeechRule.Type.NODE: return '[n]';
98     case cvox.SpeechRule.Type.MULTI: return '[m]';
99     case cvox.SpeechRule.Type.TEXT: return '[t]';
100     case cvox.SpeechRule.Type.PERSONALITY: return '[p]';
101     default: throw 'Unknown type error: ' + speechType;
102   }
107  * Defines a component within a speech rule.
108  * @param {{type: cvox.SpeechRule.Type, content: string}} kwargs The input
109  * component in JSON format.
110  * @constructor
111  */
112 cvox.SpeechRule.Component = function(kwargs) {
113   /** @type {cvox.SpeechRule.Type} */
114   this.type = kwargs.type;
116   /** @type {string} */
117   this.content = kwargs.content;
122  * Parses a valid string representation of a speech component into a Component
123  * object.
124  * @param {string} input The input string.
125  * @return {cvox.SpeechRule.Component} The resulting component.
126  */
127 cvox.SpeechRule.Component.fromString = function(input) {
128   // The output JSON.
129   var output = {};
131   // Parse the type.
132   output.type = cvox.SpeechRule.Type.fromString(input.substring(0, 3));
134   // Prep the rest of the parsing.
135   var rest = input.slice(3).trimLeft();
136   if (!rest) {
137     throw new cvox.SpeechRule.OutputError('Missing content.');
138   }
140   switch (output.type) {
141     case cvox.SpeechRule.Type.TEXT:
142       if (rest[0] == '"') {
143         var quotedString = cvox.SpeechRule.splitString_(rest, '\\(')[0].trim();
144         if (quotedString.slice(-1) != '"') {
145           throw new cvox.SpeechRule.OutputError('Invalid string syntax.');
146         }
147         output.content = quotedString;
148         rest = rest.slice(quotedString.length).trim();
149         if (rest.indexOf('(') == -1) {
150           rest = '';
151         }
152         // This break is conditional. If the content is not an explicit string,
153         // it can be treated like node and multi type.
154         break;
155       }
156     case cvox.SpeechRule.Type.NODE:
157     case cvox.SpeechRule.Type.MULTI:
158       var bracket = rest.indexOf(' (');
159       if (bracket == -1) {
160         output.content = rest.trim();
161         rest = '';
162         break;
163       }
164       output.content = rest.substring(0, bracket).trim();
165       rest = rest.slice(bracket).trimLeft();
166     break;
167   }
168   output = new cvox.SpeechRule.Component(output);
169   if (rest) {
170     output.addAttributes(rest);
171   }
172   return output;
177  * @override
178  */
179 cvox.SpeechRule.Component.prototype.toString = function() {
180   var strs = '';
181   strs += cvox.SpeechRule.Type.toString(this.type);
182   strs += this.content ? ' ' + this.content : '';
183   var attribs = this.getAttributes();
184   if (attribs.length > 0) {
185     strs += ' (' + attribs.join(', ') + ')';
186   }
187   return strs;
192  * Adds a single attribute to the component.
193  * @param {string} attr String representation of an attribute.
194  */
195 cvox.SpeechRule.Component.prototype.addAttribute = function(attr) {
196   var colon = attr.indexOf(':');
197   if (colon == -1) {
198     this[attr.trim()] = 'true';
199   } else {
200     this[attr.substring(0, colon).trim()] = attr.slice(colon + 1).trim();
201   }
206  * Adds a list of attributes to the component.
207  * @param {string} attrs String representation of attribute list.
208  */
209 cvox.SpeechRule.Component.prototype.addAttributes = function(attrs) {
210   if (attrs[0] != '(' || attrs.slice(-1) != ')') {
211     throw new cvox.SpeechRule.OutputError(
212         'Invalid attribute expression: ' + attrs);
213   }
214   var attribs = cvox.SpeechRule.splitString_(attrs.slice(1, -1), ',');
215   for (var i = 0; i < attribs.length; i++) {
216     this.addAttribute(attribs[i]);
217   }
222  * Transforms the attributes of an object into a list of strings.
223  * @return {Array<string>} List of translated attribute:value strings.
224  */
225 cvox.SpeechRule.Component.prototype.getAttributes = function() {
226   var attribs = [];
227   for (var key in this) {
228     if (key != 'content' && key != 'type' && typeof(this[key]) != 'function') {
229       attribs.push(key + ':' + this[key]);
230     }
231   }
232   return attribs;
237  * A speech rule is a collection of speech components.
238  * @param {Array<cvox.SpeechRule.Component>} components The input rule.
239  * @constructor
240  */
241 cvox.SpeechRule.Action = function(components) {
242   /** @type {Array<cvox.SpeechRule.Component>} */
243   this.components = components;
248  * Parses an input string into a speech rule class object.
249  * @param {string} input The input string.
250  * @return {cvox.SpeechRule.Action} The resulting object.
251  */
252 cvox.SpeechRule.Action.fromString = function(input) {
253   var comps = cvox.SpeechRule.splitString_(input, ';')
254       .filter(function(x) {return x.match(/\S/);})
255       .map(function(x) {return x.trim();});
256   var newComps = [];
257   for (var i = 0; i < comps.length; i++) {
258     var comp = cvox.SpeechRule.Component.fromString(comps[i]);
259     if (comp) {
260       newComps.push(comp);
261     }
262   }
263 return new cvox.SpeechRule.Action(newComps);
268  * @override
269  */
270 cvox.SpeechRule.Action.prototype.toString = function() {
271   var comps = this.components.map(function(c) { return c.toString(); });
272   return comps.join('; ');
276 // TODO (sorge) Separatation of xpath expressions and custom functions.
277 // Also test validity of xpath expressions.
279  * Constructs a valid precondition for a speech rule.
280  * @param {string} query A node selector function or xpath expression.
281  * @param {Array<string>=} opt_constraints A list of constraint functions.
282  * @constructor
283  */
284 cvox.SpeechRule.Precondition = function(query, opt_constraints) {
285   /** @type {string} */
286   this.query = query;
288   /** @type {!Array<string>} */
289   this.constraints = opt_constraints || [];
294  * @override
295  */
296 cvox.SpeechRule.Precondition.prototype.toString = function() {
297   var constrs = this.constraints.join(', ');
298   return this.query + ', ' + constrs;
303  * Split a string wrt. a given separator symbol while not splitting inside of a
304  * double quoted string. For example, splitting
305  * '[t] "matrix; 3 by 3"; [n] ./*[1]' with separators ';' would yield
306  * ['[t] "matrix; 3 by 3"', ' [n] ./*[1]'].
307  * @param {string} str String to be split.
308  * @param {string} sep Separator symbol.
309  * @return {Array<string>} A list of single component strings.
310  * @private
311  */
312 cvox.SpeechRule.splitString_ = function(str, sep) {
313   var strList = [];
314   var prefix = '';
316   while (str != '') {
317     var sepPos = str.search(sep);
318     if (sepPos == -1) {
319       if ((str.match(/"/g) || []).length % 2 != 0) {
320         throw new cvox.SpeechRule.OutputError(
321             'Invalid string in expression: ' + str);
322       }
323       strList.push(prefix + str);
324       prefix = '';
325       str = '';
326     } else if (
327         (str.substring(0, sepPos).match(/"/g) || []).length % 2 == 0) {
328       strList.push(prefix + str.substring(0, sepPos));
329       prefix = '';
330       str = str.substring(sepPos + 1);
331     } else {
332       var nextQuot = str.substring(sepPos).search('"');
333       if (nextQuot == -1) {
334         throw new cvox.SpeechRule.OutputError(
335             'Invalid string in expression: ' + str);
336       } else {
337         prefix = prefix + str.substring(0, sepPos + nextQuot + 1);
338         str = str.substring(sepPos + nextQuot + 1);
339       }
340     }
341   }
342   if (prefix) {
343     strList.push(prefix);
344   }
345   return strList;
350  * Attributes for dynamic constraints.
351  * We define one default attribute as style. Speech rule stores can add other
352  * attributes later.
353  * @enum {string}
354  */
355 cvox.SpeechRule.DynamicCstrAttrib =
357   STYLE: 'style'
362  * Dynamic constraints are a means to specialize rules that can be changed
363  * dynamically by the user, for example by choosing different styles, etc.
364  * @typedef {!Object<cvox.SpeechRule.DynamicCstrAttrib, string>}
365  */
366 cvox.SpeechRule.DynamicCstr;
370  * Error object for signaling parsing errors.
371  * @param {string} msg The error message.
372  * @constructor
373  * @extends {Error}
374  */
375 cvox.SpeechRule.OutputError = function(msg) {
376   this.name = 'RuleError';
377   this.message = msg || '';
379 goog.inherits(cvox.SpeechRule.OutputError, Error);