-
Notifications
You must be signed in to change notification settings - Fork 59
/
engine.js
162 lines (143 loc) · 4.83 KB
/
engine.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// perfect singleton
window.engine = (new (function() {
this.Q = MathJax.Hub.queue;
this.tex = null;
this.mml = null;
this.buffer = [];
// bind helper.
this.bind = function(method) {
var engine = this;
return function() {
return method.apply(engine, arguments);
};
};
// Initialize engine.
this._init = function() {
this.Q.Push(this.bind(function () {
this.tex = {
div: document.getElementById("math-tex"),
jax: MathJax.Hub.getAllJax("math-tex")[0],
last_width: null,
last_q: ''
}
this.mml = {
div: document.getElementById("math-mml"),
jax: MathJax.Hub.getAllJax("math-mml")[0],
last_width: null,
last_q: ''
}
this._process_buffered();
}));
};
// This helper function determines whether or not a <text> node inside the SVG
// output from MathJax is an error message. It uses the default error message
// fill color. Note that the constant #C00 could be overriden by the MathJax
// config!!
this._text_is_error = function(txt) {
return txt.getAttribute("fill") == "#C00" &&
txt.getAttribute("stroke") == "none";
};
// Serialize an (svg) element
this._serialize = function(svg) {
var tmpDiv = document.createElement('div');
tmpDiv.appendChild(svg);
return tmpDiv.innerHTML;
};
// MathJax keeps parts of SVG symbols in one hidden svg at
// the begining of the DOM, this function should take two
// SVGs and return one stand-alone svg which could be
// displayed like an image on some different page.
this._merge = function(svg) {
var origDefs = document.getElementById('MathJax_SVG_Hidden')
.nextSibling.childNodes[0];
var defs = origDefs.cloneNode(false);
// append shallow defs and change xmlns.
svg.insertBefore(defs, svg.childNodes[0]);
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
// clone and copy all used paths into local defs.
// xlink:href in uses FIX
var uses = svg.getElementsByTagName("use");
for (var k = 0; k < uses.length; ++k) {
var id = uses[k].getAttribute("href");
defs.appendChild(
document.getElementById(id.substr(1)).cloneNode(true)
);
uses[k].setAttribute("xlink:href", id);
}
svg.style.position = "static";
return this._serialize(svg);
};
// If someone calls process() before init is complete,
// that call will be stored into a buffer. After the init
// is complete, all buffer stuff will get resolved.
this._process_buffered = function() {
for (var i = 0; i < this.buffer.length; ++i) {
this.process(this.buffer[i][0], this.buffer[i][1]);
}
this.buffer = [];
};
// When process() is finished, the callback cb will be invoked with an
// array [<q string>, <svg out>].
// If there is an error during the rendering then the second
// element, instead of a string, will be a nested array with
// one string element giving the error message.
this.process = function(query, cb) {
// For debugging, the console doesn't work from here, but you can return dummy
// data, as follows. It will show up in the browser instead of the real results.
//cb([query.num, query.q, ["debug message"]]);
//return;
var type = query.type;
if (this[type] === null || this[type].jax === null) {
this.buffer.push( [query, cb] );
}
else {
var q = query.q,
width = query.width,
t = this[type],
div = t.div,
jax = t.jax;
if (width === null) {
//div.removeAttribute('style');
// Let's just use a default width of 1000 (arbitrary)
div.setAttribute('style', 'width: 1000px');
}
else {
div.setAttribute('style', 'width: ' + width + 'px');
}
// Possibilities:
// - if q and width are the same as last time, no need to Rerender
// - if q is the same, but width is not, then Rerender() (calling
// Text() does not work)
// - if q is not the same, call Text()
if (t.last_q == q && t.last_width !== width) {
this.Q.Push(["Rerender", jax]);
}
else if (t.last_q != q) {
this.Q.Push(["Text", jax, q]);
}
t.last_q = q;
t.last_width = width;
this.Q.Push(this.bind(function() {
var svg_elem = div.getElementsByTagName("svg")[0];
var ret = null;
if (!svg_elem) {
ret = ['MathJax error'];
}
else {
var texts = svg_elem.getElementsByTagName("text");
for (var i = 0; i < texts.length; ++i) {
if (this._text_is_error(texts[i])) {
ret = [texts[i].textContent];
break;
}
}
}
if (!ret) { // no error
ret = this._merge(svg_elem.cloneNode(true));
}
cb([query.num, query.q, ret]);
}));
}
};
this._init();
}));