1
/*************************************************************
5
* The main support code for the MathJax Hub, including the
6
* Ajax, Callback, Messaging, and Object-Oriented Programming
7
* libraries, as well as the base Jax classes, and startup
10
* ---------------------------------------------------------------------
12
* Copyright (c) 2009-2012 Design Science, Inc.
14
* Licensed under the Apache License, Version 2.0 (the "License");
15
* you may not use this file except in compliance with the License.
16
* You may obtain a copy of the License at
18
* http://www.apache.org/licenses/LICENSE-2.0
20
* Unless required by applicable law or agreed to in writing, software
21
* distributed under the License is distributed on an "AS IS" BASIS,
22
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
* See the License for the specific language governing permissions and
24
* limitations under the License.
27
if (document.getElementById && document.childNodes && document.createElement) {
29
if (!window.MathJax) {window.MathJax= {}}
30
if (!MathJax.Hub) { // skip if already loaded
32
MathJax.version = "2.1";
33
MathJax.fileversion = "2.1";
35
/**********************************************************/
37
(function (BASENAME) {
38
var BASE = window[BASENAME];
39
if (!BASE) {BASE = window[BASENAME] = {}}
41
var PROTO = []; // a static object used to indicate when a prototype is being created
42
var OBJECT = function (def) {
43
var obj = def.constructor; if (!obj) {obj = new Function("")}
44
for (var id in def) {if (id !== 'constructor' && def.hasOwnProperty(id)) {obj[id] = def[id]}}
47
var CONSTRUCTOR = function () {
48
return new Function ("return arguments.callee.Init.call(this,arguments)");
51
// Test for Safari 2.x bug (can't replace prototype for result of new Function()).
52
// (We don't use this version for everyone since it is a closure and we don't need that).
54
var BUGTEST = CONSTRUCTOR(); BUGTEST.prototype = {bug_test: 1};
55
if (!BUGTEST.prototype.bug_test) {
56
CONSTRUCTOR = function () {
57
return function () {return arguments.callee.Init.call(this,arguments)};
61
BASE.Object = OBJECT({
62
constructor: CONSTRUCTOR(),
64
Subclass: function (def,classdef) {
65
var obj = CONSTRUCTOR();
66
obj.SUPER = this; obj.Init = this.Init;
67
obj.Subclass = this.Subclass; obj.Augment = this.Augment;
68
obj.protoFunction = this.protoFunction;
69
obj.can = this.can; obj.has = this.has; obj.isa = this.isa;
70
obj.prototype = new this(PROTO);
71
obj.prototype.constructor = obj; // the real constructor
72
obj.Augment(def,classdef);
76
Init: function (args) {
78
if (args.length === 1 && args[0] === PROTO) {return obj}
79
if (!(obj instanceof args.callee)) {obj = new args.callee(PROTO)}
80
return obj.Init.apply(obj,args) || obj;
83
Augment: function (def,classdef) {
86
for (id in def) {if (def.hasOwnProperty(id)) {this.protoFunction(id,def[id])}}
87
// MSIE doesn't list toString even if it is not native so handle it separately
88
if (def.toString !== this.prototype.toString && def.toString !== {}.toString)
89
{this.protoFunction('toString',def.toString)}
91
if (classdef != null) {
92
for (id in classdef) {if (classdef.hasOwnProperty(id)) {this[id] = classdef[id]}}
97
protoFunction: function (id,def) {
98
this.prototype[id] = def;
99
if (typeof def === "function") {def.SUPER = this.SUPER.prototype}
103
Init: function () {},
104
SUPER: function (fn) {return fn.callee.SUPER},
105
can: function (method) {return typeof(this[method]) === "function"},
106
has: function (property) {return typeof(this[property]) !== "undefined"},
107
isa: function (obj) {return (obj instanceof Object) && (this instanceof obj)}
110
can: function (method) {return this.prototype.can.call(this,method)},
111
has: function (property) {return this.prototype.has.call(this,property)},
112
isa: function (obj) {
113
var constructor = this;
114
while (constructor) {
115
if (constructor === obj) {return true} else {constructor = constructor.SUPER}
121
SimpleSUPER: OBJECT({
122
constructor: function (def) {return this.SimpleSUPER.define(def)},
124
define: function (src) {
127
for (var id in src) {if (src.hasOwnProperty(id)) {dst[id] = this.wrap(id,src[id])}}
128
// MSIE doesn't list toString even if it is not native so handle it separately
129
if (src.toString !== this.prototype.toString && src.toString !== {}.toString)
130
{dst.toString = this.wrap('toString',src.toString)}
135
wrap: function (id,f) {
136
if (typeof(f) === 'function' && f.toString().match(/\.\s*SUPER\s*\(/)) {
137
var fn = new Function(this.wrapper);
138
fn.label = id; fn.original = f; f = fn;
139
fn.toString = this.stringify;
144
wrapper: function () {
145
var fn = arguments.callee;
146
this.SUPER = fn.SUPER[fn.label];
147
try {var result = fn.original.apply(this,arguments)}
148
catch (err) {delete this.SUPER; throw err}
151
}.toString().replace(/^\s*function\s*\(\)\s*\{\s*/i,"").replace(/\s*\}\s*$/i,""),
153
toString: function () {
154
return this.original.toString.apply(this.original,arguments);
161
/**********************************************************/
164
* Create a callback function from various forms of data:
166
* MathJax.Callback(fn) -- callback to a function
168
* MathJax.Callback([fn]) -- callback to function
169
* MathJax.Callback([fn,data...])
170
* -- callback to function with given data as arguments
171
* MathJax.Callback([object,fn])
172
* -- call fn with object as "this"
173
* MathJax.Callback([object,fn,data...])
174
* -- call fn with object as "this" and data as arguments
175
* MathJax.Callback(["method",object])
176
* -- call method of object wth object as "this"
177
* MathJax.Callback(["method",object,data...])
178
* -- as above, but with data as arguments to method
180
* MathJax.Callback({hook: fn, data: [...], object: this})
181
* -- give function, data, and object to act as "this" explicitly
183
* MathJax.Callback("code") -- callback that compiles and executes a string
185
* MathJax.Callback([...],i)
186
* -- use slice of array starting at i and interpret
187
* result as above. (Used for passing "arguments" array
188
* and trimming initial arguments, if any.)
192
* MathJax.Callback.After([...],cb1,cb2,...)
193
* -- make a callback that isn't called until all the other
194
* ones are called first. I.e., wait for a union of
195
* callbacks to occur before making the given callback.
199
* MathJax.Callback.Queue([callback,...])
200
* -- make a synchronized queue of commands that process
201
* sequentially, waiting for those that return uncalled
206
* MathJax.Callback.Signal(name)
207
* -- finds or creates a names signal, to which listeners
208
* can be attached and are signaled by messages posted
209
* to the signal. Responses can be asynchronous.
212
(function (BASENAME) {
213
var BASE = window[BASENAME];
214
if (!BASE) {BASE = window[BASENAME] = {}}
216
// Create a callback from an associative array
218
var CALLBACK = function (data) {
219
var cb = new Function("return arguments.callee.execute.apply(arguments.callee,arguments)");
220
for (var id in CALLBACK.prototype) {
221
if (CALLBACK.prototype.hasOwnProperty(id)) {
222
if (typeof(data[id]) !== 'undefined') {cb[id] = data[id]}
223
else {cb[id] = CALLBACK.prototype[id]}
226
cb.toString = CALLBACK.prototype.toString;
229
CALLBACK.prototype = {
231
hook: function () {},
234
execute: function () {
235
if (!this.called || this.autoReset) {
236
this.called = !this.autoReset;
237
return this.hook.apply(this.object,this.data.concat([].slice.call(arguments,0)));
240
reset: function () {delete this.called},
241
toString: function () {return this.hook.toString.apply(this.hook,arguments)}
243
var ISCALLBACK = function (f) {
244
return (typeof(f) === "function" && f.isCallback);
247
// Evaluate a string in global context
249
var EVAL = function (code) {return eval.call(window,code)}
250
EVAL("var __TeSt_VaR__ = 1"); // check if it works in global context
251
if (window.__TeSt_VaR__) {
252
try { delete window.__TeSt_VaR__; } // NOTE IE9 throws when in IE7 mode
253
catch (error) { window.__TeSt_VaR__ = null; }
255
if (window.execScript) {
257
EVAL = function (code) {
259
code = "try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}";
260
window.execScript(code);
261
var result = BASE.__result; delete BASE.__result; delete BASE.__code;
262
if (result instanceof Error) {throw result}
267
EVAL = function (code) {
269
code = "try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}";
270
var head = (document.getElementsByTagName("head"))[0]; if (!head) {head = document.body}
271
var script = document.createElement("script");
272
script.appendChild(document.createTextNode(code));
273
head.appendChild(script); head.removeChild(script);
274
var result = BASE.__result; delete BASE.__result; delete BASE.__code;
275
if (result instanceof Error) {throw result}
281
// Create a callback from various types of data
283
var USING = function (args,i) {
284
if (arguments.length > 1) {
285
if (arguments.length === 2 && !(typeof arguments[0] === 'function') &&
286
arguments[0] instanceof Object && typeof arguments[1] === 'number')
287
{args = [].slice.call(args,i)}
288
else {args = [].slice.call(arguments,0)}
290
if (args instanceof Array && args.length === 1) {args = args[0]}
291
if (typeof args === 'function') {
292
if (args.execute === CALLBACK.prototype.execute) {return args}
293
return CALLBACK({hook: args});
294
} else if (args instanceof Array) {
295
if (typeof(args[0]) === 'string' && args[1] instanceof Object &&
296
typeof args[1][args[0]] === 'function') {
297
return CALLBACK({hook: args[1][args[0]], object: args[1], data: args.slice(2)});
298
} else if (typeof args[0] === 'function') {
299
return CALLBACK({hook: args[0], data: args.slice(1)});
300
} else if (typeof args[1] === 'function') {
301
return CALLBACK({hook: args[1], object: args[0], data: args.slice(2)});
303
} else if (typeof(args) === 'string') {
304
return CALLBACK({hook: EVAL, data: [args]});
305
} else if (args instanceof Object) {
306
return CALLBACK(args);
307
} else if (typeof(args) === 'undefined') {
310
throw Error("Can't make callback from given data");
314
// Wait for a given time to elapse and then perform the callback
316
var DELAY = function (time,callback) {
317
callback = USING(callback);
318
callback.timeout = setTimeout(callback,time);
323
// Callback used by AFTER, QUEUE, and SIGNAL to check if calls have completed
325
var WAITFOR = function (callback,signal) {
326
callback = USING(callback);
327
if (!callback.called) {WAITSIGNAL(callback,signal); signal.pending++}
329
var WAITEXECUTE = function () {
330
var signals = this.signal; delete this.signal;
331
this.execute = this.oldExecute; delete this.oldExecute;
332
var result = this.execute.apply(this,arguments);
333
if (ISCALLBACK(result) && !result.called) {WAITSIGNAL(result,signals)} else {
334
for (var i = 0, m = signals.length; i < m; i++) {
335
signals[i].pending--;
336
if (signals[i].pending <= 0) {signals[i].call()}
340
var WAITSIGNAL = function (callback,signals) {
341
if (!(signals instanceof Array)) {signals = [signals]}
342
if (!callback.signal) {
343
callback.oldExecute = callback.execute;
344
callback.execute = WAITEXECUTE;
345
callback.signal = signals;
346
} else if (signals.length === 1) {callback.signal.push(signals[0])}
347
else {callback.signal = callback.signal.concat(signals)}
351
// Create a callback that is called when a collection of other callbacks have
352
// all been executed. If the callback gets called immediately (i.e., the
353
// others are all already called), check if it returns another callback
354
// and return that instead.
356
var AFTER = function (callback) {
357
callback = USING(callback);
358
callback.pending = 0;
359
for (var i = 1, m = arguments.length; i < m; i++)
360
{if (arguments[i]) {WAITFOR(arguments[i],callback)}}
361
if (callback.pending === 0) {
362
var result = callback();
363
if (ISCALLBACK(result)) {callback = result}
369
// An array of prioritized hooks that are executed sequentially
370
// with a given set of data.
372
var HOOKS = MathJax.Object.Subclass({
374
// Initialize the array and the auto-reset status
376
Init: function (reset) {
381
// Add a callback to the list, in priority order (default priority is 10)
383
Add: function (hook,priority) {
384
if (priority == null) {priority = 10}
385
if (!ISCALLBACK(hook)) {hook = USING(hook)}
386
hook.priority = priority;
387
var i = this.hooks.length;
388
while (i > 0 && priority < this.hooks[i-1].priority) {i--}
389
this.hooks.splice(i,0,hook);
392
Remove: function (hook) {
393
for (var i = 0, m = this.hooks.length; i < m; i++) {
394
if (this.hooks[i] === hook) {this.hooks.splice(i,1); return}
398
// Execute the list of callbacks, resetting them if requested.
399
// If any return callbacks, return a callback that will be
400
// executed when they all have completed.
402
Execute: function () {
403
var callbacks = [{}];
404
for (var i = 0, m = this.hooks.length; i < m; i++) {
405
if (this.reset) {this.hooks[i].reset()}
406
var result = this.hooks[i].apply(window,arguments);
407
if (ISCALLBACK(result) && !result.called) {callbacks.push(result)}
409
if (callbacks.length === 1) {return null}
410
if (callbacks.length === 2) {return callbacks[1]}
411
return AFTER.apply({},callbacks);
416
// Run an array of callbacks passing them the given data.
417
// (Legacy function, since this has been replaced by the HOOKS object).
419
var EXECUTEHOOKS = function (hooks,data,reset) {
420
if (!hooks) {return null}
421
if (!(hooks instanceof Array)) {hooks = [hooks]}
422
if (!(data instanceof Array)) {data = (data == null ? [] : [data])}
423
var handler = HOOKS(reset);
424
for (var i = 0, m = hooks.length; i < m; i++) {handler.Add(hooks[i])}
425
return handler.Execute.apply(handler,data);
429
// Command queue that performs commands in order, waiting when
430
// necessary for commands to complete asynchronousely
432
var QUEUE = BASE.Object.Subclass({
434
// Create the queue and push any commands that are specified
437
this.pending = 0; this.running = 0;
439
this.Push.apply(this,arguments);
442
// Add commands to the queue and run them. Adding a callback object
443
// (rather than a callback specification) queues a wait for that callback.
444
// Return the final callback for synchronization purposes.
448
for (var i = 0, m = arguments.length; i < m; i++) {
449
callback = USING(arguments[i]);
450
if (callback === arguments[i] && !callback.called)
451
{callback = USING(["wait",this,callback])}
452
this.queue.push(callback);
454
if (!this.running && !this.pending) {this.Process()}
458
// Process the command queue if we aren't waiting on another command
460
Process: function (queue) {
461
while (!this.running && !this.pending && this.queue.length) {
462
var callback = this.queue[0];
463
queue = this.queue.slice(1); this.queue = [];
464
this.Suspend(); var result = callback(); this.Resume();
465
if (queue.length) {this.queue = queue.concat(this.queue)}
466
if (ISCALLBACK(result) && !result.called) {WAITFOR(result,this)}
470
// Suspend/Resume command processing on this queue
472
Suspend: function () {this.running++},
473
Resume: function () {if (this.running) {this.running--}},
475
// Used by WAITFOR to restart the queue when an action completes
477
call: function () {this.Process.apply(this,arguments)},
478
wait: function (callback) {return callback}
482
// Create a named signal that listeners can attach to, to be signaled by
483
// postings made to the signal. Posts are queued if they occur while one
484
// is already in process.
486
var SIGNAL = QUEUE.Subclass({
487
Init: function (name) {
488
QUEUE.prototype.Init.call(this);
490
this.posted = []; // the messages posted so far
491
this.listeners = HOOKS(true); // those with interest in this signal
494
// Post a message to the signal listeners, with callback for when complete
496
Post: function (message,callback,forget) {
497
callback = USING(callback);
498
if (this.posting || this.pending) {
499
this.Push(["Post",this,message,callback,forget]);
501
this.callback = callback; callback.reset();
502
if (!forget) {this.posted.push(message)}
503
this.Suspend(); this.posting = true;
504
var result = this.listeners.Execute(message);
505
if (ISCALLBACK(result) && !result.called) {WAITFOR(result,this)}
506
this.Resume(); delete this.posting;
507
if (!this.pending) {this.call()}
512
// Clear the post history (so new listeners won't get old messages)
514
Clear: function (callback) {
515
callback = USING(callback);
516
if (this.posting || this.pending) {
517
callback = this.Push(["Clear",this,callback]);
525
// Call the callback (all replies are in) and process the command queue
527
call: function () {this.callback(this); this.Process()},
530
// A listener calls this to register intrest in the signal (so it will be called
531
// when posts occur). If ignorePast is true, it will not be sent the post history.
533
Interest: function (callback,ignorePast,priority) {
534
callback = USING(callback);
535
this.listeners.Add(callback,priority);
537
for (var i = 0, m = this.posted.length; i < m; i++) {
539
var result = callback(this.posted[i]);
540
if (ISCALLBACK(result) && i === this.posted.length-1) {WAITFOR(result,this)}
546
// A listener calls this to remove itself from a signal
548
NoInterest: function (callback) {
549
this.listeners.Remove(callback);
553
// Hook a callback to a particular message on this signal
555
MessageHook: function (msg,callback,priority) {
556
callback = USING(callback);
557
if (!this.hooks) {this.hooks = {}; this.Interest(["ExecuteHooks",this])}
558
if (!this.hooks[msg]) {this.hooks[msg] = HOOKS(true)}
559
this.hooks[msg].Add(callback,priority);
560
for (var i = 0, m = this.posted.length; i < m; i++)
561
{if (this.posted[i] == msg) {callback.reset(); callback(this.posted[i])}}
565
// Execute the message hooks for the given message
567
ExecuteHooks: function (msg,more) {
568
var type = ((msg instanceof Array) ? msg[0] : msg);
569
if (!this.hooks[type]) {return null}
570
return this.hooks[type].Execute(msg);
574
signals: {}, // the named signals
575
find: function (name) {
576
if (!SIGNAL.signals[name]) {SIGNAL.signals[name] = new SIGNAL(name)}
577
return SIGNAL.signals[name];
582
// The main entry-points
584
BASE.Callback = BASE.CallBack = USING;
585
BASE.Callback.Delay = DELAY;
586
BASE.Callback.After = AFTER;
587
BASE.Callback.Queue = QUEUE;
588
BASE.Callback.Signal = SIGNAL.find;
589
BASE.Callback.Hooks = HOOKS;
590
BASE.Callback.ExecuteHooks = EXECUTEHOOKS;
594
/**********************************************************/
596
(function (BASENAME) {
597
var BASE = window[BASENAME];
598
if (!BASE) {BASE = window[BASENAME] = {}}
600
var isSafari2 = (navigator.vendor === "Apple Computer, Inc." &&
601
typeof navigator.vendorSub === "undefined");
602
var sheets = 0; // used by Safari2
605
// Update sheets count and look up the head object
607
var HEAD = function (head) {
608
if (document.styleSheets && document.styleSheets.length > sheets)
609
{sheets = document.styleSheets.length}
611
head = (document.getElementsByTagName("head"))[0];
612
if (!head) {head = document.body}
618
// Remove scripts that are completed so they don't clutter up the HEAD.
619
// This runs via setTimeout since IE7 can't remove the script while it is running.
621
var SCRIPTS = []; // stores scripts to be removed after a delay
622
var REMOVESCRIPTS = function () {
623
for (var i = 0, m = SCRIPTS.length; i < m; i++) {BASE.Ajax.head.removeChild(SCRIPTS[i])}
628
loaded: {}, // files already loaded
629
loading: {}, // files currently in process of loading
630
loadHooks: {}, // hooks to call when files are loaded
631
timeout: 15*1000, // timeout for loading of files (15 seconds)
632
styleDelay: 1, // delay to use before styles are available
633
config: {root: ""}, // URL of root directory to load from
636
OK: 1, // file is loading or did load OK
637
ERROR: -1 // file timed out during load
640
rootPattern: new RegExp("^\\["+BASENAME+"\\]"),
643
// Return a complete URL to a file (replacing the root pattern)
645
fileURL: function (file) {return file.replace(this.rootPattern,this.config.root)},
648
// Load a file if it hasn't been already.
649
// Make sure the file URL is "safe"?
651
Require: function (file,callback) {
652
callback = BASE.Callback(callback); var type;
653
if (file instanceof Object) {for (var i in file) {}; type = i.toUpperCase(); file = file[i]}
654
else {type = file.split(/\./).pop().toUpperCase()}
655
file = this.fileURL(file);
656
// FIXME: check that URL is OK
657
if (this.loaded[file]) {
658
callback(this.loaded[file]);
660
var FILE = {}; FILE[type] = file;
661
this.Load(FILE,callback);
667
// Load a file regardless of where it is and whether it has
668
// already been loaded.
670
Load: function (file,callback) {
671
callback = BASE.Callback(callback); var type;
672
if (file instanceof Object) {for (var i in file) {}; type = i.toUpperCase(); file = file[i]}
673
else {type = file.split(/\./).pop().toUpperCase()}
674
file = this.fileURL(file);
675
if (this.loading[file]) {
676
this.addHook(file,callback);
678
this.head = HEAD(this.head);
679
if (this.loader[type]) {this.loader[type].call(this,file,callback)}
680
else {throw Error("Can't load files of type "+type)}
686
// Register a load hook for a particular file (it will be called when
687
// loadComplete() is called for that file)
689
LoadHook: function (file,callback,priority) {
690
callback = BASE.Callback(callback);
691
if (file instanceof Object) {for (var i in file) {file = file[i]}}
692
file = this.fileURL(file);
693
if (this.loaded[file]) {callback(this.loaded[file])}
694
else {this.addHook(file,callback,priority)}
697
addHook: function (file,callback,priority) {
698
if (!this.loadHooks[file]) {this.loadHooks[file] = MathJax.Callback.Hooks()}
699
this.loadHooks[file].Add(callback,priority);
703
// Used when files are combined in a preloading configuration file
705
Preloading: function () {
706
for (var i = 0, m = arguments.length; i < m; i++) {
707
var file = this.fileURL(arguments[i]);
708
if (!this.loading[file]) {this.loading[file] = {preloaded: true}}
713
// Code used to load the various types of files
714
// (JS for JavaScript, CSS for style sheets)
718
// Create a SCRIPT tag to load the file
720
JS: function (file,callback) {
721
var script = document.createElement("script");
722
var timeout = BASE.Callback(["loadTimeout",this,file]);
723
this.loading[file] = {
725
message: BASE.Message.File(file),
726
timeout: setTimeout(timeout,this.timeout),
727
status: this.STATUS.OK,
730
script.onerror = timeout; // doesn't work in IE and no apparent substitute
731
script.type = "text/javascript";
733
this.head.appendChild(script);
736
// Create a LINK tag to load the style sheet
738
CSS: function (file,callback) {
739
var link = document.createElement("link");
740
link.rel = "stylesheet"; link.type = "text/css"; link.href = file;
741
this.loading[file] = {
743
message: BASE.Message.File(file),
744
status: this.STATUS.OK
746
this.head.appendChild(link);
747
this.timer.create.call(this,[this.timer.file,file],link);
752
// Timing code for checking when style sheets are available.
756
// Create the timing callback and start the timing loop.
757
// We use a delay because some browsers need it to allow the styles
760
create: function (callback,node) {
761
callback = BASE.Callback(callback);
762
if (node.nodeName === "STYLE" && node.styleSheet &&
763
typeof(node.styleSheet.cssText) !== 'undefined') {
764
callback(this.STATUS.OK); // MSIE processes style immediately, but doesn't set its styleSheet!
765
} else if (window.chrome && typeof(window.sessionStorage) !== "undefined" &&
766
node.nodeName === "STYLE") {
767
callback(this.STATUS.OK); // Same for Chrome 5 (beta), Grrr.
768
} else if (isSafari2) {
769
this.timer.start(this,[this.timer.checkSafari2,sheets++,callback],this.styleDelay);
771
this.timer.start(this,[this.timer.checkLength,node,callback],this.styleDelay);
776
// Start the timer for the given callback checker
778
start: function (AJAX,check,delay,timeout) {
779
check = BASE.Callback(check);
780
check.execute = this.execute; check.time = this.time;
781
check.STATUS = AJAX.STATUS; check.timeout = timeout || AJAX.timeout;
782
check.delay = check.total = 0;
783
if (delay) {setTimeout(check,delay)} else {check()}
786
// Increment the time total, increase the delay
787
// and test if we are past the timeout time.
789
time: function (callback) {
790
this.total += this.delay;
791
this.delay = Math.floor(this.delay * 1.05 + 5);
792
if (this.total >= this.timeout) {callback(this.STATUS.ERROR); return 1}
796
// For JS file loads, call the proper routine according to status
798
file: function (file,status) {
799
if (status < 0) {BASE.Ajax.loadTimeout(file)} else {BASE.Ajax.loadComplete(file)}
802
// Call the hook with the required data
804
execute: function () {this.hook.call(this.object,this,this.data[0],this.data[1])},
806
// Safari2 doesn't set the link's stylesheet, so we need to look in the
807
// document.styleSheets array for the new sheet when it is created
809
checkSafari2: function (check,length,callback) {
810
if (check.time(callback)) return;
811
if (document.styleSheets.length > length &&
812
document.styleSheets[length].cssRules &&
813
document.styleSheets[length].cssRules.length)
814
{callback(check.STATUS.OK)} else {setTimeout(check,check.delay)}
817
// Look for the stylesheets rules and check when they are defined
818
// and no longer of length zero. (This assumes there actually ARE
819
// some rules in the stylesheet.)
821
checkLength: function (check,node,callback) {
822
if (check.time(callback)) return;
823
var isStyle = 0; var sheet = (node.sheet || node.styleSheet);
824
try {if ((sheet.cssRules||sheet.rules||[]).length > 0) {isStyle = 1}} catch(err) {
825
if (err.message.match(/protected variable|restricted URI/)) {isStyle = 1}
826
else if (err.message.match(/Security error/)) {
827
// Firefox3 gives "Security error" for missing files, so
828
// can't distinguish that from OK files on remote servers.
829
// or OK files in different directory from local files.
830
isStyle = 1; // just say it is OK (can't really tell)
834
// Opera 9.6 requires this setTimeout
835
setTimeout(BASE.Callback([callback,check.STATUS.OK]),0);
837
setTimeout(check,check.delay);
843
// JavaScript code must call this when they are completely initialized
844
// (this allows them to perform asynchronous actions before indicating
845
// that they are complete).
847
loadComplete: function (file) {
848
file = this.fileURL(file);
849
var loading = this.loading[file];
850
if (loading && !loading.preloaded) {
851
BASE.Message.Clear(loading.message);
852
clearTimeout(loading.timeout);
853
if (loading.script) {
854
if (SCRIPTS.length === 0) {setTimeout(REMOVESCRIPTS,0)}
855
SCRIPTS.push(loading.script);
857
this.loaded[file] = loading.status; delete this.loading[file];
858
this.addHook(file,loading.callback);
860
if (loading) {delete this.loading[file]}
861
this.loaded[file] = this.STATUS.OK;
862
loading = {status: this.STATUS.OK}
864
if (!this.loadHooks[file]) {return null}
865
return this.loadHooks[file].Execute(loading.status);
869
// If a file fails to load within the timeout period (or the onerror handler
870
// is called), this routine runs to signal the error condition.
872
loadTimeout: function (file) {
873
if (this.loading[file].timeout) {clearTimeout(this.loading[file].timeout)}
874
this.loading[file].status = this.STATUS.ERROR;
875
this.loadError(file);
876
this.loadComplete(file);
880
// The default error hook for file load failures
882
loadError: function (file) {
883
BASE.Message.Set("File failed to load: "+file,null,2000);
884
BASE.Hub.signal.Post(["file load error",file]);
888
// Defines a style sheet from a hash of style declarations (key:value pairs
889
// where the key is the style selector and the value is a hash of CSS attributes
892
Styles: function (styles,callback) {
893
var styleString = this.StyleString(styles);
894
if (styleString === "") {
895
callback = BASE.Callback(callback);
898
var style = document.createElement("style"); style.type = "text/css";
899
this.head = HEAD(this.head);
900
this.head.appendChild(style);
901
if (style.styleSheet && typeof(style.styleSheet.cssText) !== 'undefined') {
902
style.styleSheet.cssText = styleString;
904
style.appendChild(document.createTextNode(styleString));
906
callback = this.timer.create.call(this,callback,style);
912
// Create a stylesheet string from a style declaration object
914
StyleString: function (styles) {
915
if (typeof(styles) === 'string') {return styles}
916
var string = "", id, style;
917
for (id in styles) {if (styles.hasOwnProperty(id)) {
918
if (typeof styles[id] === 'string') {
919
string += id + " {"+styles[id]+"}\n";
920
} else if (styles[id] instanceof Array) {
921
for (var i = 0; i < styles[id].length; i++) {
922
style = {}; style[id] = styles[id][i];
923
string += this.StyleString(style);
925
} else if (id.substr(0,6) === '@media') {
926
string += id + " {"+this.StyleString(styles[id])+"}\n";
927
} else if (styles[id] != null) {
929
for (var name in styles[id]) {if (styles[id].hasOwnProperty(name)) {
930
if (styles[id][name] != null)
931
{style[style.length] = name + ': ' + styles[id][name]}
933
string += id +" {"+style.join('; ')+"}\n";
942
/**********************************************************/
946
// Create an HTML element with given attributes and content.
947
// The def parameter is an (optional) object containing key:value pairs
948
// of the attributes and their values, and contents is an (optional)
949
// array of strings to be inserted as text, or arrays of the form
950
// [type,def,contents] that describes an HTML element to be inserted
951
// into the current element. Thus the contents can describe a complete
952
// HTML snippet of arbitrary complexity. E.g.:
954
// MathJax.HTML.Element("span",{id:"mySpan",style{"font-style":"italic"}},[
955
// "(See the ",["a",{href:"http://www.mathjax.org"},["MathJax home page"]],
956
// " for more details.)"]);
958
Element: function (type,def,contents) {
959
var obj = document.createElement(type);
962
var style = def.style; def.style = {};
963
for (var id in style) {if (style.hasOwnProperty(id))
964
{def.style[id.replace(/-([a-z])/g,this.ucMatch)] = style[id]}}
966
MathJax.Hub.Insert(obj,def);
969
if (!(contents instanceof Array)) {contents = [contents]}
970
for (var i = 0; i < contents.length; i++) {
971
if (contents[i] instanceof Array) {
972
obj.appendChild(this.Element(contents[i][0],contents[i][1],contents[i][2]));
974
obj.appendChild(document.createTextNode(contents[i]));
980
ucMatch: function (match,c) {return c.toUpperCase()},
981
addElement: function (span,type,def,contents) {return span.appendChild(this.Element(type,def,contents))},
982
TextNode: function (text) {return document.createTextNode(text)},
983
addText: function (span,text) {return span.appendChild(this.TextNode(text))},
986
// Set and get the text of a script
988
setScript: function (script,text) {
989
if (this.setScriptBug) {script.text = text} else {
990
while (script.firstChild) {script.removeChild(script.firstChild)}
991
this.addText(script,text);
994
getScript: function (script) {
995
var text = (script.text === "" ? script.innerHTML : script.text);
996
return text.replace(/^\s+/,"").replace(/\s+$/,"");
1007
// Save an object as a named cookie
1009
Set: function (name,def) {
1012
for (var id in def) {if (def.hasOwnProperty(id)) {
1013
keys.push(id+":"+def[id].toString().replace(/&/g,"&&"));
1016
var cookie = this.prefix+"."+name+"="+escape(keys.join('&;'));
1018
var time = new Date(); time.setDate(time.getDate() + this.expires);
1019
cookie += '; expires='+time.toGMTString();
1021
document.cookie = cookie+"; path=/";
1025
// Get the contents of a named cookie and incorporate
1026
// it into the given object (or return a fresh one)
1028
Get: function (name,obj) {
1029
if (!obj) {obj = {}}
1030
var pattern = new RegExp("(?:^|;\\s*)"+this.prefix+"\\."+name+"=([^;]*)(?:;|$)");
1031
var match = pattern.exec(document.cookie);
1032
if (match && match[1] !== "") {
1033
var keys = unescape(match[1]).split('&;');
1034
for (var i = 0, m = keys.length; i < m; i++) {
1035
match = keys[i].match(/([^:]+):(.*)/);
1036
var value = match[2].replace(/&&/g,'&');
1037
if (value === "true") {value = true} else if (value === "false") {value = false}
1038
else if (value.match(/^-?(\d+(\.\d+)?|\.\d+)$/)) {value = parseFloat(value)}
1039
obj[match[1]] = value;
1049
/**********************************************************/
1052
ready: false, // used to tell when the styles are available
1053
log: [{}], current: null,
1054
textNodeBug: (navigator.vendor === "Apple Computer, Inc." &&
1055
typeof navigator.vendorSub === "undefined") ||
1056
(window.hasOwnProperty && window.hasOwnProperty("konqueror")), // Konqueror displays some gibberish with text.nodeValue = "..."
1059
"#MathJax_Message": {
1060
position: "fixed", left: "1px", bottom: "2px",
1061
'background-color': "#E6E6E6", border: "1px solid #959595",
1062
margin: "0px", padding: "2px 8px",
1063
'z-index': "102", color: "black", 'font-size': "80%",
1064
width: "auto", 'white-space': "nowrap"
1067
"#MathJax_MSIE_Frame": {
1068
position: "absolute",
1069
top:0, left: 0, width: "0px", 'z-index': 101,
1070
border: "0px", margin: "0px", padding: "0px"
1075
MSIE: function (browser) {
1076
MathJax.Hub.config.styles["#MathJax_Message"].position = "absolute";
1077
MathJax.Message.quirks = (document.compatMode === "BackCompat");
1079
Chrome: function (browser) {
1080
MathJax.Hub.config.styles["#MathJax_Message"].bottom = "1.5em";
1081
MathJax.Hub.config.styles["#MathJax_Message"].left = "1em";
1085
Init: function (styles) {
1086
if (styles) {this.ready = true}
1087
if (!document.body || !this.ready) {return false}
1089
// ASCIIMathML replaces the entire page with a copy of itself (@#!#%@!!)
1090
// so check that this.div is still part of the page, otherwise look up
1091
// the copy and use that.
1093
if (this.div && this.div.parentNode == null) {
1094
this.div = document.getElementById("MathJax_Message");
1095
if (this.div) {this.text = this.div.firstChild}
1098
var frame = document.body;
1099
if (MathJax.Hub.Browser.isMSIE) {
1100
frame = this.frame = this.addDiv(document.body); frame.removeAttribute("id");
1101
frame.style.position = "absolute";
1102
frame.style.border = frame.style.margin = frame.style.padding = "0px";
1103
frame.style.zIndex = "101"; frame.style.height = "0px";
1104
frame = this.addDiv(frame);
1105
frame.id = "MathJax_MSIE_Frame";
1106
window.attachEvent("onscroll",this.MoveFrame);
1107
window.attachEvent("onresize",this.MoveFrame);
1110
this.div = this.addDiv(frame); this.div.style.display = "none";
1111
this.text = this.div.appendChild(document.createTextNode(""));
1116
addDiv: function (parent) {
1117
var div = document.createElement("div");
1118
div.id = "MathJax_Message";
1119
if (parent.firstChild) {parent.insertBefore(div,parent.firstChild)}
1120
else {parent.appendChild(div)}
1124
MoveFrame: function () {
1125
var body = (MathJax.Message.quirks ? document.body : document.documentElement);
1126
var frame = MathJax.Message.frame;
1127
frame.style.left = body.scrollLeft + 'px';
1128
frame.style.top = body.scrollTop + 'px';
1129
frame.style.width = body.clientWidth + 'px';
1130
frame = frame.firstChild;
1131
frame.style.height = body.clientHeight + 'px';
1134
filterText: function (text,n) {
1135
if (MathJax.Hub.config.messageStyle === "simple") {
1136
if (text.match(/^Loading /)) {
1137
if (!this.loading) {this.loading = "Loading "}
1138
text = this.loading; this.loading += ".";
1139
} else if (text.match(/^Processing /)) {
1140
if (!this.processing) {this.processing = "Processing "}
1141
text = this.processing; this.processing += ".";
1142
} else if (text.match(/^Typesetting /)) {
1143
if (!this.typesetting) {this.typesetting = "Typesetting "}
1144
text = this.typesetting; this.typesetting += ".";
1150
Set: function (text,n,clearDelay) {
1151
if (this.timer) {clearTimeout(this.timer); delete this.timeout}
1152
if (n == null) {n = this.log.length; this.log[n] = {}}
1153
this.log[n].text = text; this.log[n].filteredText = text = this.filterText(text,n);
1154
if (typeof(this.log[n].next) === "undefined") {
1155
this.log[n].next = this.current;
1156
if (this.current != null) {this.log[this.current].prev = n}
1159
if (this.current === n && MathJax.Hub.config.messageStyle !== "none") {
1161
if (this.textNodeBug) {this.div.innerHTML = text} else {this.text.nodeValue = text}
1162
this.div.style.display = "";
1163
if (this.status) {window.status = ""; delete this.status}
1165
window.status = text;
1169
if (clearDelay) {setTimeout(MathJax.Callback(["Clear",this,n]),clearDelay)}
1170
else if (clearDelay == 0) {this.Clear(n,0)}
1174
Clear: function (n,delay) {
1175
if (this.log[n].prev != null) {this.log[this.log[n].prev].next = this.log[n].next}
1176
if (this.log[n].next != null) {this.log[this.log[n].next].prev = this.log[n].prev}
1177
if (this.current === n) {
1178
this.current = this.log[n].next;
1180
if (this.div.parentNode == null) {this.Init()} // see ASCIIMathML comments above
1181
if (this.current == null) {
1182
if (this.timer) {clearTimeout(this.timer); delete this.timer}
1183
if (delay == null) {delay = 600}
1184
if (delay === 0) {this.Remove()}
1185
else {this.timer = setTimeout(MathJax.Callback(["Remove",this]),delay)}
1186
} else if (MathJax.Hub.config.messageStyle !== "none") {
1187
if (this.textNodeBug) {this.div.innerHTML = this.log[this.current].filteredText}
1188
else {this.text.nodeValue = this.log[this.current].filteredText}
1190
if (this.status) {window.status = ""; delete this.status}
1191
} else if (this.status) {
1192
window.status = (this.current == null ? "" : this.log[this.current].text);
1195
delete this.log[n].next; delete this.log[n].prev;
1196
delete this.log[n].filteredText;
1199
Remove: function () {
1200
// FIXME: do a fade out or something else interesting?
1201
this.text.nodeValue = "";
1202
this.div.style.display = "none";
1205
File: function (file) {
1206
var root = MathJax.Ajax.config.root;
1207
if (file.substr(0,root.length) === root) {file = "[MathJax]"+file.substr(root.length)}
1208
return this.Set("Loading "+file);
1213
for (var i = 1, m = this.log.length; i < m; i++) {strings[i] = this.log[i].text}
1214
return strings.join("\n");
1219
/**********************************************************/
1224
config: [], // list of configuration files to load
1225
styleSheets: [], // list of CSS files to load
1226
styles: { // styles to generate in-line
1227
".MathJax_Preview": {color: "#888"}
1229
jax: [], // list of input and output jax to load
1230
extensions: [], // list of extensions to load
1231
preJax: null, // pattern to remove from before math script tag
1232
postJax: null, // pattern to remove from after math script tag
1233
displayAlign: 'center', // how to align displayed equations (left, center, right)
1234
displayIndent: '0', // indentation for displayed equations (when not centered)
1235
preRemoveClass: 'MathJax_Preview', // class of objects to remove preceeding math script
1236
showProcessingMessages: true, // display "Processing math: nn%" messages or not
1237
messageStyle: "normal", // set to "none" or "simple" (for "Loading..." and "Processing...")
1238
delayStartupUntil: "none", // set to "onload" to delay setup until the onload handler runs
1239
// set to "configured" to delay startup until MathJax.Hub.Configured() is called
1240
// set to a Callback to wait for before continuing with the startup
1241
skipStartupTypeset: false, // set to true to skip PreProcess and Process during startup
1242
"v1.0-compatible": true, // set to false to prevent message about configuration change
1243
elements: [], // array of elements to process when none is given explicitly
1244
positionToHash: true, // after initial typeset pass, position to #hash location?
1246
showMathMenu: true, // attach math context menu to typeset math?
1247
showMathMenuMSIE: true, // separtely determine if MSIE should have math menu
1248
// (since the code for that is a bit delicate)
1251
zoom: "None", // when to do MathZoom
1252
CTRL: false, // require CTRL for MathZoom?
1253
ALT: false, // require Alt or Option?
1254
CMD: false, // require CMD?
1255
Shift: false, // require Shift?
1256
discoverable: false, // make math menu discoverable on hover?
1257
zscale: "200%", // the scaling factor for MathZoom
1258
renderer: "", // set when Jax are loaded
1259
font: "Auto", // what font HTML-CSS should use
1260
context: "MathJax", // or "Browser" for pass-through to browser menu
1261
mpContext: false, // true means pass menu events to MathPlayer in IE
1262
mpMouse: false, // true means pass mouse events to MathPlayer in IE
1263
texHints: true // include class names for TeXAtom elements
1267
message: ["[Math Processing Error]"], // HTML snippet structure for message to use
1268
style: {color: "#CC0000", "font-style":"italic"} // style for message
1272
preProcessors: MathJax.Callback.Hooks(true), // list of callbacks for preprocessing (initialized by extensions)
1273
inputJax: {}, // mime-type mapped to input jax (by registration)
1274
outputJax: {order:{}}, // mime-type mapped to output jax list (by registration)
1276
processUpdateTime: 250, // time between screen updates when processing math (milliseconds)
1277
processUpdateDelay: 10, // pause between screen updates to allow other processing (milliseconds)
1279
signal: MathJax.Callback.Signal("Hub"), // Signal used for Hub events
1281
Config: function (def) {
1282
this.Insert(this.config,def);
1283
if (this.config.Augment) {this.Augment(this.config.Augment)}
1285
CombineConfig: function (name,def) {
1286
var config = this.config, id, parent; name = name.split(/\./);
1287
for (var i = 0, m = name.length; i < m; i++) {
1288
id = name[i]; if (!config[id]) {config[id] = {}}
1289
parent = config; config = config[id];
1291
parent[id] = config = this.Insert(def,config);
1296
PreProcessor: function () {MathJax.Hub.preProcessors.Add.apply(MathJax.Hub.preProcessors,arguments)},
1297
MessageHook: function () {return MathJax.Hub.signal.MessageHook.apply(MathJax.Hub.signal,arguments)},
1298
StartupHook: function () {return MathJax.Hub.Startup.signal.MessageHook.apply(MathJax.Hub.Startup.signal,arguments)},
1299
LoadHook: function () {return MathJax.Ajax.LoadHook.apply(MathJax.Ajax,arguments)}
1302
getAllJax: function (element) {
1303
var jax = [], scripts = this.elementScripts(element);
1304
for (var i = 0, m = scripts.length; i < m; i++) {
1305
if (scripts[i].MathJax && scripts[i].MathJax.elementJax)
1306
{jax.push(scripts[i].MathJax.elementJax)}
1311
getJaxByType: function (type,element) {
1312
var jax = [], scripts = this.elementScripts(element);
1313
for (var i = 0, m = scripts.length; i < m; i++) {
1314
if (scripts[i].MathJax && scripts[i].MathJax.elementJax &&
1315
scripts[i].MathJax.elementJax.mimeType === type)
1316
{jax.push(scripts[i].MathJax.elementJax)}
1321
getJaxByInputType: function (type,element) {
1322
var jax = [], scripts = this.elementScripts(element);
1323
for (var i = 0, m = scripts.length; i < m; i++) {
1324
if (scripts[i].MathJax && scripts[i].MathJax.elementJax &&
1325
scripts[i].type && scripts[i].type.replace(/ *;(.|\s)*/,"") === type)
1326
{jax.push(scripts[i].MathJax.elementJax)}
1331
getJaxFor: function (element) {
1332
if (typeof(element) === 'string') {element = document.getElementById(element)}
1333
if (element && element.MathJax) {return element.MathJax.elementJax}
1334
if (element && element.isMathJax) {
1335
while (element && !element.jaxID) {element = element.parentNode}
1336
if (element) {return MathJax.OutputJax[element.jaxID].getJaxFromMath(element)}
1341
isJax: function (element) {
1342
if (typeof(element) === 'string') {element = document.getElementById(element)}
1343
if (element && element.isMathJax) {return 1}
1344
if (element && element.tagName != null && element.tagName.toLowerCase() === 'script') {
1345
if (element.MathJax)
1346
{return (element.MathJax.state === MathJax.ElementJax.STATE.PROCESSED ? 1 : -1)}
1347
if (element.type && this.inputJax[element.type.replace(/ *;(.|\s)*/,"")]) {return -1}
1352
setRenderer: function (renderer,type) {
1353
if (!renderer) return;
1354
if (!MathJax.OutputJax[renderer]) {
1355
this.config.menuSettings.renderer = "";
1356
var file = "[MathJax]/jax/output/"+renderer+"/config.js";
1357
return MathJax.Ajax.Require(file,["setRenderer",this,renderer,type]);
1359
this.config.menuSettings.renderer = renderer;
1360
if (type == null) {type = "jax/mml"}
1361
var jax = this.outputJax;
1362
if (jax[type] && jax[type].length) {
1363
if (renderer !== jax[type][0].id) {
1364
jax[type].unshift(MathJax.OutputJax[renderer]);
1365
return this.signal.Post(["Renderer Selected",renderer]);
1372
Queue: function () {
1373
return this.queue.Push.apply(this.queue,arguments);
1376
Typeset: function (element,callback) {
1377
if (!MathJax.isReady) return null;
1378
var ec = this.elementCallback(element,callback);
1379
var queue = MathJax.Callback.Queue();
1380
for (var i = 0, m = ec.elements.length; i < m; i++) {
1381
if (ec.elements[i]) {
1383
["PreProcess",this,ec.elements[i]],
1384
["Process",this,ec.elements[i]]
1388
return queue.Push(ec.callback);
1391
PreProcess: function (element,callback) {
1392
var ec = this.elementCallback(element,callback);
1393
var queue = MathJax.Callback.Queue();
1394
for (var i = 0, m = ec.elements.length; i < m; i++) {
1395
if (ec.elements[i]) {
1397
["Post",this.signal,["Begin PreProcess",ec.elements[i]]],
1398
(arguments.callee.disabled? {} : ["Execute",this.preProcessors,ec.elements[i]]),
1399
["Post",this.signal,["End PreProcess",ec.elements[i]]]
1403
return queue.Push(ec.callback);
1406
Process: function (element,callback) {return this.takeAction("Process",element,callback)},
1407
Update: function (element,callback) {return this.takeAction("Update",element,callback)},
1408
Reprocess: function (element,callback) {return this.takeAction("Reprocess",element,callback)},
1409
Rerender: function (element,callback) {return this.takeAction("Rerender",element,callback)},
1411
takeAction: function (action,element,callback) {
1412
var ec = this.elementCallback(element,callback);
1413
var queue = MathJax.Callback.Queue(["Clear",this.signal]);
1414
for (var i = 0, m = ec.elements.length; i < m; i++) {
1415
if (ec.elements[i]) {
1417
scripts: [], // filled in by prepareScripts
1418
start: new Date().getTime(), // timer for processing messages
1419
i: 0, j: 0, // current script, current jax
1420
jax: {}, // scripts grouped by output jax
1421
jaxIDs: [] // id's of jax used
1424
["Post",this.signal,["Begin "+action,ec.elements[i]]],
1425
["Post",this.signal,["Begin Math",ec.elements[i],action]],
1426
["prepareScripts",this,action,ec.elements[i],state],
1427
["Post",this.signal,["Begin Math Input",ec.elements[i],action]],
1428
["processInput",this,state],
1429
["Post",this.signal,["End Math Input",ec.elements[i],action]],
1430
["prepareOutput",this,state,"preProcess"],
1431
["Post",this.signal,["Begin Math Output",ec.elements[i],action]],
1432
["processOutput",this,state],
1433
["Post",this.signal,["End Math Output",ec.elements[i],action]],
1434
["prepareOutput",this,state,"postProcess"],
1435
["Post",this.signal,["End Math",ec.elements[i],action]],
1436
["Post",this.signal,["End "+action,ec.elements[i]]]
1440
return queue.Push(ec.callback);
1444
Process: function (script) {},
1445
Update: function (script) {
1446
var jax = script.MathJax.elementJax;
1447
if (jax && jax.needsUpdate()) {jax.Remove(true); script.MathJax.state = jax.STATE.UPDATE}
1448
else {script.MathJax.state = jax.STATE.PROCESSED}
1450
Reprocess: function (script) {
1451
var jax = script.MathJax.elementJax;
1452
if (jax) {jax.Remove(true); script.MathJax.state = jax.STATE.UPDATE}
1454
Rerender: function (script) {
1455
var jax = script.MathJax.elementJax;
1456
if (jax) {jax.Remove(true); script.MathJax.state = jax.STATE.OUTPUT}
1460
prepareScripts: function (action,element,state) {
1461
if (arguments.callee.disabled) return;
1462
var scripts = this.elementScripts(element);
1463
var STATE = MathJax.ElementJax.STATE;
1464
for (var i = 0, m = scripts.length; i < m; i++) {
1465
var script = scripts[i];
1466
if (script.type && this.inputJax[script.type.replace(/ *;(.|\n)*/,"")]) {
1467
if (script.MathJax) {
1468
if (script.MathJax.elementJax && script.MathJax.elementJax.hover) {
1469
MathJax.Extension.MathEvents.Hover.ClearHover(script.MathJax.elementJax);
1471
if (script.MathJax.state !== STATE.PENDING) {this.scriptAction[action](script)}
1473
if (!script.MathJax) {script.MathJax = {state: STATE.PENDING}}
1474
if (script.MathJax.state !== STATE.PROCESSED) {state.scripts.push(script)}
1479
checkScriptSiblings: function (script) {
1480
if (script.MathJax.checked) return;
1481
var config = this.config, pre = script.previousSibling;
1482
if (pre && pre.nodeName === "#text") {
1483
var preJax,postJax, post = script.nextSibling;
1484
if (post && post.nodeName !== "#text") {post = null}
1485
if (config.preJax) {
1486
if (typeof(config.preJax) === "string") {config.preJax = new RegExp(config.preJax+"$")}
1487
preJax = pre.nodeValue.match(config.preJax);
1489
if (config.postJax && post) {
1490
if (typeof(config.postJax) === "string") {config.postJax = new RegExp("^"+config.postJax)}
1491
postJax = post.nodeValue.match(config.postJax);
1493
if (preJax && (!config.postJax || postJax)) {
1494
pre.nodeValue = pre.nodeValue.replace
1495
(config.preJax,(preJax.length > 1? preJax[1] : ""));
1498
if (postJax && (!config.preJax || preJax)) {
1499
post.nodeValue = post.nodeValue.replace
1500
(config.postJax,(postJax.length > 1? postJax[1] : ""));
1502
if (pre && !pre.nodeValue.match(/\S/)) {pre = pre.previousSibling}
1504
if (config.preRemoveClass && pre && pre.className === config.preRemoveClass)
1505
{script.MathJax.preview = pre}
1506
script.MathJax.checked = 1;
1509
processInput: function (state) {
1510
var jax, STATE = MathJax.ElementJax.STATE;
1511
var script, prev, m = state.scripts.length;
1514
// Loop through the scripts
1516
while (state.i < m) {
1517
script = state.scripts[state.i]; if (!script) {state.i++; continue}
1519
// Remove previous error marker, if any
1521
prev = script.previousSibling;
1522
if (prev && prev.className === "MathJax_Error") {prev.parentNode.removeChild(prev)}
1524
// Check if already processed or needs processing
1526
if (!script.MathJax || script.MathJax.state === STATE.PROCESSED) {state.i++; continue};
1527
if (!script.MathJax.elementJax || script.MathJax.state === STATE.UPDATE) {
1528
this.checkScriptSiblings(script); // remove preJax/postJax etc.
1529
var type = script.type.replace(/ *;(.|\s)*/,""); // the input jax type
1530
jax = this.inputJax[type].Process(script,state); // run the input jax
1531
if (typeof jax === 'function') { // if a callback was returned
1532
if (jax.called) continue; // go back and call Process() again
1533
this.RestartAfter(jax); // wait for the callback
1535
jax.Attach(script,this.inputJax[type].id); // register the jax on the script
1536
this.saveScript(jax,state,script,STATE); // add script to state
1537
} else if (script.MathJax.state === STATE.OUTPUT) {
1538
this.saveScript(script.MathJax.elementJax,state,script,STATE); // add script to state
1541
// Go on to the next script, and check if we need to update the processing message
1543
state.i++; var now = new Date().getTime();
1544
if (now - state.start > this.processUpdateTime && state.i < state.scripts.length)
1545
{state.start = now; this.RestartAfter(MathJax.Callback.Delay(1))}
1547
} catch (err) {return this.processError(err,state,"Input")}
1549
// Put up final message, reset the state and return
1551
if (state.scripts.length && this.config.showProcessingMessages)
1552
{MathJax.Message.Set("Processing math: 100%",0)}
1553
state.start = new Date().getTime(); state.i = state.j = 0;
1556
saveScript: function (jax,state,script,STATE) {
1558
// Check that output jax exists
1560
if (!this.outputJax[jax.mimeType]) {
1561
script.MathJax.state = STATE.UPDATE;
1562
throw Error("No output jax registered for "+jax.mimeType);
1565
// Record the output jax
1566
// and put this script in the queue for that jax
1568
jax.outputJax = this.outputJax[jax.mimeType][0].id;
1569
if (!state.jax[jax.outputJax]) {
1570
if (state.jaxIDs.length === 0) {
1571
// use original array until we know there are more (rather than two copies)
1572
state.jax[jax.outputJax] = state.scripts;
1574
if (state.jaxIDs.length === 1) // get the script so far for the existing jax
1575
{state.jax[state.jaxIDs[0]] = state.scripts.slice(0,state.i)}
1576
state.jax[jax.outputJax] = []; // start a new array for the new jax
1578
state.jaxIDs.push(jax.outputJax); // save the ID of the jax
1580
if (state.jaxIDs.length > 1) {state.jax[jax.outputJax].push(script)}
1582
// Mark script as needing output
1584
script.MathJax.state = STATE.OUTPUT;
1588
// Pre- and post-process scripts by jax
1589
// (to get scaling factors, hide/show output, and so on)
1590
// Since this can cause the jax to load, we need to trap restarts
1592
prepareOutput: function (state,method) {
1593
while (state.j < state.jaxIDs.length) {
1594
var id = state.jaxIDs[state.j], JAX = MathJax.OutputJax[id];
1597
var result = JAX[method](state);
1598
if (typeof result === 'function') {
1599
if (result.called) continue; // go back and try again
1600
this.RestartAfter(result);
1604
MathJax.Message.Set("Error preparing "+id+" output ("+method+")",null,600);
1605
MathJax.Hub.lastPrepError = err;
1608
return MathJax.Callback.After(["prepareOutput",this,state,method],err.restart);
1616
processOutput: function (state) {
1617
var result, STATE = MathJax.ElementJax.STATE, script, m = state.scripts.length;
1620
// Loop through the scripts
1622
while (state.i < m) {
1624
// Check that there is an element jax
1626
script = state.scripts[state.i]; if (!script || !script.MathJax) {state.i++; continue}
1627
var jax = script.MathJax.elementJax; if (!jax) {state.i++; continue}
1629
// Call the output Jax's Process method (which will be its Translate()
1630
// method once loaded). Mark it as complete and remove the preview.
1632
result = MathJax.OutputJax[jax.outputJax].Process(script,state);
1633
script.MathJax.state = STATE.PROCESSED; state.i++;
1634
if (script.MathJax.preview) {script.MathJax.preview.innerHTML = ""}
1636
// Signal that new math is available
1638
this.signal.Post(["New Math",jax.inputID]); // FIXME: wait for this? (i.e., restart if returns uncalled callback)
1640
// Update the processing message, if needed
1642
var now = new Date().getTime();
1643
if (now - state.start > this.processUpdateTime && state.i < state.scripts.length)
1644
{state.start = now; this.RestartAfter(MathJax.Callback.Delay(this.processUpdateDelay))}
1646
} catch (err) {return this.processError(err,state,"Output")}
1648
// Put up the typesetting-complete message
1650
if (state.scripts.length && this.config.showProcessingMessages) {
1651
MathJax.Message.Set("Typesetting math: 100%",0);
1652
MathJax.Message.Clear(0);
1654
state.i = state.j = 0;
1658
processMessage: function (state,type) {
1659
var m = Math.floor(state.i/(state.scripts.length)*100);
1660
var message = (type === "Output" ? "Typesetting" : "Processing");
1661
if (this.config.showProcessingMessages) {MathJax.Message.Set(message+" math: "+m+"%",0)}
1664
processError: function (err,state,type) {
1666
if (!this.config.errorSettings.message) {throw err}
1667
this.formatError(state.scripts[state.i],err); state.i++;
1669
this.processMessage(state,type);
1670
return MathJax.Callback.After(["process"+type,this,state],err.restart);
1673
formatError: function (script,err) {
1674
var error = MathJax.HTML.Element("span",{className:"MathJax_Error"},this.config.errorSettings.message);
1675
error.jaxID = "Error";
1676
if (MathJax.Extension.MathEvents) {
1677
error.oncontextmenu = MathJax.Extension.MathEvents.Event.Menu;
1678
error.onmousedown = MathJax.Extension.MathEvents.Event.Mousedown;
1680
MathJax.Ajax.Require("[MathJax]/extensions/MathEvents.js",function () {
1681
error.oncontextmenu = MathJax.Extension.MathEvents.Event.Menu;
1682
error.onmousedown = MathJax.Extension.MathEvents.Event.Mousedown;
1685
script.parentNode.insertBefore(error,script);
1686
if (script.MathJax.preview) {script.MathJax.preview.innerHTML = ""}
1687
this.lastError = err;
1688
this.signal.Post(["Math Processing Error",script,err]);
1691
RestartAfter: function (callback) {
1692
throw this.Insert(Error("restart"),{restart: MathJax.Callback(callback)});
1695
elementCallback: function (element,callback) {
1696
if (callback == null && (element instanceof Array || typeof element === 'function'))
1697
{try {MathJax.Callback(element); callback = element; element = null} catch(e) {}}
1698
if (element == null) {element = this.config.elements || []}
1699
if (!(element instanceof Array)) {element = [element]}
1700
element = [].concat(element); // make a copy so the original isn't changed
1701
for (var i = 0, m = element.length; i < m; i++)
1702
{if (typeof(element[i]) === 'string') {element[i] = document.getElementById(element[i])}}
1703
if (element.length == 0) {element.push(document.body)}
1704
if (!callback) {callback = {}}
1705
return {elements: element, callback: callback};
1708
elementScripts: function (element) {
1709
if (typeof(element) === 'string') {element = document.getElementById(element)}
1710
if (element == null) {element = document.body}
1711
if (element.tagName != null && element.tagName.toLowerCase() === "script") {return [element]}
1712
return element.getElementsByTagName("script");
1715
Insert: function (dst,src) {
1716
for (var id in src) {if (src.hasOwnProperty(id)) {
1717
// allow for concatenation of arrays?
1718
if (typeof src[id] === 'object' && !(src[id] instanceof Array) &&
1719
(typeof dst[id] === 'object' || typeof dst[id] === 'function')) {
1720
this.Insert(dst[id],src[id]);
1728
MathJax.Hub.Insert(MathJax.Hub.config.styles,MathJax.Message.styles);
1729
MathJax.Hub.Insert(MathJax.Hub.config.styles,{".MathJax_Error":MathJax.Hub.config.errorSettings.style});
1732
// Storage area for extensions and preprocessors
1734
MathJax.Extension = {};
1739
MathJax.Hub.Configured = MathJax.Callback({}); // called when configuration is complete
1740
MathJax.Hub.Startup = {
1741
script: "", // the startup script from the SCRIPT call that loads MathJax.js
1742
queue: MathJax.Callback.Queue(), // Queue used for startup actions
1743
signal: MathJax.Callback.Signal("Startup"), // Signal used for startup events
1747
// Load the configuration files
1749
Config: function () {
1750
this.queue.Push(["Post",this.signal,"Begin Config"]);
1752
// Check for user cookie configuration
1754
var user = MathJax.HTML.Cookie.Get("user");
1755
if (user.URL || user.Config) {
1757
"MathJax has found a user-configuration cookie that includes code to be run. " +
1758
"Do you want to run it?\n\n"+
1759
"(You should press Cancel unless you set up the cookie yourself.)"
1761
if (user.URL) {this.queue.Push(["Require",MathJax.Ajax,user.URL])}
1762
if (user.Config) {this.queue.Push(new Function(user.Config))}
1763
} else {MathJax.HTML.Cookie.Set("user",{})}
1766
// Run the config files, if any are given in the parameter list
1768
if (this.params.config) {
1769
var files = this.params.config.split(/,/);
1770
for (var i = 0, m = files.length; i < m; i++) {
1771
if (!files[i].match(/\.js$/)) {files[i] += ".js"}
1772
this.queue.Push(["Require",MathJax.Ajax,this.URL("config",files[i])]);
1776
// Run the deprecated configuration script, if any (ignoring return value)
1777
// Wait for the startup delay signal
1778
// Run the mathjax-config blocks
1779
// Handle the default configuration (v1.0 compatible)
1780
// Load the files in the configuration's config array
1782
if (this.script.match(/\S/)) {this.queue.Push(this.script+";\n1;")}
1784
["ConfigDelay",this],
1785
["ConfigBlocks",this],
1786
["ConfigDefault",this],
1787
[function (THIS) {return THIS.loadArray(MathJax.Hub.config.config,"config",null,true)},this],
1788
["Post",this.signal,"End Config"]
1792
// Return the delay callback
1794
ConfigDelay: function () {
1795
var delay = this.params.delayStartupUntil || MathJax.Hub.config.delayStartupUntil;
1796
if (delay === "onload") {return this.onload}
1797
if (delay === "configured") {return MathJax.Hub.Configured}
1801
// Run the scipts of type=text/x-mathajx-config
1803
ConfigBlocks: function () {
1804
var scripts = document.getElementsByTagName("script");
1805
var last = null, queue = MathJax.Callback.Queue();
1806
for (var i = 0, m = scripts.length; i < m; i++) {
1807
var type = String(scripts[i].type).replace(/ /g,"");
1808
if (type.match(/^text\/x-mathjax-config(;.*)?$/) && !type.match(/;executed=true/)) {
1809
scripts[i].type += ";executed=true";
1810
last = queue.Push(scripts[i].innerHTML+";\n1;");
1816
// Check for v1.0 no-configuration and put up a warning message.
1818
ConfigDefault: function () {
1819
var CONFIG = MathJax.Hub.config;
1820
if (CONFIG["v1.0-compatible"] && (CONFIG.jax||[]).length === 0 &&
1821
!this.params.config && (CONFIG.config||[]).length === 0)
1822
{return MathJax.Ajax.Require(this.URL("extensions","v1.0-warning.js"))}
1826
// Read cookie and set up menu defaults
1827
// (adjust the jax to accommodate renderer preferences)
1829
Cookie: function () {
1830
return this.queue.Push(
1831
["Post",this.signal,"Begin Cookie"],
1832
["Get",MathJax.HTML.Cookie,"menu",MathJax.Hub.config.menuSettings],
1833
[function (config) {
1834
var renderer = config.menuSettings.renderer, jax = config.jax;
1836
var name = "output/"+renderer; jax.sort();
1837
for (var i = 0, m = jax.length; i < m; i++) {
1838
if (jax[i].substr(0,7) === "output/") break;
1840
if (i == m-1) {jax.pop()} else {
1841
while (i < m) {if (jax[i] === name) {jax.splice(i,1); break}; i++}
1845
},MathJax.Hub.config],
1846
["Post",this.signal,"End Cookie"]
1850
// Setup stylesheets and extra styles
1852
Styles: function () {
1853
return this.queue.Push(
1854
["Post",this.signal,"Begin Styles"],
1855
["loadArray",this,MathJax.Hub.config.styleSheets,"config"],
1856
["Styles",MathJax.Ajax,MathJax.Hub.config.styles],
1857
["Post",this.signal,"End Styles"]
1861
// Load the input and output jax
1864
var config = MathJax.Hub.config, jax = MathJax.Hub.outputJax;
1865
// Save the order of the output jax since they are loading asynchronously
1866
for (var i = 0, m = config.jax.length, k = 0; i < m; i++) {
1867
var name = config.jax[i].substr(7);
1868
if (config.jax[i].substr(0,7) === "output/" && jax.order[name] == null)
1869
{jax.order[name] = k; k++}
1871
var queue = MathJax.Callback.Queue();
1873
["Post",this.signal,"Begin Jax"],
1874
["loadArray",this,config.jax,"jax","config.js"],
1875
["Post",this.signal,"End Jax"]
1879
// Load the extensions
1881
Extensions: function () {
1882
var queue = MathJax.Callback.Queue();
1884
["Post",this.signal,"Begin Extensions"],
1885
["loadArray",this,MathJax.Hub.config.extensions,"extensions"],
1886
["Post",this.signal,"End Extensions"]
1891
// Initialize the Message system
1893
Message: function () {
1894
MathJax.Message.Init(true);
1898
// Set the math menu renderer, if it isn't already
1899
// (this must come after the jax are loaded)
1902
var menu = MathJax.Hub.config.menuSettings, jax = MathJax.Hub.outputJax, registered;
1903
for (var id in jax) {if (jax.hasOwnProperty(id)) {
1904
if (jax[id].length) {registered = jax[id]; break}
1906
if (registered && registered.length) {
1907
if (menu.renderer && menu.renderer !== registered[0].id)
1908
{registered.unshift(MathJax.OutputJax[menu.renderer])}
1909
menu.renderer = registered[0].id;
1914
// Set the location to the designated hash position
1917
if (MathJax.Hub.config.positionToHash && document.location.hash &&
1918
document.body && document.body.scrollIntoView) {
1919
var name = document.location.hash.substr(1);
1920
var target = document.getElementById(name);
1922
var a = document.getElementsByTagName("a");
1923
for (var i = 0, m = a.length; i < m; i++)
1924
{if (a[i].name === name) {target = a[i]; break}}
1927
while (!target.scrollIntoView) {target = target.parentNode}
1928
target = this.HashCheck(target);
1929
if (target && target.scrollIntoView)
1930
{setTimeout(function () {target.scrollIntoView(true)},1)}
1934
HashCheck: function (target) {
1935
if (target.isMathJax) {
1936
var jax = MathJax.Hub.getJaxFor(target);
1937
if (jax && MathJax.OutputJax[jax.outputJax].hashCheck)
1938
{target = MathJax.OutputJax[jax.outputJax].hashCheck(target)}
1944
// Load the Menu and Zoom code, if it hasn't already been loaded.
1945
// This is called after the initial typeset, so should no longer be
1946
// competing with other page loads, but will make these available
1947
// if needed later on.
1949
MenuZoom: function () {
1950
if (!MathJax.Extension.MathMenu) {
1952
MathJax.Callback(["Require",MathJax.Ajax,"[MathJax]/extensions/MathMenu.js",{}]),
1956
if (!MathJax.Extension.MathZoom) {
1958
MathJax.Callback(["Require",MathJax.Ajax,"[MathJax]/extensions/MathZoom.js",{}]),
1965
// Setup the onload callback
1967
onLoad: function () {
1968
var onload = this.onload =
1969
MathJax.Callback(function () {MathJax.Hub.Startup.signal.Post("onLoad")});
1970
if (document.body && document.readyState)
1971
if (MathJax.Hub.Browser.isMSIE) {
1972
// IE can change from loading to interactive before
1973
// full page is ready, so go with complete (even though
1974
// that means we may have to wait longer).
1975
if (document.readyState === "complete") {return [onload]}
1976
} else if (document.readyState !== "loading") {return [onload]}
1977
if (window.addEventListener) {
1978
window.addEventListener("load",onload,false);
1979
if (!this.params.noDOMContentEvent)
1980
{window.addEventListener("DOMContentLoaded",onload,false)}
1982
else if (window.attachEvent) {window.attachEvent("onload",onload)}
1983
else {window.onload = onload}
1988
// Perform the initial typesetting (or skip if configuration says to)
1990
Typeset: function (element,callback) {
1991
if (MathJax.Hub.config.skipStartupTypeset) {return function () {}}
1992
return this.queue.Push(
1993
["Post",this.signal,"Begin Typeset"],
1994
["Typeset",MathJax.Hub,element,callback],
1995
["Post",this.signal,"End Typeset"]
2000
// Create a URL in the MathJax hierarchy
2002
URL: function (dir,name) {
2003
if (!name.match(/^([a-z]+:\/\/|\[|\/)/)) {name = "[MathJax]/"+dir+"/"+name}
2008
// Load an array of files, waiting for all of them
2009
// to be loaded before going on
2011
loadArray: function (files,dir,name,synchronous) {
2013
if (!(files instanceof Array)) {files = [files]}
2015
var queue = MathJax.Callback.Queue(), callback = {}, file;
2016
for (var i = 0, m = files.length; i < m; i++) {
2017
file = this.URL(dir,files[i]);
2018
if (name) {file += "/" + name}
2019
if (synchronous) {queue.Push(["Require",MathJax.Ajax,file,callback])}
2020
else {queue.Push(MathJax.Ajax.Require(file,callback))}
2022
return queue.Push({}); // wait for everything to finish
2031
/**********************************************************/
2033
(function (BASENAME) {
2034
var BASE = window[BASENAME], ROOT = "["+BASENAME+"]";
2035
var HUB = BASE.Hub, AJAX = BASE.Ajax, CALLBACK = BASE.Callback;
2037
var JAX = MathJax.Object.Subclass({
2039
require: null, // array of files to load before jax.js is complete
2042
// Make a subclass and return an instance of it.
2043
// (FIXME: should we replace config with a copy of the constructor's
2044
// config? Otherwise all subclasses share the same config structure.)
2046
Init: function (def,cdef) {
2047
if (arguments.length === 0) {return this}
2048
return (this.constructor.Subclass(def,cdef))();
2051
// Augment by merging with class definition (not replacing)
2053
Augment: function (def,cdef) {
2054
var cObject = this.constructor, ndef = {};
2056
for (var id in def) {if (def.hasOwnProperty(id)) {
2057
if (typeof def[id] === "function")
2058
{cObject.protoFunction(id,def[id])} else {ndef[id] = def[id]}
2060
// MSIE doesn't list toString even if it is not native so handle it separately
2061
if (def.toString !== cObject.prototype.toString && def.toString !== {}.toString)
2062
{cObject.protoFunction('toString',def.toString)}
2064
HUB.Insert(cObject.prototype,ndef);
2065
cObject.Augment(null,cdef);
2068
Translate: function (script,state) {
2069
throw Error(this.directory+"/"+this.JAXFILE+" failed to define the Translate() method");
2071
Register: function (mimetype) {},
2072
Config: function () {
2073
this.config = HUB.CombineConfig(this.id,this.config);
2074
if (this.config.Augment) {this.Augment(this.config.Augment)}
2076
Startup: function () {},
2077
loadComplete: function (file) {
2078
if (file === "config.js") {
2079
return AJAX.loadComplete(this.directory+"/"+file);
2081
var queue = CALLBACK.Queue();
2083
HUB.Register.StartupHook("End Config",{}), // wait until config complete
2084
["Post",HUB.Startup.signal,this.id+" Jax Config"],
2086
["Post",HUB.Startup.signal,this.id+" Jax Require"],
2087
// Config may set the required and extensions array,
2088
// so use functions to delay making the reference until needed
2089
[function (THIS) {return MathJax.Hub.Startup.loadArray(THIS.require,this.directory)},this],
2090
[function (config,id) {return MathJax.Hub.Startup.loadArray(config.extensions,"extensions/"+id)},this.config||{},this.id],
2091
["Post",HUB.Startup.signal,this.id+" Jax Startup"],
2093
["Post",HUB.Startup.signal,this.id+" Jax Ready"]
2095
if (this.copyTranslate) {
2098
THIS.preProcess = THIS.preTranslate;
2099
THIS.Process = THIS.Translate;
2100
THIS.postProcess = THIS.postTranslate;
2101
},this.constructor.prototype]
2104
return queue.Push(["loadComplete",AJAX,this.directory+"/"+file]);
2110
directory: ROOT+"/jax",
2111
extensionDir: ROOT+"/extensions"
2114
/***********************************/
2116
BASE.InputJax = JAX.Subclass({
2117
elementJax: "mml", // the element jax to load for this input jax
2118
copyTranslate: true,
2119
Process: function (script,state) {
2120
var queue = CALLBACK.Queue(), file;
2121
// Load any needed element jax
2122
var jax = this.elementJax; if (!(jax instanceof Array)) {jax = [jax]}
2123
for (var i = 0, m = jax.length; i < m; i++) {
2124
file = BASE.ElementJax.directory+"/"+jax[i]+"/"+this.JAXFILE;
2125
if (!this.require) {this.require = []}
2126
else if (!(this.require instanceof Array)) {this.require = [this.require]};
2127
this.require.push(file); // so Startup will wait for it to be loaded
2128
queue.Push(AJAX.Require(file));
2130
// Load the input jax
2131
file = this.directory+"/"+this.JAXFILE;
2132
var load = queue.Push(AJAX.Require(file));
2134
this.constructor.prototype.Process = function () {
2135
if (!load.called) {return load}
2136
throw Error(file+" failed to load properly");
2139
// Load the associated output jax
2140
jax = HUB.outputJax["jax/"+jax[0]];
2141
if (jax) {queue.Push(AJAX.Require(jax[0].directory+"/"+this.JAXFILE))}
2142
return queue.Push({});
2144
needsUpdate: function (jax) {
2145
var script = jax.SourceElement();
2146
return (jax.originalText !== BASE.HTML.getScript(script));
2148
Register: function (mimetype) {
2149
if (!HUB.inputJax) {HUB.inputJax = {}}
2150
HUB.inputJax[mimetype] = this;
2155
directory: JAX.directory+"/input",
2156
extensionDir: JAX.extensionDir
2159
/***********************************/
2161
BASE.OutputJax = JAX.Subclass({
2162
copyTranslate: true,
2163
preProcess: function (state) {
2164
var load, file = this.directory+"/"+this.JAXFILE;
2165
this.constructor.prototype.preProcess = function (state) {
2166
if (!load.called) {return load}
2167
throw Error(file+" failed to load properly");
2169
load = AJAX.Require(file);
2172
Register: function (mimetype) {
2173
var jax = HUB.outputJax;
2174
if (!jax[mimetype]) {jax[mimetype] = []}
2175
// If the output jax is earlier in the original configuration list, put it first here
2176
if (jax[mimetype].length && (this.id === HUB.config.menuSettings.renderer ||
2177
(jax.order[this.id]||0) < (jax.order[jax[mimetype][0].id]||0)))
2178
{jax[mimetype].unshift(this)} else {jax[mimetype].push(this)}
2179
// Make sure the element jax is loaded before Startup is called
2180
if (!this.require) {this.require = []}
2181
else if (!(this.require instanceof Array)) {this.require = [this.require]};
2182
this.require.push(BASE.ElementJax.directory+"/"+(mimetype.split(/\//)[1])+"/"+this.JAXFILE);
2184
Remove: function (jax) {}
2188
directory: JAX.directory+"/output",
2189
extensionDir: JAX.extensionDir,
2190
fontDir: ROOT+(BASE.isPacked?"":"/..")+"/fonts",
2191
imageDir: ROOT+(BASE.isPacked?"":"/..")+"/images"
2194
/***********************************/
2196
BASE.ElementJax = JAX.Subclass({
2197
// make a subclass, not an instance
2198
Init: function (def,cdef) {return this.constructor.Subclass(def,cdef)},
2206
Text: function (text,callback) {
2207
var script = this.SourceElement();
2208
BASE.HTML.setScript(script,text);
2209
script.MathJax.state = this.STATE.UPDATE;
2210
return HUB.Update(script,callback);
2212
Reprocess: function (callback) {
2213
var script = this.SourceElement();
2214
script.MathJax.state = this.STATE.UPDATE;
2215
return HUB.Reprocess(script,callback);
2217
Update: function (callback) {return this.Rerender(callback)},
2218
Rerender: function (callback) {
2219
var script = this.SourceElement();
2220
script.MathJax.state = this.STATE.OUTPUT;
2221
return HUB.Process(script,callback);
2223
Remove: function (keep) {
2224
if (this.hover) {this.hover.clear(this)}
2225
BASE.OutputJax[this.outputJax].Remove(this);
2227
HUB.signal.Post(["Remove Math",this.inputID]); // wait for this to finish?
2231
needsUpdate: function () {
2232
return BASE.InputJax[this.inputJax].needsUpdate(this);
2235
SourceElement: function () {return document.getElementById(this.inputID)},
2237
Attach: function (script,inputJax) {
2238
var jax = script.MathJax.elementJax;
2239
if (script.MathJax.state === this.STATE.UPDATE) {
2242
jax = script.MathJax.elementJax = this;
2243
if (script.id) {this.inputID = script.id}
2244
else {script.id = this.inputID = BASE.ElementJax.GetID(); this.newID = 1}
2246
jax.originalText = BASE.HTML.getScript(script);
2247
jax.inputJax = inputJax;
2248
if (jax.root) {jax.root.inputID = jax.inputID}
2251
Detach: function () {
2252
var script = this.SourceElement(); if (!script) return;
2253
try {delete script.MathJax} catch(err) {script.MathJax = null}
2254
if (this.newID) {script.id = ""}
2256
Clone: function (jax) {
2259
if (!this.hasOwnProperty(id)) continue;
2260
if (typeof(jax[id]) === 'undefined' && id !== 'newID') {delete this[id]}
2263
if (!jax.hasOwnProperty(id)) continue;
2264
if (typeof(this[id]) === 'undefined' || (this[id] !== jax[id] && id !== 'inputID'))
2265
{this[id] = jax[id]}
2271
directory: JAX.directory+"/element",
2272
extensionDir: JAX.extensionDir,
2273
ID: 0, // jax counter (for IDs)
2275
PENDING: 1, // script is identified as math but not yet processed
2276
PROCESSED: 2, // script has been processed
2277
UPDATE: 3, // elementJax should be updated
2278
OUTPUT: 4 // output should be updated (input is OK)
2281
GetID: function () {this.ID++; return "MathJax-Element-"+this.ID},
2282
Subclass: function () {
2283
var obj = JAX.Subclass.apply(this,arguments);
2284
obj.loadComplete = this.prototype.loadComplete;
2288
BASE.ElementJax.prototype.STATE = BASE.ElementJax.STATE;
2291
// Some "Fake" jax used to allow menu access for "Math Processing Error" messages
2293
BASE.OutputJax.Error = {
2294
id: "Error", version: "2.1", config: {},
2295
ContextMenu: function () {return BASE.Extension.MathEvents.Event.ContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)},
2296
Mousedown: function () {return BASE.Extension.MathEvents.Event.AltContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)},
2297
getJaxFromMath: function () {return {inputJax:"Error", outputJax:"Error", originalText:"Math Processing Error"}}
2299
BASE.InputJax.Error = {
2300
id: "Error", version: "2.1", config: {},
2301
sourceMenuTitle: "Error Message"
2306
/**********************************************************/
2308
(function (BASENAME) {
2309
var BASE = window[BASENAME];
2310
if (!BASE) {BASE = window[BASENAME] = {}}
2312
var HUB = BASE.Hub; var STARTUP = HUB.Startup; var CONFIG = HUB.config;
2313
var HEAD = document.getElementsByTagName("head")[0];
2314
if (!HEAD) {HEAD = document.childNodes[0]};
2315
var scripts = (document.documentElement || document).getElementsByTagName("script");
2316
var namePattern = new RegExp("(^|/)"+BASENAME+"\\.js(\\?.*)?$");
2317
for (var i = scripts.length-1; i >= 0; i--) {
2318
if ((scripts[i].src||"").match(namePattern)) {
2319
STARTUP.script = scripts[i].innerHTML;
2321
var params = RegExp.$2.substr(1).split(/\&/);
2322
for (var j = 0, m = params.length; j < m; j++) {
2323
var KV = params[j].match(/(.*)=(.*)/);
2324
if (KV) {STARTUP.params[unescape(KV[1])] = unescape(KV[2])}
2327
CONFIG.root = scripts[i].src.replace(/(^|\/)[^\/]*(\?.*)?$/,'');
2331
BASE.Ajax.config = CONFIG;
2334
isMac: (navigator.platform.substr(0,3) === "Mac"),
2335
isPC: (navigator.platform.substr(0,3) === "Win"),
2336
isMSIE: (window.ActiveXObject != null && window.clipboardData != null),
2337
isFirefox: ((window.netscape != null || window.mozPaintCount != null) &&
2338
document.ATTRIBUTE_NODE != null && !window.opera),
2339
isSafari: (navigator.userAgent.match(/ (Apple)?WebKit\//) != null &&
2340
(!window.chrome || window.chrome.loadTimes == null)),
2341
isChrome: (window.chrome != null && window.chrome.loadTimes != null),
2342
isOpera: (window.opera != null && window.opera.version != null),
2343
isKonqueror: (window.hasOwnProperty && window.hasOwnProperty("konqueror") && navigator.vendor == "KDE"),
2344
versionAtLeast: function (v) {
2345
var bv = (this.version).split('.'); v = (new String(v)).split('.');
2346
for (var i = 0, m = v.length; i < m; i++)
2347
{if (bv[i] != v[i]) {return parseInt(bv[i]||"0") >= parseInt(v[i])}}
2350
Select: function (choices) {
2351
var browser = choices[HUB.Browser];
2352
if (browser) {return browser(HUB.Browser)}
2357
var AGENT = navigator.userAgent
2358
.replace(/^Mozilla\/(\d+\.)+\d+ /,"") // remove initial Mozilla, which is never right
2359
.replace(/[a-z][-a-z0-9._: ]+\/\d+[^ ]*-[^ ]*\.([a-z][a-z])?\d+ /i,"") // remove linux version
2360
.replace(/Gentoo |Ubuntu\/(\d+\.)*\d+ (\([^)]*\) )?/,""); // special case for these
2362
HUB.Browser = HUB.Insert(HUB.Insert(new String("Unknown"),{version: "0.0"}),BROWSERS);
2363
for (var browser in BROWSERS) {if (BROWSERS.hasOwnProperty(browser)) {
2364
if (BROWSERS[browser] && browser.substr(0,2) === "is") {
2365
browser = browser.slice(2);
2366
if (browser === "Mac" || browser === "PC") continue;
2367
HUB.Browser = HUB.Insert(new String(browser),BROWSERS);
2368
var VERSION = new RegExp(
2369
".*(Version)/((?:\\d+\\.)+\\d+)|" + // for Safari and Opera10
2370
".*("+browser+")"+(browser == "MSIE" ? " " : "/")+"((?:\\d+\\.)*\\d+)|"+ // for one of the main browser
2371
"(?:^|\\(| )([a-z][-a-z0-9._: ]+|(?:Apple)?WebKit)/((?:\\d+\\.)+\\d+)"); // for unrecognized browser
2372
var MATCH = VERSION.exec(AGENT) || ["","","","unknown","0.0"];
2373
HUB.Browser.name = (MATCH[1] == "Version" ? browser : (MATCH[3] || MATCH[5]));
2374
HUB.Browser.version = MATCH[2] || MATCH[4] || MATCH[6];
2380
// Initial browser-specific info (e.g., touch up version or name)
2382
HUB.Browser.Select({
2383
Safari: function (browser) {
2384
var v = parseInt((String(browser.version).split("."))[0]);
2385
if (v > 85) {browser.webkit = browser.version}
2386
if (v >= 534) {browser.version = "5.1"}
2387
else if (v >= 533) {browser.version = "5.0"}
2388
else if (v >= 526) {browser.version = "4.0"}
2389
else if (v >= 525) {browser.version = "3.1"}
2390
else if (v > 500) {browser.version = "3.0"}
2391
else if (v > 400) {browser.version = "2.0"}
2392
else if (v > 85) {browser.version = "1.0"}
2393
browser.isMobile = (navigator.appVersion.match(/Mobile/i) != null);
2394
browser.noContextMenu = browser.isMobile;
2396
Firefox: function (browser) {
2397
if ((browser.version === "0.0" || navigator.userAgent.match(/Firefox/) == null) &&
2398
navigator.product === "Gecko") {
2399
var rv = navigator.userAgent.match(/[\/ ]rv:(\d+\.\d.*?)[\) ]/);
2400
if (rv) {browser.version = rv[1]}
2402
var date = (navigator.buildID||navigator.productSub||"0").substr(0,8);
2403
if (date >= "20111220") {browser.version = "9.0"}
2404
else if (date >= "20111120") {browser.version = "8.0"}
2405
else if (date >= "20110927") {browser.version = "7.0"}
2406
else if (date >= "20110816") {browser.version = "6.0"}
2407
else if (date >= "20110621") {browser.version = "5.0"}
2408
else if (date >= "20110320") {browser.version = "4.0"}
2409
else if (date >= "20100121") {browser.version = "3.6"}
2410
else if (date >= "20090630") {browser.version = "3.5"}
2411
else if (date >= "20080617") {browser.version = "3.0"}
2412
else if (date >= "20061024") {browser.version = "2.0"}
2415
browser.isMobile = (navigator.appVersion.match(/Android/i) != null ||
2416
navigator.userAgent.match(/ Fennec\//) != null);
2418
Opera: function (browser) {browser.version = opera.version()},
2419
MSIE: function (browser) {
2420
browser.isIE9 = !!(document.documentMode && (window.performance || window.msPerformance));
2421
MathJax.HTML.setScriptBug = !browser.isIE9 || document.documentMode < 9;
2422
var MathPlayer = false;
2423
try {new ActiveXObject("MathPlayer.Factory.1"); browser.hasMathPlayer = MathPlayer = true}
2426
if (MathPlayer && !STARTUP.params.NoMathPlayer) {
2427
var mathplayer = document.createElement("object");
2428
mathplayer.id = "mathplayer"; mathplayer.classid = "clsid:32F66A20-7614-11D4-BD11-00104BD3F987";
2429
document.getElementsByTagName("head")[0].appendChild(mathplayer);
2430
document.namespaces.add("m","http://www.w3.org/1998/Math/MathML");
2431
browser.mpNamespace = true;
2432
if (document.readyState && (document.readyState === "loading" ||
2433
document.readyState === "interactive")) {
2434
document.write('<?import namespace="m" implementation="#MathPlayer">');
2435
browser.mpImported = true;
2438
// Adding any namespace avoids a crash in IE9 in IE9-standards mode
2439
// (any reference to document.namespaces before document.readyState is
2440
// "complete" causes an "unspecified error" to be thrown)
2441
document.namespaces.add("mjx_IE_fix","http://www.w3.org/1999/xlink");
2446
HUB.Browser.Select(MathJax.Message.browsers);
2448
HUB.queue = BASE.Callback.Queue();
2450
["Post",STARTUP.signal,"Begin"],
2454
["Message",STARTUP],
2456
// Do Jax and Extensions in parallel, but wait for them all to complete
2457
var queue = BASE.Callback.Queue(
2459
STARTUP.Extensions()
2461
return queue.Push({});
2465
function () {MathJax.isReady = true}, // indicates that MathJax is ready to process math
2466
["Typeset",STARTUP],
2468
["MenuZoom",STARTUP],
2469
["Post",STARTUP.signal,"End"]
2476
/**********************************************************/