[Extensions] Make extension message bubble factory platform-abstract
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / common / traverse_math.js
blobdf4814ee58d0c7f17b9900a7566a2fe0db769e58
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 goog.provide('cvox.TraverseMath');
7 goog.require('cvox.ChromeVox');
8 goog.require('cvox.DomUtil');
9 goog.require('cvox.SemanticTree');
12 /**
13 * Initializes the traversal with the provided math node.
15 * @constructor
17 cvox.TraverseMath = function() {
18 /**
19 * The active math <MATH> node. In this context, "active" means that this is
20 * the math expression the TraverseMath object is navigating.
21 * @type {Node}
23 this.activeMath = null;
25 /**
26 * The node currently under inspection.
27 * @type {Node}
29 this.activeNode = null;
31 /**
32 * Dictionary of all LaTeX elements in the page if there are any.
33 * @type {!Object<string, !Node>}
34 * @private
36 this.allTexs_ = {};
38 /**
39 * Dictionary of all MathJaxs elements in the page if there are any.
40 * @type {!Object<string, !Node>}
41 * @private
43 this.allMathjaxs_ = {};
45 /**
46 * Dictionary of all MathJaxs elements that have not yet been translated at
47 * page load or during MathJax rendering.
48 * @type {!Object<string, !Node>}
49 * @private
51 this.todoMathjaxs_ = {};
53 /**
54 * When traversing a Mathjax node this will contain the internal
55 * MathML representation of the node.
56 * @type {Node}
58 this.activeMathmlHost = null;
60 /**
61 * Semantic representation of the current node.
62 * @type {Node}
64 this.activeSemanticHost = null;
66 /**
67 * List of domain names.
68 * @type {Array<string>}
70 this.allDomains = [];
72 /**
73 * List of style names.
74 * @type {Array<string>}
76 this.allStyles = [];
78 /**
79 * Current domain.
80 * @type {string}
82 this.domain = 'default';
84 /**
85 * Current style.
86 * @type {string}
88 this.style = 'short';
90 /**
91 * Initialize special objects if necessary.
93 if (cvox.ChromeVox.mathJax) {
94 this.initializeMathjaxs();
95 this.initializeAltMaths();
98 goog.addSingletonGetter(cvox.TraverseMath);
102 * @type {boolean}
103 * @private
105 cvox.TraverseMath.setSemantic_ = false;
109 * Toggles the semantic setting.
110 * @return {boolean} True if semantic interpretation is switched on. False
111 * otherwise.
113 cvox.TraverseMath.toggleSemantic = function() {
114 return cvox.TraverseMath.setSemantic_ = !cvox.TraverseMath.setSemantic_;
119 * Initializes a traversal of a math expression.
120 * @param {Node} node A MathML node.
122 cvox.TraverseMath.prototype.initialize = function(node) {
123 if (cvox.DomUtil.isMathImg(node)) {
124 // If a node has a cvoxid attribute we know that it contains a LaTeX
125 // expression that we have rewritten into its corresponding MathML
126 // representation, which we can speak and walk.
127 if (!node.hasAttribute('cvoxid')) {
128 return;
130 var cvoxid = node.getAttribute('cvoxid');
131 node = this.allTexs_[cvoxid];
133 if (cvox.DomUtil.isMathJax(node)) {
134 this.activeMathmlHost = this.allMathjaxs_[node.getAttribute('id')];
136 this.activeMath = this.activeMathmlHost || node;
137 this.activeNode = this.activeMathmlHost || node;
138 if (this.activeNode && cvox.TraverseMath.setSemantic_ &&
139 this.activeNode.nodeType == Node.ELEMENT_NODE) {
140 this.activeNode =
141 (new cvox.SemanticTree(/** @type {!Element} */ (this.activeNode))).xml();
147 * Adds a mapping of a MathJax node to its MathML representation to the
148 * dictionary of MathJax elements.
149 * @param {!Node} mml The MathML node.
150 * @param {string} id The MathJax node id.
152 cvox.TraverseMath.prototype.addMathjax = function(mml, id) {
153 var spanId = cvox.DomUtil.getMathSpanId(id);
154 if (spanId) {
155 this.allMathjaxs_[spanId] = mml;
156 } else {
157 this.redoMathjaxs(mml, id);
163 * Retries to compute MathML representations of MathJax elements, if
164 * they have not been filled in during rendering.
165 * @param {!Node} mml The MathML node.
166 * @param {string} id The MathJax node id.
168 cvox.TraverseMath.prototype.redoMathjaxs = function(mml, id) {
169 var fetch = goog.bind(function() {this.addMathjax(mml, id);}, this);
170 setTimeout(fetch, 500);
175 * Initializes the MathJax to MathML mapping.
176 * We first try to get all MathJax elements that are already being rendered.
177 * Secondly, we register a signal to get updated on all elements that are
178 * rendered or re-rendered later.
180 cvox.TraverseMath.prototype.initializeMathjaxs = function() {
181 var callback =
182 goog.bind(function(mml, id) {
183 this.addMathjax(mml, id);
184 }, this);
185 cvox.ChromeVox.mathJax.isMathjaxActive(
186 function(bool) {
187 if (bool) {
188 cvox.ChromeVox.mathJax.getAllJax(callback);
189 cvox.ChromeVox.mathJax.registerSignal(callback, 'New Math');
196 * Initializes the elements in the page that we identify as potentially
197 * containing tex or asciimath alt text.
199 cvox.TraverseMath.prototype.initializeAltMaths = function() {
200 if (!document.querySelector(
201 cvox.DomUtil.altMathQuerySelector('tex') + ', ' +
202 cvox.DomUtil.altMathQuerySelector('asciimath'))) {
203 return;
205 var callback = goog.bind(
206 function(mml, id) {
207 this.allTexs_[id] = mml;
208 }, this);
209 // Inject a minimalistic version of MathJax into the page.
210 cvox.ChromeVox.mathJax.injectScripts();
211 // Once MathJax is injected we harvest all Latex and AsciiMath in alt
212 // attributes and translate them to MathML expression.
213 cvox.ChromeVox.mathJax.isMathjaxActive(
214 function(active) {
215 if (active) {
216 cvox.ChromeVox.mathJax.configMediaWiki();
217 cvox.ChromeVox.mathJax.getAllTexs(callback);
218 cvox.ChromeVox.mathJax.getAllAsciiMaths(callback);
225 * Moves to the next leaf node in the current Math expression if it exists.
226 * @param {boolean} reverse True if reversed. False by default.
227 * @param {function(!Node):boolean} pred Predicate deciding what a leaf is.
228 * @return {Node} The next node.
230 cvox.TraverseMath.prototype.nextLeaf = function(reverse, pred) {
231 if (this.activeNode && this.activeMath) {
232 var next = pred(this.activeNode) ?
233 cvox.DomUtil.directedFindNextNode(
234 this.activeNode, this.activeMath, reverse, pred) :
235 cvox.DomUtil.directedFindFirstNode(this.activeNode, reverse, pred);
236 if (next) {
237 this.activeNode = next;
240 return this.activeNode;
244 // TODO (sorge) Refactor this logic into single walkers.
246 * Returns a string with the content of the active node.
247 * @return {string} The active content.
249 cvox.TraverseMath.prototype.activeContent = function() {
250 return this.activeNode.textContent;
255 * Moves to the next subtree from a given node in a depth first fashion.
256 * @param {boolean} reverse True if reversed. False by default.
257 * @param {function(!Node):boolean} pred Predicate deciding what a subtree is.
258 * @return {Node} The next subtree.
260 cvox.TraverseMath.prototype.nextSubtree = function(reverse, pred) {
261 if (!this.activeNode || !this.activeMath) {
262 return null;
264 if (!reverse) {
265 var child = cvox.DomUtil.directedFindFirstNode(
266 this.activeNode, reverse, pred);
267 if (child) {
268 this.activeNode = child;
269 } else {
270 var next = cvox.DomUtil.directedFindNextNode(
271 this.activeNode, this.activeMath, reverse, pred);
272 if (next) {
273 this.activeNode = next;
276 } else {
277 if (this.activeNode == this.activeMath) {
278 var child = cvox.DomUtil.directedFindDeepestNode(
279 this.activeNode, reverse, pred);
280 if (child != this.activeNode) {
281 this.activeNode = child;
282 return this.activeNode;
285 var prev = cvox.DomUtil.directedFindNextNode(
286 this.activeNode, this.activeMath, reverse, pred, true, true);
287 if (prev) {
288 this.activeNode = prev;
291 return this.activeNode;
296 * left or right in the math expression.
297 * Navigation is bounded by the presence of a sibling.
298 * @param {boolean} r True to move left; false to move right.
299 * @return {Node} The result.
301 cvox.TraverseMath.prototype.nextSibling = function(r) {
302 if (!this.activeNode || !this.activeMath) {
303 return null;
305 var node = this.activeNode;
306 node = r ? node.previousSibling : node.nextSibling;
307 if (!node) {
308 return null;
310 this.activeNode = node;
311 return this.activeNode;
316 * Moves up or down the math expression.
317 * Navigation is bounded by the root math expression.
318 * @param {boolean} r True to move up; false to move down.
319 * @return {Node} The result.
321 cvox.TraverseMath.prototype.nextParentChild = function(r) {
322 if (!this.activeNode || !this.activeMath) {
323 return null;
325 if (this.activeNode == this.activeMath && r) {
326 return null;
328 var node = this.activeNode;
329 node = r ? node.parentNode : node.firstChild;
330 if (!node) {
331 return null;
333 this.activeNode = node;
334 return this.activeNode;
339 * Adds a list of domains and styles to the existing one.
340 * @param {Array<string>} domains List of domain names.
341 * @param {Array<string>} styles List of style names.
343 cvox.TraverseMath.prototype.addDomainsAndStyles = function(domains, styles) {
344 this.allDomains.push.apply(
345 this.allDomains,
346 domains.filter(
347 goog.bind(function(x) {return this.allDomains.indexOf(x) < 0;},
348 this)));
349 this.allStyles.push.apply(
350 this.allStyles,
351 styles.filter(
352 goog.bind(function(x) {return this.allStyles.indexOf(x) < 0;},
353 this)));
358 * Gets a list of domains and styles from the symbol and function mappings.
359 * Depending on the platform they either live in the background page or
360 * in the android math map.
362 cvox.TraverseMath.prototype.initDomainsAndStyles = function() {
363 if (cvox.ChromeVox.host['mathMap']) {
364 this.addDomainsAndStyles(
365 cvox.ChromeVox.host['mathMap'].allDomains,
366 cvox.ChromeVox.host['mathMap'].allStyles);
367 } else {
368 cvox.ChromeVox.host.sendToBackgroundPage(
369 {'target': 'Math',
370 'action': 'getDomains'});
376 * Sets the domain for the TraverseMath object to the next one in the list
377 * restarting from the first, if necessary.
378 * @return {string} The name of the newly set domain.
380 cvox.TraverseMath.prototype.cycleDomain = function() {
381 this.initDomainsAndStyles();
382 var index = this.allDomains.indexOf(this.domain);
383 if (index == -1) {
384 return this.domain;
386 this.domain = this.allDomains[(++index) % this.allDomains.length];
387 return this.domain;
392 * Sets the style for the TraverseMath object to the next one in the list
393 * restarting from the first, if necessary.
394 * @return {string} The name of the newly set style.
396 cvox.TraverseMath.prototype.cycleStyle = function() {
397 this.initDomainsAndStyles();
398 var index = this.allStyles.indexOf(this.style);
399 if (index == -1) {
400 return this.domain;
402 this.style = this.allStyles[(++index) % this.allStyles.length];
403 return this.style;
408 * Sets the domain for the TraverseMath object.
409 * @param {string} domain Name of the domain.
410 * @private
412 cvox.TraverseMath.prototype.setDomain_ = function(domain) {
413 if (this.allDomains.indexOf(domain) != -1) {
414 this.domain = domain;
415 } else {
416 this.domain = 'default';
422 * Sets the style for the TraverseMath object.
423 * @param {string} style Name of the style.
424 * @private
426 cvox.TraverseMath.prototype.setStyle_ = function(style) {
427 if (this.allStyles.indexOf(style) != -1) {
428 this.style = style;
429 } else {
430 this.style = 'default';
436 * Gets the active node attached to the current document.
437 * @return {Node} The active node, if it exists.
439 cvox.TraverseMath.prototype.getAttachedActiveNode = function() {
440 var node = this.activeNode;
441 if (!node || node.nodeType != Node.ELEMENT_NODE) {
442 return null;
444 var id = node.getAttribute('spanID');
445 return document.getElementById(id);