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');
13 * Initializes the traversal with the provided math node.
17 cvox
.TraverseMath = function() {
19 * The active math <MATH> node. In this context, "active" means that this is
20 * the math expression the TraverseMath object is navigating.
23 this.activeMath
= null;
26 * The node currently under inspection.
29 this.activeNode
= null;
32 * Dictionary of all LaTeX elements in the page if there are any.
33 * @type {!Object<string, !Node>}
39 * Dictionary of all MathJaxs elements in the page if there are any.
40 * @type {!Object<string, !Node>}
43 this.allMathjaxs_
= {};
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>}
51 this.todoMathjaxs_
= {};
54 * When traversing a Mathjax node this will contain the internal
55 * MathML representation of the node.
58 this.activeMathmlHost
= null;
61 * Semantic representation of the current node.
64 this.activeSemanticHost
= null;
67 * List of domain names.
68 * @type {Array<string>}
73 * List of style names.
74 * @type {Array<string>}
82 this.domain
= 'default';
91 * Initialize special objects if necessary.
93 if (cvox
.ChromeVox
.mathJax
) {
94 this.initializeMathjaxs();
95 this.initializeAltMaths();
98 goog
.addSingletonGetter(cvox
.TraverseMath
);
105 cvox
.TraverseMath
.setSemantic_
= false;
109 * Toggles the semantic setting.
110 * @return {boolean} True if semantic interpretation is switched on. False
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')) {
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
) {
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
);
155 this.allMathjaxs_
[spanId
] = mml
;
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() {
182 goog
.bind(function(mml
, id
) {
183 this.addMathjax(mml
, id
);
185 cvox
.ChromeVox
.mathJax
.isMathjaxActive(
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'))) {
205 var callback
= goog
.bind(
207 this.allTexs_
[id
] = mml
;
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(
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
);
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
) {
265 var child
= cvox
.DomUtil
.directedFindFirstNode(
266 this.activeNode
, reverse
, pred
);
268 this.activeNode
= child
;
270 var next
= cvox
.DomUtil
.directedFindNextNode(
271 this.activeNode
, this.activeMath
, reverse
, pred
);
273 this.activeNode
= next
;
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);
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
) {
305 var node
= this.activeNode
;
306 node
= r
? node
.previousSibling
: node
.nextSibling
;
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
) {
325 if (this.activeNode
== this.activeMath
&& r
) {
328 var node
= this.activeNode
;
329 node
= r
? node
.parentNode
: node
.firstChild
;
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(
347 goog
.bind(function(x
) {return this.allDomains
.indexOf(x
) < 0;},
349 this.allStyles
.push
.apply(
352 goog
.bind(function(x
) {return this.allStyles
.indexOf(x
) < 0;},
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
);
368 cvox
.ChromeVox
.host
.sendToBackgroundPage(
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
);
386 this.domain
= this.allDomains
[(++index
) % this.allDomains
.length
];
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
);
402 this.style
= this.allStyles
[(++index
) % this.allStyles
.length
];
408 * Sets the domain for the TraverseMath object.
409 * @param {string} domain Name of the domain.
412 cvox
.TraverseMath
.prototype.setDomain_ = function(domain
) {
413 if (this.allDomains
.indexOf(domain
) != -1) {
414 this.domain
= domain
;
416 this.domain
= 'default';
422 * Sets the style for the TraverseMath object.
423 * @param {string} style Name of the style.
426 cvox
.TraverseMath
.prototype.setStyle_ = function(style
) {
427 if (this.allStyles
.indexOf(style
) != -1) {
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
) {
444 var id
= node
.getAttribute('spanID');
445 return document
.getElementById(id
);