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 }}