mediawiki-extensions-Math/modules/MathJax/unpacked/MathJax.js

3096 lines
115 KiB
JavaScript
Raw Normal View History

/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/*************************************************************
*
* MathJax.js
*
* The main support code for the MathJax Hub, including the
* Ajax, Callback, Messaging, and Object-Oriented Programming
* libraries, as well as the base Jax classes, and startup
* processing code.
*
* ---------------------------------------------------------------------
*
* Copyright (c) 2009-2013 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
if (window.MathJax) {window.MathJax = {AuthorConfig: window.MathJax}} else {window.MathJax = {}}
// MathJax.isPacked = true; // This line is uncommented by the packer.
if (document.getElementById && document.childNodes && document.createElement) {
if (!MathJax.Hub) { // skip if already loaded
MathJax.version = "2.3";
MathJax.fileversion = "2.3";
/**********************************************************/
(function (BASENAME) {
var BASE = window[BASENAME];
if (!BASE) {BASE = window[BASENAME] = {}}
var PROTO = []; // a static object used to indicate when a prototype is being created
var OBJECT = function (def) {
var obj = def.constructor; if (!obj) {obj = new Function("")}
for (var id in def) {if (id !== 'constructor' && def.hasOwnProperty(id)) {obj[id] = def[id]}}
return obj;
};
var CONSTRUCTOR = function () {
return new Function ("return arguments.callee.Init.call(this,arguments)");
};
//
// Test for Safari 2.x bug (can't replace prototype for result of new Function()).
// (We don't use this version for everyone since it is a closure and we don't need that).
//
var BUGTEST = CONSTRUCTOR(); BUGTEST.prototype = {bug_test: 1};
if (!BUGTEST.prototype.bug_test) {
CONSTRUCTOR = function () {
return function () {return arguments.callee.Init.call(this,arguments)};
};
};
BASE.Object = OBJECT({
constructor: CONSTRUCTOR(),
Subclass: function (def,classdef) {
var obj = CONSTRUCTOR();
obj.SUPER = this; obj.Init = this.Init;
obj.Subclass = this.Subclass; obj.Augment = this.Augment;
obj.protoFunction = this.protoFunction;
obj.can = this.can; obj.has = this.has; obj.isa = this.isa;
obj.prototype = new this(PROTO);
obj.prototype.constructor = obj; // the real constructor
obj.Augment(def,classdef);
return obj;
},
Init: function (args) {
var obj = this;
if (args.length === 1 && args[0] === PROTO) {return obj}
if (!(obj instanceof args.callee)) {obj = new args.callee(PROTO)}
return obj.Init.apply(obj,args) || obj;
},
Augment: function (def,classdef) {
var id;
if (def != null) {
for (id in def) {if (def.hasOwnProperty(id)) {this.protoFunction(id,def[id])}}
// MSIE doesn't list toString even if it is not native so handle it separately
if (def.toString !== this.prototype.toString && def.toString !== {}.toString)
{this.protoFunction('toString',def.toString)}
}
if (classdef != null) {
for (id in classdef) {if (classdef.hasOwnProperty(id)) {this[id] = classdef[id]}}
}
return this;
},
protoFunction: function (id,def) {
this.prototype[id] = def;
if (typeof def === "function") {def.SUPER = this.SUPER.prototype}
},
prototype: {
Init: function () {},
SUPER: function (fn) {return fn.callee.SUPER},
can: function (method) {return typeof(this[method]) === "function"},
has: function (property) {return typeof(this[property]) !== "undefined"},
isa: function (obj) {return (obj instanceof Object) && (this instanceof obj)}
},
can: function (method) {return this.prototype.can.call(this,method)},
has: function (property) {return this.prototype.has.call(this,property)},
isa: function (obj) {
var constructor = this;
while (constructor) {
if (constructor === obj) {return true} else {constructor = constructor.SUPER}
}
return false;
},
SimpleSUPER: OBJECT({
constructor: function (def) {return this.SimpleSUPER.define(def)},
define: function (src) {
var dst = {};
if (src != null) {
for (var id in src) {if (src.hasOwnProperty(id)) {dst[id] = this.wrap(id,src[id])}}
// MSIE doesn't list toString even if it is not native so handle it separately
if (src.toString !== this.prototype.toString && src.toString !== {}.toString)
{dst.toString = this.wrap('toString',src.toString)}
}
return dst;
},
wrap: function (id,f) {
if (typeof(f) === 'function' && f.toString().match(/\.\s*SUPER\s*\(/)) {
var fn = new Function(this.wrapper);
fn.label = id; fn.original = f; f = fn;
fn.toString = this.stringify;
}
return f;
},
wrapper: function () {
var fn = arguments.callee;
this.SUPER = fn.SUPER[fn.label];
try {var result = fn.original.apply(this,arguments)}
catch (err) {delete this.SUPER; throw err}
delete this.SUPER;
return result;
}.toString().replace(/^\s*function\s*\(\)\s*\{\s*/i,"").replace(/\s*\}\s*$/i,""),
toString: function () {
return this.original.toString.apply(this.original,arguments);
}
})
});
})("MathJax");
/**********************************************************/
/*
* Create a callback function from various forms of data:
*
* MathJax.Callback(fn) -- callback to a function
*
* MathJax.Callback([fn]) -- callback to function
* MathJax.Callback([fn,data...])
* -- callback to function with given data as arguments
* MathJax.Callback([object,fn])
* -- call fn with object as "this"
* MathJax.Callback([object,fn,data...])
* -- call fn with object as "this" and data as arguments
* MathJax.Callback(["method",object])
* -- call method of object wth object as "this"
* MathJax.Callback(["method",object,data...])
* -- as above, but with data as arguments to method
*
* MathJax.Callback({hook: fn, data: [...], object: this})
* -- give function, data, and object to act as "this" explicitly
*
* MathJax.Callback("code") -- callback that compiles and executes a string
*
* MathJax.Callback([...],i)
* -- use slice of array starting at i and interpret
* result as above. (Used for passing "arguments" array
* and trimming initial arguments, if any.)
*/
/*
* MathJax.Callback.After([...],cb1,cb2,...)
* -- make a callback that isn't called until all the other
* ones are called first. I.e., wait for a union of
* callbacks to occur before making the given callback.
*/
/*
* MathJax.Callback.Queue([callback,...])
* -- make a synchronized queue of commands that process
* sequentially, waiting for those that return uncalled
* callbacks.
*/
/*
* MathJax.Callback.Signal(name)
* -- finds or creates a names signal, to which listeners
* can be attached and are signaled by messages posted
* to the signal. Responses can be asynchronous.
*/
(function (BASENAME) {
var BASE = window[BASENAME];
if (!BASE) {BASE = window[BASENAME] = {}}
//
// Create a callback from an associative array
//
var CALLBACK = function (data) {
var cb = new Function("return arguments.callee.execute.apply(arguments.callee,arguments)");
for (var id in CALLBACK.prototype) {
if (CALLBACK.prototype.hasOwnProperty(id)) {
if (typeof(data[id]) !== 'undefined') {cb[id] = data[id]}
else {cb[id] = CALLBACK.prototype[id]}
}
}
cb.toString = CALLBACK.prototype.toString;
return cb;
};
CALLBACK.prototype = {
isCallback: true,
hook: function () {},
data: [],
object: window,
execute: function () {
if (!this.called || this.autoReset) {
this.called = !this.autoReset;
return this.hook.apply(this.object,this.data.concat([].slice.call(arguments,0)));
}
},
reset: function () {delete this.called},
toString: function () {return this.hook.toString.apply(this.hook,arguments)}
};
var ISCALLBACK = function (f) {
return (typeof(f) === "function" && f.isCallback);
}
//
// Evaluate a string in global context
//
var EVAL = function (code) {return eval.call(window,code)}
EVAL("var __TeSt_VaR__ = 1"); // check if it works in global context
if (window.__TeSt_VaR__) {
try { delete window.__TeSt_VaR__; } // NOTE IE9 throws when in IE7 mode
catch (error) { window.__TeSt_VaR__ = null; }
} else {
if (window.execScript) {
// IE
EVAL = function (code) {
BASE.__code = code;
code = "try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}";
window.execScript(code);
var result = BASE.__result; delete BASE.__result; delete BASE.__code;
if (result instanceof Error) {throw result}
return result;
}
} else {
// Safari2
EVAL = function (code) {
BASE.__code = code;
code = "try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}";
var head = (document.getElementsByTagName("head"))[0]; if (!head) {head = document.body}
var script = document.createElement("script");
script.appendChild(document.createTextNode(code));
head.appendChild(script); head.removeChild(script);
var result = BASE.__result; delete BASE.__result; delete BASE.__code;
if (result instanceof Error) {throw result}
return result;
}
}
}
//
// Create a callback from various types of data
//
var USING = function (args,i) {
if (arguments.length > 1) {
if (arguments.length === 2 && !(typeof arguments[0] === 'function') &&
arguments[0] instanceof Object && typeof arguments[1] === 'number')
{args = [].slice.call(args,i)}
else {args = [].slice.call(arguments,0)}
}
if (args instanceof Array && args.length === 1) {args = args[0]}
if (typeof args === 'function') {
if (args.execute === CALLBACK.prototype.execute) {return args}
return CALLBACK({hook: args});
} else if (args instanceof Array) {
if (typeof(args[0]) === 'string' && args[1] instanceof Object &&
typeof args[1][args[0]] === 'function') {
return CALLBACK({hook: args[1][args[0]], object: args[1], data: args.slice(2)});
} else if (typeof args[0] === 'function') {
return CALLBACK({hook: args[0], data: args.slice(1)});
} else if (typeof args[1] === 'function') {
return CALLBACK({hook: args[1], object: args[0], data: args.slice(2)});
}
} else if (typeof(args) === 'string') {
return CALLBACK({hook: EVAL, data: [args]});
} else if (args instanceof Object) {
return CALLBACK(args);
} else if (typeof(args) === 'undefined') {
return CALLBACK({});
}
throw Error("Can't make callback from given data");
};
//
// Wait for a given time to elapse and then perform the callback
//
var DELAY = function (time,callback) {
callback = USING(callback);
callback.timeout = setTimeout(callback,time);
return callback;
};
//
// Callback used by AFTER, QUEUE, and SIGNAL to check if calls have completed
//
var WAITFOR = function (callback,signal) {
callback = USING(callback);
if (!callback.called) {WAITSIGNAL(callback,signal); signal.pending++}
};
var WAITEXECUTE = function () {
var signals = this.signal; delete this.signal;
this.execute = this.oldExecute; delete this.oldExecute;
var result = this.execute.apply(this,arguments);
if (ISCALLBACK(result) && !result.called) {WAITSIGNAL(result,signals)} else {
for (var i = 0, m = signals.length; i < m; i++) {
signals[i].pending--;
if (signals[i].pending <= 0) {signals[i].call()}
}
}
};
var WAITSIGNAL = function (callback,signals) {
if (!(signals instanceof Array)) {signals = [signals]}
if (!callback.signal) {
callback.oldExecute = callback.execute;
callback.execute = WAITEXECUTE;
callback.signal = signals;
} else if (signals.length === 1) {callback.signal.push(signals[0])}
else {callback.signal = callback.signal.concat(signals)}
};
//
// Create a callback that is called when a collection of other callbacks have
// all been executed. If the callback gets called immediately (i.e., the
// others are all already called), check if it returns another callback
// and return that instead.
//
var AFTER = function (callback) {
callback = USING(callback);
callback.pending = 0;
for (var i = 1, m = arguments.length; i < m; i++)
{if (arguments[i]) {WAITFOR(arguments[i],callback)}}
if (callback.pending === 0) {
var result = callback();
if (ISCALLBACK(result)) {callback = result}
}
return callback;
};
//
// An array of prioritized hooks that are executed sequentially
// with a given set of data.
//
var HOOKS = MathJax.Object.Subclass({
//
// Initialize the array and the auto-reset status
//
Init: function (reset) {
this.hooks = [];
this.reset = reset;
},
//
// Add a callback to the list, in priority order (default priority is 10)
//
Add: function (hook,priority) {
if (priority == null) {priority = 10}
if (!ISCALLBACK(hook)) {hook = USING(hook)}
hook.priority = priority;
var i = this.hooks.length;
while (i > 0 && priority < this.hooks[i-1].priority) {i--}
this.hooks.splice(i,0,hook);
return hook;
},
Remove: function (hook) {
for (var i = 0, m = this.hooks.length; i < m; i++) {
if (this.hooks[i] === hook) {this.hooks.splice(i,1); return}
}
},
//
// Execute the list of callbacks, resetting them if requested.
// If any return callbacks, return a callback that will be
// executed when they all have completed.
//
Execute: function () {
var callbacks = [{}];
for (var i = 0, m = this.hooks.length; i < m; i++) {
if (this.reset) {this.hooks[i].reset()}
var result = this.hooks[i].apply(window,arguments);
if (ISCALLBACK(result) && !result.called) {callbacks.push(result)}
}
if (callbacks.length === 1) {return null}
if (callbacks.length === 2) {return callbacks[1]}
return AFTER.apply({},callbacks);
}
});
//
// Run an array of callbacks passing them the given data.
// (Legacy function, since this has been replaced by the HOOKS object).
//
var EXECUTEHOOKS = function (hooks,data,reset) {
if (!hooks) {return null}
if (!(hooks instanceof Array)) {hooks = [hooks]}
if (!(data instanceof Array)) {data = (data == null ? [] : [data])}
var handler = HOOKS(reset);
for (var i = 0, m = hooks.length; i < m; i++) {handler.Add(hooks[i])}
return handler.Execute.apply(handler,data);
};
//
// Command queue that performs commands in order, waiting when
// necessary for commands to complete asynchronousely
//
var QUEUE = BASE.Object.Subclass({
//
// Create the queue and push any commands that are specified
//
Init: function () {
this.pending = 0; this.running = 0;
this.queue = [];
this.Push.apply(this,arguments);
},
//
// Add commands to the queue and run them. Adding a callback object
// (rather than a callback specification) queues a wait for that callback.
// Return the final callback for synchronization purposes.
//
Push: function () {
var callback;
for (var i = 0, m = arguments.length; i < m; i++) {
callback = USING(arguments[i]);
if (callback === arguments[i] && !callback.called)
{callback = USING(["wait",this,callback])}
this.queue.push(callback);
}
if (!this.running && !this.pending) {this.Process()}
return callback;
},
//
// Process the command queue if we aren't waiting on another command
//
Process: function (queue) {
while (!this.running && !this.pending && this.queue.length) {
var callback = this.queue[0];
queue = this.queue.slice(1); this.queue = [];
this.Suspend(); var result = callback(); this.Resume();
if (queue.length) {this.queue = queue.concat(this.queue)}
if (ISCALLBACK(result) && !result.called) {WAITFOR(result,this)}
}
},
//
// Suspend/Resume command processing on this queue
//
Suspend: function () {this.running++},
Resume: function () {if (this.running) {this.running--}},
//
// Used by WAITFOR to restart the queue when an action completes
//
call: function () {this.Process.apply(this,arguments)},
wait: function (callback) {return callback}
});
//
// Create a named signal that listeners can attach to, to be signaled by
// postings made to the signal. Posts are queued if they occur while one
// is already in process.
//
var SIGNAL = QUEUE.Subclass({
Init: function (name) {
QUEUE.prototype.Init.call(this);
this.name = name;
this.posted = []; // the messages posted so far
this.listeners = HOOKS(true); // those with interest in this signal
},
//
// Post a message to the signal listeners, with callback for when complete
//
Post: function (message,callback,forget) {
callback = USING(callback);
if (this.posting || this.pending) {
this.Push(["Post",this,message,callback,forget]);
} else {
this.callback = callback; callback.reset();
if (!forget) {this.posted.push(message)}
this.Suspend(); this.posting = true;
var result = this.listeners.Execute(message);
if (ISCALLBACK(result) && !result.called) {WAITFOR(result,this)}
this.Resume(); delete this.posting;
if (!this.pending) {this.call()}
}
return callback;
},
//
// Clear the post history (so new listeners won't get old messages)
//
Clear: function (callback) {
callback = USING(callback);
if (this.posting || this.pending) {
callback = this.Push(["Clear",this,callback]);
} else {
this.posted = [];
callback();
}
return callback;
},
//
// Call the callback (all replies are in) and process the command queue
//
call: function () {this.callback(this); this.Process()},
//
// A listener calls this to register intrest in the signal (so it will be called
// when posts occur). If ignorePast is true, it will not be sent the post history.
//
Interest: function (callback,ignorePast,priority) {
callback = USING(callback);
this.listeners.Add(callback,priority);
if (!ignorePast) {
for (var i = 0, m = this.posted.length; i < m; i++) {
callback.reset();
var result = callback(this.posted[i]);
if (ISCALLBACK(result) && i === this.posted.length-1) {WAITFOR(result,this)}
}
}
return callback;
},
//
// A listener calls this to remove itself from a signal
//
NoInterest: function (callback) {
this.listeners.Remove(callback);
},
//
// Hook a callback to a particular message on this signal
//
MessageHook: function (msg,callback,priority) {
callback = USING(callback);
if (!this.hooks) {this.hooks = {}; this.Interest(["ExecuteHooks",this])}
if (!this.hooks[msg]) {this.hooks[msg] = HOOKS(true)}
this.hooks[msg].Add(callback,priority);
for (var i = 0, m = this.posted.length; i < m; i++)
{if (this.posted[i] == msg) {callback.reset(); callback(this.posted[i])}}
return callback;
},
//
// Execute the message hooks for the given message
//
ExecuteHooks: function (msg,more) {
var type = ((msg instanceof Array) ? msg[0] : msg);
if (!this.hooks[type]) {return null}
return this.hooks[type].Execute(msg);
}
},{
signals: {}, // the named signals
find: function (name) {
if (!SIGNAL.signals[name]) {SIGNAL.signals[name] = new SIGNAL(name)}
return SIGNAL.signals[name];
}
});
//
// The main entry-points
//
BASE.Callback = BASE.CallBack = USING;
BASE.Callback.Delay = DELAY;
BASE.Callback.After = AFTER;
BASE.Callback.Queue = QUEUE;
BASE.Callback.Signal = SIGNAL.find;
BASE.Callback.Hooks = HOOKS;
BASE.Callback.ExecuteHooks = EXECUTEHOOKS;
})("MathJax");
/**********************************************************/
(function (BASENAME) {
var BASE = window[BASENAME];
if (!BASE) {BASE = window[BASENAME] = {}}
var isSafari2 = (navigator.vendor === "Apple Computer, Inc." &&
typeof navigator.vendorSub === "undefined");
var sheets = 0; // used by Safari2
//
// Update sheets count and look up the head object
//
var HEAD = function (head) {
if (document.styleSheets && document.styleSheets.length > sheets)
{sheets = document.styleSheets.length}
if (!head) {
head = (document.getElementsByTagName("head"))[0];
if (!head) {head = document.body}
}
return head;
};
//
// Remove scripts that are completed so they don't clutter up the HEAD.
// This runs via setTimeout since IE7 can't remove the script while it is running.
//
var SCRIPTS = []; // stores scripts to be removed after a delay
var REMOVESCRIPTS = function () {
for (var i = 0, m = SCRIPTS.length; i < m; i++) {BASE.Ajax.head.removeChild(SCRIPTS[i])}
SCRIPTS = [];
};
BASE.Ajax = {
loaded: {}, // files already loaded
loading: {}, // files currently in process of loading
loadHooks: {}, // hooks to call when files are loaded
timeout: 15*1000, // timeout for loading of files (15 seconds)
styleDelay: 1, // delay to use before styles are available
config: {root: ""}, // URL of root directory to load from
STATUS: {
OK: 1, // file is loading or did load OK
ERROR: -1 // file timed out during load
},
rootPattern: new RegExp("^\\["+BASENAME+"\\]"),
//
// Return a complete URL to a file (replacing the root pattern)
//
fileURL: function (file) {return file.replace(this.rootPattern,this.config.root)},
//
// Load a file if it hasn't been already.
// Make sure the file URL is "safe"?
//
Require: function (file,callback) {
callback = BASE.Callback(callback); var type;
if (file instanceof Object) {
for (var i in file)
{if (file.hasOwnProperty(i)) {type = i.toUpperCase(); file = file[i]}}
} else {type = file.split(/\./).pop().toUpperCase()}
file = this.fileURL(file);
// FIXME: check that URL is OK
if (this.loaded[file]) {
callback(this.loaded[file]);
} else {
var FILE = {}; FILE[type] = file;
this.Load(FILE,callback);
}
return callback;
},
//
// Load a file regardless of where it is and whether it has
// already been loaded.
//
Load: function (file,callback) {
callback = BASE.Callback(callback); var type;
if (file instanceof Object) {
for (var i in file)
{if (file.hasOwnProperty(i)) {type = i.toUpperCase(); file = file[i]}}
} else {type = file.split(/\./).pop().toUpperCase()}
file = this.fileURL(file);
if (this.loading[file]) {
this.addHook(file,callback);
} else {
this.head = HEAD(this.head);
if (this.loader[type]) {this.loader[type].call(this,file,callback)}
else {throw Error("Can't load files of type "+type)}
}
return callback;
},
//
// Register a load hook for a particular file (it will be called when
// loadComplete() is called for that file)
//
LoadHook: function (file,callback,priority) {
callback = BASE.Callback(callback);
if (file instanceof Object)
{for (var i in file) {if (file.hasOwnProperty(i)) {file = file[i]}}}
file = this.fileURL(file);
if (this.loaded[file]) {callback(this.loaded[file])}
else {this.addHook(file,callback,priority)}
return callback;
},
addHook: function (file,callback,priority) {
if (!this.loadHooks[file]) {this.loadHooks[file] = MathJax.Callback.Hooks()}
this.loadHooks[file].Add(callback,priority);
},
//
// Used when files are combined in a preloading configuration file
//
Preloading: function () {
for (var i = 0, m = arguments.length; i < m; i++) {
var file = this.fileURL(arguments[i]);
if (!this.loading[file]) {this.loading[file] = {preloaded: true}}
}
},
//
// Code used to load the various types of files
// (JS for JavaScript, CSS for style sheets)
//
loader: {
//
// Create a SCRIPT tag to load the file
//
JS: function (file,callback) {
var script = document.createElement("script");
var timeout = BASE.Callback(["loadTimeout",this,file]);
this.loading[file] = {
callback: callback,
timeout: setTimeout(timeout,this.timeout),
status: this.STATUS.OK,
script: script
};
// Add this to the structure above after it is created to prevent recursion
// when loading the initial localization file (before loading messsage is available)
this.loading[file].message = BASE.Message.File(file);
script.onerror = timeout; // doesn't work in IE and no apparent substitute
script.type = "text/javascript";
script.src = file;
this.head.appendChild(script);
},
//
// Create a LINK tag to load the style sheet
//
CSS: function (file,callback) {
var link = document.createElement("link");
link.rel = "stylesheet"; link.type = "text/css"; link.href = file;
this.loading[file] = {
callback: callback,
message: BASE.Message.File(file),
status: this.STATUS.OK
};
this.head.appendChild(link);
this.timer.create.call(this,[this.timer.file,file],link);
}
},
//
// Timing code for checking when style sheets are available.
//
timer: {
//
// Create the timing callback and start the timing loop.
// We use a delay because some browsers need it to allow the styles
// to be processed.
//
create: function (callback,node) {
callback = BASE.Callback(callback);
if (node.nodeName === "STYLE" && node.styleSheet &&
typeof(node.styleSheet.cssText) !== 'undefined') {
callback(this.STATUS.OK); // MSIE processes style immediately, but doesn't set its styleSheet!
} else if (window.chrome && node.nodeName === "LINK") {
callback(this.STATUS.OK); // Chrome doesn't give access to cssRules for stylesheet in
// a link node, so we can't detect when it is loaded.
} else if (isSafari2) {
this.timer.start(this,[this.timer.checkSafari2,sheets++,callback],this.styleDelay);
} else {
this.timer.start(this,[this.timer.checkLength,node,callback],this.styleDelay);
}
return callback;
},
//
// Start the timer for the given callback checker
//
start: function (AJAX,check,delay,timeout) {
check = BASE.Callback(check);
check.execute = this.execute; check.time = this.time;
check.STATUS = AJAX.STATUS; check.timeout = timeout || AJAX.timeout;
check.delay = check.total = delay || 0;
if (delay) {setTimeout(check,delay)} else {check()}
},
//
// Increment the time total, increase the delay
// and test if we are past the timeout time.
//
time: function (callback) {
this.total += this.delay;
this.delay = Math.floor(this.delay * 1.05 + 5);
if (this.total >= this.timeout) {callback(this.STATUS.ERROR); return 1}
return 0;
},
//
// For JS file loads, call the proper routine according to status
//
file: function (file,status) {
if (status < 0) {BASE.Ajax.loadTimeout(file)} else {BASE.Ajax.loadComplete(file)}
},
//
// Call the hook with the required data
//
execute: function () {this.hook.call(this.object,this,this.data[0],this.data[1])},
//
// Safari2 doesn't set the link's stylesheet, so we need to look in the
// document.styleSheets array for the new sheet when it is created
//
checkSafari2: function (check,length,callback) {
if (check.time(callback)) return;
if (document.styleSheets.length > length &&
document.styleSheets[length].cssRules &&
document.styleSheets[length].cssRules.length)
{callback(check.STATUS.OK)} else {setTimeout(check,check.delay)}
},
//
// Look for the stylesheets rules and check when they are defined
// and no longer of length zero. (This assumes there actually ARE
// some rules in the stylesheet.)
//
checkLength: function (check,node,callback) {
if (check.time(callback)) return;
var isStyle = 0; var sheet = (node.sheet || node.styleSheet);
try {if ((sheet.cssRules||sheet.rules||[]).length > 0) {isStyle = 1}} catch(err) {
if (err.message.match(/protected variable|restricted URI/)) {isStyle = 1}
else if (err.message.match(/Security error/)) {
// Firefox3 gives "Security error" for missing files, so
// can't distinguish that from OK files on remote servers.
// or OK files in different directory from local files.
isStyle = 1; // just say it is OK (can't really tell)
}
}
if (isStyle) {
// Opera 9.6 requires this setTimeout
setTimeout(BASE.Callback([callback,check.STATUS.OK]),0);
} else {
setTimeout(check,check.delay);
}
}
},
//
// JavaScript code must call this when they are completely initialized
// (this allows them to perform asynchronous actions before indicating
// that they are complete).
//
loadComplete: function (file) {
file = this.fileURL(file);
var loading = this.loading[file];
if (loading && !loading.preloaded) {
BASE.Message.Clear(loading.message);
clearTimeout(loading.timeout);
if (loading.script) {
if (SCRIPTS.length === 0) {setTimeout(REMOVESCRIPTS,0)}
SCRIPTS.push(loading.script);
}
this.loaded[file] = loading.status; delete this.loading[file];
this.addHook(file,loading.callback);
} else {
if (loading) {delete this.loading[file]}
this.loaded[file] = this.STATUS.OK;
loading = {status: this.STATUS.OK}
}
if (!this.loadHooks[file]) {return null}
return this.loadHooks[file].Execute(loading.status);
},
//
// If a file fails to load within the timeout period (or the onerror handler
// is called), this routine runs to signal the error condition.
//
loadTimeout: function (file) {
if (this.loading[file].timeout) {clearTimeout(this.loading[file].timeout)}
this.loading[file].status = this.STATUS.ERROR;
this.loadError(file);
this.loadComplete(file);
},
//
// The default error hook for file load failures
//
loadError: function (file) {
BASE.Message.Set(["LoadFailed","File failed to load: %1",file],null,2000);
BASE.Hub.signal.Post(["file load error",file]);
},
//
// Defines a style sheet from a hash of style declarations (key:value pairs
// where the key is the style selector and the value is a hash of CSS attributes
// and values).
//
Styles: function (styles,callback) {
var styleString = this.StyleString(styles);
if (styleString === "") {
callback = BASE.Callback(callback);
callback();
} else {
var style = document.createElement("style"); style.type = "text/css";
this.head = HEAD(this.head);
this.head.appendChild(style);
if (style.styleSheet && typeof(style.styleSheet.cssText) !== 'undefined') {
style.styleSheet.cssText = styleString;
} else {
style.appendChild(document.createTextNode(styleString));
}
callback = this.timer.create.call(this,callback,style);
}
return callback;
},
//
// Create a stylesheet string from a style declaration object
//
StyleString: function (styles) {
if (typeof(styles) === 'string') {return styles}
var string = "", id, style;
for (id in styles) {if (styles.hasOwnProperty(id)) {
if (typeof styles[id] === 'string') {
string += id + " {"+styles[id]+"}\n";
} else if (styles[id] instanceof Array) {
for (var i = 0; i < styles[id].length; i++) {
style = {}; style[id] = styles[id][i];
string += this.StyleString(style);
}
} else if (id.substr(0,6) === '@media') {
string += id + " {"+this.StyleString(styles[id])+"}\n";
} else if (styles[id] != null) {
style = [];
for (var name in styles[id]) {if (styles[id].hasOwnProperty(name)) {
if (styles[id][name] != null)
{style[style.length] = name + ': ' + styles[id][name]}
}}
string += id +" {"+style.join('; ')+"}\n";
}
}}
return string;
}
};
})("MathJax");
/**********************************************************/
MathJax.HTML = {
//
// Create an HTML element with given attributes and content.
// The def parameter is an (optional) object containing key:value pairs
// of the attributes and their values, and contents is an (optional)
// array of strings to be inserted as text, or arrays of the form
// [type,def,contents] that describes an HTML element to be inserted
// into the current element. Thus the contents can describe a complete
// HTML snippet of arbitrary complexity. E.g.:
//
// MathJax.HTML.Element("span",{id:"mySpan",style{"font-style":"italic"}},[
// "(See the ",["a",{href:"http://www.mathjax.org"},["MathJax home page"]],
// " for more details.)"]);
//
Element: function (type,def,contents) {
var obj = document.createElement(type);
if (def) {
if (def.style) {
var style = def.style; def.style = {};
for (var id in style) {if (style.hasOwnProperty(id))
{def.style[id.replace(/-([a-z])/g,this.ucMatch)] = style[id]}}
}
MathJax.Hub.Insert(obj,def);
}
if (contents) {
if (!(contents instanceof Array)) {contents = [contents]}
for (var i = 0; i < contents.length; i++) {
if (contents[i] instanceof Array) {
obj.appendChild(this.Element(contents[i][0],contents[i][1],contents[i][2]));
} else if (type === "script") { // IE throws an error if script is added as a text node
this.setScript(obj, contents[i]);
} else {
obj.appendChild(document.createTextNode(contents[i]));
}
}
}
return obj;
},
ucMatch: function (match,c) {return c.toUpperCase()},
addElement: function (span,type,def,contents) {return span.appendChild(this.Element(type,def,contents))},
TextNode: function (text) {return document.createTextNode(text)},
addText: function (span,text) {return span.appendChild(this.TextNode(text))},
//
// Set and get the text of a script
//
setScript: function (script,text) {
if (this.setScriptBug) {script.text = text} else {
while (script.firstChild) {script.removeChild(script.firstChild)}
this.addText(script,text);
}
},
getScript: function (script) {
var text = (script.text === "" ? script.innerHTML : script.text);
return text.replace(/^\s+/,"").replace(/\s+$/,"");
},
//
// Manage cookies
//
Cookie: {
prefix: "mjx",
expires: 365,
//
// Save an object as a named cookie
//
Set: function (name,def) {
var keys = [];
if (def) {
for (var id in def) {if (def.hasOwnProperty(id)) {
keys.push(id+":"+def[id].toString().replace(/&/g,"&&"));
}}
}
var cookie = this.prefix+"."+name+"="+escape(keys.join('&;'));
if (this.expires) {
var time = new Date(); time.setDate(time.getDate() + this.expires);
cookie += '; expires='+time.toGMTString();
}
try {document.cookie = cookie+"; path=/"} catch (err) {} // ignore errors saving cookies
},
//
// Get the contents of a named cookie and incorporate
// it into the given object (or return a fresh one)
//
Get: function (name,obj) {
if (!obj) {obj = {}}
var pattern = new RegExp("(?:^|;\\s*)"+this.prefix+"\\."+name+"=([^;]*)(?:;|$)");
var match = pattern.exec(document.cookie);
if (match && match[1] !== "") {
var keys = unescape(match[1]).split('&;');
for (var i = 0, m = keys.length; i < m; i++) {
match = keys[i].match(/([^:]+):(.*)/);
var value = match[2].replace(/&&/g,'&');
if (value === "true") {value = true} else if (value === "false") {value = false}
else if (value.match(/^-?(\d+(\.\d+)?|\.\d+)$/)) {value = parseFloat(value)}
obj[match[1]] = value;
}
}
return obj;
}
}
};
/**********************************************************/
MathJax.Localization = {
locale: "en",
directory: "[MathJax]/localization",
strings: {
// Currently, this list is not modified by the MathJax-i18n script. You can
// run the following command in MathJax/unpacked/localization to update it:
//
// find -name *.js | xargs grep menuTitle\: | grep -v qqq | sed "s/^\.\/\(.*\)\/.*\.js\: / \"\1\"\: \{/" | sed "s/,$/\},/" | sed "s/\"English\"/\"English\", isLoaded: true/" > tmp ; sort tmp > tmp2 ; sed "$ s/,$//" tmp2 ; rm tmp*
//
// This only takes languages with localization data so you must also add
// the languages that use a remap but are not translated at all.
//
"br": {menuTitle: "brezhoneg"},
"cdo": {menuTitle: "M\u00ECng-d\u0115\u0324ng-ng\u1E73\u0304"},
"cs": {menuTitle: "\u010Desky"},
"da": {menuTitle: "dansk"},
"de": {menuTitle: "Deutsch"},
"en": {menuTitle: "English", isLoaded: true},
"eo": {menuTitle: "Esperanto"},
"es": {menuTitle: "espa\u00F1ol"},
"fa": {menuTitle: "\u0641\u0627\u0631\u0633\u06CC"},
"fi": {menuTitle: "suomi"},
"fr": {menuTitle: "fran\u00E7ais"},
"gl": {menuTitle: "galego"},
"he": {menuTitle: "\u05E2\u05D1\u05E8\u05D9\u05EA"},
"ia": {menuTitle: "interlingua"},
"it": {menuTitle: "italiano"},
"ja": {menuTitle: "\u65E5\u672C\u8A9E"},
"ko": {menuTitle: "\uD55C\uAD6D\uC5B4"},
"lb": {menuTitle: "L\u00EBtzebuergesch"},
"mk": {menuTitle: "\u043C\u0430\u043A\u0435\u0434\u043E\u043D\u0441\u043A\u0438"},
"nl": {menuTitle: "Nederlands"},
"oc": {menuTitle: "occitan"},
"pl": {menuTitle: "polski"},
"pt-br": {menuTitle: "portugu\u00EAs do Brasil"},
"pt": {menuTitle: "portugus\u00EA"},
"ru": {menuTitle: "\u0440\u0443\u0441\u0441\u043A\u0438\u0439"},
"sl": {menuTitle: "sloven\u0161\u010Dina"},
"sv": {menuTitle: "svenska"},
"tr": {menuTitle: "T\u00FCrk\u00E7e"},
"uk": {menuTitle: "\u0443\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"},
"zh-hans": {menuTitle: "\u4E2D\u6587\uFF08\u7B80\u4F53\uFF09"}
},
//
// The pattern for substitution escapes:
// %n or %{n} or %{plural:%n|option1|option1|...} or %c
//
pattern: /%(\d+|\{\d+\}|\{[a-z]+:\%\d+(?:\|(?:%\{\d+\}|%.|[^\}])*)+\}|.)/g,
SPLIT: ("axb".split(/(x)/).length === 3 ?
function (string,regex) {return string.split(regex)} :
//
// IE8 and below don't do split() correctly when the pattern includes
// parentheses (the split should include the matched exrepssions).
// So implement it by hand here.
//
function (string,regex) {
var result = [], match, last = 0;
regex.lastIndex = 0;
while (match = regex.exec(string)) {
result.push(string.substr(last,match.index));
result.push.apply(result,match.slice(1));
last = match.index + match[0].length;
}
result.push(string.substr(last));
return result;
}),
_: function (id,phrase) {
if (phrase instanceof Array) {return this.processSnippet(id,phrase)}
return this.processString(this.lookupPhrase(id,phrase),[].slice.call(arguments,2));
},
processString: function (string,args,domain) {
//
// Process arguments for substitution
// If the argument is a snippet (and we are processing snippets) do so,
// Otherwise, if it is a number, convert it for the lacale
//
var i, m;
for (i = 0, m = args.length; i < m; i++) {
if (domain && args[i] instanceof Array) {args[i] = this.processSnippet(domain,args[i])}
}
//
// Split string at escapes and process them individually
//
var parts = this.SPLIT(string,this.pattern);
for (i = 1, m = parts.length; i < m; i += 2) {
var c = parts[i].charAt(0); // first char will be { or \d or a char to be kept literally
if (c >= "0" && c <= "9") { // %n
parts[i] = args[parts[i]-1];
if (typeof parts[i] === "number") parts[i] = this.number(parts[i]);
} else if (c === "{") { // %{n} or %{plural:%n|...}
c = parts[i].substr(1);
if (c >= "0" && c <= "9") { // %{n}
parts[i] = args[parts[i].substr(1,parts[i].length-2)-1];
if (typeof parts[i] === "number") parts[i] = this.number(parts[i]);
} else { // %{plural:%n|...}
var match = parts[i].match(/^\{([a-z]+):%(\d+)\|(.*)\}$/);
if (match) {
if (match[1] === "plural") {
var n = args[match[2]-1];
if (typeof n === "undefined") {
parts[i] = "???"; // argument doesn't exist
} else {
n = this.plural(n) - 1; // index of the form to use
var plurals = match[3].replace(/(^|[^%])(%%)*%\|/g,"$1$2%\uEFEF").split(/\|/); // the parts (replacing %| with a special character)
if (n >= 0 && n < plurals.length) {
parts[i] = this.processString(plurals[n].replace(/\uEFEF/g,"|"),args,domain);
} else {
parts[i] = "???"; // no string for this index
}
}
} else {parts[i] = "%"+parts[i]} // not "plural", put back the % and leave unchanged
}
}
}
if (parts[i] == null) {parts[i] = "???"}
}
//
// If we are not forming a snippet, return the completed string
//
if (!domain) {return parts.join("")}
//
// We need to return an HTML snippet, so buld it from the
// broken up string with inserted parts (that could be snippets)
//
var snippet = [], part = "";
for (i = 0; i < m; i++) {
part += parts[i]; i++; // add the string and move on to substitution result
if (i < m) {
if (parts[i] instanceof Array) { // substitution was a snippet
snippet.push(part); // add the accumulated string
snippet = snippet.concat(parts[i]); // concatenate the substution snippet
part = ""; // start accumulating a new string
} else { // substitution was a string
part += parts[i]; // add to accumulating string
}
}
}
if (part !== "") {snippet.push(part)} // add final string
return snippet;
},
processSnippet: function (domain,snippet) {
var result = []; // the new snippet
//
// Look through the original snippet for
// strings or snippets to translate
//
for (var i = 0, m = snippet.length; i < m; i++) {
if (snippet[i] instanceof Array) {
//
// This could be a sub-snippet:
// ["tag"] or ["tag",{properties}] or ["tag",{properties},snippet]
// Or it could be something to translate:
// [id,string,args] or [domain,snippet]
var data = snippet[i];
if (typeof data[1] === "string") { // [id,string,args]
var id = data[0]; if (!(id instanceof Array)) {id = [domain,id]}
var phrase = this.lookupPhrase(id,data[1]);
result = result.concat(this.processMarkdown(phrase,data.slice(2),domain));
} else if (data[1] instanceof Array) { // [domain,snippet]
result = result.concat(this.processSnippet.apply(this,data));
} else if (data.length >= 3) { // ["tag",{properties},snippet]
result.push([data[0],data[1],this.processSnippet(domain,data[2])]);
} else { // ["tag"] or ["tag",{properties}]
result.push(snippet[i]);
}
} else { // a string
result.push(snippet[i]);
}
}
return result;
},
markdownPattern: /(%.)|(\*{1,3})((?:%.|.)+?)\2|(`+)((?:%.|.)+?)\4|\[((?:%.|.)+?)\]\(([^\s\)]+)\)/,
// %c or *bold*, **italics**, ***bold-italics***, or `code`, or [link](url)
processMarkdown: function (phrase,args,domain) {
var result = [], data;
//
// Split the string by the Markdown pattern
// (the text blocks are separated by
// c,stars,star-text,backtics,code-text,link-text,URL).
// Start with teh first text string from the split.
//
var parts = phrase.split(this.markdownPattern);
var string = parts[0];
//
// Loop through the matches and process them
//
for (var i = 1, m = parts.length; i < m; i += 8) {
if (parts[i+1]) { // stars (for bold/italic)
//
// Select the tag to use by number of stars (three stars requires two tags)
//
data = this.processString(parts[i+2],args,domain);
if (!(data instanceof Array)) {data = [data]}
data = [["b","i","i"][parts[i+1].length-1],{},data]; // number of stars determines type
if (parts[i+1].length === 3) {data = ["b",{},data]} // bold-italic
} else if (parts[i+3]) { // backtics (for code)
//
// Remove one leading or trailing space, and process substitutions
// Make a <code> tag
//
data = this.processString(parts[i+4].replace(/^\s/,"").replace(/\s$/,""),args,domain);
if (!(data instanceof Array)) {data = [data]}
data = ["code",{},data];
} else if (parts[i+5]) { // hyperlink
//
// Process the link text, and make an <a> tag with the URL
//
data = this.processString(parts[i+5],args,domain);
if (!(data instanceof Array)) {data = [data]}
data = ["a",{href:this.processString(parts[i+6],args),target:"_blank"},data];
} else {
//
// Escaped character (%c) gets added into the string.
//
string += parts[i]; data = null;
}
//
// If there is a tag to insert,
// Add any pending string, then push the tag
//
if (data) {
result = this.concatString(result,string,args,domain);
result.push(data); string = "";
}
//
// Process the string that follows matches pattern
//
if (parts[i+7] !== "") {string += parts[i+7]}
};
//
// Add any pending string and return the resulting snippet
//
result = this.concatString(result,string,args,domain);
return result;
},
concatString: function (result,string,args,domain) {
if (string != "") {
//
// Process the substutions.
// If the result is not a snippet, turn it into one.
// Then concatenate the snippet to the current one
//
string = this.processString(string,args,domain);
if (!(string instanceof Array)) {string = [string]}
result = result.concat(string);
}
return result;
},
lookupPhrase: function (id,phrase,domain) {
//
// Get the domain and messageID
//
if (!domain) {domain = "_"}
if (id instanceof Array) {domain = (id[0] || "_"); id = (id[1] || "")}
//
// Check if the data is available and if not,
// load it and throw a restart error so the calling
// code can wait for the load and try again.
//
var load = this.loadDomain(domain);
if (load) {MathJax.Hub.RestartAfter(load)}
//
// Look up the message in the localization data
// (if not found, the original English is used)
//
var localeData = this.strings[this.locale];
if (localeData) {
if (localeData.domains && domain in localeData.domains) {
var domainData = localeData.domains[domain];
if (domainData.strings && id in domainData.strings)
{phrase = domainData.strings[id]}
}
}
//
// return the translated phrase
//
return phrase;
},
//
// Load a langauge data file from the proper
// directory and file.
//
loadFile: function (file,data,callback) {
callback = MathJax.Callback(callback||{});
file = (data.file || file); // the data's file name or the default name
if (!file.match(/\.js$/)) {file += ".js"} // add .js if needed
//
// Add the directory if the file doesn't
// contain a full URL already.
//
if (!file.match(/^([a-z]+:|\[MathJax\])/)) {
var dir = (this.strings[this.locale].directory ||
this.directory + "/" + this.locale ||
"[MathJax]/localization/" + this.locale);
file = dir + "/" + file;
}
//
// Load the file and mark the data as loaded (even if it
// failed to load, so we don't continue to try to load it
// over and over).
//
var load = MathJax.Ajax.Require(file,function () {data.isLoaded = true; return callback()});
//
// Return the callback if needed, otherwise null.
//
return (load.called ? null : load);
},
//
// Check to see if the localization data are loaded
// for the given domain; if not, load the data file,
// and return a callback for the loading operation.
// Otherwise return null (data are loaded).
//
loadDomain: function (domain,callback) {
var load, localeData = this.strings[this.locale];
if (localeData) {
if (!localeData.isLoaded) {
load = this.loadFile(this.locale,localeData);
if (load) {
return MathJax.Callback.Queue(
load,["loadDomain",this,domain] // call again to load domain
).Push(callback);
}
}
if (localeData.domains && domain in localeData.domains) {
var domainData = localeData.domains[domain];
if (!domainData.isLoaded) {
load = this.loadFile(domain,domainData);
if (load) {return MathJax.Callback.Queue(load).Push(callback)}
}
}
}
// localization data are loaded, so just do the callback
return MathJax.Callback(callback)();
},
//
// Perform a function, properly handling
// restarts due to localization file loads.
//
// Note that this may return before the function
// has been called successfully, so you should
// consider fn as running asynchronously. (Callbacks
// can be used to synchronize it with other actions.)
//
Try: function (fn) {
fn = MathJax.Callback(fn); fn.autoReset = true;
try {fn()} catch (err) {
if (!err.restart) {throw err}
MathJax.Callback.After(["Try",this,fn],err.restart);
}
},
//
// Reset the current language
//
resetLocale: function(locale) {
// Selection algorithm:
// 1) Downcase locale name (e.g. "en-US" => "en-us")
// 2) Try a parent language (e.g. "en-us" => "en")
// 3) Try the fallback specified in the data (e.g. "pt" => "pt-br")
// 4) Otherwise don't change the locale.
if (!locale) return;
locale = locale.toLowerCase();
while (!this.strings[locale]) {
var dashPos = locale.lastIndexOf("-");
if (dashPos === -1) return;
locale = locale.substring(0, dashPos);
}
var remap = this.strings[locale].remap;
this.locale = remap ? remap : locale;
},
//
// Set the current language
//
setLocale: function(locale) {
this.resetLocale(locale);
if (MathJax.Menu) {this.loadDomain("MathMenu")}
},
//
// Add or update a language or domain
//
addTranslation: function (locale,domain,definition) {
var data = this.strings[locale], isNew = false;
if (!data) {data = this.strings[locale] = {}; isNew = true}
if (!data.domains) {data.domains = {}}
if (domain) {
if (!data.domains[domain]) {data.domains[domain] = {}}
data = data.domains[domain];
}
MathJax.Hub.Insert(data,definition);
if (isNew && MathJax.Menu.menu) {MathJax.Menu.CreateLocaleMenu()}
},
//
// Set CSS for an element based on font requirements
//
setCSS: function (div) {
var locale = this.strings[this.locale];
if (locale) {
if (locale.fontFamily) {div.style.fontFamily = locale.fontFamily}
if (locale.fontDirection) {
div.style.direction = locale.fontDirection;
if (locale.fontDirection === "rtl") {div.style.textAlign = "right"}
}
}
return div;
},
//
// Get the language's font family or direction
//
fontFamily: function () {
var locale = this.strings[this.locale];
return (locale ? locale.fontFamily : null);
},
fontDirection: function () {
var locale = this.strings[this.locale];
return (locale ? locale.fontDirection : null);
},
//
// Get the language's plural index for a number
//
plural: function (n) {
var locale = this.strings[this.locale];
if (locale && locale.plural) {return locale.plural(n)}
// default
if (n == 1) {return 1} // one
return 2; // other
},
//
// Convert a number to language-specific form
//
number: function(n) {
var locale = this.strings[this.locale];
if (locale && locale.number) {return locale.number(n)}
// default
return n;
}
};
/**********************************************************/
MathJax.Message = {
ready: false, // used to tell when the styles are available
log: [{}], current: null,
textNodeBug: (navigator.vendor === "Apple Computer, Inc." &&
typeof navigator.vendorSub === "undefined") ||
(window.hasOwnProperty && window.hasOwnProperty("konqueror")), // Konqueror displays some gibberish with text.nodeValue = "..."
styles: {
"#MathJax_Message": {
position: "fixed", left: "1px", bottom: "2px",
'background-color': "#E6E6E6", border: "1px solid #959595",
margin: "0px", padding: "2px 8px",
'z-index': "102", color: "black", 'font-size': "80%",
width: "auto", 'white-space': "nowrap"
},
"#MathJax_MSIE_Frame": {
position: "absolute",
top:0, left: 0, width: "0px", 'z-index': 101,
border: "0px", margin: "0px", padding: "0px"
}
},
browsers: {
MSIE: function (browser) {
MathJax.Hub.config.styles["#MathJax_Message"].position = "absolute";
MathJax.Message.quirks = (document.compatMode === "BackCompat");
},
Chrome: function (browser) {
MathJax.Hub.config.styles["#MathJax_Message"].bottom = "1.5em";
MathJax.Hub.config.styles["#MathJax_Message"].left = "1em";
}
},
Init: function (styles) {
if (styles) {this.ready = true}
if (!document.body || !this.ready) {return false}
//
// ASCIIMathML replaces the entire page with a copy of itself (@#!#%@!!)
// so check that this.div is still part of the page, otherwise look up
// the copy and use that.
//
if (this.div && this.div.parentNode == null) {
this.div = document.getElementById("MathJax_Message");
if (this.div) {this.text = this.div.firstChild}
}
if (!this.div) {
var frame = document.body;
if (MathJax.Hub.Browser.isMSIE) {
frame = this.frame = this.addDiv(document.body); frame.removeAttribute("id");
frame.style.position = "absolute";
frame.style.border = frame.style.margin = frame.style.padding = "0px";
frame.style.zIndex = "101"; frame.style.height = "0px";
frame = this.addDiv(frame);
frame.id = "MathJax_MSIE_Frame";
window.attachEvent("onscroll",this.MoveFrame);
window.attachEvent("onresize",this.MoveFrame);
this.MoveFrame();
}
this.div = this.addDiv(frame); this.div.style.display = "none";
this.text = this.div.appendChild(document.createTextNode(""));
}
return true;
},
addDiv: function (parent) {
var div = document.createElement("div");
div.id = "MathJax_Message";
if (parent.firstChild) {parent.insertBefore(div,parent.firstChild)}
else {parent.appendChild(div)}
return div;
},
MoveFrame: function () {
var body = (MathJax.Message.quirks ? document.body : document.documentElement);
var frame = MathJax.Message.frame;
frame.style.left = body.scrollLeft + 'px';
frame.style.top = body.scrollTop + 'px';
frame.style.width = body.clientWidth + 'px';
frame = frame.firstChild;
frame.style.height = body.clientHeight + 'px';
},
localize: function (message) {
return MathJax.Localization._(message,message);
},
filterText: function (text,n,id) {
if (MathJax.Hub.config.messageStyle === "simple") {
if (id === "LoadFile") {
if (!this.loading) {this.loading = this.localize("Loading") + " "}
text = this.loading; this.loading += ".";
} else if (id === "ProcessMath") {
if (!this.processing) {this.processing = this.localize("Processing") + " "}
text = this.processing; this.processing += ".";
} else if (id === "TypesetMath") {
if (!this.typesetting) {this.typesetting = this.localize("Typesetting") + " "}
text = this.typesetting; this.typesetting += ".";
}
}
return text;
},
Set: function (text,n,clearDelay) {
if (n == null) {n = this.log.length; this.log[n] = {}}
//
// Translate message if it is [id,message,arguments]
//
var id = "";
if (text instanceof Array) {
id = text[0]; if (id instanceof Array) {id = id[1]}
//
// Localization._() will throw a restart error if a localization file
// needs to be loaded, so trap that and redo the Set() call
// after it is loaded.
//
try {
text = MathJax.Localization._.apply(MathJax.Localization,text);
} catch (err) {
if (!err.restart) {throw err}
if (!err.restart.called) {
//
// Mark it so we can tell if the Clear() comes before the message is displayed
//
if (this.log[n].restarted == null) {this.log[n].restarted = 0}
this.log[n].restarted++; delete this.log[n].cleared;
MathJax.Callback.After(["Set",this,text,n,clearDelay],err.restart);
return n;
}
}
}
//
// Clear the timout timer.
//
if (this.timer) {clearTimeout(this.timer); delete this.timer}
//
// Save the message and filtered message.
//
this.log[n].text = text; this.log[n].filteredText = text = this.filterText(text,n,id);
//
// Hook the message into the message list so we can tell
// what message to put up when this one is removed.
//
if (typeof(this.log[n].next) === "undefined") {
this.log[n].next = this.current;
if (this.current != null) {this.log[this.current].prev = n}
this.current = n;
}
//
// Show the message if it is the currently active one.
//
if (this.current === n && MathJax.Hub.config.messageStyle !== "none") {
if (this.Init()) {
if (this.textNodeBug) {this.div.innerHTML = text} else {this.text.nodeValue = text}
this.div.style.display = "";
if (this.status) {window.status = ""; delete this.status}
} else {
window.status = text;
this.status = true;
}
}
//
// Check if the message was resetarted to load a localization file
// and if it has been cleared in the meanwhile.
//
if (this.log[n].restarted) {
if (this.log[n].cleared) {clearDelay = 0}
if (--this.log[n].restarted === 0) {delete this.log[n].cleared}
}
//
// Check if we need to clear the message automatically.
//
if (clearDelay) {setTimeout(MathJax.Callback(["Clear",this,n]),clearDelay)}
else if (clearDelay == 0) {this.Clear(n,0)}
//
// Return the message number.
//
return n;
},
Clear: function (n,delay) {
//
// Detatch the message from the active list.
//
if (this.log[n].prev != null) {this.log[this.log[n].prev].next = this.log[n].next}
if (this.log[n].next != null) {this.log[this.log[n].next].prev = this.log[n].prev}
//
// If it is the current message, get the next one to show.
//
if (this.current === n) {
this.current = this.log[n].next;
if (this.text) {
if (this.div.parentNode == null) {this.Init()} // see ASCIIMathML comments above
if (this.current == null) {
//
// If there are no more messages, remove the message box.
//
if (this.timer) {clearTimeout(this.timer); delete this.timer}
if (delay == null) {delay = 600}
if (delay === 0) {this.Remove()}
else {this.timer = setTimeout(MathJax.Callback(["Remove",this]),delay)}
} else if (MathJax.Hub.config.messageStyle !== "none") {
//
// If there is an old message, put it in place
//
if (this.textNodeBug) {this.div.innerHTML = this.log[this.current].filteredText}
else {this.text.nodeValue = this.log[this.current].filteredText}
}
if (this.status) {window.status = ""; delete this.status}
} else if (this.status) {
window.status = (this.current == null ? "" : this.log[this.current].text);
}
}
//
// Clean up the log data no longer needed
//
delete this.log[n].next; delete this.log[n].prev;
delete this.log[n].filteredText;
//
// If this is a restarted localization message, mark that it has been cleared
// while waiting for the file to load.
//
if (this.log[n].restarted) {this.log[n].cleared = true}
},
Remove: function () {
// FIXME: do a fade out or something else interesting?
this.text.nodeValue = "";
this.div.style.display = "none";
},
File: function (file) {
var root = MathJax.Ajax.config.root;
if (file.substr(0,root.length) === root) {file = "[MathJax]"+file.substr(root.length)}
return this.Set(["LoadFile","Loading %1",file],null,null);
},
Log: function () {
var strings = [];
for (var i = 1, m = this.log.length; i < m; i++) {strings[i] = this.log[i].text}
return strings.join("\n");
}
};
/**********************************************************/
MathJax.Hub = {
config: {
root: "",
config: [], // list of configuration files to load
styleSheets: [], // list of CSS files to load
styles: { // styles to generate in-line
".MathJax_Preview": {color: "#888"}
},
jax: [], // list of input and output jax to load
extensions: [], // list of extensions to load
preJax: null, // pattern to remove from before math script tag
postJax: null, // pattern to remove from after math script tag
displayAlign: 'center', // how to align displayed equations (left, center, right)
displayIndent: '0', // indentation for displayed equations (when not centered)
preRemoveClass: 'MathJax_Preview', // class of objects to remove preceeding math script
showProcessingMessages: true, // display "Processing math: nn%" messages or not
messageStyle: "normal", // set to "none" or "simple" (for "Loading..." and "Processing...")
delayStartupUntil: "none", // set to "onload" to delay setup until the onload handler runs
// set to "configured" to delay startup until MathJax.Hub.Configured() is called
// set to a Callback to wait for before continuing with the startup
skipStartupTypeset: false, // set to true to skip PreProcess and Process during startup
elements: [], // array of elements to process when none is given explicitly
positionToHash: true, // after initial typeset pass, position to #hash location?
showMathMenu: true, // attach math context menu to typeset math?
showMathMenuMSIE: true, // separtely determine if MSIE should have math menu
// (since the code for that is a bit delicate)
menuSettings: {
zoom: "None", // when to do MathZoom
CTRL: false, // require CTRL for MathZoom?
ALT: false, // require Alt or Option?
CMD: false, // require CMD?
Shift: false, // require Shift?
discoverable: false, // make math menu discoverable on hover?
zscale: "200%", // the scaling factor for MathZoom
renderer: "", // set when Jax are loaded
font: "Auto", // what font HTML-CSS should use
context: "MathJax", // or "Browser" for pass-through to browser menu
locale: "en", // the language to use for messages
mpContext: false, // true means pass menu events to MathPlayer in IE
mpMouse: false, // true means pass mouse events to MathPlayer in IE
texHints: true // include class names for TeXAtom elements
},
errorSettings: {
// localized HTML snippet structure for message to use
message: ["[",["MathProcessingError","Math Processing Error"],"]"],
style: {color: "#CC0000", "font-style":"italic"} // style for message
}
},
preProcessors: MathJax.Callback.Hooks(true), // list of callbacks for preprocessing (initialized by extensions)
inputJax: {}, // mime-type mapped to input jax (by registration)
outputJax: {order:{}}, // mime-type mapped to output jax list (by registration)
processUpdateTime: 250, // time between screen updates when processing math (milliseconds)
processUpdateDelay: 10, // pause between screen updates to allow other processing (milliseconds)
signal: MathJax.Callback.Signal("Hub"), // Signal used for Hub events
Config: function (def) {
this.Insert(this.config,def);
if (this.config.Augment) {this.Augment(this.config.Augment)}
},
CombineConfig: function (name,def) {
var config = this.config, id, parent; name = name.split(/\./);
for (var i = 0, m = name.length; i < m; i++) {
id = name[i]; if (!config[id]) {config[id] = {}}
parent = config; config = config[id];
}
parent[id] = config = this.Insert(def,config);
return config;
},
Register: {
PreProcessor: function () {MathJax.Hub.preProcessors.Add.apply(MathJax.Hub.preProcessors,arguments)},
MessageHook: function () {return MathJax.Hub.signal.MessageHook.apply(MathJax.Hub.signal,arguments)},
StartupHook: function () {return MathJax.Hub.Startup.signal.MessageHook.apply(MathJax.Hub.Startup.signal,arguments)},
LoadHook: function () {return MathJax.Ajax.LoadHook.apply(MathJax.Ajax,arguments)}
},
getAllJax: function (element) {
var jax = [], scripts = this.elementScripts(element);
for (var i = 0, m = scripts.length; i < m; i++) {
if (scripts[i].MathJax && scripts[i].MathJax.elementJax)
{jax.push(scripts[i].MathJax.elementJax)}
}
return jax;
},
getJaxByType: function (type,element) {
var jax = [], scripts = this.elementScripts(element);
for (var i = 0, m = scripts.length; i < m; i++) {
if (scripts[i].MathJax && scripts[i].MathJax.elementJax &&
scripts[i].MathJax.elementJax.mimeType === type)
{jax.push(scripts[i].MathJax.elementJax)}
}
return jax;
},
getJaxByInputType: function (type,element) {
var jax = [], scripts = this.elementScripts(element);
for (var i = 0, m = scripts.length; i < m; i++) {
if (scripts[i].MathJax && scripts[i].MathJax.elementJax &&
scripts[i].type && scripts[i].type.replace(/ *;(.|\s)*/,"") === type)
{jax.push(scripts[i].MathJax.elementJax)}
}
return jax;
},
getJaxFor: function (element) {
if (typeof(element) === 'string') {element = document.getElementById(element)}
if (element && element.MathJax) {return element.MathJax.elementJax}
if (element && element.isMathJax) {
while (element && !element.jaxID) {element = element.parentNode}
if (element) {return MathJax.OutputJax[element.jaxID].getJaxFromMath(element)}
}
return null;
},
isJax: function (element) {
if (typeof(element) === 'string') {element = document.getElementById(element)}
if (element && element.isMathJax) {return 1}
if (element && element.tagName != null && element.tagName.toLowerCase() === 'script') {
if (element.MathJax)
{return (element.MathJax.state === MathJax.ElementJax.STATE.PROCESSED ? 1 : -1)}
if (element.type && this.inputJax[element.type.replace(/ *;(.|\s)*/,"")]) {return -1}
}
return 0;
},
setRenderer: function (renderer,type) {
if (!renderer) return;
if (!MathJax.OutputJax[renderer]) {
this.config.menuSettings.renderer = "";
var file = "[MathJax]/jax/output/"+renderer+"/config.js";
return MathJax.Ajax.Require(file,["setRenderer",this,renderer,type]);
} else {
this.config.menuSettings.renderer = renderer;
if (type == null) {type = "jax/mml"}
var jax = this.outputJax;
if (jax[type] && jax[type].length) {
if (renderer !== jax[type][0].id) {
jax[type].unshift(MathJax.OutputJax[renderer]);
return this.signal.Post(["Renderer Selected",renderer]);
}
}
return null;
}
},
Queue: function () {
return this.queue.Push.apply(this.queue,arguments);
},
Typeset: function (element,callback) {
if (!MathJax.isReady) return null;
var ec = this.elementCallback(element,callback);
var queue = MathJax.Callback.Queue();
for (var i = 0, m = ec.elements.length; i < m; i++) {
if (ec.elements[i]) {
queue.Push(
["PreProcess",this,ec.elements[i]],
["Process",this,ec.elements[i]]
);
}
}
return queue.Push(ec.callback);
},
PreProcess: function (element,callback) {
var ec = this.elementCallback(element,callback);
var queue = MathJax.Callback.Queue();
for (var i = 0, m = ec.elements.length; i < m; i++) {
if (ec.elements[i]) {
queue.Push(
["Post",this.signal,["Begin PreProcess",ec.elements[i]]],
(arguments.callee.disabled? {} : ["Execute",this.preProcessors,ec.elements[i]]),
["Post",this.signal,["End PreProcess",ec.elements[i]]]
);
}
}
return queue.Push(ec.callback);
},
Process: function (element,callback) {return this.takeAction("Process",element,callback)},
Update: function (element,callback) {return this.takeAction("Update",element,callback)},
Reprocess: function (element,callback) {return this.takeAction("Reprocess",element,callback)},
Rerender: function (element,callback) {return this.takeAction("Rerender",element,callback)},
takeAction: function (action,element,callback) {
var ec = this.elementCallback(element,callback);
var queue = MathJax.Callback.Queue(["Clear",this.signal]);
for (var i = 0, m = ec.elements.length; i < m; i++) {
if (ec.elements[i]) {
var state = {
scripts: [], // filled in by prepareScripts
start: new Date().getTime(), // timer for processing messages
i: 0, j: 0, // current script, current jax
jax: {}, // scripts grouped by output jax
jaxIDs: [] // id's of jax used
};
queue.Push(
["Post",this.signal,["Begin "+action,ec.elements[i]]],
["Post",this.signal,["Begin Math",ec.elements[i],action]],
["prepareScripts",this,action,ec.elements[i],state],
["Post",this.signal,["Begin Math Input",ec.elements[i],action]],
["processInput",this,state],
["Post",this.signal,["End Math Input",ec.elements[i],action]],
["prepareOutput",this,state,"preProcess"],
["Post",this.signal,["Begin Math Output",ec.elements[i],action]],
["processOutput",this,state],
["Post",this.signal,["End Math Output",ec.elements[i],action]],
["prepareOutput",this,state,"postProcess"],
["Post",this.signal,["End Math",ec.elements[i],action]],
["Post",this.signal,["End "+action,ec.elements[i]]]
);
}
}
return queue.Push(ec.callback);
},
scriptAction: {
Process: function (script) {},
Update: function (script) {
var jax = script.MathJax.elementJax;
if (jax && jax.needsUpdate()) {jax.Remove(true); script.MathJax.state = jax.STATE.UPDATE}
else {script.MathJax.state = jax.STATE.PROCESSED}
},
Reprocess: function (script) {
var jax = script.MathJax.elementJax;
if (jax) {jax.Remove(true); script.MathJax.state = jax.STATE.UPDATE}
},
Rerender: function (script) {
var jax = script.MathJax.elementJax;
if (jax) {jax.Remove(true); script.MathJax.state = jax.STATE.OUTPUT}
}
},
prepareScripts: function (action,element,state) {
if (arguments.callee.disabled) return;
var scripts = this.elementScripts(element);
var STATE = MathJax.ElementJax.STATE;
for (var i = 0, m = scripts.length; i < m; i++) {
var script = scripts[i];
if (script.type && this.inputJax[script.type.replace(/ *;(.|\n)*/,"")]) {
if (script.MathJax) {
if (script.MathJax.elementJax && script.MathJax.elementJax.hover) {
MathJax.Extension.MathEvents.Hover.ClearHover(script.MathJax.elementJax);
}
if (script.MathJax.state !== STATE.PENDING) {this.scriptAction[action](script)}
}
if (!script.MathJax) {script.MathJax = {state: STATE.PENDING}}
if (script.MathJax.state !== STATE.PROCESSED) {state.scripts.push(script)}
}
}
},
checkScriptSiblings: function (script) {
if (script.MathJax.checked) return;
var config = this.config, pre = script.previousSibling;
if (pre && pre.nodeName === "#text") {
var preJax,postJax, post = script.nextSibling;
if (post && post.nodeName !== "#text") {post = null}
if (config.preJax) {
if (typeof(config.preJax) === "string") {config.preJax = new RegExp(config.preJax+"$")}
preJax = pre.nodeValue.match(config.preJax);
}
if (config.postJax && post) {
if (typeof(config.postJax) === "string") {config.postJax = new RegExp("^"+config.postJax)}
postJax = post.nodeValue.match(config.postJax);
}
if (preJax && (!config.postJax || postJax)) {
pre.nodeValue = pre.nodeValue.replace
(config.preJax,(preJax.length > 1? preJax[1] : ""));
pre = null;
}
if (postJax && (!config.preJax || preJax)) {
post.nodeValue = post.nodeValue.replace
(config.postJax,(postJax.length > 1? postJax[1] : ""));
}
if (pre && !pre.nodeValue.match(/\S/)) {pre = pre.previousSibling}
}
if (config.preRemoveClass && pre && pre.className === config.preRemoveClass)
{script.MathJax.preview = pre}
script.MathJax.checked = 1;
},
processInput: function (state) {
var jax, STATE = MathJax.ElementJax.STATE;
var script, prev, m = state.scripts.length;
try {
//
// Loop through the scripts
//
while (state.i < m) {
script = state.scripts[state.i]; if (!script) {state.i++; continue}
//
// Remove previous error marker, if any
//
prev = script.previousSibling;
if (prev && prev.className === "MathJax_Error") {prev.parentNode.removeChild(prev)}
//
// Check if already processed or needs processing
//
if (!script.MathJax || script.MathJax.state === STATE.PROCESSED) {state.i++; continue};
if (!script.MathJax.elementJax || script.MathJax.state === STATE.UPDATE) {
this.checkScriptSiblings(script); // remove preJax/postJax etc.
var type = script.type.replace(/ *;(.|\s)*/,""); // the input jax type
jax = this.inputJax[type].Process(script,state); // run the input jax
if (typeof jax === 'function') { // if a callback was returned
if (jax.called) continue; // go back and call Process() again
this.RestartAfter(jax); // wait for the callback
}
jax.Attach(script,this.inputJax[type].id); // register the jax on the script
this.saveScript(jax,state,script,STATE); // add script to state
} else if (script.MathJax.state === STATE.OUTPUT) {
this.saveScript(script.MathJax.elementJax,state,script,STATE); // add script to state
}
//
// Go on to the next script, and check if we need to update the processing message
//
state.i++; var now = new Date().getTime();
if (now - state.start > this.processUpdateTime && state.i < state.scripts.length)
{state.start = now; this.RestartAfter(MathJax.Callback.Delay(1))}
}
} catch (err) {return this.processError(err,state,"Input")}
//
// Put up final message, reset the state and return
//
if (state.scripts.length && this.config.showProcessingMessages)
{MathJax.Message.Set(["ProcessMath","Processing math: %1%%",100],0)}
state.start = new Date().getTime(); state.i = state.j = 0;
return null;
},
saveScript: function (jax,state,script,STATE) {
//
// Check that output jax exists
//
if (!this.outputJax[jax.mimeType]) {
script.MathJax.state = STATE.UPDATE;
throw Error("No output jax registered for "+jax.mimeType);
}
//
// Record the output jax
// and put this script in the queue for that jax
//
jax.outputJax = this.outputJax[jax.mimeType][0].id;
if (!state.jax[jax.outputJax]) {
if (state.jaxIDs.length === 0) {
// use original array until we know there are more (rather than two copies)
state.jax[jax.outputJax] = state.scripts;
} else {
if (state.jaxIDs.length === 1) // get the script so far for the existing jax
{state.jax[state.jaxIDs[0]] = state.scripts.slice(0,state.i)}
state.jax[jax.outputJax] = []; // start a new array for the new jax
}
state.jaxIDs.push(jax.outputJax); // save the ID of the jax
}
if (state.jaxIDs.length > 1) {state.jax[jax.outputJax].push(script)}
//
// Mark script as needing output
//
script.MathJax.state = STATE.OUTPUT;
},
//
// Pre- and post-process scripts by jax
// (to get scaling factors, hide/show output, and so on)
// Since this can cause the jax to load, we need to trap restarts
//
prepareOutput: function (state,method) {
while (state.j < state.jaxIDs.length) {
var id = state.jaxIDs[state.j], JAX = MathJax.OutputJax[id];
if (JAX[method]) {
try {
var result = JAX[method](state);
if (typeof result === 'function') {
if (result.called) continue; // go back and try again
this.RestartAfter(result);
}
} catch (err) {
if (!err.restart) {
MathJax.Message.Set(["PrepError","Error preparing %1 output (%2)",id,method],null,600);
MathJax.Hub.lastPrepError = err;
state.j++;
}
return MathJax.Callback.After(["prepareOutput",this,state,method],err.restart);
}
}
state.j++;
}
return null;
},
processOutput: function (state) {
var result, STATE = MathJax.ElementJax.STATE, script, m = state.scripts.length;
try {
//
// Loop through the scripts
//
while (state.i < m) {
//
// Check that there is an element jax
//
script = state.scripts[state.i];
if (!script || !script.MathJax || script.MathJax.error) {state.i++; continue}
var jax = script.MathJax.elementJax; if (!jax) {state.i++; continue}
//
// Call the output Jax's Process method (which will be its Translate()
// method once loaded). Mark it as complete and remove the preview.
//
result = MathJax.OutputJax[jax.outputJax].Process(script,state);
script.MathJax.state = STATE.PROCESSED; state.i++;
if (script.MathJax.preview) {script.MathJax.preview.innerHTML = ""}
//
// Signal that new math is available
//
this.signal.Post(["New Math",jax.inputID]); // FIXME: wait for this? (i.e., restart if returns uncalled callback)
//
// Update the processing message, if needed
//
var now = new Date().getTime();
if (now - state.start > this.processUpdateTime && state.i < state.scripts.length)
{state.start = now; this.RestartAfter(MathJax.Callback.Delay(this.processUpdateDelay))}
}
} catch (err) {return this.processError(err,state,"Output")}
//
// Put up the typesetting-complete message
//
if (state.scripts.length && this.config.showProcessingMessages) {
MathJax.Message.Set(["TypesetMath","Typesetting math: %1%%",100],0);
MathJax.Message.Clear(0);
}
state.i = state.j = 0;
return null;
},
processMessage: function (state,type) {
var m = Math.floor(state.i/(state.scripts.length)*100);
var message = (type === "Output" ? ["TypesetMath","Typesetting math: %1%%"] :
["ProcessMath","Processing math: %1%%"]);
if (this.config.showProcessingMessages) {MathJax.Message.Set(message.concat(m),0)}
},
processError: function (err,state,type) {
if (!err.restart) {
if (!this.config.errorSettings.message) {throw err}
this.formatError(state.scripts[state.i],err); state.i++;
}
this.processMessage(state,type);
return MathJax.Callback.After(["process"+type,this,state],err.restart);
},
formatError: function (script,err) {
//
// Get the error message, URL, and line, and save it for
// reporting in the Show Math As Error menu
//
var message = "Error: "+err.message+"\n";
if (err.sourceURL) {message += "\nfile: "+err.sourceURL}
if (err.line) {message += "\nline: "+err.line}
script.MathJax.error = MathJax.OutputJax.Error.Jax(message,script);
//
// Create the [Math Processing Error] span
//
var errorSettings = this.config.errorSettings;
var errorText = MathJax.Localization._(errorSettings.messageId,errorSettings.message);
var error = MathJax.HTML.Element("span",
{className:"MathJax_Error", jaxID:"Error", isMathJax:true},errorText);
//
// Attach the menu events
//
if (MathJax.Extension.MathEvents) {
error.oncontextmenu = MathJax.Extension.MathEvents.Event.Menu;
error.onmousedown = MathJax.Extension.MathEvents.Event.Mousedown;
} else {
MathJax.Ajax.Require("[MathJax]/extensions/MathEvents.js",function () {
error.oncontextmenu = MathJax.Extension.MathEvents.Event.Menu;
error.onmousedown = MathJax.Extension.MathEvents.Event.Mousedown;
});
}
//
// Insert the error into the page and remove any preview
//
script.parentNode.insertBefore(error,script);
if (script.MathJax.preview) {script.MathJax.preview.innerHTML = ""}
//
// Save the error for debugging purposes
// Report the error as a signal
//
this.lastError = err;
this.signal.Post(["Math Processing Error",script,err]);
},
RestartAfter: function (callback) {
throw this.Insert(Error("restart"),{restart: MathJax.Callback(callback)});
},
elementCallback: function (element,callback) {
if (callback == null && (element instanceof Array || typeof element === 'function'))
{try {MathJax.Callback(element); callback = element; element = null} catch(e) {}}
if (element == null) {element = this.config.elements || []}
if (!(element instanceof Array)) {element = [element]}
element = [].concat(element); // make a copy so the original isn't changed
for (var i = 0, m = element.length; i < m; i++)
{if (typeof(element[i]) === 'string') {element[i] = document.getElementById(element[i])}}
if (!document.body) {document.body = document.getElementsByTagName("body")[0]}
if (element.length == 0) {element.push(document.body)}
if (!callback) {callback = {}}
return {elements: element, callback: callback};
},
elementScripts: function (element) {
if (element instanceof Array) {
var scripts = [];
for (var i = 0, m = element.length; i < m; i++)
{scripts.push.apply(scripts,this.elementScripts(element[i]))}
return scripts;
}
if (typeof(element) === 'string') {element = document.getElementById(element)}
if (!document.body) {document.body = document.getElementsByTagName("body")[0]}
if (element == null) {element = document.body}
if (element.tagName != null && element.tagName.toLowerCase() === "script") {return [element]}
return element.getElementsByTagName("script");
},
Insert: function (dst,src) {
for (var id in src) {if (src.hasOwnProperty(id)) {
// allow for concatenation of arrays?
if (typeof src[id] === 'object' && !(src[id] instanceof Array) &&
(typeof dst[id] === 'object' || typeof dst[id] === 'function')) {
this.Insert(dst[id],src[id]);
} else {
dst[id] = src[id];
}
}}
return dst;
},
// Old browsers (e.g. Internet Explorer <= 8) do not support trim().
SplitList: ("trim" in String.prototype ?
function (list) {return list.trim().split(/\s+/)} :
function (list) {return list.replace(/^\s+/,'').
replace(/\s+$/,'').split(/\s+/)})
};
MathJax.Hub.Insert(MathJax.Hub.config.styles,MathJax.Message.styles);
MathJax.Hub.Insert(MathJax.Hub.config.styles,{".MathJax_Error":MathJax.Hub.config.errorSettings.style});
//
// Storage area for extensions and preprocessors
//
MathJax.Extension = {};
//
// Hub Startup code
//
MathJax.Hub.Configured = MathJax.Callback({}); // called when configuration is complete
MathJax.Hub.Startup = {
script: "", // the startup script from the SCRIPT call that loads MathJax.js
queue: MathJax.Callback.Queue(), // Queue used for startup actions
signal: MathJax.Callback.Signal("Startup"), // Signal used for startup events
params: {},
//
// Load the configuration files
//
Config: function () {
this.queue.Push(["Post",this.signal,"Begin Config"]);
//
// If a locale is given as a parameter,
// set the locale and the default menu value for the locale
//
if (this.params.locale) {
MathJax.Localization.resetLocale(this.params.locale);
MathJax.Hub.config.menuSettings.locale = this.params.locale;
}
//
// Check for user cookie configuration
//
var user = MathJax.HTML.Cookie.Get("user");
if (user.URL || user.Config) {
if (confirm(
MathJax.Localization._("CookieConfig",
"MathJax has found a user-configuration cookie that includes code to "+
"be run. Do you want to run it?\n\n"+
"(You should press Cancel unless you set up the cookie yourself.)")
)) {
if (user.URL) {this.queue.Push(["Require",MathJax.Ajax,user.URL])}
if (user.Config) {this.queue.Push(new Function(user.Config))}
} else {MathJax.HTML.Cookie.Set("user",{})}
}
//
// Run the config files, if any are given in the parameter list
//
if (this.params.config) {
var files = this.params.config.split(/,/);
for (var i = 0, m = files.length; i < m; i++) {
if (!files[i].match(/\.js$/)) {files[i] += ".js"}
this.queue.Push(["Require",MathJax.Ajax,this.URL("config",files[i])]);
}
}
//
// Perform author configuration from in-line MathJax = {...}
//
this.queue.Push(["Config",MathJax.Hub,MathJax.AuthorConfig]);
//
// Run the deprecated configuration script, if any (ignoring return value)
// Wait for the startup delay signal
// Run the mathjax-config blocks
// Load the files in the configuration's config array
//
if (this.script.match(/\S/)) {this.queue.Push(this.script+";\n1;")}
this.queue.Push(
["ConfigDelay",this],
["ConfigBlocks",this],
[function (THIS) {return THIS.loadArray(MathJax.Hub.config.config,"config",null,true)},this],
["Post",this.signal,"End Config"]
);
},
//
// Return the delay callback
//
ConfigDelay: function () {
var delay = this.params.delayStartupUntil || MathJax.Hub.config.delayStartupUntil;
if (delay === "onload") {return this.onload}
if (delay === "configured") {return MathJax.Hub.Configured}
return delay;
},
//
// Run the scripts of type=text/x-mathjax-config
//
ConfigBlocks: function () {
var scripts = document.getElementsByTagName("script");
var last = null, queue = MathJax.Callback.Queue();
for (var i = 0, m = scripts.length; i < m; i++) {
var type = String(scripts[i].type).replace(/ /g,"");
if (type.match(/^text\/x-mathjax-config(;.*)?$/) && !type.match(/;executed=true/)) {
scripts[i].type += ";executed=true";
last = queue.Push(scripts[i].innerHTML+";\n1;");
}
}
return last;
},
//
// Read cookie and set up menu defaults
// (set the locale according to the cookie)
// (adjust the jax to accommodate renderer preferences)
//
Cookie: function () {
return this.queue.Push(
["Post",this.signal,"Begin Cookie"],
["Get",MathJax.HTML.Cookie,"menu",MathJax.Hub.config.menuSettings],
[function (config) {
if (config.menuSettings.locale)
{MathJax.Localization.resetLocale(config.menuSettings.locale)}
var renderer = config.menuSettings.renderer, jax = config.jax;
if (renderer) {
var name = "output/"+renderer; jax.sort();
for (var i = 0, m = jax.length; i < m; i++) {
if (jax[i].substr(0,7) === "output/") break;
}
if (i == m-1) {jax.pop()} else {
while (i < m) {if (jax[i] === name) {jax.splice(i,1); break}; i++}
}
jax.unshift(name);
}
},MathJax.Hub.config],
["Post",this.signal,"End Cookie"]
);
},
//
// Setup stylesheets and extra styles
//
Styles: function () {
return this.queue.Push(
["Post",this.signal,"Begin Styles"],
["loadArray",this,MathJax.Hub.config.styleSheets,"config"],
["Styles",MathJax.Ajax,MathJax.Hub.config.styles],
["Post",this.signal,"End Styles"]
);
},
//
// Load the input and output jax
//
Jax: function () {
var config = MathJax.Hub.config, jax = MathJax.Hub.outputJax;
// Save the order of the output jax since they are loading asynchronously
for (var i = 0, m = config.jax.length, k = 0; i < m; i++) {
var name = config.jax[i].substr(7);
if (config.jax[i].substr(0,7) === "output/" && jax.order[name] == null)
{jax.order[name] = k; k++}
}
var queue = MathJax.Callback.Queue();
return queue.Push(
["Post",this.signal,"Begin Jax"],
["loadArray",this,config.jax,"jax","config.js"],
["Post",this.signal,"End Jax"]
);
},
//
// Load the extensions
//
Extensions: function () {
var queue = MathJax.Callback.Queue();
return queue.Push(
["Post",this.signal,"Begin Extensions"],
["loadArray",this,MathJax.Hub.config.extensions,"extensions"],
["Post",this.signal,"End Extensions"]
);
},
//
// Initialize the Message system
//
Message: function () {
MathJax.Message.Init(true);
},
//
// Set the math menu renderer, if it isn't already
// (this must come after the jax are loaded)
//
Menu: function () {
var menu = MathJax.Hub.config.menuSettings, jax = MathJax.Hub.outputJax, registered;
for (var id in jax) {if (jax.hasOwnProperty(id)) {
if (jax[id].length) {registered = jax[id]; break}
}}
if (registered && registered.length) {
if (menu.renderer && menu.renderer !== registered[0].id)
{registered.unshift(MathJax.OutputJax[menu.renderer])}
menu.renderer = registered[0].id;
}
},
//
// Set the location to the designated hash position
//
Hash: function () {
if (MathJax.Hub.config.positionToHash && document.location.hash &&
document.body && document.body.scrollIntoView) {
var name = document.location.hash.substr(1);
var target = document.getElementById(name);
if (!target) {
var a = document.getElementsByTagName("a");
for (var i = 0, m = a.length; i < m; i++)
{if (a[i].name === name) {target = a[i]; break}}
}
if (target) {
while (!target.scrollIntoView) {target = target.parentNode}
target = this.HashCheck(target);
if (target && target.scrollIntoView)
{setTimeout(function () {target.scrollIntoView(true)},1)}
}
}
},
HashCheck: function (target) {
if (target.isMathJax) {
var jax = MathJax.Hub.getJaxFor(target);
if (jax && MathJax.OutputJax[jax.outputJax].hashCheck)
{target = MathJax.OutputJax[jax.outputJax].hashCheck(target)}
}
return target;
},
//
// Load the Menu and Zoom code, if it hasn't already been loaded.
// This is called after the initial typeset, so should no longer be
// competing with other page loads, but will make these available
// if needed later on.
//
MenuZoom: function () {
if (!MathJax.Extension.MathMenu) {
setTimeout(
function () {
MathJax.Callback.Queue(
["Require",MathJax.Ajax,"[MathJax]/extensions/MathMenu.js",{}],
["loadDomain",MathJax.Localization,"MathMenu"]
)
},1000
);
} else {
setTimeout(
MathJax.Callback(["loadDomain",MathJax.Localization,"MathMenu"]),
1000
);
}
if (!MathJax.Extension.MathZoom) {
setTimeout(
MathJax.Callback(["Require",MathJax.Ajax,"[MathJax]/extensions/MathZoom.js",{}]),
2000
);
}
},
//
// Setup the onload callback
//
onLoad: function () {
var onload = this.onload =
MathJax.Callback(function () {MathJax.Hub.Startup.signal.Post("onLoad")});
if (document.body && document.readyState)
if (MathJax.Hub.Browser.isMSIE) {
// IE can change from loading to interactive before
// full page is ready, so go with complete (even though
// that means we may have to wait longer).
if (document.readyState === "complete") {return [onload]}
} else if (document.readyState !== "loading") {return [onload]}
if (window.addEventListener) {
window.addEventListener("load",onload,false);
if (!this.params.noDOMContentEvent)
{window.addEventListener("DOMContentLoaded",onload,false)}
}
else if (window.attachEvent) {window.attachEvent("onload",onload)}
else {window.onload = onload}
return onload;
},
//
// Perform the initial typesetting (or skip if configuration says to)
//
Typeset: function (element,callback) {
if (MathJax.Hub.config.skipStartupTypeset) {return function () {}}
return this.queue.Push(
["Post",this.signal,"Begin Typeset"],
["Typeset",MathJax.Hub,element,callback],
["Post",this.signal,"End Typeset"]
);
},
//
// Create a URL in the MathJax hierarchy
//
URL: function (dir,name) {
if (!name.match(/^([a-z]+:\/\/|\[|\/)/)) {name = "[MathJax]/"+dir+"/"+name}
return name;
},
//
// Load an array of files, waiting for all of them
// to be loaded before going on
//
loadArray: function (files,dir,name,synchronous) {
if (files) {
if (!(files instanceof Array)) {files = [files]}
if (files.length) {
var queue = MathJax.Callback.Queue(), callback = {}, file;
for (var i = 0, m = files.length; i < m; i++) {
file = this.URL(dir,files[i]);
if (name) {file += "/" + name}
if (synchronous) {queue.Push(["Require",MathJax.Ajax,file,callback])}
else {queue.Push(MathJax.Ajax.Require(file,callback))}
}
return queue.Push({}); // wait for everything to finish
}
}
return null;
}
};
/**********************************************************/
(function (BASENAME) {
var BASE = window[BASENAME], ROOT = "["+BASENAME+"]";
var HUB = BASE.Hub, AJAX = BASE.Ajax, CALLBACK = BASE.Callback;
var JAX = MathJax.Object.Subclass({
JAXFILE: "jax.js",
require: null, // array of files to load before jax.js is complete
config: {},
//
// Make a subclass and return an instance of it.
// (FIXME: should we replace config with a copy of the constructor's
// config? Otherwise all subclasses share the same config structure.)
//
Init: function (def,cdef) {
if (arguments.length === 0) {return this}
return (this.constructor.Subclass(def,cdef))();
},
//
// Augment by merging with class definition (not replacing)
//
Augment: function (def,cdef) {
var cObject = this.constructor, ndef = {};
if (def != null) {
for (var id in def) {if (def.hasOwnProperty(id)) {
if (typeof def[id] === "function")
{cObject.protoFunction(id,def[id])} else {ndef[id] = def[id]}
}}
// MSIE doesn't list toString even if it is not native so handle it separately
if (def.toString !== cObject.prototype.toString && def.toString !== {}.toString)
{cObject.protoFunction('toString',def.toString)}
}
HUB.Insert(cObject.prototype,ndef);
cObject.Augment(null,cdef);
return this;
},
Translate: function (script,state) {
throw Error(this.directory+"/"+this.JAXFILE+" failed to define the Translate() method");
},
Register: function (mimetype) {},
Config: function () {
this.config = HUB.CombineConfig(this.id,this.config);
if (this.config.Augment) {this.Augment(this.config.Augment)}
},
Startup: function () {},
loadComplete: function (file) {
if (file === "config.js") {
return AJAX.loadComplete(this.directory+"/"+file);
} else {
var queue = CALLBACK.Queue();
queue.Push(
HUB.Register.StartupHook("End Config",{}), // wait until config complete
["Post",HUB.Startup.signal,this.id+" Jax Config"],
["Config",this],
["Post",HUB.Startup.signal,this.id+" Jax Require"],
// Config may set the required and extensions array,
// so use functions to delay making the reference until needed
[function (THIS) {return MathJax.Hub.Startup.loadArray(THIS.require,this.directory)},this],
[function (config,id) {return MathJax.Hub.Startup.loadArray(config.extensions,"extensions/"+id)},this.config||{},this.id],
["Post",HUB.Startup.signal,this.id+" Jax Startup"],
["Startup",this],
["Post",HUB.Startup.signal,this.id+" Jax Ready"]
);
if (this.copyTranslate) {
queue.Push(
[function (THIS) {
THIS.preProcess = THIS.preTranslate;
THIS.Process = THIS.Translate;
THIS.postProcess = THIS.postTranslate;
},this.constructor.prototype]
);
}
return queue.Push(["loadComplete",AJAX,this.directory+"/"+file]);
}
}
},{
id: "Jax",
version: "2.3",
directory: ROOT+"/jax",
extensionDir: ROOT+"/extensions"
});
/***********************************/
BASE.InputJax = JAX.Subclass({
elementJax: "mml", // the element jax to load for this input jax
sourceMenuTitle: /*_(MathMenu)*/ ["Original","Original Form"],
copyTranslate: true,
Process: function (script,state) {
var queue = CALLBACK.Queue(), file;
// Load any needed element jax
var jax = this.elementJax; if (!(jax instanceof Array)) {jax = [jax]}
for (var i = 0, m = jax.length; i < m; i++) {
file = BASE.ElementJax.directory+"/"+jax[i]+"/"+this.JAXFILE;
if (!this.require) {this.require = []}
else if (!(this.require instanceof Array)) {this.require = [this.require]};
this.require.push(file); // so Startup will wait for it to be loaded
queue.Push(AJAX.Require(file));
}
// Load the input jax
file = this.directory+"/"+this.JAXFILE;
var load = queue.Push(AJAX.Require(file));
if (!load.called) {
this.constructor.prototype.Process = function () {
if (!load.called) {return load}
throw Error(file+" failed to load properly");
}
}
// Load the associated output jax
jax = HUB.outputJax["jax/"+jax[0]];
if (jax) {queue.Push(AJAX.Require(jax[0].directory+"/"+this.JAXFILE))}
return queue.Push({});
},
needsUpdate: function (jax) {
var script = jax.SourceElement();
return (jax.originalText !== BASE.HTML.getScript(script));
},
Register: function (mimetype) {
if (!HUB.inputJax) {HUB.inputJax = {}}
HUB.inputJax[mimetype] = this;
}
},{
id: "InputJax",
version: "2.3",
directory: JAX.directory+"/input",
extensionDir: JAX.extensionDir
});
/***********************************/
BASE.OutputJax = JAX.Subclass({
copyTranslate: true,
preProcess: function (state) {
var load, file = this.directory+"/"+this.JAXFILE;
this.constructor.prototype.preProcess = function (state) {
if (!load.called) {return load}
throw Error(file+" failed to load properly");
}
load = AJAX.Require(file);
return load;
},
Register: function (mimetype) {
var jax = HUB.outputJax;
if (!jax[mimetype]) {jax[mimetype] = []}
// If the output jax is earlier in the original configuration list, put it first here
if (jax[mimetype].length && (this.id === HUB.config.menuSettings.renderer ||
(jax.order[this.id]||0) < (jax.order[jax[mimetype][0].id]||0)))
{jax[mimetype].unshift(this)} else {jax[mimetype].push(this)}
// Make sure the element jax is loaded before Startup is called
if (!this.require) {this.require = []}
else if (!(this.require instanceof Array)) {this.require = [this.require]};
this.require.push(BASE.ElementJax.directory+"/"+(mimetype.split(/\//)[1])+"/"+this.JAXFILE);
},
Remove: function (jax) {}
},{
id: "OutputJax",
version: "2.3",
directory: JAX.directory+"/output",
extensionDir: JAX.extensionDir,
fontDir: ROOT+(BASE.isPacked?"":"/..")+"/fonts",
imageDir: ROOT+(BASE.isPacked?"":"/..")+"/images"
});
/***********************************/
BASE.ElementJax = JAX.Subclass({
// make a subclass, not an instance
Init: function (def,cdef) {return this.constructor.Subclass(def,cdef)},
inputJax: null,
outputJax: null,
inputID: null,
originalText: "",
mimeType: "",
sourceMenuTitle: /*_(MathMenu)*/ ["MathMLcode","MathML Code"],
Text: function (text,callback) {
var script = this.SourceElement();
BASE.HTML.setScript(script,text);
script.MathJax.state = this.STATE.UPDATE;
return HUB.Update(script,callback);
},
Reprocess: function (callback) {
var script = this.SourceElement();
script.MathJax.state = this.STATE.UPDATE;
return HUB.Reprocess(script,callback);
},
Update: function (callback) {return this.Rerender(callback)},
Rerender: function (callback) {
var script = this.SourceElement();
script.MathJax.state = this.STATE.OUTPUT;
return HUB.Process(script,callback);
},
Remove: function (keep) {
if (this.hover) {this.hover.clear(this)}
BASE.OutputJax[this.outputJax].Remove(this);
if (!keep) {
HUB.signal.Post(["Remove Math",this.inputID]); // wait for this to finish?
this.Detach();
}
},
needsUpdate: function () {
return BASE.InputJax[this.inputJax].needsUpdate(this);
},
SourceElement: function () {return document.getElementById(this.inputID)},
Attach: function (script,inputJax) {
var jax = script.MathJax.elementJax;
if (script.MathJax.state === this.STATE.UPDATE) {
jax.Clone(this);
} else {
jax = script.MathJax.elementJax = this;
if (script.id) {this.inputID = script.id}
else {script.id = this.inputID = BASE.ElementJax.GetID(); this.newID = 1}
}
jax.originalText = BASE.HTML.getScript(script);
jax.inputJax = inputJax;
if (jax.root) {jax.root.inputID = jax.inputID}
return jax;
},
Detach: function () {
var script = this.SourceElement(); if (!script) return;
try {delete script.MathJax} catch(err) {script.MathJax = null}
if (this.newID) {script.id = ""}
},
Clone: function (jax) {
var id;
for (id in this) {
if (!this.hasOwnProperty(id)) continue;
if (typeof(jax[id]) === 'undefined' && id !== 'newID') {delete this[id]}
}
for (id in jax) {
if (!jax.hasOwnProperty(id)) continue;
if (typeof(this[id]) === 'undefined' || (this[id] !== jax[id] && id !== 'inputID'))
{this[id] = jax[id]}
}
}
},{
id: "ElementJax",
version: "2.3",
directory: JAX.directory+"/element",
extensionDir: JAX.extensionDir,
ID: 0, // jax counter (for IDs)
STATE: {
PENDING: 1, // script is identified as math but not yet processed
PROCESSED: 2, // script has been processed
UPDATE: 3, // elementJax should be updated
OUTPUT: 4 // output should be updated (input is OK)
},
GetID: function () {this.ID++; return "MathJax-Element-"+this.ID},
Subclass: function () {
var obj = JAX.Subclass.apply(this,arguments);
obj.loadComplete = this.prototype.loadComplete;
return obj;
}
});
BASE.ElementJax.prototype.STATE = BASE.ElementJax.STATE;
//
// Some "Fake" jax used to allow menu access for "Math Processing Error" messages
//
BASE.OutputJax.Error = {
id: "Error", version: "2.3", config: {},
ContextMenu: function () {return BASE.Extension.MathEvents.Event.ContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)},
Mousedown: function () {return BASE.Extension.MathEvents.Event.AltContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)},
getJaxFromMath: function (math) {return (math.nextSibling.MathJax||{}).error},
Jax: function (text,script) {
var jax = MathJax.Hub.inputJax[script.type.replace(/ *;(.|\s)*/,"")];
return {
inputJax: (jax||{id:"Error"}).id, // Use Error InputJax as fallback
outputJax: "Error",
sourceMenuTitle: /*_(MathMenu)*/ ["ErrorMessage","Error Message"],
sourceMenuFormat: "Error",
originalText: MathJax.HTML.getScript(script),
errorText: text
}
}
};
BASE.InputJax.Error = {
id: "Error", version: "2.3", config: {},
sourceMenuTitle: /*_(MathMenu)*/ ["Original","Original Form"]
};
})("MathJax");
/**********************************************************/
(function (BASENAME) {
var BASE = window[BASENAME];
if (!BASE) {BASE = window[BASENAME] = {}}
var HUB = BASE.Hub; var STARTUP = HUB.Startup; var CONFIG = HUB.config;
var HEAD = document.getElementsByTagName("head")[0];
if (!HEAD) {HEAD = document.childNodes[0]};
var scripts = (document.documentElement || document).getElementsByTagName("script");
var namePattern = new RegExp("(^|/)"+BASENAME+"\\.js(\\?.*)?$");
for (var i = scripts.length-1; i >= 0; i--) {
if ((scripts[i].src||"").match(namePattern)) {
STARTUP.script = scripts[i].innerHTML;
if (RegExp.$2) {
var params = RegExp.$2.substr(1).split(/\&/);
for (var j = 0, m = params.length; j < m; j++) {
var KV = params[j].match(/(.*)=(.*)/);
if (KV) {STARTUP.params[unescape(KV[1])] = unescape(KV[2])}
}
}
CONFIG.root = scripts[i].src.replace(/(^|\/)[^\/]*(\?.*)?$/,'');
break;
}
}
BASE.Ajax.config = CONFIG;
var BROWSERS = {
isMac: (navigator.platform.substr(0,3) === "Mac"),
isPC: (navigator.platform.substr(0,3) === "Win"),
isMSIE: (window.ActiveXObject != null && window.clipboardData != null),
isFirefox: (navigator.userAgent.match(/Gecko/) != null &&
navigator.userAgent.match(/KHTML/) == null),
isSafari: (navigator.userAgent.match(/ (Apple)?WebKit\//) != null &&
(!window.chrome || window.chrome.loadTimes == null)),
isChrome: (window.chrome != null && window.chrome.loadTimes != null),
isOpera: (window.opera != null && window.opera.version != null),
isKonqueror: (window.hasOwnProperty && window.hasOwnProperty("konqueror") && navigator.vendor == "KDE"),
versionAtLeast: function (v) {
var bv = (this.version).split('.'); v = (new String(v)).split('.');
for (var i = 0, m = v.length; i < m; i++)
{if (bv[i] != v[i]) {return parseInt(bv[i]||"0") >= parseInt(v[i])}}
return true;
},
Select: function (choices) {
var browser = choices[HUB.Browser];
if (browser) {return browser(HUB.Browser)}
return null;
}
};
var AGENT = navigator.userAgent
.replace(/^Mozilla\/(\d+\.)+\d+ /,"") // remove initial Mozilla, which is never right
.replace(/[a-z][-a-z0-9._: ]+\/\d+[^ ]*-[^ ]*\.([a-z][a-z])?\d+ /i,"") // remove linux version
.replace(/Gentoo |Ubuntu\/(\d+\.)*\d+ (\([^)]*\) )?/,""); // special case for these
HUB.Browser = HUB.Insert(HUB.Insert(new String("Unknown"),{version: "0.0"}),BROWSERS);
for (var browser in BROWSERS) {if (BROWSERS.hasOwnProperty(browser)) {
if (BROWSERS[browser] && browser.substr(0,2) === "is") {
browser = browser.slice(2);
if (browser === "Mac" || browser === "PC") continue;
HUB.Browser = HUB.Insert(new String(browser),BROWSERS);
var VERSION = new RegExp(
".*(Version)/((?:\\d+\\.)+\\d+)|" + // for Safari and Opera10
".*("+browser+")"+(browser == "MSIE" ? " " : "/")+"((?:\\d+\\.)*\\d+)|"+ // for one of the main browser
"(?:^|\\(| )([a-z][-a-z0-9._: ]+|(?:Apple)?WebKit)/((?:\\d+\\.)+\\d+)"); // for unrecognized browser
var MATCH = VERSION.exec(AGENT) || ["","","","unknown","0.0"];
HUB.Browser.name = (MATCH[1] == "Version" ? browser : (MATCH[3] || MATCH[5]));
HUB.Browser.version = MATCH[2] || MATCH[4] || MATCH[6];
break;
}
}};
//
// Initial browser-specific info (e.g., touch up version or name)
//
HUB.Browser.Select({
Safari: function (browser) {
var v = parseInt((String(browser.version).split("."))[0]);
if (v > 85) {browser.webkit = browser.version}
if (v >= 534) {browser.version = "5.1"}
else if (v >= 533) {browser.version = "5.0"}
else if (v >= 526) {browser.version = "4.0"}
else if (v >= 525) {browser.version = "3.1"}
else if (v > 500) {browser.version = "3.0"}
else if (v > 400) {browser.version = "2.0"}
else if (v > 85) {browser.version = "1.0"}
browser.isMobile = (navigator.appVersion.match(/Mobile/i) != null);
browser.noContextMenu = browser.isMobile;
},
Firefox: function (browser) {
if ((browser.version === "0.0" || navigator.userAgent.match(/Firefox/) == null) &&
navigator.product === "Gecko") {
var rv = navigator.userAgent.match(/[\/ ]rv:(\d+\.\d.*?)[\) ]/);
if (rv) {browser.version = rv[1]}
else {
var date = (navigator.buildID||navigator.productSub||"0").substr(0,8);
if (date >= "20111220") {browser.version = "9.0"}
else if (date >= "20111120") {browser.version = "8.0"}
else if (date >= "20110927") {browser.version = "7.0"}
else if (date >= "20110816") {browser.version = "6.0"}
else if (date >= "20110621") {browser.version = "5.0"}
else if (date >= "20110320") {browser.version = "4.0"}
else if (date >= "20100121") {browser.version = "3.6"}
else if (date >= "20090630") {browser.version = "3.5"}
else if (date >= "20080617") {browser.version = "3.0"}
else if (date >= "20061024") {browser.version = "2.0"}
}
}
browser.isMobile = (navigator.appVersion.match(/Android/i) != null ||
navigator.userAgent.match(/ Fennec\//) != null ||
navigator.userAgent.match(/Mobile/) != null);
},
Opera: function (browser) {browser.version = opera.version()},
MSIE: function (browser) {
browser.isIE9 = !!(document.documentMode && (window.performance || window.msPerformance));
MathJax.HTML.setScriptBug = !browser.isIE9 || document.documentMode < 9;
var MathPlayer = false;
try {new ActiveXObject("MathPlayer.Factory.1"); browser.hasMathPlayer = MathPlayer = true}
catch (err) {}
try {
if (MathPlayer && !STARTUP.params.NoMathPlayer) {
var mathplayer = document.createElement("object");
mathplayer.id = "mathplayer"; mathplayer.classid = "clsid:32F66A20-7614-11D4-BD11-00104BD3F987";
document.getElementsByTagName("head")[0].appendChild(mathplayer);
document.namespaces.add("m","http://www.w3.org/1998/Math/MathML");
browser.mpNamespace = true;
if (document.readyState && (document.readyState === "loading" ||
document.readyState === "interactive")) {
document.write('<?import namespace="m" implementation="#MathPlayer">');
browser.mpImported = true;
}
} else {
// Adding any namespace avoids a crash in IE9 in IE9-standards mode
// (any reference to document.namespaces before document.readyState is
// "complete" causes an "unspecified error" to be thrown)
document.namespaces.add("mjx_IE_fix","http://www.w3.org/1999/xlink");
}
} catch (err) {}
}
});
HUB.Browser.Select(MathJax.Message.browsers);
if (BASE.AuthorConfig && typeof BASE.AuthorConfig.AuthorInit === "function") {BASE.AuthorConfig.AuthorInit()}
HUB.queue = BASE.Callback.Queue();
HUB.queue.Push(
["Post",STARTUP.signal,"Begin"],
["Config",STARTUP],
["Cookie",STARTUP],
["Styles",STARTUP],
["Message",STARTUP],
function () {
// Do Jax and Extensions in parallel, but wait for them all to complete
var queue = BASE.Callback.Queue(
STARTUP.Jax(),
STARTUP.Extensions()
);
return queue.Push({});
},
["Menu",STARTUP],
STARTUP.onLoad(),
function () {MathJax.isReady = true}, // indicates that MathJax is ready to process math
["Typeset",STARTUP],
["Hash",STARTUP],
["MenuZoom",STARTUP],
["Post",STARTUP.signal,"End"]
);
})("MathJax");
}}