146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file.
4a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)/**
6a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @fileoverview Bridge to MathJax functions from the ChromeVox content script.
7a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *
8a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) */
9a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
10a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)if (typeof(goog) != 'undefined' && goog.provide) {
11a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  goog.provide('cvox.MathJax');
12a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
13a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
14a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)if (typeof(goog) != 'undefined' && goog.require) {
15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  goog.require('cvox.Api');
16a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  goog.require('cvox.MathJaxExternalUtil');
17a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
18a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)(function() {
20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
21a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * The channel between the page and content script.
22a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @type {MessageChannel}
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
24a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var channel_ = new MessageChannel();
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
28a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @constructor
29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax = function() {
31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
32a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
33a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
34a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Initializes message channel in Chromevox.
36a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
37a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.initMessage = function() {
38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    channel_.port1.onmessage = function(evt) {
39a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      cvox.MathJax.execMessage(evt.data);
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    };
41a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    window.postMessage('cvox.MathJaxPortSetup', [channel_.port2], '*');
42a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
43a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
44a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
45a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
46a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Post a message to Chromevox.
47a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} cmd The command to be executed in Chromevox.
48a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} callbackId A string representing the callback id.
49a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {Object.<string, *>} args Dictionary of arguments.
50a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.postMessage = function(cmd, callbackId, args) {
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    channel_.port1.postMessage({'cmd': cmd, 'id': callbackId, 'args': args});
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Executes a command for an incoming message.
58a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {{cmd: string, id: string, args: string}} msg A
59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   *     serializable message.
60a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.execMessage = function(msg) {
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var args = msg.args;
63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    switch (msg.cmd) {
64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case 'Active':
65a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJax.isActive(msg.id);
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      break;
67a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case 'AllJax':
68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJax.getAllJax(msg.id);
69a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      break;
70a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case 'AsciiMathToMml':
71a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJax.asciiMathToMml(msg.id, args.alt, args.id);
72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      break;
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case 'InjectScripts':
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJax.injectScripts();
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      break;
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case 'ConfWikipedia':
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJax.configMediaWiki();
78a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      break;
79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case 'RegSig':
80a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJax.registerSignal(msg.id, args.sig);
81a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      break;
82a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case 'TexToMml':
83a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJax.texToMml(msg.id, args.alt, args.id);
84a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      break;
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
86a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
88a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
89a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
90a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Compute the MathML representation for all currently available MathJax
91a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * nodes.
92a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} callbackId A string representing the callback id.
93a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
94a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.getAllJax = function(callbackId) {
95a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    cvox.MathJaxExternalUtil.getAllJax(
96a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJax.getMathmlCallback_(callbackId));
97a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
98a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
99a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
100a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
101a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Registers a callback for a particular Mathjax signal.
102a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} callbackId A string representing the callback id.
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} signal The Mathjax signal on which to fire the callback.
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.registerSignal = function(callbackId, signal) {
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    cvox.MathJaxExternalUtil.registerSignal(
107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJax.getMathmlCallback_(callbackId), signal);
108a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Constructs a callback that posts a string with the MathML representation of
113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * a MathJax element to ChromeVox.
114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} callbackId A string representing the callback id.
115a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @return {function(Node, string)} A function taking a Mathml node and an id
116a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * string.
117a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @private
118a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.getMathmlCallback_ = function(callbackId) {
120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return function(mml, id) {
121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      cvox.MathJax.postMessage('NodeMml', callbackId,
122a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                               {'mathml': mml, 'elementId': id});
123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    };
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Inject a minimalistic MathJax script into a page for LaTeX translation.
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.injectScripts = function() {
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    cvox.MathJaxExternalUtil.injectConfigScript();
132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    cvox.MathJaxExternalUtil.injectLoadScript();
133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Loads configurations for MediaWiki pages (e.g., Wikipedia).
138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.configMediaWiki = function() {
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        cvox.MathJaxExternalUtil.configMediaWiki();
141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Translates a LaTeX expressions into a MathML element.
146a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} callbackId A string representing the callback id.
147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} tex The LaTeX expression.
148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} cvoxId A string representing the cvox id for the node.
149a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
150a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.texToMml = function(callbackId, tex, cvoxId) {
151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    cvox.MathJaxExternalUtil.texToMml(
152a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        function(mml) {
153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          cvox.MathJax.getMathmlCallback_(callbackId)(mml, cvoxId);
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        },
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        tex);
156a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
157a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
159a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
160a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Translates an AsciiMath expression into a MathML element.
161a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} callbackId A string representing the callback id.
162a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} asciiMath The AsciiMath expression.
163a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} cvoxId A string representing the cvox id for the node.
164a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
165a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.asciiMathToMml = function(callbackId, asciiMath, cvoxId) {
166a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    cvox.MathJaxExternalUtil.asciiMathToMml(
167a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        function(mml) {
168a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          cvox.MathJax.getMathmlCallback_(callbackId)(mml, cvoxId);
169a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        },
170a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        asciiMath);
171a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
172a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
173a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
174a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /**
175a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * Check if MathJax is injected in the page.
176a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   * @param {string} callbackId A string representing the callback id.
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)   */
178a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.isActive = function(callbackId) {
179a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    cvox.MathJax.postMessage(
180a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        'Active', callbackId,
181a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        {'status': cvox.MathJaxExternalUtil.isActive()});
182a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  };
183a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Initializing the bridge.
186a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  cvox.MathJax.initMessage();
187a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
188a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)})();
189