www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

MathJax.js (125453B)


      1 /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 
      4 /*************************************************************
      5  *
      6  *  MathJax.js
      7  *  
      8  *  The main support code for the MathJax Hub, including the
      9  *  Ajax, Callback, Messaging, and Object-Oriented Programming
     10  *  libraries, as well as the base Jax classes, and startup
     11  *  processing code.
     12  *  
     13  *  ---------------------------------------------------------------------
     14  *  
     15  *  Copyright (c) 2009-2015 The MathJax Consortium
     16  * 
     17  *  Licensed under the Apache License, Version 2.0 (the "License");
     18  *  you may not use this file except in compliance with the License.
     19  *  You may obtain a copy of the License at
     20  * 
     21  *      http://www.apache.org/licenses/LICENSE-2.0
     22  * 
     23  *  Unless required by applicable law or agreed to in writing, software
     24  *  distributed under the License is distributed on an "AS IS" BASIS,
     25  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     26  *  See the License for the specific language governing permissions and
     27  *  limitations under the License.
     28  */
     29 
     30 
     31 //
     32 //  Check if browser can support MathJax (no one fails this nowadays)
     33 //
     34 if (document.getElementById && document.childNodes && document.createElement) {
     35 //
     36 //  Skip if MathJax is already loaded
     37 //
     38 if (!(window.MathJax && MathJax.Hub)) {
     39   
     40 //
     41 //  Get author configuration from MathJax variable, if any
     42 //
     43 if (window.MathJax) {window.MathJax = {AuthorConfig: window.MathJax}}
     44                else {window.MathJax = {}}
     45 
     46 // MathJax.isPacked = true; // This line is uncommented by the packer.
     47 
     48 MathJax.version = "2.6.1";
     49 MathJax.fileversion = "2.6.1";
     50 MathJax.cdnVersion = "2.6.1";  // specifies a revision to break caching
     51 MathJax.cdnFileVersions = {};  // can be used to specify revisions for individual files
     52 
     53 /**********************************************************/
     54 
     55 (function (BASENAME) {
     56   var BASE = window[BASENAME];
     57   if (!BASE) {BASE = window[BASENAME] = {}}
     58 
     59   var PROTO = [];  // a static object used to indicate when a prototype is being created
     60   var OBJECT = function (def) {
     61     var obj = def.constructor; if (!obj) {obj = function () {}}
     62     for (var id in def) {if (id !== 'constructor' && def.hasOwnProperty(id)) {obj[id] = def[id]}}
     63     return obj;
     64   };
     65   var CONSTRUCTOR = function () {
     66     return function () {return arguments.callee.Init.call(this,arguments)};
     67   };
     68 
     69   BASE.Object = OBJECT({
     70     constructor: CONSTRUCTOR(),
     71     
     72     Subclass: function (def,classdef) {
     73       var obj = CONSTRUCTOR();
     74       obj.SUPER = this; obj.Init = this.Init;
     75       obj.Subclass = this.Subclass; obj.Augment = this.Augment;
     76       obj.protoFunction = this.protoFunction;
     77       obj.can = this.can; obj.has = this.has; obj.isa = this.isa;
     78       obj.prototype = new this(PROTO);
     79       obj.prototype.constructor = obj;  // the real constructor
     80       obj.Augment(def,classdef);
     81       return obj;
     82     },
     83   
     84     Init: function (args) {
     85       var obj = this;
     86       if (args.length === 1 && args[0] === PROTO) {return obj}
     87       if (!(obj instanceof args.callee)) {obj = new args.callee(PROTO)}
     88       return obj.Init.apply(obj,args) || obj;
     89     },
     90     
     91     Augment: function (def,classdef) {
     92       var id;
     93       if (def != null) {
     94         for (id in def) {if (def.hasOwnProperty(id)) {this.protoFunction(id,def[id])}}
     95         // MSIE doesn't list toString even if it is not native so handle it separately
     96         if (def.toString !== this.prototype.toString && def.toString !== {}.toString)
     97           {this.protoFunction('toString',def.toString)}
     98       }
     99       if (classdef != null) {
    100         for (id in classdef) {if (classdef.hasOwnProperty(id)) {this[id] = classdef[id]}}
    101       }
    102       return this;
    103     },
    104   
    105     protoFunction: function (id,def) {
    106       this.prototype[id] = def;
    107       if (typeof def === "function") {def.SUPER = this.SUPER.prototype}
    108     },
    109   
    110     prototype: {
    111       Init: function () {},
    112       SUPER: function (fn) {return fn.callee.SUPER},
    113       can: function (method) {return typeof(this[method]) === "function"},
    114       has: function (property) {return typeof(this[property]) !== "undefined"},
    115       isa: function (obj) {return (obj instanceof Object) && (this instanceof obj)}
    116     },
    117   
    118     can: function (method)   {return this.prototype.can.call(this,method)},
    119     has: function (property) {return this.prototype.has.call(this,property)},
    120     isa: function (obj) {
    121       var constructor = this;
    122       while (constructor) {
    123         if (constructor === obj) {return true} else {constructor = constructor.SUPER}
    124       }
    125       return false;
    126     },
    127 
    128 
    129     SimpleSUPER: OBJECT({
    130       constructor: function (def) {return this.SimpleSUPER.define(def)},
    131 
    132       define: function (src) {
    133 	var dst = {};
    134 	if (src != null) {
    135           for (var id in src) {if (src.hasOwnProperty(id)) {dst[id] = this.wrap(id,src[id])}}
    136 	  // MSIE doesn't list toString even if it is not native so handle it separately
    137           if (src.toString !== this.prototype.toString && src.toString !== {}.toString)
    138             {dst.toString = this.wrap('toString',src.toString)}
    139 	}
    140 	return dst;
    141       },
    142 
    143       wrap: function (id,f) {
    144         if (typeof(f) !== 'function' || !f.toString().match(/\.\s*SUPER\s*\(/)) {return f}
    145         var fn = function () {
    146           this.SUPER = fn.SUPER[id];
    147           try {var result = f.apply(this,arguments)} catch (err) {delete this.SUPER; throw err}
    148           delete this.SUPER;
    149           return result;
    150         }
    151         fn.toString = function () {return f.toString.apply(f,arguments)}
    152         return fn;
    153       }
    154 
    155     })
    156   });
    157 
    158 })("MathJax");
    159 
    160 /**********************************************************/
    161 
    162 /*
    163  *  Create a callback function from various forms of data:
    164  *  
    165  *     MathJax.Callback(fn)    -- callback to a function
    166  *
    167  *     MathJax.Callback([fn])  -- callback to function
    168  *     MathJax.Callback([fn,data...])
    169  *                             -- callback to function with given data as arguments
    170  *     MathJax.Callback([object,fn])
    171  *                             -- call fn with object as "this"
    172  *     MathJax.Callback([object,fn,data...])
    173  *                             -- call fn with object as "this" and data as arguments
    174  *     MathJax.Callback(["method",object])
    175  *                             -- call method of object wth object as "this"
    176  *     MathJax.Callback(["method",object,data...])
    177  *                             -- as above, but with data as arguments to method
    178  *
    179  *     MathJax.Callback({hook: fn, data: [...], object: this})
    180  *                             -- give function, data, and object to act as "this" explicitly
    181  *
    182  *     MathJax.Callback("code")  -- callback that compiles and executes a string
    183  *
    184  *     MathJax.Callback([...],i)
    185  *                             -- use slice of array starting at i and interpret
    186  *                                result as above.  (Used for passing "arguments" array
    187  *                                and trimming initial arguments, if any.)
    188  */
    189 
    190 /*
    191  *    MathJax.Callback.After([...],cb1,cb2,...)
    192  *                             -- make a callback that isn't called until all the other
    193  *                                ones are called first.  I.e., wait for a union of
    194  *                                callbacks to occur before making the given callback.
    195  */
    196 
    197 /*
    198  *  MathJax.Callback.Queue([callback,...])
    199  *                             -- make a synchronized queue of commands that process
    200  *                                sequentially, waiting for those that return uncalled
    201  *                                callbacks.
    202  */
    203 
    204 /*
    205  *  MathJax.Callback.Signal(name)
    206  *                             -- finds or creates a names signal, to which listeners
    207  *                                can be attached and are signaled by messages posted
    208  *                                to the signal.  Responses can be asynchronous.
    209  */
    210 
    211 (function (BASENAME) {
    212   var BASE = window[BASENAME];
    213   if (!BASE) {BASE = window[BASENAME] = {}}
    214   //
    215   //  Create a callback from an associative array
    216   //
    217   var CALLBACK = function (data) {
    218     var cb = function () {return arguments.callee.execute.apply(arguments.callee,arguments)};
    219     for (var id in CALLBACK.prototype) {
    220       if (CALLBACK.prototype.hasOwnProperty(id)) {
    221         if (typeof(data[id]) !== 'undefined') {cb[id] = data[id]}
    222                                          else {cb[id] = CALLBACK.prototype[id]}
    223       }
    224     }
    225     cb.toString = CALLBACK.prototype.toString;
    226     return cb;
    227   };
    228   CALLBACK.prototype = {
    229     isCallback: true,
    230     hook: function () {},
    231     data: [],
    232     object: window,
    233     execute: function () {
    234       if (!this.called || this.autoReset) {
    235         this.called = !this.autoReset;
    236         return this.hook.apply(this.object,this.data.concat([].slice.call(arguments,0)));
    237       }
    238     },
    239     reset: function () {delete this.called},
    240     toString: function () {return this.hook.toString.apply(this.hook,arguments)}
    241   };
    242   var ISCALLBACK = function (f) {
    243     return (typeof(f) === "function" && f.isCallback);
    244   }
    245 
    246   //
    247   //  Evaluate a string in global context
    248   //
    249   var EVAL = function (code) {return eval.call(window,code)}
    250   var TESTEVAL = function () {
    251     EVAL("var __TeSt_VaR__ = 1"); // check if it works in global context
    252     if (window.__TeSt_VaR__) {
    253       try { delete window.__TeSt_VaR__; } // NOTE IE9 throws when in IE7 mode
    254       catch (error) { window.__TeSt_VaR__ = null; } 
    255     } else {
    256       if (window.execScript) {
    257         // IE
    258         EVAL = function (code) {
    259           BASE.__code = code;
    260           code = "try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}";
    261           window.execScript(code);
    262           var result = BASE.__result; delete BASE.__result; delete BASE.__code;
    263           if (result instanceof Error) {throw result}
    264           return result;
    265         }
    266       } else {
    267         // Safari2
    268         EVAL = function (code) {
    269           BASE.__code = code;
    270           code = "try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}";
    271           var head = (document.getElementsByTagName("head"))[0]; if (!head) {head = document.body}
    272           var script = document.createElement("script");
    273           script.appendChild(document.createTextNode(code));
    274           head.appendChild(script); head.removeChild(script);
    275           var result = BASE.__result; delete BASE.__result; delete BASE.__code;
    276           if (result instanceof Error) {throw result}
    277           return result;
    278         }
    279       }
    280     }
    281     TESTEVAL = null;
    282   }
    283 
    284   //
    285   //  Create a callback from various types of data
    286   //
    287   var USING = function (args,i) {
    288     if (arguments.length > 1) {
    289       if (arguments.length === 2 && !(typeof arguments[0] === 'function') &&
    290           arguments[0] instanceof Object && typeof arguments[1] === 'number')
    291             {args = [].slice.call(args,i)}
    292       else {args = [].slice.call(arguments,0)}
    293     }
    294     if (args instanceof Array && args.length === 1) {args = args[0]}
    295     if (typeof args === 'function') {
    296       if (args.execute === CALLBACK.prototype.execute) {return args}
    297       return CALLBACK({hook: args});
    298     } else if (args instanceof Array) {
    299       if (typeof(args[0]) === 'string' && args[1] instanceof Object &&
    300                  typeof args[1][args[0]] === 'function') {
    301         return CALLBACK({hook: args[1][args[0]], object: args[1], data: args.slice(2)});
    302       } else if (typeof args[0] === 'function') {
    303         return CALLBACK({hook: args[0], data: args.slice(1)});
    304       } else if (typeof args[1] === 'function') {
    305         return CALLBACK({hook: args[1], object: args[0], data: args.slice(2)});
    306       }
    307     } else if (typeof(args) === 'string') {
    308       if (TESTEVAL) TESTEVAL();
    309       return CALLBACK({hook: EVAL, data: [args]});
    310     } else if (args instanceof Object) {
    311       return CALLBACK(args);
    312     } else if (typeof(args) === 'undefined') {
    313       return CALLBACK({});
    314     }
    315     throw Error("Can't make callback from given data");
    316   };
    317   
    318   //
    319   //  Wait for a given time to elapse and then perform the callback
    320   //
    321   var DELAY = function (time,callback) {
    322     callback = USING(callback);
    323     callback.timeout = setTimeout(callback,time);
    324     return callback;
    325   };
    326 
    327   //
    328   //  Callback used by AFTER, QUEUE, and SIGNAL to check if calls have completed
    329   //
    330   var WAITFOR = function (callback,signal) {
    331     callback = USING(callback);
    332     if (!callback.called) {WAITSIGNAL(callback,signal); signal.pending++}
    333   };
    334   var WAITEXECUTE = function () {
    335     var signals = this.signal; delete this.signal;
    336     this.execute = this.oldExecute; delete this.oldExecute;
    337     var result = this.execute.apply(this,arguments);
    338     if (ISCALLBACK(result) && !result.called) {WAITSIGNAL(result,signals)} else {
    339       for (var i = 0, m = signals.length; i < m; i++) {
    340         signals[i].pending--;
    341         if (signals[i].pending <= 0) {signals[i].call()}
    342       }
    343     }
    344   };
    345   var WAITSIGNAL = function (callback,signals) {
    346     if (!(signals instanceof Array)) {signals = [signals]}
    347     if (!callback.signal) {
    348       callback.oldExecute = callback.execute;
    349       callback.execute = WAITEXECUTE;
    350       callback.signal = signals;
    351     } else if (signals.length === 1) {callback.signal.push(signals[0])}
    352       else {callback.signal = callback.signal.concat(signals)}
    353   };
    354 
    355   //
    356   //  Create a callback that is called when a collection of other callbacks have
    357   //  all been executed.  If the callback gets called immediately (i.e., the
    358   //  others are all already called), check if it returns another callback
    359   //  and return that instead.
    360   //
    361   var AFTER = function (callback) {
    362     callback = USING(callback);
    363     callback.pending = 0;
    364     for (var i = 1, m = arguments.length; i < m; i++)
    365       {if (arguments[i]) {WAITFOR(arguments[i],callback)}}
    366     if (callback.pending === 0) {
    367       var result = callback();
    368       if (ISCALLBACK(result)) {callback = result}
    369     }
    370     return callback;
    371   };
    372 
    373   //
    374   //  An array of prioritized hooks that are executed sequentially
    375   //  with a given set of data.
    376   //
    377   var HOOKS = MathJax.Object.Subclass({
    378     //
    379     //  Initialize the array and the auto-reset status
    380     //
    381     Init: function (reset) {
    382       this.hooks = [];
    383       this.remove = []; // used when hooks are removed during execution of list
    384       this.reset = reset;
    385       this.running = false;
    386     },
    387     //
    388     //  Add a callback to the list, in priority order (default priority is 10)
    389     //
    390     Add: function (hook,priority) {
    391       if (priority == null) {priority = 10}
    392       if (!ISCALLBACK(hook)) {hook = USING(hook)}
    393       hook.priority = priority;
    394       var i = this.hooks.length;
    395       while (i > 0 && priority < this.hooks[i-1].priority) {i--}
    396       this.hooks.splice(i,0,hook);
    397       return hook;
    398     },
    399     Remove: function (hook) {
    400       for (var i = 0, m = this.hooks.length; i < m; i++) {
    401         if (this.hooks[i] === hook) {
    402           if (this.running) {this.remove.push(i)}
    403             else {this.hooks.splice(i,1)}
    404           return;
    405         }
    406       }
    407     },
    408     //
    409     //  Execute the list of callbacks, resetting them if requested.
    410     //  If any return callbacks, return a callback that will be 
    411     //  executed when they all have completed.
    412     //  Remove any hooks that requested being removed during processing.
    413     //
    414     Execute: function () {
    415       var callbacks = [{}];
    416       this.running = true;
    417       for (var i = 0, m = this.hooks.length; i < m; i++) {
    418         if (this.reset) {this.hooks[i].reset()}
    419         var result = this.hooks[i].apply(window,arguments);
    420         if (ISCALLBACK(result) && !result.called) {callbacks.push(result)}
    421       }
    422       this.running = false;
    423       if (this.remove.length) {this.RemovePending()}
    424       if (callbacks.length === 1) {return null}
    425       if (callbacks.length === 2) {return callbacks[1]}
    426       return AFTER.apply({},callbacks);
    427     },
    428     //
    429     //  Remove hooks that asked to be removed during execution of list
    430     //
    431     RemovePending: function () {
    432       this.remove = this.remove.sort();
    433       for (var i = this.remove.length-1; i >= 0; i--) {this.hooks.splice(i,1)}
    434       this.remove = [];
    435     }
    436 
    437   });
    438   
    439   //
    440   //  Run an array of callbacks passing them the given data.
    441   //  (Legacy function, since this has been replaced by the HOOKS object).
    442   //
    443   var EXECUTEHOOKS = function (hooks,data,reset) {
    444     if (!hooks) {return null}
    445     if (!(hooks instanceof Array)) {hooks = [hooks]}
    446     if (!(data instanceof Array))  {data = (data == null ? [] : [data])}
    447     var handler = HOOKS(reset);
    448     for (var i = 0, m = hooks.length; i < m; i++) {handler.Add(hooks[i])}
    449     return handler.Execute.apply(handler,data);
    450   };
    451    
    452   //
    453   //  Command queue that performs commands in order, waiting when
    454   //  necessary for commands to complete asynchronousely
    455   //
    456   var QUEUE = BASE.Object.Subclass({
    457     //
    458     //  Create the queue and push any commands that are specified
    459     //
    460     Init: function () {
    461       this.pending = this.running = 0;
    462       this.queue = [];
    463       this.Push.apply(this,arguments);
    464     },
    465     //
    466     //  Add commands to the queue and run them. Adding a callback object
    467     //  (rather than a callback specification) queues a wait for that callback.
    468     //  Return the final callback for synchronization purposes.
    469     //
    470     Push: function () {
    471       var callback;
    472       for (var i = 0, m = arguments.length; i < m; i++) {
    473         callback = USING(arguments[i]);
    474         if (callback === arguments[i] && !callback.called)
    475           {callback = USING(["wait",this,callback])}
    476         this.queue.push(callback);
    477       }
    478       if (!this.running && !this.pending) {this.Process()}
    479       return callback;
    480     },
    481     //
    482     //  Process the command queue if we aren't waiting on another command
    483     //
    484     Process: function (queue) {
    485       while (!this.running && !this.pending && this.queue.length) {
    486         var callback = this.queue[0];
    487         queue = this.queue.slice(1); this.queue = [];
    488         this.Suspend(); var result = callback(); this.Resume();
    489         if (queue.length) {this.queue = queue.concat(this.queue)}
    490         if (ISCALLBACK(result) && !result.called) {WAITFOR(result,this)}
    491       }
    492     },
    493     //
    494     //  Suspend/Resume command processing on this queue
    495     //
    496     Suspend: function () {this.running++},
    497     Resume: function () {if (this.running) {this.running--}},
    498     //
    499     //  Used by WAITFOR to restart the queue when an action completes
    500     //
    501     call: function () {this.Process.apply(this,arguments)},
    502     wait: function (callback) {return callback}
    503   });
    504   
    505   //
    506   //  Create a named signal that listeners can attach to, to be signaled by
    507   //  postings made to the signal.  Posts are queued if they occur while one
    508   //  is already in process.
    509   //
    510   var SIGNAL = QUEUE.Subclass({
    511     Init: function (name) {
    512       QUEUE.prototype.Init.call(this);
    513       this.name = name;
    514       this.posted = [];              // the messages posted so far
    515       this.listeners = HOOKS(true);  // those with interest in this signal
    516       this.posting = false;
    517       this.callback = null;
    518     },
    519     //
    520     // Post a message to the signal listeners, with callback for when complete
    521     //
    522     Post: function (message,callback,forget) {
    523       callback = USING(callback);
    524       if (this.posting || this.pending) {
    525         this.Push(["Post",this,message,callback,forget]);
    526       } else {
    527         this.callback = callback; callback.reset();
    528         if (!forget) {this.posted.push(message)}
    529         this.Suspend(); this.posting = true;
    530         var result = this.listeners.Execute(message);
    531         if (ISCALLBACK(result) && !result.called) {WAITFOR(result,this)}
    532         this.Resume(); this.posting = false;
    533         if (!this.pending) {this.call()}
    534       }
    535       return callback;
    536     },
    537     //
    538     //  Clear the post history (so new listeners won't get old messages)
    539     //
    540     Clear: function (callback) {
    541       callback = USING(callback);
    542       if (this.posting || this.pending) {
    543         callback = this.Push(["Clear",this,callback]);
    544       } else {
    545         this.posted = [];
    546         callback();
    547       }
    548       return callback;
    549     },
    550     //
    551     //  Call the callback (all replies are in) and process the command queue
    552     //
    553     call: function () {this.callback(this); this.Process()},
    554     
    555     //
    556     //  A listener calls this to register interest in the signal (so it will be called
    557     //  when posts occur).  If ignorePast is true, it will not be sent the post history.
    558     //
    559     Interest: function (callback,ignorePast,priority) {
    560       callback = USING(callback);
    561       this.listeners.Add(callback,priority);
    562       if (!ignorePast) {
    563         for (var i = 0, m = this.posted.length; i < m; i++) {
    564           callback.reset();
    565           var result = callback(this.posted[i]);
    566           if (ISCALLBACK(result) && i === this.posted.length-1) {WAITFOR(result,this)}
    567         }
    568       }
    569       return callback;
    570     },
    571     //
    572     //  A listener calls this to remove itself from a signal
    573     //
    574     NoInterest: function (callback) {
    575       this.listeners.Remove(callback);
    576     },
    577     
    578     //
    579     //  Hook a callback to a particular message on this signal
    580     //
    581     MessageHook: function (msg,callback,priority) {
    582       callback = USING(callback);
    583       if (!this.hooks) {this.hooks = {}; this.Interest(["ExecuteHooks",this])}
    584       if (!this.hooks[msg]) {this.hooks[msg] = HOOKS(true)}
    585       this.hooks[msg].Add(callback,priority);
    586       for (var i = 0, m = this.posted.length; i < m; i++)
    587         {if (this.posted[i] == msg) {callback.reset(); callback(this.posted[i])}}
    588       callback.msg = msg; // keep track so we can remove it
    589       return callback;
    590     },
    591     //
    592     //  Execute the message hooks for the given message
    593     //
    594     ExecuteHooks: function (msg) {
    595       var type = ((msg instanceof Array) ? msg[0] : msg);
    596       if (!this.hooks[type]) {return null}
    597       return this.hooks[type].Execute(msg);
    598     },
    599     //
    600     //  Remove a hook safely
    601     //
    602     RemoveHook: function (hook) {
    603       this.hooks[hook.msg].Remove(hook);
    604     }
    605     
    606   },{
    607     signals: {},  // the named signals
    608     find: function (name) {
    609       if (!SIGNAL.signals[name]) {SIGNAL.signals[name] = new SIGNAL(name)}
    610       return SIGNAL.signals[name];
    611     }
    612   });
    613   
    614   //
    615   //  The main entry-points
    616   //
    617   BASE.Callback = BASE.CallBack = USING;
    618   BASE.Callback.Delay = DELAY;
    619   BASE.Callback.After = AFTER;
    620   BASE.Callback.Queue = QUEUE;
    621   BASE.Callback.Signal = SIGNAL.find;
    622   BASE.Callback.Hooks = HOOKS;
    623   BASE.Callback.ExecuteHooks = EXECUTEHOOKS;
    624 })("MathJax");
    625 
    626 
    627 /**********************************************************/
    628 
    629 (function (BASENAME) {
    630   var BASE = window[BASENAME];
    631   if (!BASE) {BASE = window[BASENAME] = {}}
    632   
    633   var isSafari2 = (navigator.vendor === "Apple Computer, Inc." &&
    634                    typeof navigator.vendorSub === "undefined");
    635   var sheets = 0; // used by Safari2
    636 
    637   //
    638   //  Update sheets count and look up the head object
    639   //  
    640   var HEAD = function (head) {
    641     if (document.styleSheets && document.styleSheets.length > sheets)
    642       {sheets = document.styleSheets.length}
    643     if (!head) {
    644       head = document.head || ((document.getElementsByTagName("head"))[0]);
    645       if (!head) {head = document.body}
    646     }
    647     return head;
    648   };
    649   
    650   //
    651   //  Remove scripts that are completed so they don't clutter up the HEAD.
    652   //  This runs via setTimeout since IE7 can't remove the script while it is running.
    653   //
    654   var SCRIPTS = [];  // stores scripts to be removed after a delay
    655   var REMOVESCRIPTS = function () {
    656     for (var i = 0, m = SCRIPTS.length; i < m; i++) {BASE.Ajax.head.removeChild(SCRIPTS[i])}
    657     SCRIPTS = [];
    658   };
    659   
    660   var PATH = {};
    661   PATH[BASENAME] = "";  // empty path gets the root URL
    662   
    663   BASE.Ajax = {
    664     loaded: {},         // files already loaded
    665     loading: {},        // files currently in process of loading
    666     loadHooks: {},      // hooks to call when files are loaded
    667     timeout: 15*1000,   // timeout for loading of files (15 seconds)
    668     styleDelay: 1,      // delay to use before styles are available
    669     config: {
    670       root: "",         // URL of root directory to load from
    671       path: PATH        // paths to named URL's (e.g., [MathJax]/...)
    672     },
    673 
    674     STATUS: {
    675       OK: 1,         // file is loading or did load OK
    676       ERROR: -1      // file timed out during load
    677     },
    678 
    679     //
    680     //  Return a complete URL to a file (replacing any root names)
    681     //
    682     fileURL: function (file) {
    683       var match = file.match(/^\[([-._a-z0-9]+)\]/i);
    684       if (match && match[1] in PATH)
    685         {file = (PATH[match[1]]||this.config.root) + file.substr(match[1].length+2)}
    686       return file;
    687     },
    688     //
    689     //  Replace root names if URL includes one
    690     //
    691     fileName: function (url) {
    692       var root = this.config.root;
    693       if (url.substr(0,root.length) === root) {url = "["+BASENAME+"]"+url.substr(root.length)}
    694       else {
    695         for (var id in PATH) {if (PATH.hasOwnProperty(id) && PATH[id]) {
    696           if (url.substr(0,PATH[id].length) === PATH[id])
    697             {url = "["+id+"]"+url.substr(PATH[id].length); break}
    698         }}
    699       }
    700       return url;
    701     },
    702     //
    703     //  Cache-breaking revision number for file
    704     //
    705     fileRev: function (file) {
    706       var rev = BASE.cdnFileVersions[name] || BASE.cdnVersion;
    707       if (rev) {rev = "?rev="+rev}
    708       return rev;
    709     },
    710     urlRev: function (file) {return this.fileURL(file)+this.fileRev(file)},
    711     
    712     //
    713     //  Load a file if it hasn't been already.
    714     //  Make sure the file URL is "safe"?
    715     //
    716     Require: function (file,callback) {
    717       callback = BASE.Callback(callback); var type;
    718       if (file instanceof Object) {
    719         for (var i in file)
    720           {if (file.hasOwnProperty(i)) {type = i.toUpperCase(); file = file[i]}}
    721       } else {type = file.split(/\./).pop().toUpperCase()}
    722       file = this.fileURL(file);
    723       // FIXME: check that URL is OK
    724       if (this.loaded[file]) {
    725         callback(this.loaded[file]);
    726       } else {
    727         var FILE = {}; FILE[type] = file;
    728         this.Load(FILE,callback);
    729       }
    730       return callback;
    731     },
    732 
    733     //
    734     //  Load a file regardless of where it is and whether it has
    735     //  already been loaded.
    736     //
    737     Load: function (file,callback) {
    738       callback = BASE.Callback(callback); var type;
    739       if (file instanceof Object) {
    740         for (var i in file)
    741           {if (file.hasOwnProperty(i)) {type = i.toUpperCase(); file = file[i]}}
    742       } else {type = file.split(/\./).pop().toUpperCase()}
    743       file = this.fileURL(file);
    744       if (this.loading[file]) {
    745         this.addHook(file,callback);
    746       } else {
    747         this.head = HEAD(this.head);
    748         if (this.loader[type]) {this.loader[type].call(this,file,callback)}
    749           else {throw Error("Can't load files of type "+type)}
    750       }
    751       return callback;
    752     },
    753     
    754     //
    755     //  Register a load hook for a particular file (it will be called when
    756     //  loadComplete() is called for that file)
    757     //
    758     LoadHook: function (file,callback,priority) {
    759       callback = BASE.Callback(callback);
    760       if (file instanceof Object)
    761         {for (var i in file) {if (file.hasOwnProperty(i)) {file = file[i]}}}
    762       file = this.fileURL(file);
    763       if (this.loaded[file]) {callback(this.loaded[file])}
    764         else {this.addHook(file,callback,priority)}
    765       return callback;
    766     },
    767     addHook: function (file,callback,priority) {
    768       if (!this.loadHooks[file]) {this.loadHooks[file] = MathJax.Callback.Hooks()}
    769       this.loadHooks[file].Add(callback,priority);
    770       callback.file = file;
    771     },
    772     removeHook: function (hook) {
    773       if (this.loadHooks[hook.file]) {
    774         this.loadHooks[hook.file].Remove(hook);
    775         if (!this.loadHooks[hook.file].hooks.length) {delete this.loadHooks[hook.file]}
    776       }
    777     },
    778     
    779     //
    780     //  Used when files are combined in a preloading configuration file
    781     //
    782     Preloading: function () {
    783       for (var i = 0, m = arguments.length; i < m; i++) {
    784         var file = this.fileURL(arguments[i]);
    785         if (!this.loading[file]) {this.loading[file] = {preloaded: true}}
    786       }
    787     },
    788     
    789     //
    790     //  Code used to load the various types of files
    791     //  (JS for JavaScript, CSS for style sheets)
    792     //
    793     loader: {
    794       //
    795       //  Create a SCRIPT tag to load the file
    796       //
    797       JS: function (file,callback) {
    798         var name = this.fileName(file);
    799         var script = document.createElement("script");
    800         var timeout = BASE.Callback(["loadTimeout",this,file]);
    801         this.loading[file] = {
    802           callback: callback,
    803           timeout: setTimeout(timeout,this.timeout),
    804           status: this.STATUS.OK,
    805           script: script
    806         };
    807         //
    808         // Add this to the structure above after it is created to prevent recursion
    809         //  when loading the initial localization file (before loading messsage is available)
    810         //
    811         this.loading[file].message = BASE.Message.File(name);
    812         script.onerror = timeout;  // doesn't work in IE and no apparent substitute
    813         script.type = "text/javascript";
    814         script.src = file+this.fileRev(name);
    815         this.head.appendChild(script);
    816       },
    817       //
    818       //  Create a LINK tag to load the style sheet
    819       //
    820       CSS: function (file,callback) {
    821         var name = this.fileName(file);
    822         var link = document.createElement("link");
    823         link.rel = "stylesheet"; link.type = "text/css";
    824         link.href = file+this.fileRev(name);
    825         this.loading[file] = {
    826           callback: callback,
    827           message: BASE.Message.File(name),
    828           status: this.STATUS.OK
    829         };
    830         this.head.appendChild(link);
    831         this.timer.create.call(this,[this.timer.file,file],link);
    832       }
    833     },
    834     
    835     //
    836     //  Timing code for checking when style sheets are available.
    837     //
    838     timer: {
    839       //
    840       //  Create the timing callback and start the timing loop.
    841       //  We use a delay because some browsers need it to allow the styles
    842       //  to be processed.
    843       //
    844       create: function (callback,node) {
    845         callback = BASE.Callback(callback);
    846         if (node.nodeName === "STYLE" && node.styleSheet &&
    847             typeof(node.styleSheet.cssText) !== 'undefined') {
    848           callback(this.STATUS.OK); // MSIE processes style immediately, but doesn't set its styleSheet!
    849         } else if (window.chrome && node.nodeName === "LINK") {
    850           callback(this.STATUS.OK); // Chrome doesn't give access to cssRules for stylesheet in
    851                                     //   a link node, so we can't detect when it is loaded.
    852         } else if (isSafari2) {
    853           this.timer.start(this,[this.timer.checkSafari2,sheets++,callback],this.styleDelay);
    854         } else {
    855           this.timer.start(this,[this.timer.checkLength,node,callback],this.styleDelay);
    856         }
    857         return callback;
    858       },
    859       //
    860       //  Start the timer for the given callback checker
    861       //
    862       start: function (AJAX,check,delay,timeout) {
    863         check = BASE.Callback(check);
    864         check.execute = this.execute; check.time = this.time;
    865         check.STATUS = AJAX.STATUS; check.timeout = timeout || AJAX.timeout;
    866         check.delay = check.total = delay || 0;
    867         if (delay) {setTimeout(check,delay)} else {check()}
    868       },
    869       //
    870       //  Increment the time total, increase the delay
    871       //  and test if we are past the timeout time.
    872       //  
    873       time: function (callback) {
    874         this.total += this.delay;
    875         this.delay = Math.floor(this.delay * 1.05 + 5);
    876         if (this.total >= this.timeout) {callback(this.STATUS.ERROR); return 1}
    877         return 0;
    878       },
    879       //
    880       //  For JS file loads, call the proper routine according to status
    881       //
    882       file: function (file,status) {
    883         if (status < 0) {BASE.Ajax.loadTimeout(file)} else {BASE.Ajax.loadComplete(file)}
    884       },
    885       //
    886       //  Call the hook with the required data
    887       //
    888       execute: function () {this.hook.call(this.object,this,this.data[0],this.data[1])},
    889       //
    890       //  Safari2 doesn't set the link's stylesheet, so we need to look in the
    891       //  document.styleSheets array for the new sheet when it is created
    892       //
    893       checkSafari2: function (check,length,callback) {
    894         if (check.time(callback)) return;
    895         if (document.styleSheets.length > length &&
    896             document.styleSheets[length].cssRules &&
    897             document.styleSheets[length].cssRules.length)
    898           {callback(check.STATUS.OK)} else {setTimeout(check,check.delay)}
    899       },
    900       //
    901       //  Look for the stylesheets rules and check when they are defined
    902       //  and no longer of length zero.  (This assumes there actually ARE
    903       //  some rules in the stylesheet.)
    904       //  
    905       checkLength: function (check,node,callback) {
    906         if (check.time(callback)) return;
    907         var isStyle = 0; var sheet = (node.sheet || node.styleSheet);
    908         try {if ((sheet.cssRules||sheet.rules||[]).length > 0) {isStyle = 1}} catch(err) {
    909           if (err.message.match(/protected variable|restricted URI/)) {isStyle = 1}
    910           else if (err.message.match(/Security error/)) {
    911             // Firefox3 gives "Security error" for missing files, so
    912             //   can't distinguish that from OK files on remote servers.
    913             //   or OK files in different directory from local files.
    914             isStyle = 1; // just say it is OK (can't really tell)
    915           }
    916         }
    917         if (isStyle) {
    918           // Opera 9.6 requires this setTimeout
    919           setTimeout(BASE.Callback([callback,check.STATUS.OK]),0);
    920         } else {
    921           setTimeout(check,check.delay);
    922         }
    923       }
    924     },
    925 
    926     //
    927     //  JavaScript code must call this when they are completely initialized
    928     //  (this allows them to perform asynchronous actions before indicating
    929     //  that they are complete).
    930     //
    931     loadComplete: function (file) {
    932       file = this.fileURL(file);
    933       var loading = this.loading[file];
    934       if (loading && !loading.preloaded) {
    935         BASE.Message.Clear(loading.message);
    936         clearTimeout(loading.timeout);
    937 	if (loading.script) {
    938 	  if (SCRIPTS.length === 0) {setTimeout(REMOVESCRIPTS,0)}
    939 	  SCRIPTS.push(loading.script);
    940 	}
    941         this.loaded[file] = loading.status; delete this.loading[file];
    942         this.addHook(file,loading.callback);
    943       } else {
    944         if (loading) {delete this.loading[file]}
    945         this.loaded[file] = this.STATUS.OK;
    946         loading = {status: this.STATUS.OK}
    947       }
    948       if (!this.loadHooks[file]) {return null}
    949       return this.loadHooks[file].Execute(loading.status);
    950     },
    951     
    952     //
    953     //  If a file fails to load within the timeout period (or the onerror handler
    954     //  is called), this routine runs to signal the error condition.
    955     //  
    956     loadTimeout: function (file) {
    957       if (this.loading[file].timeout) {clearTimeout(this.loading[file].timeout)}
    958       this.loading[file].status = this.STATUS.ERROR;
    959       this.loadError(file);
    960       this.loadComplete(file);
    961     },
    962     
    963     //
    964     //  The default error hook for file load failures
    965     //
    966     loadError: function (file) {
    967       BASE.Message.Set(["LoadFailed","File failed to load: %1",file],null,2000);
    968       BASE.Hub.signal.Post(["file load error",file]);
    969     },
    970 
    971     //
    972     //  Defines a style sheet from a hash of style declarations (key:value pairs
    973     //  where the key is the style selector and the value is a hash of CSS attributes 
    974     //  and values).
    975     //
    976     Styles: function (styles,callback) {
    977       var styleString = this.StyleString(styles);
    978       if (styleString === "") {
    979         callback = BASE.Callback(callback);
    980         callback();
    981       } else {
    982         var style = document.createElement("style"); style.type = "text/css";
    983         this.head = HEAD(this.head);
    984         this.head.appendChild(style);
    985         if (style.styleSheet && typeof(style.styleSheet.cssText) !== 'undefined') {
    986           style.styleSheet.cssText = styleString;
    987         } else {
    988           style.appendChild(document.createTextNode(styleString));
    989         }
    990         callback = this.timer.create.call(this,callback,style);
    991       }
    992       return callback;
    993     },
    994     
    995     //
    996     //  Create a stylesheet string from a style declaration object
    997     //
    998     StyleString: function (styles) {
    999       if (typeof(styles) === 'string') {return styles}
   1000       var string = "", id, style;
   1001       for (id in styles) {if (styles.hasOwnProperty(id)) {
   1002         if (typeof styles[id] === 'string') {
   1003           string += id + " {"+styles[id]+"}\n";
   1004         } else if (styles[id] instanceof Array) {
   1005           for (var i = 0; i < styles[id].length; i++) {
   1006             style = {}; style[id] = styles[id][i];
   1007             string += this.StyleString(style);
   1008           }
   1009         } else if (id.substr(0,6) === '@media') {
   1010           string += id + " {"+this.StyleString(styles[id])+"}\n";
   1011         } else if (styles[id] != null) {
   1012           style = [];
   1013           for (var name in styles[id]) {if (styles[id].hasOwnProperty(name)) {
   1014             if (styles[id][name] != null) 
   1015               {style[style.length] = name + ': ' + styles[id][name]}
   1016           }}
   1017           string += id +" {"+style.join('; ')+"}\n";
   1018         }
   1019       }}
   1020       return string;
   1021     }
   1022   };
   1023   
   1024 })("MathJax");
   1025 
   1026 /**********************************************************/
   1027 
   1028 MathJax.HTML = {
   1029   //
   1030   //  Create an HTML element with given attributes and content.
   1031   //  The def parameter is an (optional) object containing key:value pairs
   1032   //  of the attributes and their values, and contents is an (optional)
   1033   //  array of strings to be inserted as text, or arrays of the form
   1034   //  [type,def,contents] that describes an HTML element to be inserted
   1035   //  into the current element.  Thus the contents can describe a complete
   1036   //  HTML snippet of arbitrary complexity.  E.g.:
   1037   //  
   1038   //    MathJax.HTML.Element("span",{id:"mySpan",style{"font-style":"italic"}},[
   1039   //        "(See the ",["a",{href:"http://www.mathjax.org"},["MathJax home page"]],
   1040   //        " for more details.)"]);
   1041   // 
   1042   Element: function (type,def,contents) {
   1043     var obj = document.createElement(type), id;
   1044     if (def) {
   1045       if (def.hasOwnProperty("style")) {
   1046         var style = def.style; def.style = {};
   1047         for (id in style) {if (style.hasOwnProperty(id))
   1048           {def.style[id.replace(/-([a-z])/g,this.ucMatch)] = style[id]}}
   1049       }
   1050       MathJax.Hub.Insert(obj,def);
   1051       for (id in def) {
   1052         if (id === "role" || id.substr(0,5) === "aria-") obj.setAttribute(id,def[id]);
   1053       }
   1054     }
   1055     if (contents) {
   1056       if (!(contents instanceof Array)) {contents = [contents]}
   1057       for (var i = 0, m = contents.length; i < m; i++) {
   1058         if (contents[i] instanceof Array) {
   1059           obj.appendChild(this.Element(contents[i][0],contents[i][1],contents[i][2]));
   1060         } else if (type === "script") { // IE throws an error if script is added as a text node
   1061           this.setScript(obj, contents[i]);
   1062         } else {
   1063           obj.appendChild(document.createTextNode(contents[i]));
   1064         }
   1065       }
   1066     }
   1067     return obj;
   1068   },
   1069   ucMatch: function (match,c) {return c.toUpperCase()},
   1070   addElement: function (span,type,def,contents) {return span.appendChild(this.Element(type,def,contents))},
   1071   TextNode: function (text) {return document.createTextNode(text)},
   1072   addText: function (span,text) {return span.appendChild(this.TextNode(text))},
   1073 
   1074   //
   1075   //  Set and get the text of a script
   1076   //
   1077   setScript: function (script,text) {
   1078     if (this.setScriptBug) {script.text = text} else {
   1079       while (script.firstChild) {script.removeChild(script.firstChild)}
   1080       this.addText(script,text);
   1081     }
   1082   },
   1083   getScript: function (script) {
   1084     var text = (script.text === "" ? script.innerHTML : script.text);
   1085     return text.replace(/^\s+/,"").replace(/\s+$/,"");
   1086   },
   1087 
   1088   //
   1089   //  Manage cookies
   1090   //
   1091   Cookie: {
   1092     prefix: "mjx",
   1093     expires: 365,
   1094     
   1095     //
   1096     //  Save an object as a named cookie
   1097     //
   1098     Set: function (name,def) {
   1099       var keys = [];
   1100       if (def) {
   1101         for (var id in def) {if (def.hasOwnProperty(id)) {
   1102           keys.push(id+":"+def[id].toString().replace(/&/g,"&&"));
   1103         }}
   1104       }
   1105       var cookie = this.prefix+"."+name+"="+escape(keys.join('&;'));
   1106       if (this.expires) {
   1107         var time = new Date(); time.setDate(time.getDate() + this.expires);
   1108         cookie += '; expires='+time.toGMTString();
   1109       }
   1110       try {document.cookie = cookie+"; path=/"} catch (err) {} // ignore errors saving cookies
   1111     },
   1112     
   1113     //
   1114     //  Get the contents of a named cookie and incorporate
   1115     //  it into the given object (or return a fresh one)
   1116     //
   1117     Get: function (name,obj) {
   1118       if (!obj) {obj = {}}
   1119       var pattern = new RegExp("(?:^|;\\s*)"+this.prefix+"\\."+name+"=([^;]*)(?:;|$)");
   1120       var match;
   1121       try {match = pattern.exec(document.cookie)} catch (err) {}; // ignore errors reading cookies
   1122       if (match && match[1] !== "") {
   1123         var keys = unescape(match[1]).split('&;');
   1124         for (var i = 0, m = keys.length; i < m; i++) {
   1125           match = keys[i].match(/([^:]+):(.*)/);
   1126           var value = match[2].replace(/&&/g,'&');
   1127           if (value === "true") {value = true} else if (value === "false") {value = false}
   1128             else if (value.match(/^-?(\d+(\.\d+)?|\.\d+)$/)) {value = parseFloat(value)}
   1129           obj[match[1]] = value;
   1130         }
   1131       }
   1132       return obj;
   1133     }
   1134   }
   1135     
   1136 };
   1137 
   1138 
   1139 /**********************************************************/
   1140 
   1141 MathJax.Localization = {
   1142   
   1143   locale: "en",
   1144   directory: "[MathJax]/localization",
   1145   strings: {
   1146     // Currently, this list is not modified by the MathJax-i18n script. You can
   1147     // run the following command in MathJax/unpacked/localization to update it:
   1148     //
   1149     // 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*
   1150     //
   1151     // This only takes languages with localization data so you must also add
   1152     // the languages that use a remap but are not translated at all.
   1153     //
   1154     "ast": {menuTitle: "asturianu"},
   1155     "bg": {menuTitle: "\u0431\u044A\u043B\u0433\u0430\u0440\u0441\u043A\u0438"},
   1156     "bcc": {menuTitle: "\u0628\u0644\u0648\u0686\u06CC"},
   1157     "br": {menuTitle: "brezhoneg"},
   1158     "ca": {menuTitle: "catal\u00E0"},
   1159     "cdo": {menuTitle: "M\u00ECng-d\u0115\u0324ng-ng\u1E73\u0304"},
   1160     "cs": {menuTitle: "\u010De\u0161tina"},
   1161     "da": {menuTitle: "dansk"},
   1162     "de": {menuTitle: "Deutsch"},
   1163     "en": {menuTitle: "English", isLoaded: true},
   1164     "eo": {menuTitle: "Esperanto"},
   1165     "es": {menuTitle: "espa\u00F1ol"},
   1166     "fa": {menuTitle: "\u0641\u0627\u0631\u0633\u06CC"},
   1167     "fi": {menuTitle: "suomi"},
   1168     "fr": {menuTitle: "fran\u00E7ais"},
   1169     "gl": {menuTitle: "galego"},
   1170     "he": {menuTitle: "\u05E2\u05D1\u05E8\u05D9\u05EA"},
   1171     "ia": {menuTitle: "interlingua"},
   1172     "it": {menuTitle: "italiano"},
   1173     "ja": {menuTitle: "\u65E5\u672C\u8A9E"},
   1174     "kn": {menuTitle: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1"},
   1175     "ko": {menuTitle: "\uD55C\uAD6D\uC5B4"},
   1176     "lb": {menuTitle: "L\u00EBtzebuergesch"},
   1177     "lt": {menuTitle: "lietuvi\u0173"},
   1178     "mk": {menuTitle: "\u043C\u0430\u043A\u0435\u0434\u043E\u043D\u0441\u043A\u0438"},
   1179     "nl": {menuTitle: "Nederlands"},
   1180     "oc": {menuTitle: "occitan"},
   1181     "pl": {menuTitle: "polski"},
   1182     "pt": {menuTitle: "portugus\u00EA"},
   1183     "pt-br": {menuTitle: "portugu\u00EAs do Brasil"},
   1184     "ru": {menuTitle: "\u0440\u0443\u0441\u0441\u043A\u0438\u0439"},
   1185     "sco": {menuTitle: "Scots"},
   1186     "scn": {menuTitle: "sicilianu"},
   1187     "sl": {menuTitle: "sloven\u0161\u010Dina"},
   1188     "sv": {menuTitle: "svenska"},
   1189     "tr": {menuTitle: "T\u00FCrk\u00E7e"},
   1190     "uk": {menuTitle: "\u0443\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"},
   1191     "vi": {menuTitle: "Ti\u1EBFng Vi\u1EC7t"},
   1192     "zh-hans": {menuTitle: "\u4E2D\u6587\uFF08\u7B80\u4F53\uFF09"}
   1193   },
   1194 
   1195   //
   1196   //  The pattern for substitution escapes:
   1197   //      %n or %{n} or %{plural:%n|option1|option1|...} or %c
   1198   //
   1199   pattern: /%(\d+|\{\d+\}|\{[a-z]+:\%\d+(?:\|(?:%\{\d+\}|%.|[^\}])*)+\}|.)/g,
   1200 
   1201   SPLIT: ("axb".split(/(x)/).length === 3 ?
   1202     function (string,regex) {return string.split(regex)} :
   1203     //
   1204     //  IE8 and below don't do split() correctly when the pattern includes
   1205     //    parentheses (the split should include the matched exrepssions).
   1206     //    So implement it by hand here.
   1207     // 
   1208     function (string,regex) {
   1209       var result = [], match, last = 0;
   1210       regex.lastIndex = 0;
   1211       while ((match = regex.exec(string))) {
   1212         result.push(string.substr(last,match.index-last));
   1213         result.push.apply(result,match.slice(1));
   1214         last = match.index + match[0].length;
   1215       }
   1216       result.push(string.substr(last));
   1217       return result;
   1218     }),
   1219   
   1220   _: function (id,phrase) {
   1221     if (phrase instanceof Array) {return this.processSnippet(id,phrase)}
   1222     return this.processString(this.lookupPhrase(id,phrase),[].slice.call(arguments,2));
   1223   },
   1224   
   1225   processString: function (string,args,domain) {
   1226     //
   1227     //  Process arguments for substitution
   1228     //    If the argument is a snippet (and we are processing snippets) do so,
   1229     //    Otherwise, if it is a number, convert it for the lacale
   1230     //
   1231     var i, m;
   1232     for (i = 0, m = args.length; i < m; i++) {
   1233       if (domain && args[i] instanceof Array) {args[i] = this.processSnippet(domain,args[i])}
   1234     }
   1235     //
   1236     //  Split string at escapes and process them individually
   1237     //
   1238     var parts = this.SPLIT(string,this.pattern);
   1239     for (i = 1, m = parts.length; i < m; i += 2) {
   1240       var c = parts[i].charAt(0);  // first char will be { or \d or a char to be kept literally
   1241       if (c >= "0" && c <= "9") {    // %n
   1242         parts[i] = args[parts[i]-1];
   1243         if (typeof parts[i] === "number") parts[i] = this.number(parts[i]);
   1244       } else if (c === "{") {        // %{n} or %{plural:%n|...}
   1245         c = parts[i].substr(1);
   1246         if (c >= "0" && c <= "9") {  // %{n}
   1247           parts[i] = args[parts[i].substr(1,parts[i].length-2)-1];
   1248           if (typeof parts[i] === "number") parts[i] = this.number(parts[i]);
   1249         } else {                     // %{plural:%n|...}
   1250           var match = parts[i].match(/^\{([a-z]+):%(\d+)\|(.*)\}$/);
   1251           if (match) {
   1252             if (match[1] === "plural") {
   1253               var n = args[match[2]-1];
   1254               if (typeof n === "undefined") {
   1255                 parts[i] = "???";        // argument doesn't exist
   1256               } else {
   1257                 n = this.plural(n) - 1;  // index of the form to use
   1258                 var plurals = match[3].replace(/(^|[^%])(%%)*%\|/g,"$1$2%\uEFEF").split(/\|/); // the parts (replacing %| with a special character)
   1259                 if (n >= 0 && n < plurals.length) {
   1260                   parts[i] = this.processString(plurals[n].replace(/\uEFEF/g,"|"),args,domain);
   1261                 } else {
   1262                   parts[i] = "???";      // no string for this index
   1263                 }
   1264               }
   1265             } else {parts[i] = "%"+parts[i]}  // not "plural", put back the % and leave unchanged
   1266           }
   1267         }
   1268       }
   1269       if (parts[i] == null) {parts[i] = "???"}
   1270     }
   1271     //
   1272     //  If we are not forming a snippet, return the completed string
   1273     //  
   1274     if (!domain) {return parts.join("")}
   1275     //
   1276     //  We need to return an HTML snippet, so buld it from the
   1277     //  broken up string with inserted parts (that could be snippets)
   1278     //
   1279     var snippet = [], part = "";
   1280     for (i = 0; i < m; i++) {
   1281       part += parts[i]; i++;  // add the string and move on to substitution result
   1282       if (i < m) {
   1283         if (parts[i] instanceof Array)  {        // substitution was a snippet
   1284           snippet.push(part);                        // add the accumulated string
   1285           snippet = snippet.concat(parts[i]);        // concatenate the substution snippet
   1286           part = "";                                 // start accumulating a new string
   1287         } else {                                 // substitution was a string
   1288           part += parts[i];                          // add to accumulating string
   1289         }
   1290       }
   1291     }
   1292     if (part !== "") {snippet.push(part)} // add final string
   1293     return snippet;
   1294   },
   1295   
   1296   processSnippet: function (domain,snippet) {
   1297     var result = [];   // the new snippet
   1298     //
   1299     //  Look through the original snippet for
   1300     //   strings or snippets to translate
   1301     //
   1302     for (var i = 0, m = snippet.length; i < m; i++) {
   1303       if (snippet[i] instanceof Array) {
   1304         //
   1305         //  This could be a sub-snippet:
   1306         //    ["tag"] or ["tag",{properties}] or ["tag",{properties},snippet]
   1307         //  Or it could be something to translate:
   1308         //    [id,string,args] or [domain,snippet]
   1309         var data = snippet[i];
   1310         if (typeof data[1] === "string") {        // [id,string,args]
   1311           var id = data[0]; if (!(id instanceof Array)) {id = [domain,id]}
   1312           var phrase = this.lookupPhrase(id,data[1]);
   1313           result = result.concat(this.processMarkdown(phrase,data.slice(2),domain));
   1314         } else if (data[1] instanceof Array) {    // [domain,snippet]
   1315           result = result.concat(this.processSnippet.apply(this,data));
   1316         } else if (data.length >= 3) {            // ["tag",{properties},snippet]
   1317           result.push([data[0],data[1],this.processSnippet(domain,data[2])]);
   1318         } else {                                  // ["tag"] or ["tag",{properties}]
   1319           result.push(snippet[i]);
   1320         }
   1321       } else {                                    // a string
   1322         result.push(snippet[i]);
   1323       }
   1324     }
   1325     return result;
   1326   },
   1327   
   1328   markdownPattern: /(%.)|(\*{1,3})((?:%.|.)+?)\2|(`+)((?:%.|.)+?)\4|\[((?:%.|.)+?)\]\(([^\s\)]+)\)/,
   1329   //   %c or *bold*, **italics**, ***bold-italics***, or `code`, or [link](url)
   1330    
   1331   processMarkdown: function (phrase,args,domain) {
   1332     var result = [], data;
   1333     //
   1334     //  Split the string by the Markdown pattern
   1335     //    (the text blocks are separated by 
   1336     //      c,stars,star-text,backtics,code-text,link-text,URL).
   1337     //  Start with teh first text string from the split.
   1338     //
   1339     var parts = phrase.split(this.markdownPattern);
   1340     var string = parts[0];
   1341     //
   1342     //  Loop through the matches and process them
   1343     //
   1344     for (var i = 1, m = parts.length; i < m; i += 8) {
   1345       if (parts[i+1]) {        // stars (for bold/italic)
   1346         //
   1347         //  Select the tag to use by number of stars (three stars requires two tags)
   1348         //
   1349         data = this.processString(parts[i+2],args,domain);
   1350         if (!(data instanceof Array)) {data = [data]}
   1351         data = [["b","i","i"][parts[i+1].length-1],{},data]; // number of stars determines type
   1352         if (parts[i+1].length === 3) {data = ["b",{},data]}  // bold-italic
   1353       } else if (parts[i+3]) { //  backtics (for code)
   1354         //
   1355         //  Remove one leading or trailing space, and process substitutions
   1356         //  Make a <code> tag
   1357         //
   1358         data = this.processString(parts[i+4].replace(/^\s/,"").replace(/\s$/,""),args,domain);
   1359         if (!(data instanceof Array)) {data = [data]}
   1360         data = ["code",{},data];
   1361       } else if (parts[i+5]) { //  hyperlink
   1362         //
   1363         //  Process the link text, and make an <a> tag with the URL
   1364         //
   1365         data = this.processString(parts[i+5],args,domain);
   1366         if (!(data instanceof Array)) {data = [data]}
   1367         data = ["a",{href:this.processString(parts[i+6],args),target:"_blank"},data];
   1368       } else {
   1369         //
   1370         //  Escaped character (%c) gets added into the string.
   1371         //
   1372         string += parts[i]; data = null;
   1373       }
   1374       //
   1375       //  If there is a tag to insert,
   1376       //     Add any pending string, then push the tag
   1377       //
   1378       if (data) {
   1379         result = this.concatString(result,string,args,domain);
   1380         result.push(data); string = "";
   1381       }
   1382       //
   1383       //  Process the string that follows matches pattern
   1384       //
   1385       if (parts[i+7] !== "") {string += parts[i+7]}
   1386     };
   1387     //
   1388     //  Add any pending string and return the resulting snippet
   1389     //
   1390     result = this.concatString(result,string,args,domain);
   1391     return result;
   1392   },
   1393   concatString: function (result,string,args,domain) {
   1394     if (string != "") {
   1395       //
   1396       //  Process the substutions.
   1397       //  If the result is not a snippet, turn it into one.
   1398       //  Then concatenate the snippet to the current one
   1399       //
   1400       string = this.processString(string,args,domain);
   1401       if (!(string instanceof Array)) {string = [string]}
   1402       result = result.concat(string);
   1403     }
   1404     return result;
   1405   },
   1406 
   1407   lookupPhrase: function (id,phrase,domain) {
   1408     //
   1409     //  Get the domain and messageID
   1410     //
   1411     if (!domain) {domain = "_"}
   1412     if (id instanceof Array) {domain = (id[0] || "_"); id = (id[1] || "")}
   1413     //
   1414     //  Check if the data is available and if not,
   1415     //    load it and throw a restart error so the calling
   1416     //    code can wait for the load and try again.
   1417     //
   1418     var load = this.loadDomain(domain);
   1419     if (load) {MathJax.Hub.RestartAfter(load)}
   1420     //
   1421     //  Look up the message in the localization data
   1422     //    (if not found, the original English is used)
   1423     //
   1424     var localeData = this.strings[this.locale];
   1425     if (localeData) {
   1426       if (localeData.domains && domain in localeData.domains) {
   1427         var domainData = localeData.domains[domain];
   1428         if (domainData.strings && id in domainData.strings)
   1429           {phrase = domainData.strings[id]}
   1430       }
   1431     }
   1432     //
   1433     //  return the translated phrase
   1434     //
   1435     return phrase;
   1436   },
   1437   
   1438   //
   1439   //  Load a langauge data file from the proper
   1440   //  directory and file.
   1441   //
   1442   loadFile: function (file,data,callback) {
   1443     callback = MathJax.Callback(callback);
   1444     file = (data.file || file);  // the data's file name or the default name
   1445     if (!file.match(/\.js$/)) {file += ".js"} // add .js if needed
   1446     //
   1447     //  Add the directory if the file doesn't
   1448     //  contain a full URL already.
   1449     //
   1450     if (!file.match(/^([a-z]+:|\[MathJax\])/)) {
   1451       var dir = (this.strings[this.locale].directory  || 
   1452                  this.directory + "/" + this.locale ||
   1453                  "[MathJax]/localization/" + this.locale);
   1454       file = dir + "/" + file;
   1455     }
   1456     //
   1457     //  Load the file and mark the data as loaded (even if it
   1458     //  failed to load, so we don't continue to try to load it
   1459     //  over and over).
   1460     //
   1461     var load = MathJax.Ajax.Require(file,function () {data.isLoaded = true; return callback()});
   1462     //
   1463     //  Return the callback if needed, otherwise null.
   1464     //
   1465     return (load.called ? null : load);
   1466   },
   1467   
   1468   //
   1469   //  Check to see if the localization data are loaded
   1470   //  for the given domain; if not, load the data file,
   1471   //  and return a callback for the loading operation.
   1472   //  Otherwise return null (data are loaded).
   1473   //  
   1474   loadDomain: function (domain,callback) {
   1475     var load, localeData = this.strings[this.locale];
   1476     if (localeData) {
   1477       if (!localeData.isLoaded) {
   1478         load = this.loadFile(this.locale,localeData);
   1479         if (load) {
   1480           return MathJax.Callback.Queue(
   1481             load,["loadDomain",this,domain] // call again to load domain
   1482           ).Push(callback||{});
   1483         }
   1484       }
   1485       if (localeData.domains && domain in localeData.domains) {
   1486         var domainData = localeData.domains[domain];
   1487         if (!domainData.isLoaded) {
   1488           load = this.loadFile(domain,domainData);
   1489           if (load) {return MathJax.Callback.Queue(load).Push(callback)}
   1490         }
   1491       }
   1492     } 
   1493     // localization data are loaded, so just do the callback
   1494     return MathJax.Callback(callback)();
   1495   },
   1496 
   1497   //
   1498   //  Perform a function, properly handling
   1499   //  restarts due to localization file loads.
   1500   //
   1501   //  Note that this may return before the function
   1502   //  has been called successfully, so you should
   1503   //  consider fn as running asynchronously.  (Callbacks
   1504   //  can be used to synchronize it with other actions.)
   1505   //
   1506   Try: function (fn) {
   1507     fn = MathJax.Callback(fn); fn.autoReset = true;
   1508     try {fn()} catch (err) {
   1509       if (!err.restart) {throw err}
   1510       MathJax.Callback.After(["Try",this,fn],err.restart);
   1511     }
   1512   },
   1513 
   1514   //
   1515   //  Reset the current language
   1516   //
   1517   resetLocale: function(locale) {
   1518     // Selection algorithm:
   1519     // 1) Downcase locale name (e.g. "en-US" => "en-us")
   1520     // 2) Try a parent language (e.g. "en-us" => "en")
   1521     // 3) Try the fallback specified in the data (e.g. "pt" => "pt-br")
   1522     // 4) Otherwise don't change the locale.
   1523     if (!locale) return;
   1524     locale = locale.toLowerCase();
   1525     while (!this.strings[locale]) {
   1526       var dashPos = locale.lastIndexOf("-");
   1527       if (dashPos === -1) return;
   1528       locale = locale.substring(0, dashPos);
   1529     }
   1530     var remap = this.strings[locale].remap;
   1531     this.locale = remap ? remap : locale;
   1532   },
   1533 
   1534   //
   1535   //  Set the current language
   1536   //
   1537   setLocale: function(locale) {
   1538     this.resetLocale(locale);
   1539     if (MathJax.Menu) {this.loadDomain("MathMenu")}
   1540   },
   1541 
   1542   //
   1543   //  Add or update a language or domain
   1544   //
   1545   addTranslation: function (locale,domain,definition) {
   1546     var data = this.strings[locale], isNew = false;
   1547     if (!data) {data = this.strings[locale] = {}; isNew = true}
   1548     if (!data.domains) {data.domains = {}}
   1549     if (domain) {
   1550       if (!data.domains[domain]) {data.domains[domain] = {}}
   1551       data = data.domains[domain];
   1552     }
   1553     MathJax.Hub.Insert(data,definition);
   1554     if (isNew && MathJax.Menu.menu) {MathJax.Menu.CreateLocaleMenu()}
   1555   },
   1556   
   1557   //
   1558   //  Set CSS for an element based on font requirements
   1559   //
   1560   setCSS: function (div) {
   1561     var locale = this.strings[this.locale];
   1562     if (locale) {
   1563       if (locale.fontFamily) {div.style.fontFamily = locale.fontFamily}
   1564       if (locale.fontDirection) {
   1565         div.style.direction = locale.fontDirection;
   1566         if (locale.fontDirection === "rtl") {div.style.textAlign = "right"}
   1567       }
   1568     }
   1569     return div;
   1570   },
   1571   
   1572   //
   1573   //  Get the language's font family or direction
   1574   //
   1575   fontFamily: function () {
   1576     var locale = this.strings[this.locale];
   1577     return (locale ? locale.fontFamily : null);
   1578   },
   1579   fontDirection: function () {
   1580     var locale = this.strings[this.locale];
   1581     return (locale ? locale.fontDirection : null);
   1582   },
   1583 
   1584   //
   1585   //  Get the language's plural index for a number
   1586   //
   1587   plural: function (n) {
   1588     var locale = this.strings[this.locale];
   1589     if (locale && locale.plural) {return locale.plural(n)}
   1590     // default
   1591     if (n == 1) {return 1} // one
   1592     return 2; // other
   1593   },
   1594 
   1595   //
   1596   //  Convert a number to language-specific form
   1597   //
   1598   number: function(n) {
   1599     var locale = this.strings[this.locale];
   1600     if (locale && locale.number) {return locale.number(n)}
   1601     // default
   1602     return n;
   1603   }
   1604 };
   1605 
   1606 
   1607 /**********************************************************/
   1608 
   1609 MathJax.Message = {
   1610   ready: false,  // used to tell when the styles are available
   1611   log: [{}], current: null,
   1612   textNodeBug: (navigator.vendor === "Apple Computer, Inc." &&
   1613                 typeof navigator.vendorSub === "undefined") ||
   1614                (window.hasOwnProperty && window.hasOwnProperty("konqueror")), // Konqueror displays some gibberish with text.nodeValue = "..."
   1615   
   1616   styles: {
   1617     "#MathJax_Message": {
   1618       position: "fixed", left: "1px", bottom: "2px",
   1619       'background-color': "#E6E6E6",  border: "1px solid #959595",
   1620       margin: "0px", padding: "2px 8px",
   1621       'z-index': "102", color: "black", 'font-size': "80%",
   1622       width: "auto", 'white-space': "nowrap"
   1623     },
   1624     
   1625     "#MathJax_MSIE_Frame": {
   1626       position: "absolute",
   1627       top:0, left: 0, width: "0px", 'z-index': 101,
   1628       border: "0px", margin: "0px", padding: "0px"
   1629     }
   1630   },
   1631   
   1632   browsers: {
   1633     MSIE: function (browser) {
   1634       MathJax.Message.msieFixedPositionBug = ((document.documentMode||0) < 7);
   1635       if (MathJax.Message.msieFixedPositionBug) 
   1636         {MathJax.Hub.config.styles["#MathJax_Message"].position = "absolute"}
   1637       MathJax.Message.quirks = (document.compatMode === "BackCompat");
   1638     },
   1639     Chrome: function (browser) {
   1640       MathJax.Hub.config.styles["#MathJax_Message"].bottom = "1.5em";
   1641       MathJax.Hub.config.styles["#MathJax_Message"].left = "1em";
   1642     }
   1643   },
   1644   
   1645   Init: function (styles) {
   1646     if (styles) {this.ready = true}
   1647     if (!document.body || !this.ready) {return false}
   1648     //
   1649     //  ASCIIMathML replaces the entire page with a copy of itself (@#!#%@!!)
   1650     //  so check that this.div is still part of the page, otherwise look up
   1651     //  the copy and use that.
   1652     //
   1653     if (this.div && this.div.parentNode == null) {
   1654       this.div = document.getElementById("MathJax_Message");
   1655       if (this.div) {this.text = this.div.firstChild}
   1656     }
   1657     if (!this.div) {
   1658       var frame = document.body;
   1659       if (this.msieFixedPositionBug && window.attachEvent) {
   1660         frame = this.frame = this.addDiv(document.body); frame.removeAttribute("id");
   1661         frame.style.position = "absolute";
   1662         frame.style.border = frame.style.margin = frame.style.padding = "0px";
   1663         frame.style.zIndex = "101"; frame.style.height = "0px";
   1664         frame = this.addDiv(frame);
   1665         frame.id = "MathJax_MSIE_Frame";
   1666         window.attachEvent("onscroll",this.MoveFrame);
   1667         window.attachEvent("onresize",this.MoveFrame);
   1668         this.MoveFrame();
   1669       }
   1670       this.div = this.addDiv(frame); this.div.style.display = "none";
   1671       this.text = this.div.appendChild(document.createTextNode(""));
   1672     }
   1673     return true;
   1674   },
   1675   
   1676   addDiv: function (parent) {
   1677     var div = document.createElement("div");
   1678     div.id = "MathJax_Message";
   1679     if (parent.firstChild) {parent.insertBefore(div,parent.firstChild)}
   1680       else {parent.appendChild(div)}
   1681     return div;
   1682   },
   1683   
   1684   MoveFrame: function () {
   1685     var body = (MathJax.Message.quirks ? document.body : document.documentElement);
   1686     var frame = MathJax.Message.frame;
   1687     frame.style.left = body.scrollLeft + 'px';
   1688     frame.style.top = body.scrollTop + 'px';
   1689     frame.style.width = body.clientWidth + 'px';
   1690     frame = frame.firstChild;
   1691     frame.style.height = body.clientHeight + 'px';
   1692   },
   1693 
   1694   localize: function (message) {
   1695     return MathJax.Localization._(message,message);
   1696   },
   1697   
   1698   filterText: function (text,n,id) {
   1699     if (MathJax.Hub.config.messageStyle === "simple") {
   1700       if (id === "LoadFile") {
   1701         if (!this.loading) {this.loading = this.localize("Loading") + " "}
   1702         text = this.loading; this.loading += ".";
   1703       } else if (id === "ProcessMath") {
   1704         if (!this.processing) {this.processing = this.localize("Processing") + " "}
   1705         text = this.processing; this.processing += ".";
   1706       } else if (id === "TypesetMath") {
   1707         if (!this.typesetting) {this.typesetting = this.localize("Typesetting") + " "}
   1708         text = this.typesetting; this.typesetting += ".";
   1709       }
   1710     }
   1711     return text;
   1712   },
   1713   
   1714   Set: function (text,n,clearDelay) {
   1715     if (n == null) {n = this.log.length; this.log[n] = {}}
   1716     //
   1717     //  Translate message if it is [id,message,arguments]
   1718     //
   1719     var id = "";
   1720     if (text instanceof Array) {
   1721       id = text[0]; if (id instanceof Array) {id = id[1]}
   1722       //
   1723       // Localization._() will throw a restart error if a localization file
   1724       //   needs to be loaded, so trap that and redo the Set() call
   1725       //   after it is loaded.
   1726       //
   1727       try {
   1728         text = MathJax.Localization._.apply(MathJax.Localization,text);
   1729       } catch (err) {
   1730         if (!err.restart) {throw err}
   1731         if (!err.restart.called) {
   1732           //
   1733           //  Mark it so we can tell if the Clear() comes before the message is displayed
   1734           //
   1735           if (this.log[n].restarted == null) {this.log[n].restarted = 0}
   1736           this.log[n].restarted++; delete this.log[n].cleared;
   1737           MathJax.Callback.After(["Set",this,text,n,clearDelay],err.restart);
   1738           return n;
   1739         }
   1740       }
   1741     }
   1742     //
   1743     // Clear the timout timer.
   1744     //
   1745     if (this.timer) {clearTimeout(this.timer); delete this.timer}
   1746     //
   1747     //  Save the message and filtered message.
   1748     //
   1749     this.log[n].text = text; this.log[n].filteredText = text = this.filterText(text,n,id);
   1750     //
   1751     //  Hook the message into the message list so we can tell
   1752     //   what message to put up when this one is removed.
   1753     //
   1754     if (typeof(this.log[n].next) === "undefined") {
   1755       this.log[n].next = this.current;
   1756       if (this.current != null) {this.log[this.current].prev = n}
   1757       this.current = n;
   1758     }
   1759     //
   1760     //  Show the message if it is the currently active one.
   1761     //
   1762     if (this.current === n && MathJax.Hub.config.messageStyle !== "none") {
   1763       if (this.Init()) {
   1764         if (this.textNodeBug) {this.div.innerHTML = text} else {this.text.nodeValue = text}
   1765         this.div.style.display = "";
   1766         if (this.status) {window.status = ""; delete this.status}
   1767       } else {
   1768         window.status = text;
   1769         this.status = true;
   1770       }
   1771     }
   1772     //
   1773     //  Check if the message was resetarted to load a localization file
   1774     //    and if it has been cleared in the meanwhile.
   1775     //
   1776     if (this.log[n].restarted) {
   1777       if (this.log[n].cleared) {clearDelay = 0}
   1778       if (--this.log[n].restarted === 0) {delete this.log[n].cleared}
   1779     }
   1780     //
   1781     //  Check if we need to clear the message automatically.
   1782     //
   1783     if (clearDelay) {setTimeout(MathJax.Callback(["Clear",this,n]),clearDelay)}
   1784       else if (clearDelay == 0) {this.Clear(n,0)}
   1785     //
   1786     //  Return the message number.
   1787     //
   1788     return n;
   1789   },
   1790   
   1791   Clear: function (n,delay) {
   1792     //
   1793     //  Detatch the message from the active list.
   1794     //
   1795     if (this.log[n].prev != null) {this.log[this.log[n].prev].next = this.log[n].next}
   1796     if (this.log[n].next != null) {this.log[this.log[n].next].prev = this.log[n].prev}
   1797     //
   1798     //  If it is the current message, get the next one to show.
   1799     //
   1800     if (this.current === n) {
   1801       this.current = this.log[n].next;
   1802       if (this.text) {
   1803         if (this.div.parentNode == null) {this.Init()} // see ASCIIMathML comments above
   1804         if (this.current == null) {
   1805           //
   1806           //  If there are no more messages, remove the message box.
   1807           //
   1808           if (this.timer) {clearTimeout(this.timer); delete this.timer}
   1809           if (delay == null) {delay = 600}
   1810           if (delay === 0) {this.Remove()}
   1811 	    else {this.timer = setTimeout(MathJax.Callback(["Remove",this]),delay)}
   1812         } else if (MathJax.Hub.config.messageStyle !== "none") {
   1813           //
   1814           //  If there is an old message, put it in place
   1815           //
   1816           if (this.textNodeBug) {this.div.innerHTML = this.log[this.current].filteredText}
   1817                            else {this.text.nodeValue = this.log[this.current].filteredText}
   1818         }
   1819         if (this.status) {window.status = ""; delete this.status}
   1820       } else if (this.status) {
   1821         window.status = (this.current == null ? "" : this.log[this.current].text);
   1822       }
   1823     }
   1824     //
   1825     //  Clean up the log data no longer needed
   1826     //
   1827     delete this.log[n].next; delete this.log[n].prev;
   1828     delete this.log[n].filteredText;
   1829     //
   1830     //  If this is a restarted localization message, mark that it has been cleared
   1831     //    while waiting for the file to load.
   1832     //
   1833     if (this.log[n].restarted) {this.log[n].cleared = true}
   1834   },
   1835   
   1836   Remove: function () {
   1837     // FIXME:  do a fade out or something else interesting?
   1838     this.text.nodeValue = "";
   1839     this.div.style.display = "none";
   1840   },
   1841   
   1842   File: function (file) {
   1843     return this.Set(["LoadFile","Loading %1",file],null,null);
   1844   },
   1845   
   1846   Log: function () {
   1847     var strings = [];
   1848     for (var i = 1, m = this.log.length; i < m; i++) {strings[i] = this.log[i].text}
   1849     return strings.join("\n");
   1850   }
   1851 
   1852 };
   1853 
   1854 /**********************************************************/
   1855 
   1856 MathJax.Hub = {
   1857   config: {
   1858     root: "",
   1859     config: [],      // list of configuration files to load
   1860     styleSheets: [], // list of CSS files to load
   1861     styles: {        // styles to generate in-line
   1862       ".MathJax_Preview": {color: "#888"}
   1863     },
   1864     jax: [],         // list of input and output jax to load
   1865     extensions: [],  // list of extensions to load
   1866     preJax: null,    // pattern to remove from before math script tag
   1867     postJax: null,   // pattern to remove from after math script tag
   1868     displayAlign: 'center',       // how to align displayed equations (left, center, right)
   1869     displayIndent: '0',           // indentation for displayed equations (when not centered)
   1870     preRemoveClass: 'MathJax_Preview', // class of objects to remove preceeding math script
   1871     showProcessingMessages: true, // display "Processing math: nn%" messages or not
   1872     messageStyle: "normal",       // set to "none" or "simple" (for "Loading..." and "Processing...")
   1873     delayStartupUntil: "none",    // set to "onload" to delay setup until the onload handler runs
   1874                                   // set to "configured" to delay startup until MathJax.Hub.Configured() is called
   1875                                   // set to a Callback to wait for before continuing with the startup
   1876     skipStartupTypeset: false,    // set to true to skip PreProcess and Process during startup
   1877     elements: [],             // array of elements to process when none is given explicitly
   1878     positionToHash: true,    // after initial typeset pass, position to #hash location?
   1879      
   1880     showMathMenu: true,      // attach math context menu to typeset math?
   1881     showMathMenuMSIE: true,  // separtely determine if MSIE should have math menu
   1882                              //  (since the code for that is a bit delicate)
   1883     
   1884     menuSettings: {
   1885       zoom: "None",        //  when to do MathZoom
   1886       CTRL: false,         //    require CTRL for MathZoom?
   1887       ALT: false,          //    require Alt or Option?
   1888       CMD: false,          //    require CMD?
   1889       Shift: false,        //    require Shift?
   1890       discoverable: false, //  make math menu discoverable on hover?
   1891       zscale: "200%",      //  the scaling factor for MathZoom
   1892       renderer: null,      //  set when Jax are loaded
   1893       font: "Auto",        //  what font HTML-CSS should use
   1894       context: "MathJax",  //  or "Browser" for pass-through to browser menu
   1895       locale: null,        //  the language to use for messages
   1896       mpContext: false,    //  true means pass menu events to MathPlayer in IE
   1897       mpMouse: false,      //  true means pass mouse events to MathPlayer in IE
   1898       texHints: true,      //  include class names for TeXAtom elements
   1899       FastPreview: null,   //  use PreviewHTML output as preview?
   1900       assistiveMML: null,  //  include hidden MathML for screen readers?
   1901       inTabOrder: true,    //  set to false if math elements should be included in the tabindex
   1902       semantics: false     //  add semantics tag with original form in MathML output
   1903     },
   1904     
   1905     errorSettings: {
   1906        // localized HTML snippet structure for message to use
   1907       message: ["[",["MathProcessingError","Math Processing Error"],"]"],
   1908       style: {color: "#CC0000", "font-style":"italic"}  // style for message
   1909     },
   1910     
   1911     ignoreMMLattributes: {}  // attributes not to copy to HTML-CSS or SVG output
   1912                              //   from MathML input (in addition to the ones in MML.nocopyAttributes).
   1913                              //   An id set to true will be ignored, one set to false will
   1914                              //   be allowed (even if other criteria normally would prevent
   1915                              //   it from being copied); use false carefully!
   1916   },
   1917   
   1918   preProcessors: MathJax.Callback.Hooks(true), // list of callbacks for preprocessing (initialized by extensions)
   1919   inputJax: {},          // mime-type mapped to input jax (by registration)
   1920   outputJax: {order:{}}, // mime-type mapped to output jax list (by registration)
   1921 
   1922   processSectionDelay: 50, // pause between input and output phases of processing
   1923   processUpdateTime: 250, // time between screen updates when processing math (milliseconds)
   1924   processUpdateDelay: 10, // pause between screen updates to allow other processing (milliseconds)
   1925 
   1926   signal: MathJax.Callback.Signal("Hub"), // Signal used for Hub events
   1927 
   1928   Config: function (def) {
   1929     this.Insert(this.config,def);
   1930     if (this.config.Augment) {this.Augment(this.config.Augment)}
   1931   },
   1932   CombineConfig: function (name,def) {
   1933     var config = this.config, id, parent; name = name.split(/\./);
   1934     for (var i = 0, m = name.length; i < m; i++) {
   1935       id = name[i]; if (!config[id]) {config[id] = {}}
   1936       parent = config; config = config[id];
   1937     }
   1938     parent[id] = config = this.Insert(def,config);
   1939     return config;
   1940   },
   1941   
   1942   Register: {
   1943     PreProcessor: function () {return MathJax.Hub.preProcessors.Add.apply(MathJax.Hub.preProcessors,arguments)},
   1944     MessageHook: function () {return MathJax.Hub.signal.MessageHook.apply(MathJax.Hub.signal,arguments)},
   1945     StartupHook: function () {return MathJax.Hub.Startup.signal.MessageHook.apply(MathJax.Hub.Startup.signal,arguments)},
   1946     LoadHook: function () {return MathJax.Ajax.LoadHook.apply(MathJax.Ajax,arguments)}
   1947   },
   1948   UnRegister: {
   1949     PreProcessor: function (hook) {MathJax.Hub.preProcessors.Remove(hook)},
   1950     MessageHook: function (hook) {MathJax.Hub.signal.RemoveHook(hook)},
   1951     StartupHook: function (hook) {MathJax.Hub.Startup.signal.RemoveHook(hook)},
   1952     LoadHook: function (hook) {MathJax.Ajax.removeHook(hook)}
   1953   },
   1954   
   1955   getAllJax: function (element) {
   1956     var jax = [], scripts = this.elementScripts(element);
   1957     for (var i = 0, m = scripts.length; i < m; i++) {
   1958       if (scripts[i].MathJax && scripts[i].MathJax.elementJax)
   1959         {jax.push(scripts[i].MathJax.elementJax)}
   1960     }
   1961     return jax;
   1962   },
   1963   
   1964   getJaxByType: function (type,element) {
   1965     var jax = [], scripts = this.elementScripts(element);
   1966     for (var i = 0, m = scripts.length; i < m; i++) {
   1967       if (scripts[i].MathJax && scripts[i].MathJax.elementJax &&
   1968           scripts[i].MathJax.elementJax.mimeType === type)
   1969             {jax.push(scripts[i].MathJax.elementJax)}
   1970     }
   1971     return jax;
   1972   },
   1973   
   1974   getJaxByInputType: function (type,element) {
   1975     var jax = [], scripts = this.elementScripts(element);
   1976     for (var i = 0, m = scripts.length; i < m; i++) {
   1977       if (scripts[i].MathJax && scripts[i].MathJax.elementJax &&
   1978           scripts[i].type && scripts[i].type.replace(/ *;(.|\s)*/,"") === type)
   1979         {jax.push(scripts[i].MathJax.elementJax)}
   1980     }
   1981     return jax;
   1982   },
   1983   
   1984   getJaxFor: function (element) {
   1985     if (typeof(element) === 'string') {element = document.getElementById(element)}
   1986     if (element && element.MathJax) {return element.MathJax.elementJax}
   1987     if (this.isMathJaxNode(element)) {
   1988       if (!element.isMathJax) {element = element.firstChild}  // for NativeMML output
   1989       while (element && !element.jaxID) {element = element.parentNode}
   1990       if (element) {return MathJax.OutputJax[element.jaxID].getJaxFromMath(element)}
   1991     }
   1992     return null;
   1993   },
   1994   
   1995   isJax: function (element) {
   1996     if (typeof(element) === 'string') {element = document.getElementById(element)}
   1997     if (this.isMathJaxNode(element)) {return 1}
   1998     if (element && (element.tagName||"").toLowerCase() === 'script') {
   1999       if (element.MathJax) 
   2000         {return (element.MathJax.state === MathJax.ElementJax.STATE.PROCESSED ? 1 : -1)}
   2001       if (element.type && this.inputJax[element.type.replace(/ *;(.|\s)*/,"")]) {return -1}
   2002     }
   2003     return 0;
   2004   },
   2005   isMathJaxNode: function (element) {
   2006     return !!element && (element.isMathJax || (element.className||"") === "MathJax_MathML");
   2007   },
   2008   
   2009   setRenderer: function (renderer,type) {
   2010     if (!renderer) return;
   2011     if (!MathJax.OutputJax[renderer]) {
   2012       this.config.menuSettings.renderer = "";
   2013       var file = "[MathJax]/jax/output/"+renderer+"/config.js";
   2014       return MathJax.Ajax.Require(file,["setRenderer",this,renderer,type]);
   2015     } else {
   2016       this.config.menuSettings.renderer = renderer;
   2017       if (type == null) {type = "jax/mml"}
   2018       var jax = this.outputJax;
   2019       if (jax[type] && jax[type].length) {
   2020         if (renderer !== jax[type][0].id) {
   2021           jax[type].unshift(MathJax.OutputJax[renderer]);
   2022           return this.signal.Post(["Renderer Selected",renderer]);
   2023         }
   2024       }
   2025       return null;
   2026     }
   2027   },
   2028 
   2029   Queue: function () {
   2030     return this.queue.Push.apply(this.queue,arguments);
   2031   },
   2032   
   2033   Typeset: function (element,callback) {
   2034     if (!MathJax.isReady) return null;
   2035     var ec = this.elementCallback(element,callback);
   2036     if (ec.count) {
   2037       var queue = MathJax.Callback.Queue(
   2038         ["PreProcess",this,ec.elements],
   2039         ["Process",this,ec.elements]
   2040       );
   2041     }
   2042     return queue.Push(ec.callback);
   2043   },
   2044   
   2045   PreProcess: function (element,callback) {
   2046     var ec = this.elementCallback(element,callback);
   2047     var queue = MathJax.Callback.Queue();
   2048     if (ec.count) {
   2049       var elements = (ec.count === 1 ? [ec.elements] : ec.elements);
   2050       queue.Push(["Post",this.signal,["Begin PreProcess",ec.elements]]);
   2051       for (var i = 0, m = elements.length; i < m; i++) {
   2052         if (elements[i]) {queue.Push(["Execute",this.preProcessors,elements[i]])}
   2053       }
   2054       queue.Push(["Post",this.signal,["End PreProcess",ec.elements]]);
   2055     }
   2056     return queue.Push(ec.callback);
   2057   },
   2058 
   2059   Process:   function (element,callback) {return this.takeAction("Process",element,callback)},
   2060   Update:    function (element,callback) {return this.takeAction("Update",element,callback)},
   2061   Reprocess: function (element,callback) {return this.takeAction("Reprocess",element,callback)},
   2062   Rerender:  function (element,callback) {return this.takeAction("Rerender",element,callback)},
   2063   
   2064   takeAction: function (action,element,callback) {
   2065     var ec = this.elementCallback(element,callback);
   2066     var elements = ec.elements;
   2067     var queue = MathJax.Callback.Queue(["Clear",this.signal]);
   2068     var state = {
   2069       scripts: [],                  // filled in by prepareScripts
   2070       start: new Date().getTime(),  // timer for processing messages
   2071       i: 0, j: 0,                   // current script, current jax
   2072       jax: {},                      // scripts grouped by output jax
   2073       jaxIDs: []                    // id's of jax used
   2074     };
   2075     if (ec.count) {
   2076       var delay = ["Delay",MathJax.Callback,this.processSectionDelay];
   2077       if (!delay[2]) {delay = {}}
   2078       queue.Push(
   2079         ["Post",this.signal,["Begin "+action,elements]],
   2080         ["Post",this.signal,["Begin Math",elements,action]],
   2081         ["prepareScripts",this,action,elements,state],
   2082         ["Post",this.signal,["Begin Math Input",elements,action]],
   2083         ["processInput",this,state],
   2084         ["Post",this.signal,["End Math Input",elements,action]],
   2085         delay,
   2086         ["prepareOutput",this,state,"preProcess"],
   2087         delay,
   2088         ["Post",this.signal,["Begin Math Output",elements,action]],
   2089         ["processOutput",this,state],
   2090         ["Post",this.signal,["End Math Output",elements,action]],
   2091         delay,
   2092         ["prepareOutput",this,state,"postProcess"],
   2093         delay,
   2094         ["Post",this.signal,["End Math",elements,action]],
   2095         ["Post",this.signal,["End "+action,elements]]
   2096       );
   2097     }
   2098     return queue.Push(ec.callback);
   2099   },
   2100   
   2101   scriptAction: {
   2102     Process: function (script) {},
   2103     Update: function (script) {
   2104       var jax = script.MathJax.elementJax;
   2105       if (jax && jax.needsUpdate()) {jax.Remove(true); script.MathJax.state = jax.STATE.UPDATE}
   2106         else {script.MathJax.state = jax.STATE.PROCESSED}
   2107     },
   2108     Reprocess: function (script) {
   2109       var jax = script.MathJax.elementJax;
   2110       if (jax) {jax.Remove(true); script.MathJax.state = jax.STATE.UPDATE}
   2111     },
   2112     Rerender: function (script) {
   2113       var jax = script.MathJax.elementJax;
   2114       if (jax) {jax.Remove(true); script.MathJax.state = jax.STATE.OUTPUT}
   2115     }
   2116   },
   2117   
   2118   prepareScripts: function (action,element,state) {
   2119     if (arguments.callee.disabled) return;
   2120     var scripts = this.elementScripts(element);
   2121     var STATE = MathJax.ElementJax.STATE;
   2122     for (var i = 0, m = scripts.length; i < m; i++) {
   2123       var script = scripts[i];
   2124       if (script.type && this.inputJax[script.type.replace(/ *;(.|\n)*/,"")]) {
   2125         if (script.MathJax) {
   2126           if (script.MathJax.elementJax && script.MathJax.elementJax.hover) {
   2127             MathJax.Extension.MathEvents.Hover.ClearHover(script.MathJax.elementJax);
   2128           }
   2129           if (script.MathJax.state !== STATE.PENDING) {this.scriptAction[action](script)}
   2130         }
   2131         if (!script.MathJax) {script.MathJax = {state: STATE.PENDING}}
   2132         if (script.MathJax.error) delete script.MathJax.error;
   2133         if (script.MathJax.state !== STATE.PROCESSED) {state.scripts.push(script)}
   2134       }
   2135     }
   2136   },
   2137   
   2138   checkScriptSiblings: function (script) {
   2139     if (script.MathJax.checked) return;
   2140     var config = this.config, pre = script.previousSibling;
   2141     if (pre && pre.nodeName === "#text") {
   2142       var preJax,postJax, post = script.nextSibling;
   2143       if (post && post.nodeName !== "#text") {post = null}
   2144       if (config.preJax) {
   2145         if (typeof(config.preJax) === "string") {config.preJax = new RegExp(config.preJax+"$")}
   2146         preJax = pre.nodeValue.match(config.preJax);
   2147       }
   2148       if (config.postJax && post) {
   2149         if (typeof(config.postJax) === "string") {config.postJax = new RegExp("^"+config.postJax)}
   2150         postJax = post.nodeValue.match(config.postJax);
   2151       }
   2152       if (preJax && (!config.postJax || postJax)) {
   2153         pre.nodeValue  = pre.nodeValue.replace
   2154           (config.preJax,(preJax.length > 1? preJax[1] : ""));
   2155         pre = null;
   2156       }
   2157       if (postJax && (!config.preJax || preJax)) {
   2158         post.nodeValue = post.nodeValue.replace
   2159           (config.postJax,(postJax.length > 1? postJax[1] : ""));
   2160       }
   2161       if (pre && !pre.nodeValue.match(/\S/)) {pre = pre.previousSibling}
   2162     }
   2163     if (config.preRemoveClass && pre && pre.className === config.preRemoveClass)
   2164       {script.MathJax.preview = pre}
   2165     script.MathJax.checked = 1;
   2166   },
   2167   
   2168   processInput: function (state) {
   2169     var jax, STATE = MathJax.ElementJax.STATE;
   2170     var script, prev, m = state.scripts.length;
   2171     try {
   2172       //
   2173       //  Loop through the scripts
   2174       //
   2175       while (state.i < m) {
   2176         script = state.scripts[state.i]; if (!script) {state.i++; continue}
   2177         //
   2178         //  Remove previous error marker, if any
   2179         //
   2180         prev = script.previousSibling;
   2181         if (prev && prev.className === "MathJax_Error") {prev.parentNode.removeChild(prev)}
   2182         //
   2183         //  Check if already processed or needs processing
   2184         //
   2185         if (!script.MathJax || script.MathJax.state === STATE.PROCESSED) {state.i++; continue};
   2186         if (!script.MathJax.elementJax || script.MathJax.state === STATE.UPDATE) {
   2187           this.checkScriptSiblings(script);                 // remove preJax/postJax etc.
   2188           var type = script.type.replace(/ *;(.|\s)*/,"");  // the input jax type
   2189           var input = this.inputJax[type];                  // the input jax itself
   2190           jax = input.Process(script,state);                // run the input jax
   2191           if (typeof jax === 'function') {                  // if a callback was returned
   2192             if (jax.called) continue;                       //   go back and call Process() again
   2193             this.RestartAfter(jax);                         //   wait for the callback
   2194           }
   2195           jax = jax.Attach(script,input.id);                // register the jax on the script
   2196           this.saveScript(jax,state,script,STATE);          // add script to state
   2197           this.postInputHooks.Execute(jax,input.id,script); // run global jax filters
   2198         } else if (script.MathJax.state === STATE.OUTPUT) {
   2199           this.saveScript(script.MathJax.elementJax,state,script,STATE); // add script to state
   2200         }
   2201         //
   2202         //  Go on to the next script, and check if we need to update the processing message
   2203         //
   2204         state.i++; var now = new Date().getTime();
   2205         if (now - state.start > this.processUpdateTime && state.i < state.scripts.length)
   2206           {state.start = now; this.RestartAfter(MathJax.Callback.Delay(1))}
   2207       }
   2208     } catch (err) {return this.processError(err,state,"Input")}
   2209     //
   2210     //  Put up final message, reset the state and return
   2211     //
   2212     if (state.scripts.length && this.config.showProcessingMessages)
   2213       {MathJax.Message.Set(["ProcessMath","Processing math: %1%%",100],0)}
   2214     state.start = new Date().getTime(); state.i = state.j = 0;
   2215     return null;
   2216   },
   2217   postInputHooks: MathJax.Callback.Hooks(true),  // hooks to run after element jax is created
   2218   saveScript: function (jax,state,script,STATE) {
   2219     //
   2220     //  Check that output jax exists
   2221     //
   2222     if (!this.outputJax[jax.mimeType]) {
   2223       script.MathJax.state = STATE.UPDATE;
   2224       throw Error("No output jax registered for "+jax.mimeType);
   2225     }
   2226     //
   2227     //  Record the output jax
   2228     //  and put this script in the queue for that jax
   2229     //
   2230     jax.outputJax = this.outputJax[jax.mimeType][0].id;
   2231     if (!state.jax[jax.outputJax]) {
   2232       if (state.jaxIDs.length === 0) {
   2233         // use original array until we know there are more (rather than two copies)
   2234         state.jax[jax.outputJax] = state.scripts;
   2235       } else {
   2236         if (state.jaxIDs.length === 1) // get the script so far for the existing jax
   2237           {state.jax[state.jaxIDs[0]] = state.scripts.slice(0,state.i)}
   2238         state.jax[jax.outputJax] = []; // start a new array for the new jax
   2239       }
   2240       state.jaxIDs.push(jax.outputJax); // save the ID of the jax
   2241     }
   2242     if (state.jaxIDs.length > 1) {state.jax[jax.outputJax].push(script)}
   2243     //
   2244     //  Mark script as needing output
   2245     //
   2246     script.MathJax.state = STATE.OUTPUT;
   2247   },
   2248   
   2249   //
   2250   //  Pre- and post-process scripts by jax
   2251   //    (to get scaling factors, hide/show output, and so on)
   2252   //  Since this can cause the jax to load, we need to trap restarts
   2253   //
   2254   prepareOutput: function (state,method) {
   2255     while (state.j < state.jaxIDs.length) {
   2256       var id = state.jaxIDs[state.j], JAX = MathJax.OutputJax[id];
   2257       if (JAX[method]) {
   2258         try {
   2259           var result = JAX[method](state);
   2260           if (typeof result === 'function') {
   2261             if (result.called) continue;  // go back and try again
   2262             this.RestartAfter(result);
   2263           }
   2264         } catch (err) {
   2265           if (!err.restart) {
   2266             MathJax.Message.Set(["PrepError","Error preparing %1 output (%2)",id,method],null,600);
   2267             MathJax.Hub.lastPrepError = err;
   2268             state.j++;
   2269           }
   2270           return MathJax.Callback.After(["prepareOutput",this,state,method],err.restart);
   2271         }
   2272       }
   2273       state.j++;
   2274     }
   2275     return null;
   2276   },
   2277 
   2278   processOutput: function (state) {
   2279     var result, STATE = MathJax.ElementJax.STATE, script, m = state.scripts.length;
   2280     try {
   2281       //
   2282       //  Loop through the scripts
   2283       //
   2284       while (state.i < m) {
   2285         //
   2286         //  Check that there is an element jax
   2287         //
   2288         script = state.scripts[state.i];
   2289         if (!script || !script.MathJax || script.MathJax.error) {state.i++; continue}
   2290         var jax = script.MathJax.elementJax; if (!jax) {state.i++; continue}
   2291         //
   2292         //  Call the output Jax's Process method (which will be its Translate()
   2293         //  method once loaded).  Mark it as complete and remove the preview unless
   2294         //  the Process() call returns an explicit false value (in which case, it will
   2295         //  handle this later during the postProcess phase, as HTML-CSS does).
   2296         //
   2297         result = MathJax.OutputJax[jax.outputJax].Process(script,state);
   2298         if (result !== false) {
   2299           script.MathJax.state = STATE.PROCESSED;
   2300           if (script.MathJax.preview) {script.MathJax.preview.innerHTML = ""}
   2301           //
   2302           //  Signal that new math is available
   2303           //
   2304           this.signal.Post(["New Math",jax.inputID]); // FIXME: wait for this?  (i.e., restart if returns uncalled callback)
   2305         }
   2306         //
   2307         //  Go on to next math expression
   2308         //
   2309         state.i++;
   2310         //
   2311         //  Update the processing message, if needed
   2312         //
   2313         var now = new Date().getTime();
   2314         if (now - state.start > this.processUpdateTime && state.i < state.scripts.length)
   2315           {state.start = now; this.RestartAfter(MathJax.Callback.Delay(this.processUpdateDelay))}
   2316       }
   2317     } catch (err) {return this.processError(err,state,"Output")}
   2318     //
   2319     //  Put up the typesetting-complete message
   2320     //
   2321     if (state.scripts.length && this.config.showProcessingMessages) {
   2322       MathJax.Message.Set(["TypesetMath","Typesetting math: %1%%",100],0);
   2323       MathJax.Message.Clear(0);
   2324     }
   2325     state.i = state.j = 0;
   2326     return null;
   2327   },
   2328   
   2329   processMessage: function (state,type) {
   2330     var m = Math.floor(state.i/(state.scripts.length)*100);
   2331     var message = (type === "Output" ? ["TypesetMath","Typesetting math: %1%%"] :
   2332                                        ["ProcessMath","Processing math: %1%%"]);
   2333     if (this.config.showProcessingMessages) {MathJax.Message.Set(message.concat(m),0)}
   2334   },
   2335 
   2336   processError: function (err,state,type) {
   2337     if (!err.restart) {
   2338       if (!this.config.errorSettings.message) {throw err}
   2339       this.formatError(state.scripts[state.i],err); state.i++;
   2340     }
   2341     this.processMessage(state,type);
   2342     return MathJax.Callback.After(["process"+type,this,state],err.restart);
   2343   },
   2344   
   2345   formatError: function (script,err) {
   2346     var LOCALIZE = function (id,text,arg1,arg2) {return MathJax.Localization._(id,text,arg1,arg2)};
   2347     //
   2348     //  Get the error message, URL, and line, and save it for
   2349     //    reporting in the Show Math As Error menu
   2350     //
   2351     var message = LOCALIZE("ErrorMessage","Error: %1",err.message)+"\n";
   2352     if (err.sourceURL||err.fileName) message += "\n"+LOCALIZE("ErrorFile","file: %1",err.sourceURL||err.fileName);
   2353     if (err.line||err.lineNumber) message += "\n"+LOCALIZE("ErrorLine","line: %1",err.line||err.lineNumber);
   2354     message += "\n\n"+LOCALIZE("ErrorTips","Debugging tips: use %1, inspect %2 in the browser console","'unpacked/MathJax.js'","'MathJax.Hub.lastError'");
   2355     script.MathJax.error = MathJax.OutputJax.Error.Jax(message,script);
   2356     if (script.MathJax.elementJax)
   2357       script.MathJax.error.inputID = script.MathJax.elementJax.inputID;
   2358     //
   2359     //  Create the [Math Processing Error] span
   2360     //
   2361     var errorSettings = this.config.errorSettings;
   2362     var errorText = LOCALIZE(errorSettings.messageId,errorSettings.message);
   2363     var error = MathJax.HTML.Element("span", {
   2364       className:"MathJax_Error", jaxID:"Error", isMathJax:true,
   2365       id: script.MathJax.error.inputID+"-Frame"
   2366     },[["span",null,errorText]]);
   2367     //
   2368     //  Attach the menu events
   2369     //
   2370     MathJax.Ajax.Require("[MathJax]/extensions/MathEvents.js",function () {
   2371       var EVENT = MathJax.Extension.MathEvents.Event,
   2372           HUB = MathJax.Hub;
   2373       error.oncontextmenu = EVENT.Menu;
   2374       error.onmousedown = EVENT.Mousedown;
   2375       error.onkeydown = EVENT.Keydown;
   2376       error.tabIndex = HUB.getTabOrder(HUB.getJaxFor(script));
   2377     });
   2378     //
   2379     //  Insert the error into the page and remove any preview
   2380     //
   2381     var node = document.getElementById(error.id);
   2382     if (node) node.parentNode.removeChild(node);
   2383     script.parentNode.insertBefore(error,script);
   2384     if (script.MathJax.preview) {script.MathJax.preview.innerHTML = ""}
   2385     //
   2386     //  Save the error for debugging purposes
   2387     //  Report the error as a signal
   2388     //
   2389     this.lastError = err;
   2390     this.signal.Post(["Math Processing Error",script,err]);
   2391   },
   2392   
   2393   RestartAfter: function (callback) {
   2394     throw this.Insert(Error("restart"),{restart: MathJax.Callback(callback)});
   2395   },
   2396   
   2397   elementCallback: function (element,callback) {
   2398     if (callback == null && (element instanceof Array || typeof element === 'function'))
   2399       {try {MathJax.Callback(element); callback = element; element = null} catch(e) {}}
   2400     if (element == null) {element = this.config.elements || []}
   2401     if (this.isHTMLCollection(element)) {element = this.HTMLCollection2Array(element)}
   2402     if (!(element instanceof Array)) {element = [element]}
   2403     element = [].concat(element); // make a copy so the original isn't changed
   2404     for (var i = 0, m = element.length; i < m; i++)
   2405       {if (typeof(element[i]) === 'string') {element[i] = document.getElementById(element[i])}}
   2406     if (!document.body) {document.body = document.getElementsByTagName("body")[0]}
   2407     if (element.length == 0) {element.push(document.body)}
   2408     if (!callback) {callback = {}}
   2409     return {
   2410       count: element.length, 
   2411       elements: (element.length === 1 ? element[0] : element),
   2412       callback: callback
   2413     };
   2414   },
   2415   
   2416   elementScripts: function (element) {
   2417     var scripts = [];
   2418     if (element instanceof Array || this.isHTMLCollection(element)) {
   2419       for (var i = 0, m = element.length; i < m; i++) {
   2420         var alreadyDone = 0;
   2421         for (var j = 0; j < i && !alreadyDone; j++)
   2422           {alreadyDone = element[j].contains(element[i])}
   2423         if (!alreadyDone) scripts.push.apply(scripts,this.elementScripts(element[i]));
   2424       }
   2425       return scripts;
   2426     }
   2427     if (typeof(element) === 'string') {element = document.getElementById(element)}
   2428     if (!document.body) {document.body = document.getElementsByTagName("body")[0]}
   2429     if (element == null) {element = document.body}
   2430     if (element.tagName != null && element.tagName.toLowerCase() === "script") {return [element]}
   2431     scripts = element.getElementsByTagName("script");
   2432     if (this.msieHTMLCollectionBug) {scripts = this.HTMLCollection2Array(scripts)}
   2433     return scripts;
   2434   },
   2435 
   2436   //
   2437   //  IE8 fails to check "obj instanceof HTMLCollection" for some values of obj.
   2438   //
   2439   isHTMLCollection: function (obj) {
   2440     return ("HTMLCollection" in window && typeof(obj) === "object" && obj instanceof HTMLCollection);
   2441   },
   2442   //
   2443   //  IE8 doesn't deal with HTMLCollection as an array, so convert to array
   2444   //
   2445   HTMLCollection2Array: function (nodes) {
   2446     if (!this.msieHTMLCollectionBug) {return [].slice.call(nodes)}
   2447     var NODES = [];
   2448     for (var i = 0, m = nodes.length; i < m; i++) {NODES[i] = nodes[i]}
   2449     return NODES;
   2450   },
   2451   
   2452   Insert: function (dst,src) {
   2453     for (var id in src) {if (src.hasOwnProperty(id)) {
   2454       // allow for concatenation of arrays?
   2455       if (typeof src[id] === 'object' && !(src[id] instanceof Array) &&
   2456          (typeof dst[id] === 'object' || typeof dst[id] === 'function')) {
   2457         this.Insert(dst[id],src[id]);
   2458       } else {
   2459         dst[id] = src[id];
   2460       }
   2461     }}
   2462     return dst;
   2463   },
   2464 
   2465   getTabOrder: function(script) {
   2466     return this.config.menuSettings.inTabOrder ? 0 : -1;
   2467   },
   2468 
   2469   // Old browsers (e.g. Internet Explorer <= 8) do not support trim().
   2470   SplitList: ("trim" in String.prototype ?
   2471               function (list) {return list.trim().split(/\s+/)} :
   2472               function (list) {return list.replace(/^\s+/,'').
   2473                                            replace(/\s+$/,'').split(/\s+/)})
   2474 };
   2475 MathJax.Hub.Insert(MathJax.Hub.config.styles,MathJax.Message.styles);
   2476 MathJax.Hub.Insert(MathJax.Hub.config.styles,{".MathJax_Error":MathJax.Hub.config.errorSettings.style});
   2477 
   2478 //
   2479 //  Storage area for extensions and preprocessors
   2480 //
   2481 MathJax.Extension = {};
   2482 
   2483 //
   2484 //  Hub Startup code
   2485 //
   2486 MathJax.Hub.Configured = MathJax.Callback({}); // called when configuration is complete
   2487 MathJax.Hub.Startup = {
   2488   script: "", // the startup script from the SCRIPT call that loads MathJax.js
   2489   queue:   MathJax.Callback.Queue(),           // Queue used for startup actions
   2490   signal:  MathJax.Callback.Signal("Startup"), // Signal used for startup events
   2491   params:  {},
   2492 
   2493   //
   2494   //  Load the configuration files
   2495   //
   2496   Config: function () {
   2497     this.queue.Push(["Post",this.signal,"Begin Config"]);
   2498     //
   2499     //  If a locale is given as a parameter,
   2500     //    set the locale and the default menu value for the locale
   2501     //
   2502     if (this.params.locale) {
   2503       MathJax.Localization.resetLocale(this.params.locale);
   2504       MathJax.Hub.config.menuSettings.locale = this.params.locale;
   2505     }
   2506     //
   2507     //  Run the config files, if any are given in the parameter list
   2508     //
   2509     if (this.params.config) {
   2510       var files = this.params.config.split(/,/);
   2511       for (var i = 0, m = files.length; i < m; i++) {
   2512         if (!files[i].match(/\.js$/)) {files[i] += ".js"}
   2513         this.queue.Push(["Require",MathJax.Ajax,this.URL("config",files[i])]);
   2514       }
   2515     }
   2516     //
   2517     //  Perform author configuration from in-line MathJax = {...}
   2518     //
   2519     this.queue.Push(["Config",MathJax.Hub,MathJax.AuthorConfig]);
   2520     //
   2521     //  Run the deprecated configuration script, if any (ignoring return value)
   2522     //  Wait for the startup delay signal
   2523     //  Run the mathjax-config blocks
   2524     //  Load the files in the configuration's config array
   2525     //
   2526     if (this.script.match(/\S/)) {this.queue.Push(this.script+";\n1;")}
   2527     this.queue.Push(
   2528       ["ConfigDelay",this],
   2529       ["ConfigBlocks",this],
   2530       [function (THIS) {return THIS.loadArray(MathJax.Hub.config.config,"config",null,true)},this],
   2531       ["Post",this.signal,"End Config"]
   2532     );
   2533   },
   2534   //
   2535   //  Return the delay callback
   2536   //
   2537   ConfigDelay: function () {
   2538     var delay = this.params.delayStartupUntil || MathJax.Hub.config.delayStartupUntil;
   2539     if (delay === "onload") {return this.onload}
   2540     if (delay === "configured") {return MathJax.Hub.Configured}
   2541     return delay;
   2542   },
   2543   //
   2544   //  Run the scripts of type=text/x-mathjax-config
   2545   //
   2546   ConfigBlocks: function () {
   2547     var scripts = document.getElementsByTagName("script");
   2548     var last = null, queue = MathJax.Callback.Queue();
   2549     for (var i = 0, m = scripts.length; i < m; i++) {
   2550       var type = String(scripts[i].type).replace(/ /g,"");
   2551       if (type.match(/^text\/x-mathjax-config(;.*)?$/) && !type.match(/;executed=true/)) {
   2552         scripts[i].type += ";executed=true";
   2553         last = queue.Push(scripts[i].innerHTML+";\n1;");
   2554       }
   2555     }
   2556     return last;
   2557   },
   2558 
   2559   //
   2560   //  Read cookie and set up menu defaults
   2561   //  (set the locale according to the cookie)
   2562   //  (adjust the jax to accommodate renderer preferences)
   2563   //
   2564   Cookie: function () {
   2565     return this.queue.Push(
   2566       ["Post",this.signal,"Begin Cookie"],
   2567       ["Get",MathJax.HTML.Cookie,"menu",MathJax.Hub.config.menuSettings],
   2568       [function (config) {
   2569         var SETTINGS = config.menuSettings;
   2570         if (SETTINGS.locale) MathJax.Localization.resetLocale(SETTINGS.locale);
   2571         var renderer = config.menuSettings.renderer, jax = config.jax;
   2572         if (renderer) {
   2573           var name = "output/"+renderer; jax.sort();
   2574           for (var i = 0, m = jax.length; i < m; i++) {
   2575             if (jax[i].substr(0,7) === "output/") break;
   2576           }
   2577           if (i == m-1) {jax.pop()} else {
   2578             while (i < m) {if (jax[i] === name) {jax.splice(i,1); break}; i++}
   2579           }
   2580           jax.unshift(name);
   2581         }
   2582         if (SETTINGS.CHTMLpreview != null) {
   2583           if (SETTINGS.FastPreview == null) SETTINGS.FastPreview = SETTINGS.CHTMLpreview;
   2584           delete SETTINGS.CHTMLpreview;
   2585         }
   2586         if (SETTINGS.FastPreview && !MathJax.Extension["fast-preview"])
   2587           MathJax.Hub.config.extensions.push("fast-preview.js");
   2588         if (config.menuSettings.assistiveMML && !MathJax.Extension.AssistiveMML)
   2589           MathJax.Hub.config.extensions.push("AssistiveMML.js");
   2590       },MathJax.Hub.config],
   2591       ["Post",this.signal,"End Cookie"]
   2592     );
   2593   },
   2594   //
   2595   //  Setup stylesheets and extra styles
   2596   //
   2597   Styles: function () {
   2598     return this.queue.Push(
   2599       ["Post",this.signal,"Begin Styles"],
   2600       ["loadArray",this,MathJax.Hub.config.styleSheets,"config"],
   2601       ["Styles",MathJax.Ajax,MathJax.Hub.config.styles],
   2602       ["Post",this.signal,"End Styles"]
   2603     );
   2604   },
   2605   //
   2606   //  Load the input and output jax
   2607   //
   2608   Jax: function () {
   2609     var config = MathJax.Hub.config, jax = MathJax.Hub.outputJax;
   2610     //  Save the order of the output jax since they are loading asynchronously
   2611     for (var i = 0, m = config.jax.length, k = 0; i < m; i++) {
   2612       var name = config.jax[i].substr(7);
   2613       if (config.jax[i].substr(0,7) === "output/" && jax.order[name] == null)
   2614         {jax.order[name] = k; k++}
   2615     }
   2616     var queue = MathJax.Callback.Queue();
   2617     return queue.Push(
   2618       ["Post",this.signal,"Begin Jax"],
   2619       ["loadArray",this,config.jax,"jax","config.js"],
   2620       ["Post",this.signal,"End Jax"]
   2621     );
   2622   },
   2623   //
   2624   //  Load the extensions
   2625   //
   2626   Extensions: function () {
   2627     var queue = MathJax.Callback.Queue();
   2628     return queue.Push(
   2629       ["Post",this.signal,"Begin Extensions"],
   2630       ["loadArray",this,MathJax.Hub.config.extensions,"extensions"],
   2631       ["Post",this.signal,"End Extensions"]
   2632     );
   2633   },
   2634   
   2635   //
   2636   //  Initialize the Message system
   2637   //
   2638   Message: function () {
   2639     MathJax.Message.Init(true);
   2640   },
   2641   
   2642   //
   2643   //  Set the math menu renderer, if it isn't already
   2644   //  (this must come after the jax are loaded)
   2645   //
   2646   Menu: function () {
   2647     var menu = MathJax.Hub.config.menuSettings, jax = MathJax.Hub.outputJax, registered;
   2648     for (var id in jax) {if (jax.hasOwnProperty(id)) {
   2649       if (jax[id].length) {registered = jax[id]; break}
   2650     }}
   2651     if (registered && registered.length) {
   2652       if (menu.renderer && menu.renderer !== registered[0].id)
   2653         {registered.unshift(MathJax.OutputJax[menu.renderer])}
   2654       menu.renderer = registered[0].id;
   2655     }
   2656   },
   2657   
   2658   //
   2659   //  Set the location to the designated hash position
   2660   //
   2661   Hash: function () {
   2662     if (MathJax.Hub.config.positionToHash && document.location.hash &&
   2663         document.body && document.body.scrollIntoView) {
   2664       var name = document.location.hash.substr(1);
   2665       var target = document.getElementById(name);
   2666       if (!target) {
   2667         var a = document.getElementsByTagName("a");
   2668         for (var i = 0, m = a.length; i < m; i++)
   2669           {if (a[i].name === name) {target = a[i]; break}}
   2670       }
   2671       if (target) {
   2672         while (!target.scrollIntoView) {target = target.parentNode}
   2673         target = this.HashCheck(target);
   2674         if (target && target.scrollIntoView)
   2675           {setTimeout(function () {target.scrollIntoView(true)},1)}
   2676       }
   2677     }
   2678   },
   2679   HashCheck: function (target) {
   2680     var jax = MathJax.Hub.getJaxFor(target);
   2681     if (jax && MathJax.OutputJax[jax.outputJax].hashCheck)
   2682       {target = MathJax.OutputJax[jax.outputJax].hashCheck(target)}
   2683     return target;
   2684   },
   2685   
   2686   //
   2687   //  Load the Menu and Zoom code, if it hasn't already been loaded.
   2688   //  This is called after the initial typeset, so should no longer be
   2689   //  competing with other page loads, but will make these available
   2690   //  if needed later on.
   2691   //
   2692   MenuZoom: function () {
   2693     if (MathJax.Hub.config.showMathMenu) {
   2694       if (!MathJax.Extension.MathMenu) {
   2695         setTimeout(
   2696           function () {
   2697             MathJax.Callback.Queue(
   2698               ["Require",MathJax.Ajax,"[MathJax]/extensions/MathMenu.js",{}],
   2699               ["loadDomain",MathJax.Localization,"MathMenu"]
   2700             )
   2701           },1000
   2702         );
   2703       } else {
   2704         setTimeout(
   2705           MathJax.Callback(["loadDomain",MathJax.Localization,"MathMenu"]),
   2706           1000
   2707         );
   2708       }
   2709       if (!MathJax.Extension.MathZoom) {
   2710         setTimeout(
   2711           MathJax.Callback(["Require",MathJax.Ajax,"[MathJax]/extensions/MathZoom.js",{}]),
   2712           2000
   2713         );
   2714       }
   2715     }
   2716   },
   2717   
   2718   //
   2719   //  Setup the onload callback
   2720   //
   2721   onLoad: function () {
   2722     var onload = this.onload =
   2723       MathJax.Callback(function () {MathJax.Hub.Startup.signal.Post("onLoad")});
   2724     if (document.body && document.readyState)
   2725       if (MathJax.Hub.Browser.isMSIE) {
   2726         // IE can change from loading to interactive before
   2727         //  full page is ready, so go with complete (even though
   2728         //  that means we may have to wait longer).
   2729         if (document.readyState === "complete") {return [onload]}
   2730       } else if (document.readyState !== "loading") {return [onload]}
   2731     if (window.addEventListener) {
   2732       window.addEventListener("load",onload,false);
   2733       if (!this.params.noDOMContentEvent)
   2734         {window.addEventListener("DOMContentLoaded",onload,false)}
   2735     }
   2736     else if (window.attachEvent) {window.attachEvent("onload",onload)}
   2737     else {window.onload = onload}
   2738     return onload;
   2739   },
   2740 
   2741   //
   2742   //  Perform the initial typesetting (or skip if configuration says to)
   2743   //
   2744   Typeset: function (element,callback) {
   2745     if (MathJax.Hub.config.skipStartupTypeset) {return function () {}}
   2746     return this.queue.Push(
   2747       ["Post",this.signal,"Begin Typeset"],
   2748       ["Typeset",MathJax.Hub,element,callback],
   2749       ["Post",this.signal,"End Typeset"]
   2750     );
   2751   },
   2752 
   2753   //
   2754   //  Create a URL in the MathJax hierarchy
   2755   //
   2756   URL: function (dir,name) {
   2757     if (!name.match(/^([a-z]+:\/\/|\[|\/)/)) {name = "[MathJax]/"+dir+"/"+name}
   2758     return name;
   2759   },
   2760 
   2761   //
   2762   //  Load an array of files, waiting for all of them
   2763   //  to be loaded before going on
   2764   //
   2765   loadArray: function (files,dir,name,synchronous) {
   2766     if (files) {
   2767       if (!(files instanceof Array)) {files = [files]}
   2768       if (files.length) {
   2769         var queue = MathJax.Callback.Queue(), callback = {}, file;
   2770         for (var i = 0, m = files.length; i < m; i++) {
   2771           file = this.URL(dir,files[i]);
   2772           if (name) {file += "/" + name}
   2773           if (synchronous) {queue.Push(["Require",MathJax.Ajax,file,callback])}
   2774                       else {queue.Push(MathJax.Ajax.Require(file,callback))}
   2775         }
   2776         return queue.Push({}); // wait for everything to finish
   2777       }
   2778     }
   2779     return null;
   2780   }
   2781   
   2782 };
   2783 
   2784 
   2785 /**********************************************************/
   2786 
   2787 (function (BASENAME) {
   2788   var BASE = window[BASENAME], ROOT = "["+BASENAME+"]";
   2789   var HUB = BASE.Hub, AJAX = BASE.Ajax, CALLBACK = BASE.Callback;
   2790 
   2791   var JAX = MathJax.Object.Subclass({
   2792     JAXFILE: "jax.js",
   2793     require: null, // array of files to load before jax.js is complete
   2794     config: {},
   2795     //
   2796     //  Make a subclass and return an instance of it.
   2797     //  (FIXME: should we replace config with a copy of the constructor's
   2798     //   config?  Otherwise all subclasses share the same config structure.)
   2799     //
   2800     Init: function (def,cdef) {
   2801       if (arguments.length === 0) {return this}
   2802       return (this.constructor.Subclass(def,cdef))();
   2803     },
   2804     //
   2805     //  Augment by merging with class definition (not replacing)
   2806     //
   2807     Augment: function (def,cdef) {
   2808       var cObject = this.constructor, ndef = {};
   2809       if (def != null) {
   2810         for (var id in def) {if (def.hasOwnProperty(id)) {
   2811           if (typeof def[id] === "function")
   2812             {cObject.protoFunction(id,def[id])} else {ndef[id] = def[id]}
   2813         }}
   2814         // MSIE doesn't list toString even if it is not native so handle it separately
   2815         if (def.toString !== cObject.prototype.toString && def.toString !== {}.toString)
   2816           {cObject.protoFunction('toString',def.toString)}
   2817       }
   2818       HUB.Insert(cObject.prototype,ndef);
   2819       cObject.Augment(null,cdef);
   2820       return this;
   2821     },
   2822     Translate: function (script,state) {
   2823       throw Error(this.directory+"/"+this.JAXFILE+" failed to define the Translate() method");
   2824     },
   2825     Register: function (mimetype) {},
   2826     Config: function () {
   2827       this.config = HUB.CombineConfig(this.id,this.config);
   2828       if (this.config.Augment) {this.Augment(this.config.Augment)}
   2829     },
   2830     Startup: function () {},
   2831     loadComplete: function (file) {
   2832       if (file === "config.js") {
   2833         return AJAX.loadComplete(this.directory+"/"+file);
   2834       } else {
   2835         var queue = CALLBACK.Queue();
   2836         queue.Push(
   2837           HUB.Register.StartupHook("End Config",{}), // wait until config complete
   2838           ["Post",HUB.Startup.signal,this.id+" Jax Config"],
   2839           ["Config",this],
   2840           ["Post",HUB.Startup.signal,this.id+" Jax Require"],
   2841           // Config may set the required and extensions array,
   2842           //  so use functions to delay making the reference until needed
   2843           [function (THIS) {return MathJax.Hub.Startup.loadArray(THIS.require,this.directory)},this],
   2844           [function (config,id) {return MathJax.Hub.Startup.loadArray(config.extensions,"extensions/"+id)},this.config||{},this.id],
   2845           ["Post",HUB.Startup.signal,this.id+" Jax Startup"],
   2846           ["Startup",this],
   2847           ["Post",HUB.Startup.signal,this.id+" Jax Ready"]
   2848         );
   2849         if (this.copyTranslate) {
   2850           queue.Push(
   2851             [function (THIS) {
   2852               THIS.preProcess  = THIS.preTranslate;
   2853               THIS.Process     = THIS.Translate;
   2854               THIS.postProcess = THIS.postTranslate;
   2855             },this.constructor.prototype]
   2856           );
   2857         }
   2858         return queue.Push(["loadComplete",AJAX,this.directory+"/"+file]);
   2859       }
   2860     }
   2861   },{
   2862     id: "Jax",
   2863     version: "2.6.0",
   2864     directory: ROOT+"/jax",
   2865     extensionDir: ROOT+"/extensions"
   2866   });
   2867 
   2868   /***********************************/
   2869 
   2870   BASE.InputJax = JAX.Subclass({
   2871     elementJax: "mml",  // the element jax to load for this input jax
   2872     sourceMenuTitle: /*_(MathMenu)*/ ["Original","Original Form"],
   2873     copyTranslate: true,
   2874     Process: function (script,state) {
   2875       var queue = CALLBACK.Queue(), file;
   2876       // Load any needed element jax
   2877       var jax = this.elementJax; if (!(jax instanceof Array)) {jax = [jax]}
   2878       for (var i = 0, m = jax.length; i < m; i++) {
   2879         file = BASE.ElementJax.directory+"/"+jax[i]+"/"+this.JAXFILE;
   2880         if (!this.require) {this.require = []}
   2881           else if (!(this.require instanceof Array)) {this.require = [this.require]};
   2882         this.require.push(file);  // so Startup will wait for it to be loaded
   2883         queue.Push(AJAX.Require(file));
   2884       }
   2885       // Load the input jax
   2886       file = this.directory+"/"+this.JAXFILE;
   2887       var load = queue.Push(AJAX.Require(file));
   2888       if (!load.called) {
   2889         this.constructor.prototype.Process = function () {
   2890           if (!load.called) {return load}
   2891           throw Error(file+" failed to load properly");
   2892         }
   2893       }
   2894       // Load the associated output jax
   2895       jax = HUB.outputJax["jax/"+jax[0]];
   2896       if (jax) {queue.Push(AJAX.Require(jax[0].directory+"/"+this.JAXFILE))}
   2897       return queue.Push({});
   2898     },
   2899     needsUpdate: function (jax) {
   2900       var script = jax.SourceElement();
   2901       return (jax.originalText !== BASE.HTML.getScript(script));
   2902     },
   2903     Register: function (mimetype) {
   2904       if (!HUB.inputJax) {HUB.inputJax = {}}
   2905       HUB.inputJax[mimetype] = this;
   2906     }
   2907   },{
   2908     id: "InputJax",
   2909     version: "2.6.0",
   2910     directory: JAX.directory+"/input",
   2911     extensionDir: JAX.extensionDir
   2912   });
   2913 
   2914   /***********************************/
   2915 
   2916   BASE.OutputJax = JAX.Subclass({
   2917     copyTranslate: true,
   2918     preProcess: function (state) {
   2919       var load, file = this.directory+"/"+this.JAXFILE;
   2920       this.constructor.prototype.preProcess = function (state) {
   2921 	if (!load.called) {return load}
   2922         throw Error(file+" failed to load properly");
   2923       }
   2924       load = AJAX.Require(file);
   2925       return load;
   2926     },
   2927     Register: function (mimetype) {
   2928       var jax = HUB.outputJax;
   2929       if (!jax[mimetype]) {jax[mimetype] = []}
   2930       //  If the output jax is earlier in the original configuration list, put it first here
   2931       if (jax[mimetype].length && (this.id === HUB.config.menuSettings.renderer ||
   2932             (jax.order[this.id]||0) < (jax.order[jax[mimetype][0].id]||0)))
   2933         {jax[mimetype].unshift(this)} else {jax[mimetype].push(this)}
   2934       //  Make sure the element jax is loaded before Startup is called
   2935       if (!this.require) {this.require = []}
   2936         else if (!(this.require instanceof Array)) {this.require = [this.require]};
   2937       this.require.push(BASE.ElementJax.directory+"/"+(mimetype.split(/\//)[1])+"/"+this.JAXFILE);
   2938     },
   2939     Remove: function (jax) {}
   2940   },{
   2941     id: "OutputJax",
   2942     version: "2.6.0",
   2943     directory: JAX.directory+"/output",
   2944     extensionDir: JAX.extensionDir,
   2945     fontDir: ROOT+(BASE.isPacked?"":"/..")+"/fonts",
   2946     imageDir: ROOT+(BASE.isPacked?"":"/..")+"/images"
   2947   });
   2948   
   2949   /***********************************/
   2950 
   2951   BASE.ElementJax = JAX.Subclass({
   2952     // make a subclass, not an instance
   2953     Init: function (def,cdef) {return this.constructor.Subclass(def,cdef)},
   2954     
   2955     inputJax: null,
   2956     outputJax: null,
   2957     inputID: null,
   2958     originalText: "",
   2959     mimeType: "",
   2960     sourceMenuTitle: /*_(MathMenu)*/ ["MathMLcode","MathML Code"],
   2961     
   2962     Text: function (text,callback) {
   2963       var script = this.SourceElement();
   2964       BASE.HTML.setScript(script,text);
   2965       script.MathJax.state = this.STATE.UPDATE;
   2966       return HUB.Update(script,callback);
   2967     },
   2968     Reprocess: function (callback) {
   2969       var script = this.SourceElement();
   2970       script.MathJax.state = this.STATE.UPDATE;
   2971       return HUB.Reprocess(script,callback);
   2972     },
   2973     Update: function (callback) {return this.Rerender(callback)},
   2974     Rerender: function (callback) {
   2975       var script = this.SourceElement();
   2976       script.MathJax.state = this.STATE.OUTPUT;
   2977       return HUB.Process(script,callback);
   2978     },
   2979     Remove: function (keep) {
   2980       if (this.hover) {this.hover.clear(this)}
   2981       BASE.OutputJax[this.outputJax].Remove(this);
   2982       if (!keep) {
   2983         HUB.signal.Post(["Remove Math",this.inputID]); // wait for this to finish?
   2984         this.Detach();
   2985       }
   2986     },
   2987     needsUpdate: function () {
   2988       return BASE.InputJax[this.inputJax].needsUpdate(this);
   2989     },
   2990 
   2991     SourceElement: function () {return document.getElementById(this.inputID)},
   2992     
   2993     Attach: function (script,inputJax) {
   2994       var jax = script.MathJax.elementJax;
   2995       if (script.MathJax.state === this.STATE.UPDATE) {
   2996         jax.Clone(this);
   2997       } else {
   2998         jax = script.MathJax.elementJax = this;
   2999         if (script.id) {this.inputID = script.id}
   3000           else {script.id = this.inputID = BASE.ElementJax.GetID(); this.newID = 1}
   3001       }
   3002       jax.originalText = BASE.HTML.getScript(script);
   3003       jax.inputJax = inputJax;
   3004       if (jax.root) {jax.root.inputID = jax.inputID}
   3005       return jax;
   3006     },
   3007     Detach: function () {
   3008       var script = this.SourceElement(); if (!script) return;
   3009       try {delete script.MathJax} catch(err) {script.MathJax = null}
   3010       if (this.newID) {script.id = ""}
   3011     },
   3012     Clone: function (jax) {
   3013       var id;
   3014       for (id in this) {
   3015         if (!this.hasOwnProperty(id)) continue;
   3016         if (typeof(jax[id]) === 'undefined' && id !== 'newID') {delete this[id]}
   3017       }
   3018       for (id in jax) {
   3019         if (!jax.hasOwnProperty(id)) continue;
   3020         if (typeof(this[id]) === 'undefined' || (this[id] !== jax[id] && id !== 'inputID'))
   3021           {this[id] = jax[id]}
   3022       }
   3023     }
   3024   },{
   3025     id: "ElementJax",
   3026     version: "2.6.0",
   3027     directory: JAX.directory+"/element",
   3028     extensionDir: JAX.extensionDir,
   3029     ID: 0,  // jax counter (for IDs)
   3030     STATE: {
   3031       PENDING: 1,      // script is identified as math but not yet processed
   3032       PROCESSED: 2,    // script has been processed
   3033       UPDATE: 3,       // elementJax should be updated
   3034       OUTPUT: 4        // output should be updated (input is OK)
   3035     },
   3036     
   3037     GetID: function () {this.ID++; return "MathJax-Element-"+this.ID},
   3038     Subclass: function () {
   3039       var obj = JAX.Subclass.apply(this,arguments);
   3040       obj.loadComplete = this.prototype.loadComplete;
   3041       return obj;
   3042     }
   3043   });
   3044   BASE.ElementJax.prototype.STATE = BASE.ElementJax.STATE;
   3045 
   3046   //
   3047   //  Some "Fake" jax used to allow menu access for "Math Processing Error" messages
   3048   //
   3049   BASE.OutputJax.Error = {
   3050     id: "Error", version: "2.6.0", config: {}, errors: 0,
   3051     ContextMenu: function () {return BASE.Extension.MathEvents.Event.ContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)},
   3052     Mousedown:   function () {return BASE.Extension.MathEvents.Event.AltContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)},
   3053     getJaxFromMath: function (math) {return (math.nextSibling.MathJax||{}).error},
   3054     Jax: function (text,script) {
   3055       var jax = MathJax.Hub.inputJax[script.type.replace(/ *;(.|\s)*/,"")];
   3056       this.errors++;
   3057       return {
   3058         inputJax: (jax||{id:"Error"}).id,  // Use Error InputJax as fallback
   3059         outputJax: "Error",
   3060         inputID: "MathJax-Error-"+this.errors,
   3061         sourceMenuTitle: /*_(MathMenu)*/ ["ErrorMessage","Error Message"],
   3062         sourceMenuFormat: "Error",
   3063         originalText: MathJax.HTML.getScript(script),
   3064         errorText: text
   3065       }
   3066     }
   3067   };
   3068   BASE.InputJax.Error = {
   3069     id: "Error", version: "2.6.0", config: {},
   3070     sourceMenuTitle: /*_(MathMenu)*/ ["Original","Original Form"]
   3071   };
   3072   
   3073 })("MathJax");
   3074 
   3075 /**********************************************************/
   3076 
   3077 (function (BASENAME) {
   3078   var BASE = window[BASENAME];
   3079   if (!BASE) {BASE = window[BASENAME] = {}}
   3080 
   3081   var HUB = BASE.Hub; var STARTUP = HUB.Startup; var CONFIG = HUB.config;
   3082   var HEAD = document.head || (document.getElementsByTagName("head")[0]);
   3083   if (!HEAD) {HEAD = document.childNodes[0]};
   3084   var scripts = (document.documentElement || document).getElementsByTagName("script");
   3085   if (scripts.length === 0 && HEAD.namespaceURI)
   3086     scripts = document.getElementsByTagNameNS(HEAD.namespaceURI,"script");
   3087   var namePattern = new RegExp("(^|/)"+BASENAME+"\\.js(\\?.*)?$");
   3088   for (var i = scripts.length-1; i >= 0; i--) {
   3089     if ((scripts[i].src||"").match(namePattern)) {
   3090       STARTUP.script = scripts[i].innerHTML;
   3091       if (RegExp.$2) {
   3092         var params = RegExp.$2.substr(1).split(/\&/);
   3093         for (var j = 0, m = params.length; j < m; j++) {
   3094           var KV = params[j].match(/(.*)=(.*)/);
   3095           if (KV) {STARTUP.params[unescape(KV[1])] = unescape(KV[2])}
   3096         }
   3097       }
   3098       CONFIG.root = scripts[i].src.replace(/(^|\/)[^\/]*(\?.*)?$/,'')
   3099         // convert mathjax/latest to mathjax/x.y-latest so that all files are the same version
   3100         .replace(/^(https?:\/\/cdn.mathjax.org\/mathjax\/)(latest)/,"$1"+BASE.version.split(/\./).slice(0,2).join(".")+"-$2");
   3101       BASE.Ajax.config.root = CONFIG.root;
   3102       break;
   3103     }
   3104   }
   3105 
   3106   var AGENT = navigator.userAgent;
   3107   var BROWSERS = {
   3108     isMac:       (navigator.platform.substr(0,3) === "Mac"),
   3109     isPC:        (navigator.platform.substr(0,3) === "Win"),
   3110     isMSIE:      ("ActiveXObject" in window && "clipboardData" in window),
   3111     isEdge:      ("MSGestureEvent" in window && "chrome" in window &&
   3112                      window.chrome.loadTimes == null),
   3113     isFirefox:   (!!AGENT.match(/Gecko\//) && !AGENT.match(/like Gecko/)),
   3114     isSafari:    (!!AGENT.match(/ (Apple)?WebKit\//) && !AGENT.match(/ like iPhone /) &&
   3115                      (!window.chrome || window.chrome.app == null)),
   3116     isChrome:    ("chrome" in window && window.chrome.loadTimes != null),
   3117     isOpera:     ("opera" in window && window.opera.version != null),
   3118     isKonqueror: ("konqueror" in window && navigator.vendor == "KDE"),
   3119     versionAtLeast: function (v) {
   3120       var bv = (this.version).split('.'); v = (new String(v)).split('.');
   3121       for (var i = 0, m = v.length; i < m; i++)
   3122         {if (bv[i] != v[i]) {return parseInt(bv[i]||"0") >= parseInt(v[i])}}
   3123       return true;
   3124     },
   3125     Select: function (choices) {
   3126       var browser = choices[HUB.Browser];
   3127       if (browser) {return browser(HUB.Browser)}
   3128       return null;
   3129     }
   3130   };
   3131 
   3132   var xAGENT = AGENT
   3133     .replace(/^Mozilla\/(\d+\.)+\d+ /,"")                                   // remove initial Mozilla, which is never right
   3134     .replace(/[a-z][-a-z0-9._: ]+\/\d+[^ ]*-[^ ]*\.([a-z][a-z])?\d+ /i,"")  // remove linux version
   3135     .replace(/Gentoo |Ubuntu\/(\d+\.)*\d+ (\([^)]*\) )?/,"");               // special case for these
   3136 
   3137   HUB.Browser = HUB.Insert(HUB.Insert(new String("Unknown"),{version: "0.0"}),BROWSERS);
   3138   for (var browser in BROWSERS) {if (BROWSERS.hasOwnProperty(browser)) {
   3139     if (BROWSERS[browser] && browser.substr(0,2) === "is") {
   3140       browser = browser.slice(2);
   3141       if (browser === "Mac" || browser === "PC") continue;
   3142       HUB.Browser = HUB.Insert(new String(browser),BROWSERS);
   3143       var VERSION = new RegExp(
   3144         ".*(Version/| Trident/.*; rv:)((?:\\d+\\.)+\\d+)|" +                      // for Safari, Opera10, and IE11+
   3145         ".*("+browser+")"+(browser == "MSIE" ? " " : "/")+"((?:\\d+\\.)*\\d+)|"+  // for one of the main browsers
   3146         "(?:^|\\(| )([a-z][-a-z0-9._: ]+|(?:Apple)?WebKit)/((?:\\d+\\.)+\\d+)");  // for unrecognized browser
   3147       var MATCH = VERSION.exec(xAGENT) || ["","","","unknown","0.0"];
   3148       HUB.Browser.name = (MATCH[1] != "" ? browser : (MATCH[3] || MATCH[5]));
   3149       HUB.Browser.version = MATCH[2] || MATCH[4] || MATCH[6];
   3150       break;
   3151     }
   3152   }};
   3153   
   3154   //
   3155   //  Initial browser-specific info (e.g., touch up version or name, check for MathPlayer, etc.)
   3156   //  Wrap in try/catch just in case of error (see issue #1155).
   3157   //
   3158   try {HUB.Browser.Select({
   3159     Safari: function (browser) {
   3160       var v = parseInt((String(browser.version).split("."))[0]);
   3161       if (v > 85) {browser.webkit = browser.version}
   3162       if      (v >= 538) {browser.version = "8.0"}
   3163       else if (v >= 537) {browser.version = "7.0"}
   3164       else if (v >= 536) {browser.version = "6.0"}
   3165       else if (v >= 534) {browser.version = "5.1"}
   3166       else if (v >= 533) {browser.version = "5.0"}
   3167       else if (v >= 526) {browser.version = "4.0"}
   3168       else if (v >= 525) {browser.version = "3.1"}
   3169       else if (v >  500) {browser.version = "3.0"}
   3170       else if (v >  400) {browser.version = "2.0"}
   3171       else if (v >   85) {browser.version = "1.0"}
   3172       browser.webkit = (navigator.appVersion.match(/WebKit\/(\d+)\./))[1];
   3173       browser.isMobile = (navigator.appVersion.match(/Mobile/i) != null);
   3174       browser.noContextMenu = browser.isMobile;
   3175     },
   3176     Firefox: function (browser) {
   3177       if ((browser.version === "0.0" || AGENT.match(/Firefox/) == null) &&
   3178            navigator.product === "Gecko") {
   3179         var rv = AGENT.match(/[\/ ]rv:(\d+\.\d.*?)[\) ]/);
   3180         if (rv) {browser.version = rv[1]}
   3181         else {
   3182           var date = (navigator.buildID||navigator.productSub||"0").substr(0,8);
   3183           if      (date >= "20111220") {browser.version = "9.0"}
   3184           else if (date >= "20111120") {browser.version = "8.0"}
   3185           else if (date >= "20110927") {browser.version = "7.0"}
   3186           else if (date >= "20110816") {browser.version = "6.0"}
   3187           else if (date >= "20110621") {browser.version = "5.0"}
   3188           else if (date >= "20110320") {browser.version = "4.0"}
   3189           else if (date >= "20100121") {browser.version = "3.6"}
   3190           else if (date >= "20090630") {browser.version = "3.5"}
   3191           else if (date >= "20080617") {browser.version = "3.0"}
   3192           else if (date >= "20061024") {browser.version = "2.0"}
   3193         }
   3194       }
   3195       browser.isMobile = (navigator.appVersion.match(/Android/i) != null ||
   3196                           AGENT.match(/ Fennec\//) != null ||
   3197                           AGENT.match(/Mobile/) != null);
   3198     },
   3199     Chrome: function (browser) {
   3200       browser.noContextMenu = browser.isMobile = !!navigator.userAgent.match(/ Mobile[ \/]/);
   3201     },
   3202     Opera: function (browser) {browser.version = opera.version()},
   3203     Edge: function (browser) {
   3204       browser.isMobile = !!navigator.userAgent.match(/ Phone/);
   3205     },
   3206     MSIE: function (browser) {
   3207       browser.isMobile = !!navigator.userAgent.match(/ Phone/);
   3208       browser.isIE9 = !!(document.documentMode && (window.performance || window.msPerformance));
   3209       MathJax.HTML.setScriptBug = !browser.isIE9 || document.documentMode < 9;
   3210       MathJax.Hub.msieHTMLCollectionBug = (document.documentMode < 9);
   3211       //
   3212       //  MathPlayer doesn't function properly in IE10, and not at all in IE11,
   3213       //  so don't even try to load it.
   3214       //
   3215       if (document.documentMode < 10 && !STARTUP.params.NoMathPlayer) {
   3216         try {
   3217           new ActiveXObject("MathPlayer.Factory.1");
   3218           browser.hasMathPlayer = true;
   3219         } catch (err) {}
   3220         try {
   3221           if (browser.hasMathPlayer) {
   3222             var mathplayer = document.createElement("object");
   3223             mathplayer.id = "mathplayer"; mathplayer.classid = "clsid:32F66A20-7614-11D4-BD11-00104BD3F987";
   3224             HEAD.appendChild(mathplayer);
   3225             document.namespaces.add("m","http://www.w3.org/1998/Math/MathML");
   3226             browser.mpNamespace = true;
   3227             if (document.readyState && (document.readyState === "loading" ||
   3228                                         document.readyState === "interactive")) {
   3229               document.write('<?import namespace="m" implementation="#MathPlayer">');
   3230               browser.mpImported = true;
   3231             }
   3232           } else {
   3233             //  Adding any namespace avoids a crash in IE9 in IE9-standards mode
   3234             //  (any reference to document.namespaces before document.readyState is 
   3235             //   "complete" causes an "unspecified error" to be thrown)
   3236             document.namespaces.add("mjx_IE_fix","http://www.w3.org/1999/xlink");
   3237           }
   3238         } catch (err) {}
   3239       }
   3240     }
   3241   });} catch (err) {
   3242     console.error(err.message);
   3243   }
   3244   HUB.Browser.Select(MathJax.Message.browsers);
   3245 
   3246   if (BASE.AuthorConfig && typeof BASE.AuthorConfig.AuthorInit === "function") {BASE.AuthorConfig.AuthorInit()}
   3247   HUB.queue = BASE.Callback.Queue();
   3248   HUB.queue.Push(
   3249     ["Post",STARTUP.signal,"Begin"],
   3250     ["Config",STARTUP],
   3251     ["Cookie",STARTUP],
   3252     ["Styles",STARTUP],
   3253     ["Message",STARTUP],
   3254     function () {
   3255       // Do Jax and Extensions in parallel, but wait for them all to complete
   3256       var queue = BASE.Callback.Queue(
   3257         STARTUP.Jax(),
   3258         STARTUP.Extensions()
   3259       );
   3260       return queue.Push({});
   3261     },
   3262     ["Menu",STARTUP],
   3263     STARTUP.onLoad(),
   3264     function () {MathJax.isReady = true}, // indicates that MathJax is ready to process math
   3265     ["Typeset",STARTUP],
   3266     ["Hash",STARTUP],
   3267     ["MenuZoom",STARTUP],
   3268     ["Post",STARTUP.signal,"End"]
   3269   );
   3270   
   3271 })("MathJax");
   3272 
   3273 }}