Last active
November 13, 2022 10:51
-
-
Save codehz/e502f244d05d34f16f521429315148ac to your computer and use it in GitHub Desktop.
知乎公式渲染强制使用MathJax
This file has been truncated, but you can view the full file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Zhihu MathJax | |
// @namespace http://tampermonkey.net/ | |
// @version 0.3 | |
// @description 知乎公式渲染强制使用MathJax,配合AdBlock拦截www.zhihu.com/equation较为合适(因为用脚本隐藏还是会进行网络请求。。。)(修改自 https://zhuanlan.zhihu.com/p/27551432 ,感谢 @梨梨喵 和 @帅气可爱魔理沙 ,假装可以艾特到) | |
// @author CodeHz | |
// @match http*://*.zhihu.com/* | |
// @grant GM_addStyle | |
// ==/UserScript== | |
// MathJax single file build. Licenses of its components apply | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax.js | |
* | |
* The main support code for the MathJax Hub, including the | |
* Ajax, Callback, Messaging, and Object-Oriented Programming | |
* libraries, as well as the base Jax classes, and startup | |
* processing code. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2009-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
// | |
// Check if browser can support MathJax (no one fails this nowadays) | |
// | |
if (document.getElementById && document.childNodes && document.createElement) { | |
// | |
// Skip if MathJax is already loaded | |
// | |
if (!(window.MathJax && MathJax.Hub)) { | |
// | |
// Get author configuration from MathJax variable, if any | |
// | |
if (window.MathJax) {window.MathJax = {AuthorConfig: window.MathJax}} | |
else {window.MathJax = {}} | |
// MathJax.isPacked = true; // This line is uncommented by the packer. | |
MathJax.version = "2.7.1"; | |
MathJax.fileversion = "2.7.1"; | |
MathJax.cdnVersion = "2.7.1"; // specifies a revision to break caching | |
MathJax.cdnFileVersions = {}; // can be used to specify revisions for individual files | |
/**********************************************************/ | |
(function (BASENAME) { | |
var BASE = window[BASENAME]; | |
if (!BASE) {BASE = window[BASENAME] = {}} | |
var PROTO = []; // a static object used to indicate when a prototype is being created | |
var OBJECT = function (def) { | |
var obj = def.constructor; if (!obj) {obj = function () {}} | |
for (var id in def) {if (id !== 'constructor' && def.hasOwnProperty(id)) {obj[id] = def[id]}} | |
return obj; | |
}; | |
var CONSTRUCTOR = function () { | |
return function () {return arguments.callee.Init.call(this,arguments)}; | |
}; | |
BASE.Object = OBJECT({ | |
constructor: CONSTRUCTOR(), | |
Subclass: function (def,classdef) { | |
var obj = CONSTRUCTOR(); | |
obj.SUPER = this; obj.Init = this.Init; | |
obj.Subclass = this.Subclass; obj.Augment = this.Augment; | |
obj.protoFunction = this.protoFunction; | |
obj.can = this.can; obj.has = this.has; obj.isa = this.isa; | |
obj.prototype = new this(PROTO); | |
obj.prototype.constructor = obj; // the real constructor | |
obj.Augment(def,classdef); | |
return obj; | |
}, | |
Init: function (args) { | |
var obj = this; | |
if (args.length === 1 && args[0] === PROTO) {return obj} | |
if (!(obj instanceof args.callee)) {obj = new args.callee(PROTO)} | |
return obj.Init.apply(obj,args) || obj; | |
}, | |
Augment: function (def,classdef) { | |
var id; | |
if (def != null) { | |
for (id in def) {if (def.hasOwnProperty(id)) {this.protoFunction(id,def[id])}} | |
// MSIE doesn't list toString even if it is not native so handle it separately | |
if (def.toString !== this.prototype.toString && def.toString !== {}.toString) | |
{this.protoFunction('toString',def.toString)} | |
} | |
if (classdef != null) { | |
for (id in classdef) {if (classdef.hasOwnProperty(id)) {this[id] = classdef[id]}} | |
} | |
return this; | |
}, | |
protoFunction: function (id,def) { | |
this.prototype[id] = def; | |
if (typeof def === "function") {def.SUPER = this.SUPER.prototype} | |
}, | |
prototype: { | |
Init: function () {}, | |
SUPER: function (fn) {return fn.callee.SUPER}, | |
can: function (method) {return typeof(this[method]) === "function"}, | |
has: function (property) {return typeof(this[property]) !== "undefined"}, | |
isa: function (obj) {return (obj instanceof Object) && (this instanceof obj)} | |
}, | |
can: function (method) {return this.prototype.can.call(this,method)}, | |
has: function (property) {return this.prototype.has.call(this,property)}, | |
isa: function (obj) { | |
var constructor = this; | |
while (constructor) { | |
if (constructor === obj) {return true} else {constructor = constructor.SUPER} | |
} | |
return false; | |
}, | |
SimpleSUPER: OBJECT({ | |
constructor: function (def) {return this.SimpleSUPER.define(def)}, | |
define: function (src) { | |
var dst = {}; | |
if (src != null) { | |
for (var id in src) {if (src.hasOwnProperty(id)) {dst[id] = this.wrap(id,src[id])}} | |
// MSIE doesn't list toString even if it is not native so handle it separately | |
if (src.toString !== this.prototype.toString && src.toString !== {}.toString) | |
{dst.toString = this.wrap('toString',src.toString)} | |
} | |
return dst; | |
}, | |
wrap: function (id,f) { | |
if (typeof(f) !== 'function' || !f.toString().match(/\.\s*SUPER\s*\(/)) {return f} | |
var fn = function () { | |
this.SUPER = fn.SUPER[id]; | |
try {var result = f.apply(this,arguments)} catch (err) {delete this.SUPER; throw err} | |
delete this.SUPER; | |
return result; | |
} | |
fn.toString = function () {return f.toString.apply(f,arguments)} | |
return fn; | |
} | |
}) | |
}); | |
BASE.Object.isArray = Array.isArray || function (obj) { | |
return Object.prototype.toString.call(obj) === "[object Array]"; | |
}; | |
BASE.Object.Array = Array; | |
})("MathJax"); | |
/**********************************************************/ | |
/* | |
* Create a callback function from various forms of data: | |
* | |
* MathJax.Callback(fn) -- callback to a function | |
* | |
* MathJax.Callback([fn]) -- callback to function | |
* MathJax.Callback([fn,data...]) | |
* -- callback to function with given data as arguments | |
* MathJax.Callback([object,fn]) | |
* -- call fn with object as "this" | |
* MathJax.Callback([object,fn,data...]) | |
* -- call fn with object as "this" and data as arguments | |
* MathJax.Callback(["method",object]) | |
* -- call method of object wth object as "this" | |
* MathJax.Callback(["method",object,data...]) | |
* -- as above, but with data as arguments to method | |
* | |
* MathJax.Callback({hook: fn, data: [...], object: this}) | |
* -- give function, data, and object to act as "this" explicitly | |
* | |
* MathJax.Callback("code") -- callback that compiles and executes a string | |
* | |
* MathJax.Callback([...],i) | |
* -- use slice of array starting at i and interpret | |
* result as above. (Used for passing "arguments" array | |
* and trimming initial arguments, if any.) | |
*/ | |
/* | |
* MathJax.Callback.After([...],cb1,cb2,...) | |
* -- make a callback that isn't called until all the other | |
* ones are called first. I.e., wait for a union of | |
* callbacks to occur before making the given callback. | |
*/ | |
/* | |
* MathJax.Callback.Queue([callback,...]) | |
* -- make a synchronized queue of commands that process | |
* sequentially, waiting for those that return uncalled | |
* callbacks. | |
*/ | |
/* | |
* MathJax.Callback.Signal(name) | |
* -- finds or creates a names signal, to which listeners | |
* can be attached and are signaled by messages posted | |
* to the signal. Responses can be asynchronous. | |
*/ | |
(function (BASENAME) { | |
var BASE = window[BASENAME]; | |
if (!BASE) {BASE = window[BASENAME] = {}} | |
var isArray = BASE.Object.isArray; | |
// | |
// Create a callback from an associative array | |
// | |
var CALLBACK = function (data) { | |
var cb = function () {return arguments.callee.execute.apply(arguments.callee,arguments)}; | |
for (var id in CALLBACK.prototype) { | |
if (CALLBACK.prototype.hasOwnProperty(id)) { | |
if (typeof(data[id]) !== 'undefined') {cb[id] = data[id]} | |
else {cb[id] = CALLBACK.prototype[id]} | |
} | |
} | |
cb.toString = CALLBACK.prototype.toString; | |
return cb; | |
}; | |
CALLBACK.prototype = { | |
isCallback: true, | |
hook: function () {}, | |
data: [], | |
object: window, | |
execute: function () { | |
if (!this.called || this.autoReset) { | |
this.called = !this.autoReset; | |
return this.hook.apply(this.object,this.data.concat([].slice.call(arguments,0))); | |
} | |
}, | |
reset: function () {delete this.called}, | |
toString: function () {return this.hook.toString.apply(this.hook,arguments)} | |
}; | |
var ISCALLBACK = function (f) { | |
return (typeof(f) === "function" && f.isCallback); | |
} | |
// | |
// Evaluate a string in global context | |
// | |
var EVAL = function (code) {return eval.call(window,code)} | |
var TESTEVAL = function () { | |
EVAL("var __TeSt_VaR__ = 1"); // check if it works in global context | |
if (window.__TeSt_VaR__) { | |
try { delete window.__TeSt_VaR__; } // NOTE IE9 throws when in IE7 mode | |
catch (error) { window.__TeSt_VaR__ = null; } | |
} else { | |
if (window.execScript) { | |
// IE | |
EVAL = function (code) { | |
BASE.__code = code; | |
code = "try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}"; | |
window.execScript(code); | |
var result = BASE.__result; delete BASE.__result; delete BASE.__code; | |
if (result instanceof Error) {throw result} | |
return result; | |
} | |
} else { | |
// Safari2 | |
EVAL = function (code) { | |
BASE.__code = code; | |
code = "try {"+BASENAME+".__result = eval("+BASENAME+".__code)} catch(err) {"+BASENAME+".__result = err}"; | |
var head = (document.getElementsByTagName("head"))[0]; if (!head) {head = document.body} | |
var script = document.createElement("script"); | |
script.appendChild(document.createTextNode(code)); | |
head.appendChild(script); head.removeChild(script); | |
var result = BASE.__result; delete BASE.__result; delete BASE.__code; | |
if (result instanceof Error) {throw result} | |
return result; | |
} | |
} | |
} | |
TESTEVAL = null; | |
}; | |
// | |
// Create a callback from various types of data | |
// | |
var USING = function (args,i) { | |
if (arguments.length > 1) { | |
if (arguments.length === 2 && !(typeof arguments[0] === 'function') && | |
arguments[0] instanceof Object && typeof arguments[1] === 'number') | |
{args = [].slice.call(args,i)} | |
else {args = [].slice.call(arguments,0)} | |
} | |
if (isArray(args) && args.length === 1) {args = args[0]} | |
if (typeof args === 'function') { | |
if (args.execute === CALLBACK.prototype.execute) {return args} | |
return CALLBACK({hook: args}); | |
} else if (isArray(args)) { | |
if (typeof(args[0]) === 'string' && args[1] instanceof Object && | |
typeof args[1][args[0]] === 'function') { | |
return CALLBACK({hook: args[1][args[0]], object: args[1], data: args.slice(2)}); | |
} else if (typeof args[0] === 'function') { | |
return CALLBACK({hook: args[0], data: args.slice(1)}); | |
} else if (typeof args[1] === 'function') { | |
return CALLBACK({hook: args[1], object: args[0], data: args.slice(2)}); | |
} | |
} else if (typeof(args) === 'string') { | |
if (TESTEVAL) TESTEVAL(); | |
return CALLBACK({hook: EVAL, data: [args]}); | |
} else if (args instanceof Object) { | |
return CALLBACK(args); | |
} else if (typeof(args) === 'undefined') { | |
return CALLBACK({}); | |
} | |
throw Error("Can't make callback from given data"); | |
}; | |
// | |
// Wait for a given time to elapse and then perform the callback | |
// | |
var DELAY = function (time,callback) { | |
callback = USING(callback); | |
callback.timeout = setTimeout(callback,time); | |
return callback; | |
}; | |
// | |
// Callback used by AFTER, QUEUE, and SIGNAL to check if calls have completed | |
// | |
var WAITFOR = function (callback,signal) { | |
callback = USING(callback); | |
if (!callback.called) {WAITSIGNAL(callback,signal); signal.pending++} | |
}; | |
var WAITEXECUTE = function () { | |
var signals = this.signal; delete this.signal; | |
this.execute = this.oldExecute; delete this.oldExecute; | |
var result = this.execute.apply(this,arguments); | |
if (ISCALLBACK(result) && !result.called) {WAITSIGNAL(result,signals)} else { | |
for (var i = 0, m = signals.length; i < m; i++) { | |
signals[i].pending--; | |
if (signals[i].pending <= 0) {signals[i].call()} | |
} | |
} | |
}; | |
var WAITSIGNAL = function (callback,signals) { | |
if (!isArray(signals)) {signals = [signals]} | |
if (!callback.signal) { | |
callback.oldExecute = callback.execute; | |
callback.execute = WAITEXECUTE; | |
callback.signal = signals; | |
} else if (signals.length === 1) {callback.signal.push(signals[0])} | |
else {callback.signal = callback.signal.concat(signals)} | |
}; | |
// | |
// Create a callback that is called when a collection of other callbacks have | |
// all been executed. If the callback gets called immediately (i.e., the | |
// others are all already called), check if it returns another callback | |
// and return that instead. | |
// | |
var AFTER = function (callback) { | |
callback = USING(callback); | |
callback.pending = 0; | |
for (var i = 1, m = arguments.length; i < m; i++) | |
{if (arguments[i]) {WAITFOR(arguments[i],callback)}} | |
if (callback.pending === 0) { | |
var result = callback(); | |
if (ISCALLBACK(result)) {callback = result} | |
} | |
return callback; | |
}; | |
// | |
// An array of prioritized hooks that are executed sequentially | |
// with a given set of data. | |
// | |
var HOOKS = MathJax.Object.Subclass({ | |
// | |
// Initialize the array and the auto-reset status | |
// | |
Init: function (reset) { | |
this.hooks = []; | |
this.remove = []; // used when hooks are removed during execution of list | |
this.reset = reset; | |
this.running = false; | |
}, | |
// | |
// Add a callback to the list, in priority order (default priority is 10) | |
// | |
Add: function (hook,priority) { | |
if (priority == null) {priority = 10} | |
if (!ISCALLBACK(hook)) {hook = USING(hook)} | |
hook.priority = priority; | |
var i = this.hooks.length; | |
while (i > 0 && priority < this.hooks[i-1].priority) {i--} | |
this.hooks.splice(i,0,hook); | |
return hook; | |
}, | |
Remove: function (hook) { | |
for (var i = 0, m = this.hooks.length; i < m; i++) { | |
if (this.hooks[i] === hook) { | |
if (this.running) {this.remove.push(i)} | |
else {this.hooks.splice(i,1)} | |
return; | |
} | |
} | |
}, | |
// | |
// Execute the list of callbacks, resetting them if requested. | |
// If any return callbacks, return a callback that will be | |
// executed when they all have completed. | |
// Remove any hooks that requested being removed during processing. | |
// | |
Execute: function () { | |
var callbacks = [{}]; | |
this.running = true; | |
for (var i = 0, m = this.hooks.length; i < m; i++) { | |
if (this.reset) {this.hooks[i].reset()} | |
var result = this.hooks[i].apply(window,arguments); | |
if (ISCALLBACK(result) && !result.called) {callbacks.push(result)} | |
} | |
this.running = false; | |
if (this.remove.length) {this.RemovePending()} | |
if (callbacks.length === 1) {return null} | |
if (callbacks.length === 2) {return callbacks[1]} | |
return AFTER.apply({},callbacks); | |
}, | |
// | |
// Remove hooks that asked to be removed during execution of list | |
// | |
RemovePending: function () { | |
this.remove = this.remove.sort(); | |
for (var i = this.remove.length-1; i >= 0; i--) {this.hooks.splice(i,1)} | |
this.remove = []; | |
} | |
}); | |
// | |
// Run an array of callbacks passing them the given data. | |
// (Legacy function, since this has been replaced by the HOOKS object). | |
// | |
var EXECUTEHOOKS = function (hooks,data,reset) { | |
if (!hooks) {return null} | |
if (!isArray(hooks)) {hooks = [hooks]} | |
if (!isArray(data)) {data = (data == null ? [] : [data])} | |
var handler = HOOKS(reset); | |
for (var i = 0, m = hooks.length; i < m; i++) {handler.Add(hooks[i])} | |
return handler.Execute.apply(handler,data); | |
}; | |
// | |
// Command queue that performs commands in order, waiting when | |
// necessary for commands to complete asynchronousely | |
// | |
var QUEUE = BASE.Object.Subclass({ | |
// | |
// Create the queue and push any commands that are specified | |
// | |
Init: function () { | |
this.pending = this.running = 0; | |
this.queue = []; | |
this.Push.apply(this,arguments); | |
}, | |
// | |
// Add commands to the queue and run them. Adding a callback object | |
// (rather than a callback specification) queues a wait for that callback. | |
// Return the final callback for synchronization purposes. | |
// | |
Push: function () { | |
var callback; | |
for (var i = 0, m = arguments.length; i < m; i++) { | |
callback = USING(arguments[i]); | |
if (callback === arguments[i] && !callback.called) | |
{callback = USING(["wait",this,callback])} | |
this.queue.push(callback); | |
} | |
if (!this.running && !this.pending) {this.Process()} | |
return callback; | |
}, | |
// | |
// Process the command queue if we aren't waiting on another command | |
// | |
Process: function (queue) { | |
while (!this.running && !this.pending && this.queue.length) { | |
var callback = this.queue[0]; | |
queue = this.queue.slice(1); this.queue = []; | |
this.Suspend(); var result = callback(); this.Resume(); | |
if (queue.length) {this.queue = queue.concat(this.queue)} | |
if (ISCALLBACK(result) && !result.called) {WAITFOR(result,this)} | |
} | |
}, | |
// | |
// Suspend/Resume command processing on this queue | |
// | |
Suspend: function () {this.running++}, | |
Resume: function () {if (this.running) {this.running--}}, | |
// | |
// Used by WAITFOR to restart the queue when an action completes | |
// | |
call: function () {this.Process.apply(this,arguments)}, | |
wait: function (callback) {return callback} | |
}); | |
// | |
// Create a named signal that listeners can attach to, to be signaled by | |
// postings made to the signal. Posts are queued if they occur while one | |
// is already in process. | |
// | |
var SIGNAL = QUEUE.Subclass({ | |
Init: function (name) { | |
QUEUE.prototype.Init.call(this); | |
this.name = name; | |
this.posted = []; // the messages posted so far | |
this.listeners = HOOKS(true); // those with interest in this signal | |
this.posting = false; | |
this.callback = null; | |
}, | |
// | |
// Post a message to the signal listeners, with callback for when complete | |
// | |
Post: function (message,callback,forget) { | |
callback = USING(callback); | |
if (this.posting || this.pending) { | |
this.Push(["Post",this,message,callback,forget]); | |
} else { | |
this.callback = callback; callback.reset(); | |
if (!forget) {this.posted.push(message)} | |
this.Suspend(); this.posting = true; | |
var result = this.listeners.Execute(message); | |
if (ISCALLBACK(result) && !result.called) {WAITFOR(result,this)} | |
this.Resume(); this.posting = false; | |
if (!this.pending) {this.call()} | |
} | |
return callback; | |
}, | |
// | |
// Clear the post history (so new listeners won't get old messages) | |
// | |
Clear: function (callback) { | |
callback = USING(callback); | |
if (this.posting || this.pending) { | |
callback = this.Push(["Clear",this,callback]); | |
} else { | |
this.posted = []; | |
callback(); | |
} | |
return callback; | |
}, | |
// | |
// Call the callback (all replies are in) and process the command queue | |
// | |
call: function () {this.callback(this); this.Process()}, | |
// | |
// A listener calls this to register interest in the signal (so it will be called | |
// when posts occur). If ignorePast is true, it will not be sent the post history. | |
// | |
Interest: function (callback,ignorePast,priority) { | |
callback = USING(callback); | |
this.listeners.Add(callback,priority); | |
if (!ignorePast) { | |
for (var i = 0, m = this.posted.length; i < m; i++) { | |
callback.reset(); | |
var result = callback(this.posted[i]); | |
if (ISCALLBACK(result) && i === this.posted.length-1) {WAITFOR(result,this)} | |
} | |
} | |
return callback; | |
}, | |
// | |
// A listener calls this to remove itself from a signal | |
// | |
NoInterest: function (callback) { | |
this.listeners.Remove(callback); | |
}, | |
// | |
// Hook a callback to a particular message on this signal | |
// | |
MessageHook: function (msg,callback,priority) { | |
callback = USING(callback); | |
if (!this.hooks) {this.hooks = {}; this.Interest(["ExecuteHooks",this])} | |
if (!this.hooks[msg]) {this.hooks[msg] = HOOKS(true)} | |
this.hooks[msg].Add(callback,priority); | |
for (var i = 0, m = this.posted.length; i < m; i++) | |
{if (this.posted[i] == msg) {callback.reset(); callback(this.posted[i])}} | |
callback.msg = msg; // keep track so we can remove it | |
return callback; | |
}, | |
// | |
// Execute the message hooks for the given message | |
// | |
ExecuteHooks: function (msg) { | |
var type = (isArray(msg) ? msg[0] : msg); | |
if (!this.hooks[type]) {return null} | |
return this.hooks[type].Execute(msg); | |
}, | |
// | |
// Remove a hook safely | |
// | |
RemoveHook: function (hook) { | |
this.hooks[hook.msg].Remove(hook); | |
} | |
},{ | |
signals: {}, // the named signals | |
find: function (name) { | |
if (!SIGNAL.signals[name]) {SIGNAL.signals[name] = new SIGNAL(name)} | |
return SIGNAL.signals[name]; | |
} | |
}); | |
// | |
// The main entry-points | |
// | |
BASE.Callback = BASE.CallBack = USING; | |
BASE.Callback.Delay = DELAY; | |
BASE.Callback.After = AFTER; | |
BASE.Callback.Queue = QUEUE; | |
BASE.Callback.Signal = SIGNAL.find; | |
BASE.Callback.Hooks = HOOKS; | |
BASE.Callback.ExecuteHooks = EXECUTEHOOKS; | |
})("MathJax"); | |
/**********************************************************/ | |
(function (BASENAME) { | |
var BASE = window[BASENAME]; | |
if (!BASE) {BASE = window[BASENAME] = {}} | |
var isSafari2 = (navigator.vendor === "Apple Computer, Inc." && | |
typeof navigator.vendorSub === "undefined"); | |
var sheets = 0; // used by Safari2 | |
// | |
// Update sheets count and look up the head object | |
// | |
var HEAD = function (head) { | |
if (document.styleSheets && document.styleSheets.length > sheets) | |
{sheets = document.styleSheets.length} | |
if (!head) { | |
head = document.head || ((document.getElementsByTagName("head"))[0]); | |
if (!head) {head = document.body} | |
} | |
return head; | |
}; | |
// | |
// Remove scripts that are completed so they don't clutter up the HEAD. | |
// This runs via setTimeout since IE7 can't remove the script while it is running. | |
// | |
var SCRIPTS = []; // stores scripts to be removed after a delay | |
var REMOVESCRIPTS = function () { | |
for (var i = 0, m = SCRIPTS.length; i < m; i++) {BASE.Ajax.head.removeChild(SCRIPTS[i])} | |
SCRIPTS = []; | |
}; | |
var PATH = {}; | |
PATH[BASENAME] = ""; // empty path gets the root URL | |
PATH.a11y = '[MathJax]/extensions/a11y'; // a11y extensions | |
PATH.Contrib = "https://cdn.mathjax.org/mathjax/contrib"; // the third-party extensions | |
BASE.Ajax = { | |
loaded: {}, // files already loaded | |
loading: {}, // files currently in process of loading | |
loadHooks: {}, // hooks to call when files are loaded | |
timeout: 15*1000, // timeout for loading of files (15 seconds) | |
styleDelay: 1, // delay to use before styles are available | |
config: { | |
root: "", // URL of root directory to load from | |
path: PATH // paths to named URL's (e.g., [MathJax]/...) | |
}, | |
params: {}, // filled in from MathJax.js?... | |
STATUS: { | |
OK: 1, // file is loading or did load OK | |
ERROR: -1 // file timed out during load | |
}, | |
// | |
// Return a complete URL to a file (replacing any root names) | |
// | |
fileURL: function (file) { | |
var match; | |
while ((match = file.match(/^\[([-._a-z0-9]+)\]/i)) && PATH.hasOwnProperty(match[1])) { | |
file = (PATH[match[1]]||this.config.root) + file.substr(match[1].length+2); | |
} | |
return file; | |
}, | |
// | |
// Replace root names if URL includes one | |
// | |
fileName: function (url) { | |
var root = this.config.root; | |
if (url.substr(0,root.length) === root) {url = "["+BASENAME+"]"+url.substr(root.length)} | |
do { | |
var recheck = false; | |
for (var id in PATH) {if (PATH.hasOwnProperty(id) && PATH[id]) { | |
if (url.substr(0,PATH[id].length) === PATH[id]) { | |
url = "["+id+"]"+url.substr(PATH[id].length); | |
recheck = true; | |
break; | |
} | |
}} | |
} while (recheck); | |
return url; | |
}, | |
// | |
// Cache-breaking revision number for file | |
// | |
fileRev: function (file) { | |
var V = BASE.cdnFileVersions[file] || BASE.cdnVersion || ''; | |
if (V) {V = "?V="+V} | |
return V; | |
}, | |
urlRev: function (file) {return this.fileURL(file)+this.fileRev(file)}, | |
// | |
// Load a file if it hasn't been already. | |
// Make sure the file URL is "safe"? | |
// | |
Require: function (file,callback) { | |
callback = BASE.Callback(callback); var type; | |
if (file instanceof Object) { | |
for (var i in file) | |
{if (file.hasOwnProperty(i)) {type = i.toUpperCase(); file = file[i]}} | |
} else {type = file.split(/\./).pop().toUpperCase()} | |
if (this.params.noContrib && file.substr(0,9) === "[Contrib]") { | |
callback(this.STATUS.ERROR); | |
} else { | |
file = this.fileURL(file); | |
// FIXME: check that URL is OK | |
if (this.loaded[file]) { | |
callback(this.loaded[file]); | |
} else { | |
var FILE = {}; FILE[type] = file; | |
this.Load(FILE,callback); | |
} | |
} | |
return callback; | |
}, | |
// | |
// Load a file regardless of where it is and whether it has | |
// already been loaded. | |
// | |
Load: function (file,callback) { | |
callback = BASE.Callback(callback); var type; | |
if (file instanceof Object) { | |
for (var i in file) | |
{if (file.hasOwnProperty(i)) {type = i.toUpperCase(); file = file[i]}} | |
} else {type = file.split(/\./).pop().toUpperCase()} | |
file = this.fileURL(file); | |
if (this.loading[file]) { | |
this.addHook(file,callback); | |
} else { | |
this.head = HEAD(this.head); | |
if (this.loader[type]) {this.loader[type].call(this,file,callback)} | |
else {throw Error("Can't load files of type "+type)} | |
} | |
return callback; | |
}, | |
// | |
// Register a load hook for a particular file (it will be called when | |
// loadComplete() is called for that file) | |
// | |
LoadHook: function (file,callback,priority) { | |
callback = BASE.Callback(callback); | |
if (file instanceof Object) | |
{for (var i in file) {if (file.hasOwnProperty(i)) {file = file[i]}}} | |
file = this.fileURL(file); | |
if (this.loaded[file]) {callback(this.loaded[file])} | |
else {this.addHook(file,callback,priority)} | |
return callback; | |
}, | |
addHook: function (file,callback,priority) { | |
if (!this.loadHooks[file]) {this.loadHooks[file] = MathJax.Callback.Hooks()} | |
this.loadHooks[file].Add(callback,priority); | |
callback.file = file; | |
}, | |
removeHook: function (hook) { | |
if (this.loadHooks[hook.file]) { | |
this.loadHooks[hook.file].Remove(hook); | |
if (!this.loadHooks[hook.file].hooks.length) {delete this.loadHooks[hook.file]} | |
} | |
}, | |
// | |
// Used when files are combined in a preloading configuration file | |
// | |
Preloading: function () { | |
for (var i = 0, m = arguments.length; i < m; i++) { | |
var file = this.fileURL(arguments[i]); | |
if (!this.loading[file]) {this.loading[file] = {preloaded: true}} | |
} | |
}, | |
// | |
// Code used to load the various types of files | |
// (JS for JavaScript, CSS for style sheets) | |
// | |
loader: { | |
// | |
// Create a SCRIPT tag to load the file | |
// | |
JS: function (file,callback) { | |
var name = this.fileName(file); | |
var script = document.createElement("script"); | |
var timeout = BASE.Callback(["loadTimeout",this,file]); | |
this.loading[file] = { | |
callback: callback, | |
timeout: setTimeout(timeout,this.timeout), | |
status: this.STATUS.OK, | |
script: script | |
}; | |
// | |
// Add this to the structure above after it is created to prevent recursion | |
// when loading the initial localization file (before loading messsage is available) | |
// | |
this.loading[file].message = BASE.Message.File(name); | |
script.onerror = timeout; // doesn't work in IE and no apparent substitute | |
script.type = "text/javascript"; | |
script.src = file+this.fileRev(name); | |
this.head.appendChild(script); | |
}, | |
// | |
// Create a LINK tag to load the style sheet | |
// | |
CSS: function (file,callback) { | |
var name = this.fileName(file); | |
var link = document.createElement("link"); | |
link.rel = "stylesheet"; link.type = "text/css"; | |
link.href = file+this.fileRev(name); | |
this.loading[file] = { | |
callback: callback, | |
message: BASE.Message.File(name), | |
status: this.STATUS.OK | |
}; | |
this.head.appendChild(link); | |
this.timer.create.call(this,[this.timer.file,file],link); | |
} | |
}, | |
// | |
// Timing code for checking when style sheets are available. | |
// | |
timer: { | |
// | |
// Create the timing callback and start the timing loop. | |
// We use a delay because some browsers need it to allow the styles | |
// to be processed. | |
// | |
create: function (callback,node) { | |
callback = BASE.Callback(callback); | |
if (node.nodeName === "STYLE" && node.styleSheet && | |
typeof(node.styleSheet.cssText) !== 'undefined') { | |
callback(this.STATUS.OK); // MSIE processes style immediately, but doesn't set its styleSheet! | |
} else if (window.chrome && node.nodeName === "LINK") { | |
callback(this.STATUS.OK); // Chrome doesn't give access to cssRules for stylesheet in | |
// a link node, so we can't detect when it is loaded. | |
} else if (isSafari2) { | |
this.timer.start(this,[this.timer.checkSafari2,sheets++,callback],this.styleDelay); | |
} else { | |
this.timer.start(this,[this.timer.checkLength,node,callback],this.styleDelay); | |
} | |
return callback; | |
}, | |
// | |
// Start the timer for the given callback checker | |
// | |
start: function (AJAX,check,delay,timeout) { | |
check = BASE.Callback(check); | |
check.execute = this.execute; check.time = this.time; | |
check.STATUS = AJAX.STATUS; check.timeout = timeout || AJAX.timeout; | |
check.delay = check.total = delay || 0; | |
if (delay) {setTimeout(check,delay)} else {check()} | |
}, | |
// | |
// Increment the time total, increase the delay | |
// and test if we are past the timeout time. | |
// | |
time: function (callback) { | |
this.total += this.delay; | |
this.delay = Math.floor(this.delay * 1.05 + 5); | |
if (this.total >= this.timeout) {callback(this.STATUS.ERROR); return 1} | |
return 0; | |
}, | |
// | |
// For JS file loads, call the proper routine according to status | |
// | |
file: function (file,status) { | |
if (status < 0) {BASE.Ajax.loadTimeout(file)} else {BASE.Ajax.loadComplete(file)} | |
}, | |
// | |
// Call the hook with the required data | |
// | |
execute: function () {this.hook.call(this.object,this,this.data[0],this.data[1])}, | |
// | |
// Safari2 doesn't set the link's stylesheet, so we need to look in the | |
// document.styleSheets array for the new sheet when it is created | |
// | |
checkSafari2: function (check,length,callback) { | |
if (check.time(callback)) return; | |
if (document.styleSheets.length > length && | |
document.styleSheets[length].cssRules && | |
document.styleSheets[length].cssRules.length) | |
{callback(check.STATUS.OK)} else {setTimeout(check,check.delay)} | |
}, | |
// | |
// Look for the stylesheets rules and check when they are defined | |
// and no longer of length zero. (This assumes there actually ARE | |
// some rules in the stylesheet.) | |
// | |
checkLength: function (check,node,callback) { | |
if (check.time(callback)) return; | |
var isStyle = 0; var sheet = (node.sheet || node.styleSheet); | |
try {if ((sheet.cssRules||sheet.rules||[]).length > 0) {isStyle = 1}} catch(err) { | |
if (err.message.match(/protected variable|restricted URI/)) {isStyle = 1} | |
else if (err.message.match(/Security error/)) { | |
// Firefox3 gives "Security error" for missing files, so | |
// can't distinguish that from OK files on remote servers. | |
// or OK files in different directory from local files. | |
isStyle = 1; // just say it is OK (can't really tell) | |
} | |
} | |
if (isStyle) { | |
// Opera 9.6 requires this setTimeout | |
setTimeout(BASE.Callback([callback,check.STATUS.OK]),0); | |
} else { | |
setTimeout(check,check.delay); | |
} | |
} | |
}, | |
// | |
// JavaScript code must call this when they are completely initialized | |
// (this allows them to perform asynchronous actions before indicating | |
// that they are complete). | |
// | |
loadComplete: function (file) { | |
file = this.fileURL(file); | |
var loading = this.loading[file]; | |
if (loading && !loading.preloaded) { | |
BASE.Message.Clear(loading.message); | |
clearTimeout(loading.timeout); | |
if (loading.script) { | |
if (SCRIPTS.length === 0) {setTimeout(REMOVESCRIPTS,0)} | |
SCRIPTS.push(loading.script); | |
} | |
this.loaded[file] = loading.status; delete this.loading[file]; | |
this.addHook(file,loading.callback); | |
} else { | |
if (loading) {delete this.loading[file]} | |
this.loaded[file] = this.STATUS.OK; | |
loading = {status: this.STATUS.OK} | |
} | |
if (!this.loadHooks[file]) {return null} | |
return this.loadHooks[file].Execute(loading.status); | |
}, | |
// | |
// If a file fails to load within the timeout period (or the onerror handler | |
// is called), this routine runs to signal the error condition. | |
// | |
loadTimeout: function (file) { | |
if (this.loading[file].timeout) {clearTimeout(this.loading[file].timeout)} | |
this.loading[file].status = this.STATUS.ERROR; | |
this.loadError(file); | |
this.loadComplete(file); | |
}, | |
// | |
// The default error hook for file load failures | |
// | |
loadError: function (file) { | |
BASE.Message.Set(["LoadFailed","File failed to load: %1",file],null,2000); | |
BASE.Hub.signal.Post(["file load error",file]); | |
}, | |
// | |
// Defines a style sheet from a hash of style declarations (key:value pairs | |
// where the key is the style selector and the value is a hash of CSS attributes | |
// and values). | |
// | |
Styles: function (styles,callback) { | |
var styleString = this.StyleString(styles); | |
if (styleString === "") { | |
callback = BASE.Callback(callback); | |
callback(); | |
} else { | |
var style = document.createElement("style"); style.type = "text/css"; | |
this.head = HEAD(this.head); | |
this.head.appendChild(style); | |
if (style.styleSheet && typeof(style.styleSheet.cssText) !== 'undefined') { | |
style.styleSheet.cssText = styleString; | |
} else { | |
style.appendChild(document.createTextNode(styleString)); | |
} | |
callback = this.timer.create.call(this,callback,style); | |
} | |
return callback; | |
}, | |
// | |
// Create a stylesheet string from a style declaration object | |
// | |
StyleString: function (styles) { | |
if (typeof(styles) === 'string') {return styles} | |
var string = "", id, style; | |
for (id in styles) {if (styles.hasOwnProperty(id)) { | |
if (typeof styles[id] === 'string') { | |
string += id + " {"+styles[id]+"}\n"; | |
} else if (BASE.Object.isArray(styles[id])) { | |
for (var i = 0; i < styles[id].length; i++) { | |
style = {}; style[id] = styles[id][i]; | |
string += this.StyleString(style); | |
} | |
} else if (id.substr(0,6) === '@media') { | |
string += id + " {"+this.StyleString(styles[id])+"}\n"; | |
} else if (styles[id] != null) { | |
style = []; | |
for (var name in styles[id]) {if (styles[id].hasOwnProperty(name)) { | |
if (styles[id][name] != null) | |
{style[style.length] = name + ': ' + styles[id][name]} | |
}} | |
string += id +" {"+style.join('; ')+"}\n"; | |
} | |
}} | |
return string; | |
} | |
}; | |
})("MathJax"); | |
/**********************************************************/ | |
MathJax.HTML = { | |
// | |
// Create an HTML element with given attributes and content. | |
// The def parameter is an (optional) object containing key:value pairs | |
// of the attributes and their values, and contents is an (optional) | |
// array of strings to be inserted as text, or arrays of the form | |
// [type,def,contents] that describes an HTML element to be inserted | |
// into the current element. Thus the contents can describe a complete | |
// HTML snippet of arbitrary complexity. E.g.: | |
// | |
// MathJax.HTML.Element("span",{id:"mySpan",style{"font-style":"italic"}},[ | |
// "(See the ",["a",{href:"http://www.mathjax.org"},["MathJax home page"]], | |
// " for more details.)"]); | |
// | |
Element: function (type,def,contents) { | |
var obj = document.createElement(type), id; | |
if (def) { | |
if (def.hasOwnProperty("style")) { | |
var style = def.style; def.style = {}; | |
for (id in style) {if (style.hasOwnProperty(id)) | |
{def.style[id.replace(/-([a-z])/g,this.ucMatch)] = style[id]}} | |
} | |
MathJax.Hub.Insert(obj,def); | |
for (id in def) { | |
if (id === "role" || id.substr(0,5) === "aria-") obj.setAttribute(id,def[id]); | |
} | |
} | |
if (contents) { | |
if (!MathJax.Object.isArray(contents)) {contents = [contents]} | |
for (var i = 0, m = contents.length; i < m; i++) { | |
if (MathJax.Object.isArray(contents[i])) { | |
obj.appendChild(this.Element(contents[i][0],contents[i][1],contents[i][2])); | |
} else if (type === "script") { // IE throws an error if script is added as a text node | |
this.setScript(obj, contents[i]); | |
} else { | |
obj.appendChild(document.createTextNode(contents[i])); | |
} | |
} | |
} | |
return obj; | |
}, | |
ucMatch: function (match,c) {return c.toUpperCase()}, | |
addElement: function (span,type,def,contents) {return span.appendChild(this.Element(type,def,contents))}, | |
TextNode: function (text) {return document.createTextNode(text)}, | |
addText: function (span,text) {return span.appendChild(this.TextNode(text))}, | |
// | |
// Set and get the text of a script | |
// | |
setScript: function (script,text) { | |
if (this.setScriptBug) {script.text = text} else { | |
while (script.firstChild) {script.removeChild(script.firstChild)} | |
this.addText(script,text); | |
} | |
}, | |
getScript: function (script) { | |
var text = (script.text === "" ? script.innerHTML : script.text); | |
return text.replace(/^\s+/,"").replace(/\s+$/,""); | |
}, | |
// | |
// Manage cookies | |
// | |
Cookie: { | |
prefix: "mjx", | |
expires: 365, | |
// | |
// Save an object as a named cookie | |
// | |
Set: function (name,def) { | |
var keys = []; | |
if (def) { | |
for (var id in def) {if (def.hasOwnProperty(id)) { | |
keys.push(id+":"+def[id].toString().replace(/&/g,"&&")); | |
}} | |
} | |
var cookie = this.prefix+"."+name+"="+escape(keys.join('&;')); | |
if (this.expires) { | |
var time = new Date(); time.setDate(time.getDate() + this.expires); | |
cookie += '; expires='+time.toGMTString(); | |
} | |
try {document.cookie = cookie+"; path=/"} catch (err) {} // ignore errors saving cookies | |
}, | |
// | |
// Get the contents of a named cookie and incorporate | |
// it into the given object (or return a fresh one) | |
// | |
Get: function (name,obj) { | |
if (!obj) {obj = {}} | |
var pattern = new RegExp("(?:^|;\\s*)"+this.prefix+"\\."+name+"=([^;]*)(?:;|$)"); | |
var match; | |
try {match = pattern.exec(document.cookie)} catch (err) {}; // ignore errors reading cookies | |
if (match && match[1] !== "") { | |
var keys = unescape(match[1]).split('&;'); | |
for (var i = 0, m = keys.length; i < m; i++) { | |
match = keys[i].match(/([^:]+):(.*)/); | |
var value = match[2].replace(/&&/g,'&'); | |
if (value === "true") {value = true} else if (value === "false") {value = false} | |
else if (value.match(/^-?(\d+(\.\d+)?|\.\d+)$/)) {value = parseFloat(value)} | |
obj[match[1]] = value; | |
} | |
} | |
return obj; | |
} | |
} | |
}; | |
/**********************************************************/ | |
MathJax.Localization = { | |
locale: "en", | |
directory: "[MathJax]/localization", | |
strings: { | |
// Currently, this list is not modified by the MathJax-i18n script. You can | |
// run the following command in MathJax/unpacked/localization to update it: | |
// | |
// find . -name "*.js" | xargs grep menuTitle\: | grep -v qqq | sed 's/^\.\/\(.*\)\/.*\.js\: / "\1"\: \{/' | sed 's/,$/\},/' | sed 's/"English"/"English", isLoaded: true/' > tmp ; sort tmp > tmp2 ; sed '$ s/,$//' tmp2 ; rm tmp* | |
// | |
// This only takes languages with localization data so you must also add | |
// the languages that use a remap but are not translated at all. | |
// | |
"ast": {menuTitle: "asturianu"}, | |
"bg": {menuTitle: "\u0431\u044A\u043B\u0433\u0430\u0440\u0441\u043A\u0438"}, | |
"bcc": {menuTitle: "\u0628\u0644\u0648\u0686\u06CC"}, | |
"br": {menuTitle: "brezhoneg"}, | |
"ca": {menuTitle: "catal\u00E0"}, | |
"cdo": {menuTitle: "M\u00ECng-d\u0115\u0324ng-ng\u1E73\u0304"}, | |
"cs": {menuTitle: "\u010De\u0161tina"}, | |
"da": {menuTitle: "dansk"}, | |
"de": {menuTitle: "Deutsch"}, | |
"diq": {menuTitle: "Zazaki"}, | |
"en": {menuTitle: "English", isLoaded: true}, | |
"eo": {menuTitle: "Esperanto"}, | |
"es": {menuTitle: "espa\u00F1ol"}, | |
"fa": {menuTitle: "\u0641\u0627\u0631\u0633\u06CC"}, | |
"fi": {menuTitle: "suomi"}, | |
"fr": {menuTitle: "fran\u00E7ais"}, | |
"gl": {menuTitle: "galego"}, | |
"he": {menuTitle: "\u05E2\u05D1\u05E8\u05D9\u05EA"}, | |
"ia": {menuTitle: "interlingua"}, | |
"it": {menuTitle: "italiano"}, | |
"ja": {menuTitle: "\u65E5\u672C\u8A9E"}, | |
"kn": {menuTitle: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1"}, | |
"ko": {menuTitle: "\uD55C\uAD6D\uC5B4"}, | |
"lb": {menuTitle: "L\u00EBtzebuergesch"}, | |
"lki": {menuTitle: "\u0644\u06D5\u06A9\u06CC"}, | |
"lt": {menuTitle: "lietuvi\u0173"}, | |
"mk": {menuTitle: "\u043C\u0430\u043A\u0435\u0434\u043E\u043D\u0441\u043A\u0438"}, | |
"nl": {menuTitle: "Nederlands"}, | |
"oc": {menuTitle: "occitan"}, | |
"pl": {menuTitle: "polski"}, | |
"pt": {menuTitle: "portugus\u00EA"}, | |
"pt-br": {menuTitle: "portugu\u00EAs do Brasil"}, | |
"ru": {menuTitle: "\u0440\u0443\u0441\u0441\u043A\u0438\u0439"}, | |
"sco": {menuTitle: "Scots"}, | |
"scn": {menuTitle: "sicilianu"}, | |
"sl": {menuTitle: "sloven\u0161\u010Dina"}, | |
"sv": {menuTitle: "svenska"}, | |
"tr": {menuTitle: "T\u00FCrk\u00E7e"}, | |
"uk": {menuTitle: "\u0443\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"}, | |
"vi": {menuTitle: "Ti\u1EBFng Vi\u1EC7t"}, | |
"zh-hans": {menuTitle: "\u4E2D\u6587\uFF08\u7B80\u4F53\uFF09"} | |
}, | |
// | |
// The pattern for substitution escapes: | |
// %n or %{n} or %{plural:%n|option1|option1|...} or %c | |
// | |
pattern: /%(\d+|\{\d+\}|\{[a-z]+:\%\d+(?:\|(?:%\{\d+\}|%.|[^\}])*)+\}|.)/g, | |
SPLIT: ("axb".split(/(x)/).length === 3 ? | |
function (string,regex) {return string.split(regex)} : | |
// | |
// IE8 and below don't do split() correctly when the pattern includes | |
// parentheses (the split should include the matched exrepssions). | |
// So implement it by hand here. | |
// | |
function (string,regex) { | |
var result = [], match, last = 0; | |
regex.lastIndex = 0; | |
while ((match = regex.exec(string))) { | |
result.push(string.substr(last,match.index-last)); | |
result.push.apply(result,match.slice(1)); | |
last = match.index + match[0].length; | |
} | |
result.push(string.substr(last)); | |
return result; | |
}), | |
_: function (id,phrase) { | |
if (MathJax.Object.isArray(phrase)) {return this.processSnippet(id,phrase)} | |
return this.processString(this.lookupPhrase(id,phrase),[].slice.call(arguments,2)); | |
}, | |
processString: function (string,args,domain) { | |
// | |
// Process arguments for substitution | |
// If the argument is a snippet (and we are processing snippets) do so, | |
// Otherwise, if it is a number, convert it for the lacale | |
// | |
var i, m, isArray = MathJax.Object.isArray; | |
for (i = 0, m = args.length; i < m; i++) { | |
if (domain && isArray(args[i])) {args[i] = this.processSnippet(domain,args[i])} | |
} | |
// | |
// Split string at escapes and process them individually | |
// | |
var parts = this.SPLIT(string,this.pattern); | |
for (i = 1, m = parts.length; i < m; i += 2) { | |
var c = parts[i].charAt(0); // first char will be { or \d or a char to be kept literally | |
if (c >= "0" && c <= "9") { // %n | |
parts[i] = args[parts[i]-1]; | |
if (typeof parts[i] === "number") parts[i] = this.number(parts[i]); | |
} else if (c === "{") { // %{n} or %{plural:%n|...} | |
c = parts[i].substr(1); | |
if (c >= "0" && c <= "9") { // %{n} | |
parts[i] = args[parts[i].substr(1,parts[i].length-2)-1]; | |
if (typeof parts[i] === "number") parts[i] = this.number(parts[i]); | |
} else { // %{plural:%n|...} | |
var match = parts[i].match(/^\{([a-z]+):%(\d+)\|(.*)\}$/); | |
if (match) { | |
if (match[1] === "plural") { | |
var n = args[match[2]-1]; | |
if (typeof n === "undefined") { | |
parts[i] = "???"; // argument doesn't exist | |
} else { | |
n = this.plural(n) - 1; // index of the form to use | |
var plurals = match[3].replace(/(^|[^%])(%%)*%\|/g,"$1$2%\uEFEF").split(/\|/); // the parts (replacing %| with a special character) | |
if (n >= 0 && n < plurals.length) { | |
parts[i] = this.processString(plurals[n].replace(/\uEFEF/g,"|"),args,domain); | |
} else { | |
parts[i] = "???"; // no string for this index | |
} | |
} | |
} else {parts[i] = "%"+parts[i]} // not "plural", put back the % and leave unchanged | |
} | |
} | |
} | |
if (parts[i] == null) {parts[i] = "???"} | |
} | |
// | |
// If we are not forming a snippet, return the completed string | |
// | |
if (!domain) {return parts.join("")} | |
// | |
// We need to return an HTML snippet, so buld it from the | |
// broken up string with inserted parts (that could be snippets) | |
// | |
var snippet = [], part = ""; | |
for (i = 0; i < m; i++) { | |
part += parts[i]; i++; // add the string and move on to substitution result | |
if (i < m) { | |
if (isArray(parts[i])) { // substitution was a snippet | |
snippet.push(part); // add the accumulated string | |
snippet = snippet.concat(parts[i]); // concatenate the substution snippet | |
part = ""; // start accumulating a new string | |
} else { // substitution was a string | |
part += parts[i]; // add to accumulating string | |
} | |
} | |
} | |
if (part !== "") {snippet.push(part)} // add final string | |
return snippet; | |
}, | |
processSnippet: function (domain,snippet) { | |
var result = []; // the new snippet | |
// | |
// Look through the original snippet for | |
// strings or snippets to translate | |
// | |
for (var i = 0, m = snippet.length; i < m; i++) { | |
if (MathJax.Object.isArray(snippet[i])) { | |
// | |
// This could be a sub-snippet: | |
// ["tag"] or ["tag",{properties}] or ["tag",{properties},snippet] | |
// Or it could be something to translate: | |
// [id,string,args] or [domain,snippet] | |
var data = snippet[i]; | |
if (typeof data[1] === "string") { // [id,string,args] | |
var id = data[0]; if (!MathJax.Object.isArray(id)) {id = [domain,id]} | |
var phrase = this.lookupPhrase(id,data[1]); | |
result = result.concat(this.processMarkdown(phrase,data.slice(2),domain)); | |
} else if (MathJax.Object.isArray(data[1])) { // [domain,snippet] | |
result = result.concat(this.processSnippet.apply(this,data)); | |
} else if (data.length >= 3) { // ["tag",{properties},snippet] | |
result.push([data[0],data[1],this.processSnippet(domain,data[2])]); | |
} else { // ["tag"] or ["tag",{properties}] | |
result.push(snippet[i]); | |
} | |
} else { // a string | |
result.push(snippet[i]); | |
} | |
} | |
return result; | |
}, | |
markdownPattern: /(%.)|(\*{1,3})((?:%.|.)+?)\2|(`+)((?:%.|.)+?)\4|\[((?:%.|.)+?)\]\(([^\s\)]+)\)/, | |
// %c or *bold*, **italics**, ***bold-italics***, or `code`, or [link](url) | |
processMarkdown: function (phrase,args,domain) { | |
var result = [], data; | |
// | |
// Split the string by the Markdown pattern | |
// (the text blocks are separated by | |
// c,stars,star-text,backtics,code-text,link-text,URL). | |
// Start with teh first text string from the split. | |
// | |
var parts = phrase.split(this.markdownPattern); | |
var string = parts[0]; | |
// | |
// Loop through the matches and process them | |
// | |
for (var i = 1, m = parts.length; i < m; i += 8) { | |
if (parts[i+1]) { // stars (for bold/italic) | |
// | |
// Select the tag to use by number of stars (three stars requires two tags) | |
// | |
data = this.processString(parts[i+2],args,domain); | |
if (!MathJax.Object.isArray(data)) {data = [data]} | |
data = [["b","i","i"][parts[i+1].length-1],{},data]; // number of stars determines type | |
if (parts[i+1].length === 3) {data = ["b",{},data]} // bold-italic | |
} else if (parts[i+3]) { // backtics (for code) | |
// | |
// Remove one leading or trailing space, and process substitutions | |
// Make a <code> tag | |
// | |
data = this.processString(parts[i+4].replace(/^\s/,"").replace(/\s$/,""),args,domain); | |
if (!MathJax.Object.isArray(data)) {data = [data]} | |
data = ["code",{},data]; | |
} else if (parts[i+5]) { // hyperlink | |
// | |
// Process the link text, and make an <a> tag with the URL | |
// | |
data = this.processString(parts[i+5],args,domain); | |
if (!MathJax.Object.isArray(data)) {data = [data]} | |
data = ["a",{href:this.processString(parts[i+6],args),target:"_blank"},data]; | |
} else { | |
// | |
// Escaped character (%c) gets added into the string. | |
// | |
string += parts[i]; data = null; | |
} | |
// | |
// If there is a tag to insert, | |
// Add any pending string, then push the tag | |
// | |
if (data) { | |
result = this.concatString(result,string,args,domain); | |
result.push(data); string = ""; | |
} | |
// | |
// Process the string that follows matches pattern | |
// | |
if (parts[i+7] !== "") {string += parts[i+7]} | |
}; | |
// | |
// Add any pending string and return the resulting snippet | |
// | |
result = this.concatString(result,string,args,domain); | |
return result; | |
}, | |
concatString: function (result,string,args,domain) { | |
if (string != "") { | |
// | |
// Process the substutions. | |
// If the result is not a snippet, turn it into one. | |
// Then concatenate the snippet to the current one | |
// | |
string = this.processString(string,args,domain); | |
if (!MathJax.Object.isArray(string)) {string = [string]} | |
result = result.concat(string); | |
} | |
return result; | |
}, | |
lookupPhrase: function (id,phrase,domain) { | |
// | |
// Get the domain and messageID | |
// | |
if (!domain) {domain = "_"} | |
if (MathJax.Object.isArray(id)) {domain = (id[0] || "_"); id = (id[1] || "")} | |
// | |
// Check if the data is available and if not, | |
// load it and throw a restart error so the calling | |
// code can wait for the load and try again. | |
// | |
var load = this.loadDomain(domain); | |
if (load) {MathJax.Hub.RestartAfter(load)} | |
// | |
// Look up the message in the localization data | |
// (if not found, the original English is used) | |
// | |
var localeData = this.strings[this.locale]; | |
if (localeData) { | |
if (localeData.domains && domain in localeData.domains) { | |
var domainData = localeData.domains[domain]; | |
if (domainData.strings && id in domainData.strings) | |
{phrase = domainData.strings[id]} | |
} | |
} | |
// | |
// return the translated phrase | |
// | |
return phrase; | |
}, | |
// | |
// Load a langauge data file from the proper | |
// directory and file. | |
// | |
loadFile: function (file,data,callback) { | |
callback = MathJax.Callback(callback); | |
file = (data.file || file); // the data's file name or the default name | |
if (!file.match(/\.js$/)) {file += ".js"} // add .js if needed | |
// | |
// Add the directory if the file doesn't | |
// contain a full URL already. | |
// | |
if (!file.match(/^([a-z]+:|\[MathJax\])/)) { | |
var dir = (this.strings[this.locale].directory || | |
this.directory + "/" + this.locale || | |
"[MathJax]/localization/" + this.locale); | |
file = dir + "/" + file; | |
} | |
// | |
// Load the file and mark the data as loaded (even if it | |
// failed to load, so we don't continue to try to load it | |
// over and over). | |
// | |
var load = MathJax.Ajax.Require(file,function () {data.isLoaded = true; return callback()}); | |
// | |
// Return the callback if needed, otherwise null. | |
// | |
return (load.called ? null : load); | |
}, | |
// | |
// Check to see if the localization data are loaded | |
// for the given domain; if not, load the data file, | |
// and return a callback for the loading operation. | |
// Otherwise return null (data are loaded). | |
// | |
loadDomain: function (domain,callback) { | |
var load, localeData = this.strings[this.locale]; | |
if (localeData) { | |
if (!localeData.isLoaded) { | |
load = this.loadFile(this.locale,localeData); | |
if (load) { | |
return MathJax.Callback.Queue( | |
load,["loadDomain",this,domain] // call again to load domain | |
).Push(callback||{}); | |
} | |
} | |
if (localeData.domains && domain in localeData.domains) { | |
var domainData = localeData.domains[domain]; | |
if (!domainData.isLoaded) { | |
load = this.loadFile(domain,domainData); | |
if (load) {return MathJax.Callback.Queue(load).Push(callback)} | |
} | |
} | |
} | |
// localization data are loaded, so just do the callback | |
return MathJax.Callback(callback)(); | |
}, | |
// | |
// Perform a function, properly handling | |
// restarts due to localization file loads. | |
// | |
// Note that this may return before the function | |
// has been called successfully, so you should | |
// consider fn as running asynchronously. (Callbacks | |
// can be used to synchronize it with other actions.) | |
// | |
Try: function (fn) { | |
fn = MathJax.Callback(fn); fn.autoReset = true; | |
try {fn()} catch (err) { | |
if (!err.restart) {throw err} | |
MathJax.Callback.After(["Try",this,fn],err.restart); | |
} | |
}, | |
// | |
// Reset the current language | |
// | |
resetLocale: function(locale) { | |
// Selection algorithm: | |
// 1) Downcase locale name (e.g. "en-US" => "en-us") | |
// 2) Try a parent language (e.g. "en-us" => "en") | |
// 3) Try the fallback specified in the data (e.g. "pt" => "pt-br") | |
// 4) Otherwise don't change the locale. | |
if (!locale) return; | |
locale = locale.toLowerCase(); | |
while (!this.strings[locale]) { | |
var dashPos = locale.lastIndexOf("-"); | |
if (dashPos === -1) return; | |
locale = locale.substring(0, dashPos); | |
} | |
var remap = this.strings[locale].remap; | |
this.locale = remap ? remap : locale; | |
}, | |
// | |
// Set the current language | |
// | |
setLocale: function(locale) { | |
this.resetLocale(locale); | |
if (MathJax.Menu) {this.loadDomain("MathMenu")} | |
}, | |
// | |
// Add or update a language or domain | |
// | |
addTranslation: function (locale,domain,definition) { | |
var data = this.strings[locale], isNew = false; | |
if (!data) {data = this.strings[locale] = {}; isNew = true} | |
if (!data.domains) {data.domains = {}} | |
if (domain) { | |
if (!data.domains[domain]) {data.domains[domain] = {}} | |
data = data.domains[domain]; | |
} | |
MathJax.Hub.Insert(data,definition); | |
if (isNew && MathJax.Menu.menu) {MathJax.Menu.CreateLocaleMenu()} | |
}, | |
// | |
// Set CSS for an element based on font requirements | |
// | |
setCSS: function (div) { | |
var locale = this.strings[this.locale]; | |
if (locale) { | |
if (locale.fontFamily) {div.style.fontFamily = locale.fontFamily} | |
if (locale.fontDirection) { | |
div.style.direction = locale.fontDirection; | |
if (locale.fontDirection === "rtl") {div.style.textAlign = "right"} | |
} | |
} | |
return div; | |
}, | |
// | |
// Get the language's font family or direction | |
// | |
fontFamily: function () { | |
var locale = this.strings[this.locale]; | |
return (locale ? locale.fontFamily : null); | |
}, | |
fontDirection: function () { | |
var locale = this.strings[this.locale]; | |
return (locale ? locale.fontDirection : null); | |
}, | |
// | |
// Get the language's plural index for a number | |
// | |
plural: function (n) { | |
var locale = this.strings[this.locale]; | |
if (locale && locale.plural) {return locale.plural(n)} | |
// default | |
if (n == 1) {return 1} // one | |
return 2; // other | |
}, | |
// | |
// Convert a number to language-specific form | |
// | |
number: function(n) { | |
var locale = this.strings[this.locale]; | |
if (locale && locale.number) {return locale.number(n)} | |
// default | |
return n; | |
} | |
}; | |
/**********************************************************/ | |
MathJax.Message = { | |
ready: false, // used to tell when the styles are available | |
log: [{}], current: null, | |
textNodeBug: (navigator.vendor === "Apple Computer, Inc." && | |
typeof navigator.vendorSub === "undefined") || | |
(window.hasOwnProperty && window.hasOwnProperty("konqueror")), // Konqueror displays some gibberish with text.nodeValue = "..." | |
styles: { | |
"#MathJax_Message": { | |
position: "fixed", left: "1px", bottom: "2px", | |
'background-color': "#E6E6E6", border: "1px solid #959595", | |
margin: "0px", padding: "2px 8px", | |
'z-index': "102", color: "black", 'font-size': "80%", | |
width: "auto", 'white-space': "nowrap" | |
}, | |
"#MathJax_MSIE_Frame": { | |
position: "absolute", | |
top:0, left: 0, width: "0px", 'z-index': 101, | |
border: "0px", margin: "0px", padding: "0px" | |
} | |
}, | |
browsers: { | |
MSIE: function (browser) { | |
MathJax.Message.msieFixedPositionBug = ((document.documentMode||0) < 7); | |
if (MathJax.Message.msieFixedPositionBug) | |
{MathJax.Hub.config.styles["#MathJax_Message"].position = "absolute"} | |
MathJax.Message.quirks = (document.compatMode === "BackCompat"); | |
}, | |
Chrome: function (browser) { | |
MathJax.Hub.config.styles["#MathJax_Message"].bottom = "1.5em"; | |
MathJax.Hub.config.styles["#MathJax_Message"].left = "1em"; | |
} | |
}, | |
Init: function (styles) { | |
if (styles) {this.ready = true} | |
if (!document.body || !this.ready) {return false} | |
// | |
// ASCIIMathML replaces the entire page with a copy of itself (@#!#%@!!) | |
// so check that this.div is still part of the page, otherwise look up | |
// the copy and use that. | |
// | |
if (this.div && this.div.parentNode == null) { | |
this.div = document.getElementById("MathJax_Message"); | |
if (this.div) {this.text = this.div.firstChild} | |
} | |
if (!this.div) { | |
var frame = document.body; | |
if (this.msieFixedPositionBug && window.attachEvent) { | |
frame = this.frame = this.addDiv(document.body); frame.removeAttribute("id"); | |
frame.style.position = "absolute"; | |
frame.style.border = frame.style.margin = frame.style.padding = "0px"; | |
frame.style.zIndex = "101"; frame.style.height = "0px"; | |
frame = this.addDiv(frame); | |
frame.id = "MathJax_MSIE_Frame"; | |
window.attachEvent("onscroll",this.MoveFrame); | |
window.attachEvent("onresize",this.MoveFrame); | |
this.MoveFrame(); | |
} | |
this.div = this.addDiv(frame); this.div.style.display = "none"; | |
this.text = this.div.appendChild(document.createTextNode("")); | |
} | |
return true; | |
}, | |
addDiv: function (parent) { | |
var div = document.createElement("div"); | |
div.id = "MathJax_Message"; | |
if (parent.firstChild) {parent.insertBefore(div,parent.firstChild)} | |
else {parent.appendChild(div)} | |
return div; | |
}, | |
MoveFrame: function () { | |
var body = (MathJax.Message.quirks ? document.body : document.documentElement); | |
var frame = MathJax.Message.frame; | |
frame.style.left = body.scrollLeft + 'px'; | |
frame.style.top = body.scrollTop + 'px'; | |
frame.style.width = body.clientWidth + 'px'; | |
frame = frame.firstChild; | |
frame.style.height = body.clientHeight + 'px'; | |
}, | |
localize: function (message) { | |
return MathJax.Localization._(message,message); | |
}, | |
filterText: function (text,n,id) { | |
if (MathJax.Hub.config.messageStyle === "simple") { | |
if (id === "LoadFile") { | |
if (!this.loading) {this.loading = this.localize("Loading") + " "} | |
text = this.loading; this.loading += "."; | |
} else if (id === "ProcessMath") { | |
if (!this.processing) {this.processing = this.localize("Processing") + " "} | |
text = this.processing; this.processing += "."; | |
} else if (id === "TypesetMath") { | |
if (!this.typesetting) {this.typesetting = this.localize("Typesetting") + " "} | |
text = this.typesetting; this.typesetting += "."; | |
} | |
} | |
return text; | |
}, | |
clearCounts: function () { | |
delete this.loading; | |
delete this.processing; | |
delete this.typesetting; | |
}, | |
Set: function (text,n,clearDelay) { | |
if (n == null) {n = this.log.length; this.log[n] = {}} | |
// | |
// Translate message if it is [id,message,arguments] | |
// | |
var id = ""; | |
if (MathJax.Object.isArray(text)) { | |
id = text[0]; if (MathJax.Object.isArray(id)) {id = id[1]} | |
// | |
// Localization._() will throw a restart error if a localization file | |
// needs to be loaded, so trap that and redo the Set() call | |
// after it is loaded. | |
// | |
try { | |
text = MathJax.Localization._.apply(MathJax.Localization,text); | |
} catch (err) { | |
if (!err.restart) {throw err} | |
if (!err.restart.called) { | |
// | |
// Mark it so we can tell if the Clear() comes before the message is displayed | |
// | |
if (this.log[n].restarted == null) {this.log[n].restarted = 0} | |
this.log[n].restarted++; delete this.log[n].cleared; | |
MathJax.Callback.After(["Set",this,text,n,clearDelay],err.restart); | |
return n; | |
} | |
} | |
} | |
// | |
// Clear the timout timer. | |
// | |
if (this.timer) {clearTimeout(this.timer); delete this.timer} | |
// | |
// Save the message and filtered message. | |
// | |
this.log[n].text = text; this.log[n].filteredText = text = this.filterText(text,n,id); | |
// | |
// Hook the message into the message list so we can tell | |
// what message to put up when this one is removed. | |
// | |
if (typeof(this.log[n].next) === "undefined") { | |
this.log[n].next = this.current; | |
if (this.current != null) {this.log[this.current].prev = n} | |
this.current = n; | |
} | |
// | |
// Show the message if it is the currently active one. | |
// | |
if (this.current === n && MathJax.Hub.config.messageStyle !== "none") { | |
if (this.Init()) { | |
if (this.textNodeBug) {this.div.innerHTML = text} else {this.text.nodeValue = text} | |
this.div.style.display = ""; | |
if (this.status) {window.status = ""; delete this.status} | |
} else { | |
window.status = text; | |
this.status = true; | |
} | |
} | |
// | |
// Check if the message was resetarted to load a localization file | |
// and if it has been cleared in the meanwhile. | |
// | |
if (this.log[n].restarted) { | |
if (this.log[n].cleared) {clearDelay = 0} | |
if (--this.log[n].restarted === 0) {delete this.log[n].cleared} | |
} | |
// | |
// Check if we need to clear the message automatically. | |
// | |
if (clearDelay) {setTimeout(MathJax.Callback(["Clear",this,n]),clearDelay)} | |
else if (clearDelay == 0) {this.Clear(n,0)} | |
// | |
// Return the message number. | |
// | |
return n; | |
}, | |
Clear: function (n,delay) { | |
// | |
// Detatch the message from the active list. | |
// | |
if (this.log[n].prev != null) {this.log[this.log[n].prev].next = this.log[n].next} | |
if (this.log[n].next != null) {this.log[this.log[n].next].prev = this.log[n].prev} | |
// | |
// If it is the current message, get the next one to show. | |
// | |
if (this.current === n) { | |
this.current = this.log[n].next; | |
if (this.text) { | |
if (this.div.parentNode == null) {this.Init()} // see ASCIIMathML comments above | |
if (this.current == null) { | |
// | |
// If there are no more messages, remove the message box. | |
// | |
if (this.timer) {clearTimeout(this.timer); delete this.timer} | |
if (delay == null) {delay = 600} | |
if (delay === 0) {this.Remove()} | |
else {this.timer = setTimeout(MathJax.Callback(["Remove",this]),delay)} | |
} else if (MathJax.Hub.config.messageStyle !== "none") { | |
// | |
// If there is an old message, put it in place | |
// | |
if (this.textNodeBug) {this.div.innerHTML = this.log[this.current].filteredText} | |
else {this.text.nodeValue = this.log[this.current].filteredText} | |
} | |
if (this.status) {window.status = ""; delete this.status} | |
} else if (this.status) { | |
window.status = (this.current == null ? "" : this.log[this.current].text); | |
} | |
} | |
// | |
// Clean up the log data no longer needed | |
// | |
delete this.log[n].next; delete this.log[n].prev; | |
delete this.log[n].filteredText; | |
// | |
// If this is a restarted localization message, mark that it has been cleared | |
// while waiting for the file to load. | |
// | |
if (this.log[n].restarted) {this.log[n].cleared = true} | |
}, | |
Remove: function () { | |
// FIXME: do a fade out or something else interesting? | |
this.text.nodeValue = ""; | |
this.div.style.display = "none"; | |
}, | |
File: function (file) { | |
return this.Set(["LoadFile","Loading %1",file],null,null); | |
}, | |
Log: function () { | |
var strings = []; | |
for (var i = 1, m = this.log.length; i < m; i++) {strings[i] = this.log[i].text} | |
return strings.join("\n"); | |
} | |
}; | |
/**********************************************************/ | |
MathJax.Hub = { | |
config: { | |
root: "", | |
config: [], // list of configuration files to load | |
styleSheets: [], // list of CSS files to load | |
styles: { // styles to generate in-line | |
".MathJax_Preview": {color: "#888"} | |
}, | |
jax: [], // list of input and output jax to load | |
extensions: [], // list of extensions to load | |
preJax: null, // pattern to remove from before math script tag | |
postJax: null, // pattern to remove from after math script tag | |
displayAlign: 'center', // how to align displayed equations (left, center, right) | |
displayIndent: '0', // indentation for displayed equations (when not centered) | |
preRemoveClass: 'MathJax_Preview', // class of objects to remove preceeding math script | |
showProcessingMessages: true, // display "Processing math: nn%" messages or not | |
messageStyle: "normal", // set to "none" or "simple" (for "Loading..." and "Processing...") | |
delayStartupUntil: "none", // set to "onload" to delay setup until the onload handler runs | |
// set to "configured" to delay startup until MathJax.Hub.Configured() is called | |
// set to a Callback to wait for before continuing with the startup | |
skipStartupTypeset: false, // set to true to skip PreProcess and Process during startup | |
elements: [], // array of elements to process when none is given explicitly | |
positionToHash: true, // after initial typeset pass, position to #hash location? | |
showMathMenu: true, // attach math context menu to typeset math? | |
showMathMenuMSIE: true, // separtely determine if MSIE should have math menu | |
// (since the code for that is a bit delicate) | |
menuSettings: { | |
zoom: "None", // when to do MathZoom | |
CTRL: false, // require CTRL for MathZoom? | |
ALT: false, // require Alt or Option? | |
CMD: false, // require CMD? | |
Shift: false, // require Shift? | |
discoverable: false, // make math menu discoverable on hover? | |
zscale: "200%", // the scaling factor for MathZoom | |
renderer: null, // set when Jax are loaded | |
font: "Auto", // what font HTML-CSS should use | |
context: "MathJax", // or "Browser" for pass-through to browser menu | |
locale: null, // the language to use for messages | |
mpContext: false, // true means pass menu events to MathPlayer in IE | |
mpMouse: false, // true means pass mouse events to MathPlayer in IE | |
texHints: true, // include class names for TeXAtom elements | |
FastPreview: null, // use PreviewHTML output as preview? | |
assistiveMML: null, // include hidden MathML for screen readers? | |
inTabOrder: true, // set to false if math elements should be included in the tabindex | |
semantics: false // add semantics tag with original form in MathML output | |
}, | |
errorSettings: { | |
// localized HTML snippet structure for message to use | |
message: ["[",["MathProcessingError","Math Processing Error"],"]"], | |
style: {color: "#CC0000", "font-style":"italic"} // style for message | |
}, | |
ignoreMMLattributes: {} // attributes not to copy to HTML-CSS or SVG output | |
// from MathML input (in addition to the ones in MML.nocopyAttributes). | |
// An id set to true will be ignored, one set to false will | |
// be allowed (even if other criteria normally would prevent | |
// it from being copied); use false carefully! | |
}, | |
preProcessors: MathJax.Callback.Hooks(true), // list of callbacks for preprocessing (initialized by extensions) | |
inputJax: {}, // mime-type mapped to input jax (by registration) | |
outputJax: {order:{}}, // mime-type mapped to output jax list (by registration) | |
processSectionDelay: 50, // pause between input and output phases of processing | |
processUpdateTime: 250, // time between screen updates when processing math (milliseconds) | |
processUpdateDelay: 10, // pause between screen updates to allow other processing (milliseconds) | |
signal: MathJax.Callback.Signal("Hub"), // Signal used for Hub events | |
Config: function (def) { | |
this.Insert(this.config,def); | |
if (this.config.Augment) {this.Augment(this.config.Augment)} | |
}, | |
CombineConfig: function (name,def) { | |
var config = this.config, id, parent; name = name.split(/\./); | |
for (var i = 0, m = name.length; i < m; i++) { | |
id = name[i]; if (!config[id]) {config[id] = {}} | |
parent = config; config = config[id]; | |
} | |
parent[id] = config = this.Insert(def,config); | |
return config; | |
}, | |
Register: { | |
PreProcessor: function () {return MathJax.Hub.preProcessors.Add.apply(MathJax.Hub.preProcessors,arguments)}, | |
MessageHook: function () {return MathJax.Hub.signal.MessageHook.apply(MathJax.Hub.signal,arguments)}, | |
StartupHook: function () {return MathJax.Hub.Startup.signal.MessageHook.apply(MathJax.Hub.Startup.signal,arguments)}, | |
LoadHook: function () {return MathJax.Ajax.LoadHook.apply(MathJax.Ajax,arguments)} | |
}, | |
UnRegister: { | |
PreProcessor: function (hook) {MathJax.Hub.preProcessors.Remove(hook)}, | |
MessageHook: function (hook) {MathJax.Hub.signal.RemoveHook(hook)}, | |
StartupHook: function (hook) {MathJax.Hub.Startup.signal.RemoveHook(hook)}, | |
LoadHook: function (hook) {MathJax.Ajax.removeHook(hook)} | |
}, | |
getAllJax: function (element) { | |
var jax = [], scripts = this.elementScripts(element); | |
for (var i = 0, m = scripts.length; i < m; i++) { | |
if (scripts[i].MathJax && scripts[i].MathJax.elementJax) | |
{jax.push(scripts[i].MathJax.elementJax)} | |
} | |
return jax; | |
}, | |
getJaxByType: function (type,element) { | |
var jax = [], scripts = this.elementScripts(element); | |
for (var i = 0, m = scripts.length; i < m; i++) { | |
if (scripts[i].MathJax && scripts[i].MathJax.elementJax && | |
scripts[i].MathJax.elementJax.mimeType === type) | |
{jax.push(scripts[i].MathJax.elementJax)} | |
} | |
return jax; | |
}, | |
getJaxByInputType: function (type,element) { | |
var jax = [], scripts = this.elementScripts(element); | |
for (var i = 0, m = scripts.length; i < m; i++) { | |
if (scripts[i].MathJax && scripts[i].MathJax.elementJax && | |
scripts[i].type && scripts[i].type.replace(/ *;(.|\s)*/,"") === type) | |
{jax.push(scripts[i].MathJax.elementJax)} | |
} | |
return jax; | |
}, | |
getJaxFor: function (element) { | |
if (typeof(element) === 'string') {element = document.getElementById(element)} | |
if (element && element.MathJax) {return element.MathJax.elementJax} | |
if (this.isMathJaxNode(element)) { | |
if (!element.isMathJax) {element = element.firstChild} // for NativeMML output | |
while (element && !element.jaxID) {element = element.parentNode} | |
if (element) {return MathJax.OutputJax[element.jaxID].getJaxFromMath(element)} | |
} | |
return null; | |
}, | |
isJax: function (element) { | |
if (typeof(element) === 'string') {element = document.getElementById(element)} | |
if (this.isMathJaxNode(element)) {return 1} | |
if (element && (element.tagName||"").toLowerCase() === 'script') { | |
if (element.MathJax) | |
{return (element.MathJax.state === MathJax.ElementJax.STATE.PROCESSED ? 1 : -1)} | |
if (element.type && this.inputJax[element.type.replace(/ *;(.|\s)*/,"")]) {return -1} | |
} | |
return 0; | |
}, | |
isMathJaxNode: function (element) { | |
return !!element && (element.isMathJax || (element.className||"") === "MathJax_MathML"); | |
}, | |
setRenderer: function (renderer,type) { | |
if (!renderer) return; | |
if (!MathJax.OutputJax[renderer]) { | |
this.config.menuSettings.renderer = ""; | |
var file = "[MathJax]/jax/output/"+renderer+"/config.js"; | |
return MathJax.Ajax.Require(file,["setRenderer",this,renderer,type]); | |
} else { | |
this.config.menuSettings.renderer = renderer; | |
if (type == null) {type = "jax/mml"} | |
var jax = this.outputJax; | |
if (jax[type] && jax[type].length) { | |
if (renderer !== jax[type][0].id) { | |
jax[type].unshift(MathJax.OutputJax[renderer]); | |
return this.signal.Post(["Renderer Selected",renderer]); | |
} | |
} | |
return null; | |
} | |
}, | |
Queue: function () { | |
return this.queue.Push.apply(this.queue,arguments); | |
}, | |
Typeset: function (element,callback) { | |
if (!MathJax.isReady) return null; | |
var ec = this.elementCallback(element,callback); | |
if (ec.count) { | |
var queue = MathJax.Callback.Queue( | |
["PreProcess",this,ec.elements], | |
["Process",this,ec.elements] | |
); | |
} | |
return queue.Push(ec.callback); | |
}, | |
PreProcess: function (element,callback) { | |
var ec = this.elementCallback(element,callback); | |
var queue = MathJax.Callback.Queue(); | |
if (ec.count) { | |
var elements = (ec.count === 1 ? [ec.elements] : ec.elements); | |
queue.Push(["Post",this.signal,["Begin PreProcess",ec.elements]]); | |
for (var i = 0, m = elements.length; i < m; i++) { | |
if (elements[i]) {queue.Push(["Execute",this.preProcessors,elements[i]])} | |
} | |
queue.Push(["Post",this.signal,["End PreProcess",ec.elements]]); | |
} | |
return queue.Push(ec.callback); | |
}, | |
Process: function (element,callback) {return this.takeAction("Process",element,callback)}, | |
Update: function (element,callback) {return this.takeAction("Update",element,callback)}, | |
Reprocess: function (element,callback) {return this.takeAction("Reprocess",element,callback)}, | |
Rerender: function (element,callback) {return this.takeAction("Rerender",element,callback)}, | |
takeAction: function (action,element,callback) { | |
var ec = this.elementCallback(element,callback); | |
var elements = ec.elements; | |
var queue = MathJax.Callback.Queue(["Clear",this.signal]); | |
var state = { | |
scripts: [], // filled in by prepareScripts | |
start: new Date().getTime(), // timer for processing messages | |
i: 0, j: 0, // current script, current jax | |
jax: {}, // scripts grouped by output jax | |
jaxIDs: [] // id's of jax used | |
}; | |
if (ec.count) { | |
var delay = ["Delay",MathJax.Callback,this.processSectionDelay]; | |
if (!delay[2]) {delay = {}} | |
queue.Push( | |
["clearCounts",MathJax.Message], | |
["Post",this.signal,["Begin "+action,elements]], | |
["Post",this.signal,["Begin Math",elements,action]], | |
["prepareScripts",this,action,elements,state], | |
["Post",this.signal,["Begin Math Input",elements,action]], | |
["processInput",this,state], | |
["Post",this.signal,["End Math Input",elements,action]], | |
delay, | |
["prepareOutput",this,state,"preProcess"], | |
delay, | |
["Post",this.signal,["Begin Math Output",elements,action]], | |
["processOutput",this,state], | |
["Post",this.signal,["End Math Output",elements,action]], | |
delay, | |
["prepareOutput",this,state,"postProcess"], | |
delay, | |
["Post",this.signal,["End Math",elements,action]], | |
["Post",this.signal,["End "+action,elements]], | |
["clearCounts",MathJax.Message] | |
); | |
} | |
return queue.Push(ec.callback); | |
}, | |
scriptAction: { | |
Process: function (script) {}, | |
Update: function (script) { | |
var jax = script.MathJax.elementJax; | |
if (jax && jax.needsUpdate()) {jax.Remove(true); script.MathJax.state = jax.STATE.UPDATE} | |
else {script.MathJax.state = jax.STATE.PROCESSED} | |
}, | |
Reprocess: function (script) { | |
var jax = script.MathJax.elementJax; | |
if (jax) {jax.Remove(true); script.MathJax.state = jax.STATE.UPDATE} | |
}, | |
Rerender: function (script) { | |
var jax = script.MathJax.elementJax; | |
if (jax) {jax.Remove(true); script.MathJax.state = jax.STATE.OUTPUT} | |
} | |
}, | |
prepareScripts: function (action,element,state) { | |
if (arguments.callee.disabled) return; | |
var scripts = this.elementScripts(element); | |
var STATE = MathJax.ElementJax.STATE; | |
for (var i = 0, m = scripts.length; i < m; i++) { | |
var script = scripts[i]; | |
if (script.type && this.inputJax[script.type.replace(/ *;(.|\n)*/,"")]) { | |
if (script.MathJax) { | |
if (script.MathJax.elementJax && script.MathJax.elementJax.hover) { | |
MathJax.Extension.MathEvents.Hover.ClearHover(script.MathJax.elementJax); | |
} | |
if (script.MathJax.state !== STATE.PENDING) {this.scriptAction[action](script)} | |
} | |
if (!script.MathJax) {script.MathJax = {state: STATE.PENDING}} | |
if (script.MathJax.error) delete script.MathJax.error; | |
if (script.MathJax.state !== STATE.PROCESSED) {state.scripts.push(script)} | |
} | |
} | |
}, | |
checkScriptSiblings: function (script) { | |
if (script.MathJax.checked) return; | |
var config = this.config, pre = script.previousSibling; | |
if (pre && pre.nodeName === "#text") { | |
var preJax,postJax, post = script.nextSibling; | |
if (post && post.nodeName !== "#text") {post = null} | |
if (config.preJax) { | |
if (typeof(config.preJax) === "string") {config.preJax = new RegExp(config.preJax+"$")} | |
preJax = pre.nodeValue.match(config.preJax); | |
} | |
if (config.postJax && post) { | |
if (typeof(config.postJax) === "string") {config.postJax = new RegExp("^"+config.postJax)} | |
postJax = post.nodeValue.match(config.postJax); | |
} | |
if (preJax && (!config.postJax || postJax)) { | |
pre.nodeValue = pre.nodeValue.replace | |
(config.preJax,(preJax.length > 1? preJax[1] : "")); | |
pre = null; | |
} | |
if (postJax && (!config.preJax || preJax)) { | |
post.nodeValue = post.nodeValue.replace | |
(config.postJax,(postJax.length > 1? postJax[1] : "")); | |
} | |
if (pre && !pre.nodeValue.match(/\S/)) {pre = pre.previousSibling} | |
} | |
if (config.preRemoveClass && pre && pre.className === config.preRemoveClass) | |
{script.MathJax.preview = pre} | |
script.MathJax.checked = 1; | |
}, | |
processInput: function (state) { | |
var jax, STATE = MathJax.ElementJax.STATE; | |
var script, prev, m = state.scripts.length; | |
try { | |
// | |
// Loop through the scripts | |
// | |
while (state.i < m) { | |
script = state.scripts[state.i]; if (!script) {state.i++; continue} | |
// | |
// Remove previous error marker, if any | |
// | |
prev = script.previousSibling; | |
if (prev && prev.className === "MathJax_Error") {prev.parentNode.removeChild(prev)} | |
// | |
// Check if already processed or needs processing | |
// | |
if (!script.parentNode || !script.MathJax || script.MathJax.state === STATE.PROCESSED) {state.i++; continue}; | |
if (!script.MathJax.elementJax || script.MathJax.state === STATE.UPDATE) { | |
this.checkScriptSiblings(script); // remove preJax/postJax etc. | |
var type = script.type.replace(/ *;(.|\s)*/,""); // the input jax type | |
var input = this.inputJax[type]; // the input jax itself | |
jax = input.Process(script,state); // run the input jax | |
if (typeof jax === 'function') { // if a callback was returned | |
if (jax.called) continue; // go back and call Process() again | |
this.RestartAfter(jax); // wait for the callback | |
} | |
jax = jax.Attach(script,input.id); // register the jax on the script | |
this.saveScript(jax,state,script,STATE); // add script to state | |
this.postInputHooks.Execute(jax,input.id,script); // run global jax filters | |
} else if (script.MathJax.state === STATE.OUTPUT) { | |
this.saveScript(script.MathJax.elementJax,state,script,STATE); // add script to state | |
} | |
// | |
// Go on to the next script, and check if we need to update the processing message | |
// | |
state.i++; var now = new Date().getTime(); | |
if (now - state.start > this.processUpdateTime && state.i < state.scripts.length) | |
{state.start = now; this.RestartAfter(MathJax.Callback.Delay(1))} | |
} | |
} catch (err) {return this.processError(err,state,"Input")} | |
// | |
// Put up final message, reset the state and return | |
// | |
if (state.scripts.length && this.config.showProcessingMessages) | |
{MathJax.Message.Set(["ProcessMath","Processing math: %1%%",100],0)} | |
state.start = new Date().getTime(); state.i = state.j = 0; | |
return null; | |
}, | |
postInputHooks: MathJax.Callback.Hooks(true), // hooks to run after element jax is created | |
saveScript: function (jax,state,script,STATE) { | |
// | |
// Check that output jax exists | |
// | |
if (!this.outputJax[jax.mimeType]) { | |
script.MathJax.state = STATE.UPDATE; | |
throw Error("No output jax registered for "+jax.mimeType); | |
} | |
// | |
// Record the output jax | |
// and put this script in the queue for that jax | |
// | |
jax.outputJax = this.outputJax[jax.mimeType][0].id; | |
if (!state.jax[jax.outputJax]) { | |
if (state.jaxIDs.length === 0) { | |
// use original array until we know there are more (rather than two copies) | |
state.jax[jax.outputJax] = state.scripts; | |
} else { | |
if (state.jaxIDs.length === 1) // get the script so far for the existing jax | |
{state.jax[state.jaxIDs[0]] = state.scripts.slice(0,state.i)} | |
state.jax[jax.outputJax] = []; // start a new array for the new jax | |
} | |
state.jaxIDs.push(jax.outputJax); // save the ID of the jax | |
} | |
if (state.jaxIDs.length > 1) {state.jax[jax.outputJax].push(script)} | |
// | |
// Mark script as needing output | |
// | |
script.MathJax.state = STATE.OUTPUT; | |
}, | |
// | |
// Pre- and post-process scripts by jax | |
// (to get scaling factors, hide/show output, and so on) | |
// Since this can cause the jax to load, we need to trap restarts | |
// | |
prepareOutput: function (state,method) { | |
while (state.j < state.jaxIDs.length) { | |
var id = state.jaxIDs[state.j], JAX = MathJax.OutputJax[id]; | |
if (JAX[method]) { | |
try { | |
var result = JAX[method](state); | |
if (typeof result === 'function') { | |
if (result.called) continue; // go back and try again | |
this.RestartAfter(result); | |
} | |
} catch (err) { | |
if (!err.restart) { | |
MathJax.Message.Set(["PrepError","Error preparing %1 output (%2)",id,method],null,600); | |
MathJax.Hub.lastPrepError = err; | |
state.j++; | |
} | |
return MathJax.Callback.After(["prepareOutput",this,state,method],err.restart); | |
} | |
} | |
state.j++; | |
} | |
return null; | |
}, | |
processOutput: function (state) { | |
var result, STATE = MathJax.ElementJax.STATE, script, m = state.scripts.length; | |
try { | |
// | |
// Loop through the scripts | |
// | |
while (state.i < m) { | |
// | |
// Check that there is an element jax | |
// | |
script = state.scripts[state.i]; | |
if (!script || !script.parentNode || !script.MathJax || script.MathJax.error) {state.i++; continue} | |
var jax = script.MathJax.elementJax; if (!jax) {state.i++; continue} | |
// | |
// Call the output Jax's Process method (which will be its Translate() | |
// method once loaded). Mark it as complete and remove the preview unless | |
// the Process() call returns an explicit false value (in which case, it will | |
// handle this later during the postProcess phase, as HTML-CSS does). | |
// | |
result = MathJax.OutputJax[jax.outputJax].Process(script,state); | |
if (result !== false) { | |
script.MathJax.state = STATE.PROCESSED; | |
if (script.MathJax.preview) { | |
script.MathJax.preview.innerHTML = ""; | |
script.MathJax.preview.style.display = "none"; | |
} | |
// | |
// Signal that new math is available | |
// | |
this.signal.Post(["New Math",jax.inputID]); // FIXME: wait for this? (i.e., restart if returns uncalled callback) | |
} | |
// | |
// Go on to next math expression | |
// | |
state.i++; | |
// | |
// Update the processing message, if needed | |
// | |
var now = new Date().getTime(); | |
if (now - state.start > this.processUpdateTime && state.i < state.scripts.length) | |
{state.start = now; this.RestartAfter(MathJax.Callback.Delay(this.processUpdateDelay))} | |
} | |
} catch (err) {return this.processError(err,state,"Output")} | |
// | |
// Put up the typesetting-complete message | |
// | |
if (state.scripts.length && this.config.showProcessingMessages) { | |
MathJax.Message.Set(["TypesetMath","Typesetting math: %1%%",100],0); | |
MathJax.Message.Clear(0); | |
} | |
state.i = state.j = 0; | |
return null; | |
}, | |
processMessage: function (state,type) { | |
var m = Math.floor(state.i/(state.scripts.length)*100); | |
var message = (type === "Output" ? ["TypesetMath","Typesetting math: %1%%"] : | |
["ProcessMath","Processing math: %1%%"]); | |
if (this.config.showProcessingMessages) {MathJax.Message.Set(message.concat(m),0)} | |
}, | |
processError: function (err,state,type) { | |
if (!err.restart) { | |
if (!this.config.errorSettings.message) {throw err} | |
this.formatError(state.scripts[state.i],err); state.i++; | |
} | |
this.processMessage(state,type); | |
return MathJax.Callback.After(["process"+type,this,state],err.restart); | |
}, | |
formatError: function (script,err) { | |
var LOCALIZE = function (id,text,arg1,arg2) {return MathJax.Localization._(id,text,arg1,arg2)}; | |
// | |
// Get the error message, URL, and line, and save it for | |
// reporting in the Show Math As Error menu | |
// | |
var message = LOCALIZE("ErrorMessage","Error: %1",err.message)+"\n"; | |
if (err.sourceURL||err.fileName) message += "\n"+LOCALIZE("ErrorFile","file: %1",err.sourceURL||err.fileName); | |
if (err.line||err.lineNumber) message += "\n"+LOCALIZE("ErrorLine","line: %1",err.line||err.lineNumber); | |
message += "\n\n"+LOCALIZE("ErrorTips","Debugging tips: use %1, inspect %2 in the browser console","'unpacked/MathJax.js'","'MathJax.Hub.lastError'"); | |
script.MathJax.error = MathJax.OutputJax.Error.Jax(message,script); | |
if (script.MathJax.elementJax) | |
script.MathJax.error.inputID = script.MathJax.elementJax.inputID; | |
// | |
// Create the [Math Processing Error] span | |
// | |
var errorSettings = this.config.errorSettings; | |
var errorText = LOCALIZE(errorSettings.messageId,errorSettings.message); | |
var error = MathJax.HTML.Element("span", { | |
className:"MathJax_Error", jaxID:"Error", isMathJax:true, | |
id: script.MathJax.error.inputID+"-Frame" | |
},[["span",null,errorText]]); | |
// | |
// Attach the menu events | |
// | |
MathJax.Ajax.Require("[MathJax]/extensions/MathEvents.js",function () { | |
var EVENT = MathJax.Extension.MathEvents.Event, | |
HUB = MathJax.Hub; | |
error.oncontextmenu = EVENT.Menu; | |
error.onmousedown = EVENT.Mousedown; | |
error.onkeydown = EVENT.Keydown; | |
error.tabIndex = HUB.getTabOrder(HUB.getJaxFor(script)); | |
}); | |
// | |
// Insert the error into the page and remove any preview | |
// | |
var node = document.getElementById(error.id); | |
if (node) node.parentNode.removeChild(node); | |
if (script.parentNode) script.parentNode.insertBefore(error,script); | |
if (script.MathJax.preview) { | |
script.MathJax.preview.innerHTML = ""; | |
script.MathJax.preview.style.display = "none"; | |
} | |
// | |
// Save the error for debugging purposes | |
// Report the error as a signal | |
// | |
this.lastError = err; | |
this.signal.Post(["Math Processing Error",script,err]); | |
}, | |
RestartAfter: function (callback) { | |
throw this.Insert(Error("restart"),{restart: MathJax.Callback(callback)}); | |
}, | |
elementCallback: function (element,callback) { | |
if (callback == null && (MathJax.Object.isArray(element) || typeof element === 'function')) | |
{try {MathJax.Callback(element); callback = element; element = null} catch(e) {}} | |
if (element == null) {element = this.config.elements || []} | |
if (this.isHTMLCollection(element)) {element = this.HTMLCollection2Array(element)} | |
if (!MathJax.Object.isArray(element)) {element = [element]} | |
element = [].concat(element); // make a copy so the original isn't changed | |
for (var i = 0, m = element.length; i < m; i++) | |
{if (typeof(element[i]) === 'string') {element[i] = document.getElementById(element[i])}} | |
if (!document.body) {document.body = document.getElementsByTagName("body")[0]} | |
if (element.length == 0) {element.push(document.body)} | |
if (!callback) {callback = {}} | |
return { | |
count: element.length, | |
elements: (element.length === 1 ? element[0] : element), | |
callback: callback | |
}; | |
}, | |
elementScripts: function (element) { | |
var scripts = []; | |
if (MathJax.Object.isArray(element) || this.isHTMLCollection(element)) { | |
for (var i = 0, m = element.length; i < m; i++) { | |
var alreadyDone = 0; | |
for (var j = 0; j < i && !alreadyDone; j++) | |
{alreadyDone = element[j].contains(element[i])} | |
if (!alreadyDone) scripts.push.apply(scripts,this.elementScripts(element[i])); | |
} | |
return scripts; | |
} | |
if (typeof(element) === 'string') {element = document.getElementById(element)} | |
if (!document.body) {document.body = document.getElementsByTagName("body")[0]} | |
if (element == null) {element = document.body} | |
if (element.tagName != null && element.tagName.toLowerCase() === "script") {return [element]} | |
scripts = element.getElementsByTagName("script"); | |
if (this.msieHTMLCollectionBug) {scripts = this.HTMLCollection2Array(scripts)} | |
return scripts; | |
}, | |
// | |
// IE8 fails to check "obj instanceof HTMLCollection" for some values of obj. | |
// | |
isHTMLCollection: function (obj) { | |
return ("HTMLCollection" in window && typeof(obj) === "object" && obj instanceof HTMLCollection); | |
}, | |
// | |
// IE8 doesn't deal with HTMLCollection as an array, so convert to array | |
// | |
HTMLCollection2Array: function (nodes) { | |
if (!this.msieHTMLCollectionBug) {return [].slice.call(nodes)} | |
var NODES = []; | |
for (var i = 0, m = nodes.length; i < m; i++) {NODES[i] = nodes[i]} | |
return NODES; | |
}, | |
Insert: function (dst,src) { | |
for (var id in src) {if (src.hasOwnProperty(id)) { | |
// allow for concatenation of arrays? | |
if (typeof src[id] === 'object' && !(MathJax.Object.isArray(src[id])) && | |
(typeof dst[id] === 'object' || typeof dst[id] === 'function')) { | |
this.Insert(dst[id],src[id]); | |
} else { | |
dst[id] = src[id]; | |
} | |
}} | |
return dst; | |
}, | |
getTabOrder: function(script) { | |
return this.config.menuSettings.inTabOrder ? 0 : -1; | |
}, | |
// Old browsers (e.g. Internet Explorer <= 8) do not support trim(). | |
SplitList: ("trim" in String.prototype ? | |
function (list) {return list.trim().split(/\s+/)} : | |
function (list) {return list.replace(/^\s+/,''). | |
replace(/\s+$/,'').split(/\s+/)}) | |
}; | |
MathJax.Hub.Insert(MathJax.Hub.config.styles,MathJax.Message.styles); | |
MathJax.Hub.Insert(MathJax.Hub.config.styles,{".MathJax_Error":MathJax.Hub.config.errorSettings.style}); | |
// | |
// Storage area for extensions and preprocessors | |
// | |
MathJax.Extension = {}; | |
// | |
// Hub Startup code | |
// | |
MathJax.Hub.Configured = MathJax.Callback({}); // called when configuration is complete | |
MathJax.Hub.Startup = { | |
script: "", // the startup script from the SCRIPT call that loads MathJax.js | |
queue: MathJax.Callback.Queue(), // Queue used for startup actions | |
signal: MathJax.Callback.Signal("Startup"), // Signal used for startup events | |
params: {}, | |
// | |
// Load the configuration files | |
// | |
Config: function () { | |
this.queue.Push(["Post",this.signal,"Begin Config"]); | |
// | |
// Make sure root is set before loading any files | |
// | |
if (MathJax.AuthorConfig && MathJax.AuthorConfig.root) | |
MathJax.Ajax.config.root = MathJax.AuthorConfig.root; | |
// | |
// If a locale is given as a parameter, | |
// set the locale and the default menu value for the locale | |
// | |
if (this.params.locale) { | |
MathJax.Localization.resetLocale(this.params.locale); | |
MathJax.Hub.config.menuSettings.locale = this.params.locale; | |
} | |
// | |
// Run the config files, if any are given in the parameter list | |
// | |
if (this.params.config) { | |
var files = this.params.config.split(/,/); | |
for (var i = 0, m = files.length; i < m; i++) { | |
if (!files[i].match(/\.js$/)) {files[i] += ".js"} | |
this.queue.Push(["Require",MathJax.Ajax,this.URL("config",files[i])]); | |
} | |
} | |
// | |
// Perform author configuration from in-line MathJax = {...} | |
// | |
this.queue.Push(["Config",MathJax.Hub,MathJax.AuthorConfig]); | |
// | |
// Run the deprecated configuration script, if any (ignoring return value) | |
// Wait for the startup delay signal | |
// Run the mathjax-config blocks | |
// Load the files in the configuration's config array | |
// | |
if (this.script.match(/\S/)) {this.queue.Push(this.script+";\n1;")} | |
this.queue.Push( | |
["ConfigDelay",this], | |
["ConfigBlocks",this], | |
[function (THIS) {return THIS.loadArray(MathJax.Hub.config.config,"config",null,true)},this], | |
["Post",this.signal,"End Config"] | |
); | |
}, | |
// | |
// Return the delay callback | |
// | |
ConfigDelay: function () { | |
var delay = this.params.delayStartupUntil || MathJax.Hub.config.delayStartupUntil; | |
if (delay === "onload") {return this.onload} | |
if (delay === "configured") {return MathJax.Hub.Configured} | |
return delay; | |
}, | |
// | |
// Run the scripts of type=text/x-mathjax-config | |
// | |
ConfigBlocks: function () { | |
var scripts = document.getElementsByTagName("script"); | |
var queue = MathJax.Callback.Queue(); | |
for (var i = 0, m = scripts.length; i < m; i++) { | |
var type = String(scripts[i].type).replace(/ /g,""); | |
if (type.match(/^text\/x-mathjax-config(;.*)?$/) && !type.match(/;executed=true/)) { | |
scripts[i].type += ";executed=true"; | |
queue.Push(scripts[i].innerHTML+";\n1;"); | |
} | |
} | |
return queue.Push(function () {MathJax.Ajax.config.root = MathJax.Hub.config.root}); | |
}, | |
// | |
// Read cookie and set up menu defaults | |
// (set the locale according to the cookie) | |
// (adjust the jax to accommodate renderer preferences) | |
// | |
Cookie: function () { | |
return this.queue.Push( | |
["Post",this.signal,"Begin Cookie"], | |
["Get",MathJax.HTML.Cookie,"menu",MathJax.Hub.config.menuSettings], | |
[function (config) { | |
var SETTINGS = config.menuSettings; | |
if (SETTINGS.locale) MathJax.Localization.resetLocale(SETTINGS.locale); | |
var renderer = config.menuSettings.renderer, jax = config.jax; | |
if (renderer) { | |
var name = "output/"+renderer; jax.sort(); | |
for (var i = 0, m = jax.length; i < m; i++) { | |
if (jax[i].substr(0,7) === "output/") break; | |
} | |
if (i == m-1) {jax.pop()} else { | |
while (i < m) {if (jax[i] === name) {jax.splice(i,1); break}; i++} | |
} | |
jax.unshift(name); | |
} | |
if (SETTINGS.CHTMLpreview != null) { | |
if (SETTINGS.FastPreview == null) SETTINGS.FastPreview = SETTINGS.CHTMLpreview; | |
delete SETTINGS.CHTMLpreview; | |
} | |
if (SETTINGS.FastPreview && !MathJax.Extension["fast-preview"]) | |
MathJax.Hub.config.extensions.push("fast-preview.js"); | |
if (config.menuSettings.assistiveMML && !MathJax.Extension.AssistiveMML) | |
MathJax.Hub.config.extensions.push("AssistiveMML.js"); | |
},MathJax.Hub.config], | |
["Post",this.signal,"End Cookie"] | |
); | |
}, | |
// | |
// Setup stylesheets and extra styles | |
// | |
Styles: function () { | |
return this.queue.Push( | |
["Post",this.signal,"Begin Styles"], | |
["loadArray",this,MathJax.Hub.config.styleSheets,"config"], | |
["Styles",MathJax.Ajax,MathJax.Hub.config.styles], | |
["Post",this.signal,"End Styles"] | |
); | |
}, | |
// | |
// Load the input and output jax | |
// | |
Jax: function () { | |
var config = MathJax.Hub.config, jax = MathJax.Hub.outputJax; | |
// Save the order of the output jax since they are loading asynchronously | |
for (var i = 0, m = config.jax.length, k = 0; i < m; i++) { | |
var name = config.jax[i].substr(7); | |
if (config.jax[i].substr(0,7) === "output/" && jax.order[name] == null) | |
{jax.order[name] = k; k++} | |
} | |
var queue = MathJax.Callback.Queue(); | |
return queue.Push( | |
["Post",this.signal,"Begin Jax"], | |
["loadArray",this,config.jax,"jax","config.js"], | |
["Post",this.signal,"End Jax"] | |
); | |
}, | |
// | |
// Load the extensions | |
// | |
Extensions: function () { | |
var queue = MathJax.Callback.Queue(); | |
return queue.Push( | |
["Post",this.signal,"Begin Extensions"], | |
["loadArray",this,MathJax.Hub.config.extensions,"extensions"], | |
["Post",this.signal,"End Extensions"] | |
); | |
}, | |
// | |
// Initialize the Message system | |
// | |
Message: function () { | |
MathJax.Message.Init(true); | |
}, | |
// | |
// Set the math menu renderer, if it isn't already | |
// (this must come after the jax are loaded) | |
// | |
Menu: function () { | |
var menu = MathJax.Hub.config.menuSettings, jax = MathJax.Hub.outputJax, registered; | |
for (var id in jax) {if (jax.hasOwnProperty(id)) { | |
if (jax[id].length) {registered = jax[id]; break} | |
}} | |
if (registered && registered.length) { | |
if (menu.renderer && menu.renderer !== registered[0].id) | |
{registered.unshift(MathJax.OutputJax[menu.renderer])} | |
menu.renderer = registered[0].id; | |
} | |
}, | |
// | |
// Set the location to the designated hash position | |
// | |
Hash: function () { | |
if (MathJax.Hub.config.positionToHash && document.location.hash && | |
document.body && document.body.scrollIntoView) { | |
var name = document.location.hash.substr(1); | |
var target = document.getElementById(name); | |
if (!target) { | |
var a = document.getElementsByTagName("a"); | |
for (var i = 0, m = a.length; i < m; i++) | |
{if (a[i].name === name) {target = a[i]; break}} | |
} | |
if (target) { | |
while (!target.scrollIntoView) {target = target.parentNode} | |
target = this.HashCheck(target); | |
if (target && target.scrollIntoView) | |
{setTimeout(function () {target.scrollIntoView(true)},1)} | |
} | |
} | |
}, | |
HashCheck: function (target) { | |
var jax = MathJax.Hub.getJaxFor(target); | |
if (jax && MathJax.OutputJax[jax.outputJax].hashCheck) | |
{target = MathJax.OutputJax[jax.outputJax].hashCheck(target)} | |
return target; | |
}, | |
// | |
// Load the Menu and Zoom code, if it hasn't already been loaded. | |
// This is called after the initial typeset, so should no longer be | |
// competing with other page loads, but will make these available | |
// if needed later on. | |
// | |
MenuZoom: function () { | |
if (MathJax.Hub.config.showMathMenu) { | |
if (!MathJax.Extension.MathMenu) { | |
setTimeout( | |
function () { | |
MathJax.Callback.Queue( | |
["Require",MathJax.Ajax,"[MathJax]/extensions/MathMenu.js",{}], | |
["loadDomain",MathJax.Localization,"MathMenu"] | |
) | |
},1000 | |
); | |
} else { | |
setTimeout( | |
MathJax.Callback(["loadDomain",MathJax.Localization,"MathMenu"]), | |
1000 | |
); | |
} | |
if (!MathJax.Extension.MathZoom) { | |
setTimeout( | |
MathJax.Callback(["Require",MathJax.Ajax,"[MathJax]/extensions/MathZoom.js",{}]), | |
2000 | |
); | |
} | |
} | |
}, | |
// | |
// Setup the onload callback | |
// | |
onLoad: function () { | |
var onload = this.onload = | |
MathJax.Callback(function () {MathJax.Hub.Startup.signal.Post("onLoad")}); | |
if (document.body && document.readyState) | |
if (MathJax.Hub.Browser.isMSIE) { | |
// IE can change from loading to interactive before | |
// full page is ready, so go with complete (even though | |
// that means we may have to wait longer). | |
if (document.readyState === "complete") {return [onload]} | |
} else if (document.readyState !== "loading") {return [onload]} | |
if (window.addEventListener) { | |
window.addEventListener("load",onload,false); | |
if (!this.params.noDOMContentEvent) | |
{window.addEventListener("DOMContentLoaded",onload,false)} | |
} | |
else if (window.attachEvent) {window.attachEvent("onload",onload)} | |
else {window.onload = onload} | |
return onload; | |
}, | |
// | |
// Perform the initial typesetting (or skip if configuration says to) | |
// | |
Typeset: function (element,callback) { | |
if (MathJax.Hub.config.skipStartupTypeset) {return function () {}} | |
return this.queue.Push( | |
["Post",this.signal,"Begin Typeset"], | |
["Typeset",MathJax.Hub,element,callback], | |
["Post",this.signal,"End Typeset"] | |
); | |
}, | |
// | |
// Create a URL in the MathJax hierarchy | |
// | |
URL: function (dir,name) { | |
if (!name.match(/^([a-z]+:\/\/|\[|\/)/)) {name = "[MathJax]/"+dir+"/"+name} | |
return name; | |
}, | |
// | |
// Load an array of files, waiting for all of them | |
// to be loaded before going on | |
// | |
loadArray: function (files,dir,name,synchronous) { | |
if (files) { | |
if (!MathJax.Object.isArray(files)) {files = [files]} | |
if (files.length) { | |
var queue = MathJax.Callback.Queue(), callback = {}, file; | |
for (var i = 0, m = files.length; i < m; i++) { | |
file = this.URL(dir,files[i]); | |
if (name) {file += "/" + name} | |
if (synchronous) {queue.Push(["Require",MathJax.Ajax,file,callback])} | |
else {queue.Push(MathJax.Ajax.Require(file,callback))} | |
} | |
return queue.Push({}); // wait for everything to finish | |
} | |
} | |
return null; | |
} | |
}; | |
/**********************************************************/ | |
(function (BASENAME) { | |
var BASE = window[BASENAME], ROOT = "["+BASENAME+"]"; | |
var HUB = BASE.Hub, AJAX = BASE.Ajax, CALLBACK = BASE.Callback; | |
var JAX = MathJax.Object.Subclass({ | |
JAXFILE: "jax.js", | |
require: null, // array of files to load before jax.js is complete | |
config: {}, | |
// | |
// Make a subclass and return an instance of it. | |
// (FIXME: should we replace config with a copy of the constructor's | |
// config? Otherwise all subclasses share the same config structure.) | |
// | |
Init: function (def,cdef) { | |
if (arguments.length === 0) {return this} | |
return (this.constructor.Subclass(def,cdef))(); | |
}, | |
// | |
// Augment by merging with class definition (not replacing) | |
// | |
Augment: function (def,cdef) { | |
var cObject = this.constructor, ndef = {}; | |
if (def != null) { | |
for (var id in def) {if (def.hasOwnProperty(id)) { | |
if (typeof def[id] === "function") | |
{cObject.protoFunction(id,def[id])} else {ndef[id] = def[id]} | |
}} | |
// MSIE doesn't list toString even if it is not native so handle it separately | |
if (def.toString !== cObject.prototype.toString && def.toString !== {}.toString) | |
{cObject.protoFunction('toString',def.toString)} | |
} | |
HUB.Insert(cObject.prototype,ndef); | |
cObject.Augment(null,cdef); | |
return this; | |
}, | |
Translate: function (script,state) { | |
throw Error(this.directory+"/"+this.JAXFILE+" failed to define the Translate() method"); | |
}, | |
Register: function (mimetype) {}, | |
Config: function () { | |
this.config = HUB.CombineConfig(this.id,this.config); | |
if (this.config.Augment) {this.Augment(this.config.Augment)} | |
}, | |
Startup: function () {}, | |
loadComplete: function (file) { | |
if (file === "config.js") { | |
return AJAX.loadComplete(this.directory+"/"+file); | |
} else { | |
var queue = CALLBACK.Queue(); | |
queue.Push( | |
HUB.Register.StartupHook("End Config",{}), // wait until config complete | |
["Post",HUB.Startup.signal,this.id+" Jax Config"], | |
["Config",this], | |
["Post",HUB.Startup.signal,this.id+" Jax Require"], | |
// Config may set the required and extensions array, | |
// so use functions to delay making the reference until needed | |
[function (THIS) {return MathJax.Hub.Startup.loadArray(THIS.require,this.directory)},this], | |
[function (config,id) {return MathJax.Hub.Startup.loadArray(config.extensions,"extensions/"+id)},this.config||{},this.id], | |
["Post",HUB.Startup.signal,this.id+" Jax Startup"], | |
["Startup",this], | |
["Post",HUB.Startup.signal,this.id+" Jax Ready"] | |
); | |
if (this.copyTranslate) { | |
queue.Push( | |
[function (THIS) { | |
THIS.preProcess = THIS.preTranslate; | |
THIS.Process = THIS.Translate; | |
THIS.postProcess = THIS.postTranslate; | |
},this.constructor.prototype] | |
); | |
} | |
return queue.Push(["loadComplete",AJAX,this.directory+"/"+file]); | |
} | |
} | |
},{ | |
id: "Jax", | |
version: "2.7.1", | |
directory: ROOT+"/jax", | |
extensionDir: ROOT+"/extensions" | |
}); | |
/***********************************/ | |
BASE.InputJax = JAX.Subclass({ | |
elementJax: "mml", // the element jax to load for this input jax | |
sourceMenuTitle: /*_(MathMenu)*/ ["Original","Original Form"], | |
copyTranslate: true, | |
Process: function (script,state) { | |
var queue = CALLBACK.Queue(), file; | |
// Load any needed element jax | |
var jax = this.elementJax; if (!BASE.Object.isArray(jax)) {jax = [jax]} | |
for (var i = 0, m = jax.length; i < m; i++) { | |
file = BASE.ElementJax.directory+"/"+jax[i]+"/"+this.JAXFILE; | |
if (!this.require) {this.require = []} | |
else if (!BASE.Object.isArray(this.require)) {this.require = [this.require]}; | |
this.require.push(file); // so Startup will wait for it to be loaded | |
queue.Push(AJAX.Require(file)); | |
} | |
// Load the input jax | |
file = this.directory+"/"+this.JAXFILE; | |
var load = queue.Push(AJAX.Require(file)); | |
if (!load.called) { | |
this.constructor.prototype.Process = function () { | |
if (!load.called) {return load} | |
throw Error(file+" failed to load properly"); | |
} | |
} | |
// Load the associated output jax | |
jax = HUB.outputJax["jax/"+jax[0]]; | |
if (jax) {queue.Push(AJAX.Require(jax[0].directory+"/"+this.JAXFILE))} | |
return queue.Push({}); | |
}, | |
needsUpdate: function (jax) { | |
var script = jax.SourceElement(); | |
return (jax.originalText !== BASE.HTML.getScript(script)); | |
}, | |
Register: function (mimetype) { | |
if (!HUB.inputJax) {HUB.inputJax = {}} | |
HUB.inputJax[mimetype] = this; | |
} | |
},{ | |
id: "InputJax", | |
version: "2.7.1", | |
directory: JAX.directory+"/input", | |
extensionDir: JAX.extensionDir | |
}); | |
/***********************************/ | |
BASE.OutputJax = JAX.Subclass({ | |
copyTranslate: true, | |
preProcess: function (state) { | |
var load, file = this.directory+"/"+this.JAXFILE; | |
this.constructor.prototype.preProcess = function (state) { | |
if (!load.called) {return load} | |
throw Error(file+" failed to load properly"); | |
} | |
load = AJAX.Require(file); | |
return load; | |
}, | |
Register: function (mimetype) { | |
var jax = HUB.outputJax; | |
if (!jax[mimetype]) {jax[mimetype] = []} | |
// If the output jax is earlier in the original configuration list, put it first here | |
if (jax[mimetype].length && (this.id === HUB.config.menuSettings.renderer || | |
(jax.order[this.id]||0) < (jax.order[jax[mimetype][0].id]||0))) | |
{jax[mimetype].unshift(this)} else {jax[mimetype].push(this)} | |
// Make sure the element jax is loaded before Startup is called | |
if (!this.require) {this.require = []} | |
else if (!BASE.Object.isArray(this.require)) {this.require = [this.require]}; | |
this.require.push(BASE.ElementJax.directory+"/"+(mimetype.split(/\//)[1])+"/"+this.JAXFILE); | |
}, | |
Remove: function (jax) {} | |
},{ | |
id: "OutputJax", | |
version: "2.7.1", | |
directory: JAX.directory+"/output", | |
extensionDir: JAX.extensionDir, | |
fontDir: ROOT+(BASE.isPacked?"":"/..")+"/fonts", | |
imageDir: ROOT+(BASE.isPacked?"":"/..")+"/images" | |
}); | |
/***********************************/ | |
BASE.ElementJax = JAX.Subclass({ | |
// make a subclass, not an instance | |
Init: function (def,cdef) {return this.constructor.Subclass(def,cdef)}, | |
inputJax: null, | |
outputJax: null, | |
inputID: null, | |
originalText: "", | |
mimeType: "", | |
sourceMenuTitle: /*_(MathMenu)*/ ["MathMLcode","MathML Code"], | |
Text: function (text,callback) { | |
var script = this.SourceElement(); | |
BASE.HTML.setScript(script,text); | |
script.MathJax.state = this.STATE.UPDATE; | |
return HUB.Update(script,callback); | |
}, | |
Reprocess: function (callback) { | |
var script = this.SourceElement(); | |
script.MathJax.state = this.STATE.UPDATE; | |
return HUB.Reprocess(script,callback); | |
}, | |
Update: function (callback) {return this.Rerender(callback)}, | |
Rerender: function (callback) { | |
var script = this.SourceElement(); | |
script.MathJax.state = this.STATE.OUTPUT; | |
return HUB.Process(script,callback); | |
}, | |
Remove: function (keep) { | |
if (this.hover) {this.hover.clear(this)} | |
BASE.OutputJax[this.outputJax].Remove(this); | |
if (!keep) { | |
HUB.signal.Post(["Remove Math",this.inputID]); // wait for this to finish? | |
this.Detach(); | |
} | |
}, | |
needsUpdate: function () { | |
return BASE.InputJax[this.inputJax].needsUpdate(this); | |
}, | |
SourceElement: function () {return document.getElementById(this.inputID)}, | |
Attach: function (script,inputJax) { | |
var jax = script.MathJax.elementJax; | |
if (script.MathJax.state === this.STATE.UPDATE) { | |
jax.Clone(this); | |
} else { | |
jax = script.MathJax.elementJax = this; | |
if (script.id) {this.inputID = script.id} | |
else {script.id = this.inputID = BASE.ElementJax.GetID(); this.newID = 1} | |
} | |
jax.originalText = BASE.HTML.getScript(script); | |
jax.inputJax = inputJax; | |
if (jax.root) {jax.root.inputID = jax.inputID} | |
return jax; | |
}, | |
Detach: function () { | |
var script = this.SourceElement(); if (!script) return; | |
try {delete script.MathJax} catch(err) {script.MathJax = null} | |
if (this.newID) {script.id = ""} | |
}, | |
Clone: function (jax) { | |
var id; | |
for (id in this) { | |
if (!this.hasOwnProperty(id)) continue; | |
if (typeof(jax[id]) === 'undefined' && id !== 'newID') {delete this[id]} | |
} | |
for (id in jax) { | |
if (!jax.hasOwnProperty(id)) continue; | |
if (typeof(this[id]) === 'undefined' || (this[id] !== jax[id] && id !== 'inputID')) | |
{this[id] = jax[id]} | |
} | |
} | |
},{ | |
id: "ElementJax", | |
version: "2.7.1", | |
directory: JAX.directory+"/element", | |
extensionDir: JAX.extensionDir, | |
ID: 0, // jax counter (for IDs) | |
STATE: { | |
PENDING: 1, // script is identified as math but not yet processed | |
PROCESSED: 2, // script has been processed | |
UPDATE: 3, // elementJax should be updated | |
OUTPUT: 4 // output should be updated (input is OK) | |
}, | |
GetID: function () {this.ID++; return "MathJax-Element-"+this.ID}, | |
Subclass: function () { | |
var obj = JAX.Subclass.apply(this,arguments); | |
obj.loadComplete = this.prototype.loadComplete; | |
return obj; | |
} | |
}); | |
BASE.ElementJax.prototype.STATE = BASE.ElementJax.STATE; | |
// | |
// Some "Fake" jax used to allow menu access for "Math Processing Error" messages | |
// | |
BASE.OutputJax.Error = { | |
id: "Error", version: "2.7.1", config: {}, errors: 0, | |
ContextMenu: function () {return BASE.Extension.MathEvents.Event.ContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)}, | |
Mousedown: function () {return BASE.Extension.MathEvents.Event.AltContextMenu.apply(BASE.Extension.MathEvents.Event,arguments)}, | |
getJaxFromMath: function (math) {return (math.nextSibling.MathJax||{}).error}, | |
Jax: function (text,script) { | |
var jax = MathJax.Hub.inputJax[script.type.replace(/ *;(.|\s)*/,"")]; | |
this.errors++; | |
return { | |
inputJax: (jax||{id:"Error"}).id, // Use Error InputJax as fallback | |
outputJax: "Error", | |
inputID: "MathJax-Error-"+this.errors, | |
sourceMenuTitle: /*_(MathMenu)*/ ["ErrorMessage","Error Message"], | |
sourceMenuFormat: "Error", | |
originalText: MathJax.HTML.getScript(script), | |
errorText: text | |
} | |
} | |
}; | |
BASE.InputJax.Error = { | |
id: "Error", version: "2.7.1", config: {}, | |
sourceMenuTitle: /*_(MathMenu)*/ ["Original","Original Form"] | |
}; | |
})("MathJax"); | |
/**********************************************************/ | |
(function (BASENAME) { | |
var BASE = window[BASENAME]; | |
if (!BASE) {BASE = window[BASENAME] = {}} | |
var HUB = BASE.Hub; var STARTUP = HUB.Startup; var CONFIG = HUB.config; | |
var HEAD = document.head || (document.getElementsByTagName("head")[0]); | |
if (!HEAD) {HEAD = document.childNodes[0]}; | |
var scripts = (document.documentElement || document).getElementsByTagName("script"); | |
if (scripts.length === 0 && HEAD.namespaceURI) | |
scripts = document.getElementsByTagNameNS(HEAD.namespaceURI,"script"); | |
var namePattern = new RegExp("(^|/)"+BASENAME+"\\.js(\\?.*)?$"); | |
for (var i = scripts.length-1; i >= 0; i--) { | |
if ((scripts[i].src||"").match(namePattern)) { | |
STARTUP.script = scripts[i].innerHTML; | |
if (RegExp.$2) { | |
var params = RegExp.$2.substr(1).split(/\&/); | |
for (var j = 0, m = params.length; j < m; j++) { | |
var KV = params[j].match(/(.*)=(.*)/); | |
if (KV) {STARTUP.params[unescape(KV[1])] = unescape(KV[2])} | |
else {STARTUP.params[params[j]] = true} | |
} | |
} | |
CONFIG.root = scripts[i].src.replace(/(^|\/)[^\/]*(\?.*)?$/,''); | |
BASE.Ajax.config.root = CONFIG.root; | |
BASE.Ajax.params = STARTUP.params; | |
break; | |
} | |
} | |
var AGENT = navigator.userAgent; | |
var BROWSERS = { | |
isMac: (navigator.platform.substr(0,3) === "Mac"), | |
isPC: (navigator.platform.substr(0,3) === "Win"), | |
isMSIE: ("ActiveXObject" in window && "clipboardData" in window), | |
isEdge: ("MSGestureEvent" in window && "chrome" in window && | |
window.chrome.loadTimes == null), | |
isFirefox: (!!AGENT.match(/Gecko\//) && !AGENT.match(/like Gecko/)), | |
isSafari: (!!AGENT.match(/ (Apple)?WebKit\//) && !AGENT.match(/ like iPhone /) && | |
(!window.chrome || window.chrome.app == null)), | |
isChrome: ("chrome" in window && window.chrome.loadTimes != null), | |
isOpera: ("opera" in window && window.opera.version != null), | |
isKonqueror: ("konqueror" in window && navigator.vendor == "KDE"), | |
versionAtLeast: function (v) { | |
var bv = (this.version).split('.'); v = (new String(v)).split('.'); | |
for (var i = 0, m = v.length; i < m; i++) | |
{if (bv[i] != v[i]) {return parseInt(bv[i]||"0") >= parseInt(v[i])}} | |
return true; | |
}, | |
Select: function (choices) { | |
var browser = choices[HUB.Browser]; | |
if (browser) {return browser(HUB.Browser)} | |
return null; | |
} | |
}; | |
var xAGENT = AGENT | |
.replace(/^Mozilla\/(\d+\.)+\d+ /,"") // remove initial Mozilla, which is never right | |
.replace(/[a-z][-a-z0-9._: ]+\/\d+[^ ]*-[^ ]*\.([a-z][a-z])?\d+ /i,"") // remove linux version | |
.replace(/Gentoo |Ubuntu\/(\d+\.)*\d+ (\([^)]*\) )?/,""); // special case for these | |
HUB.Browser = HUB.Insert(HUB.Insert(new String("Unknown"),{version: "0.0"}),BROWSERS); | |
for (var browser in BROWSERS) {if (BROWSERS.hasOwnProperty(browser)) { | |
if (BROWSERS[browser] && browser.substr(0,2) === "is") { | |
browser = browser.slice(2); | |
if (browser === "Mac" || browser === "PC") continue; | |
HUB.Browser = HUB.Insert(new String(browser),BROWSERS); | |
var VERSION = new RegExp( | |
".*(Version/| Trident/.*; rv:)((?:\\d+\\.)+\\d+)|" + // for Safari, Opera10, and IE11+ | |
".*("+browser+")"+(browser == "MSIE" ? " " : "/")+"((?:\\d+\\.)*\\d+)|"+ // for one of the main browsers | |
"(?:^|\\(| )([a-z][-a-z0-9._: ]+|(?:Apple)?WebKit)/((?:\\d+\\.)+\\d+)"); // for unrecognized browser | |
var MATCH = VERSION.exec(xAGENT) || ["","","","unknown","0.0"]; | |
HUB.Browser.name = (MATCH[1] != "" ? browser : (MATCH[3] || MATCH[5])); | |
HUB.Browser.version = MATCH[2] || MATCH[4] || MATCH[6]; | |
break; | |
} | |
}}; | |
// | |
// Initial browser-specific info (e.g., touch up version or name, check for MathPlayer, etc.) | |
// Wrap in try/catch just in case of error (see issue #1155). | |
// | |
try {HUB.Browser.Select({ | |
Safari: function (browser) { | |
var v = parseInt((String(browser.version).split("."))[0]); | |
if (v > 85) {browser.webkit = browser.version} | |
if (v >= 538) {browser.version = "8.0"} | |
else if (v >= 537) {browser.version = "7.0"} | |
else if (v >= 536) {browser.version = "6.0"} | |
else if (v >= 534) {browser.version = "5.1"} | |
else if (v >= 533) {browser.version = "5.0"} | |
else if (v >= 526) {browser.version = "4.0"} | |
else if (v >= 525) {browser.version = "3.1"} | |
else if (v > 500) {browser.version = "3.0"} | |
else if (v > 400) {browser.version = "2.0"} | |
else if (v > 85) {browser.version = "1.0"} | |
browser.webkit = (navigator.appVersion.match(/WebKit\/(\d+)\./))[1]; | |
browser.isMobile = (navigator.appVersion.match(/Mobile/i) != null); | |
browser.noContextMenu = browser.isMobile; | |
}, | |
Firefox: function (browser) { | |
if ((browser.version === "0.0" || AGENT.match(/Firefox/) == null) && | |
navigator.product === "Gecko") { | |
var rv = AGENT.match(/[\/ ]rv:(\d+\.\d.*?)[\) ]/); | |
if (rv) {browser.version = rv[1]} | |
else { | |
var date = (navigator.buildID||navigator.productSub||"0").substr(0,8); | |
if (date >= "20111220") {browser.version = "9.0"} | |
else if (date >= "20111120") {browser.version = "8.0"} | |
else if (date >= "20110927") {browser.version = "7.0"} | |
else if (date >= "20110816") {browser.version = "6.0"} | |
else if (date >= "20110621") {browser.version = "5.0"} | |
else if (date >= "20110320") {browser.version = "4.0"} | |
else if (date >= "20100121") {browser.version = "3.6"} | |
else if (date >= "20090630") {browser.version = "3.5"} | |
else if (date >= "20080617") {browser.version = "3.0"} | |
else if (date >= "20061024") {browser.version = "2.0"} | |
} | |
} | |
browser.isMobile = (navigator.appVersion.match(/Android/i) != null || | |
AGENT.match(/ Fennec\//) != null || | |
AGENT.match(/Mobile/) != null); | |
}, | |
Chrome: function (browser) { | |
browser.noContextMenu = browser.isMobile = !!navigator.userAgent.match(/ Mobile[ \/]/); | |
}, | |
Opera: function (browser) {browser.version = opera.version()}, | |
Edge: function (browser) { | |
browser.isMobile = !!navigator.userAgent.match(/ Phone/); | |
}, | |
MSIE: function (browser) { | |
browser.isMobile = !!navigator.userAgent.match(/ Phone/); | |
browser.isIE9 = !!(document.documentMode && (window.performance || window.msPerformance)); | |
MathJax.HTML.setScriptBug = !browser.isIE9 || document.documentMode < 9; | |
MathJax.Hub.msieHTMLCollectionBug = (document.documentMode < 9); | |
// | |
// MathPlayer doesn't function properly in IE10, and not at all in IE11, | |
// so don't even try to load it. | |
// | |
if (document.documentMode < 10 && !STARTUP.params.NoMathPlayer) { | |
try { | |
new ActiveXObject("MathPlayer.Factory.1"); | |
browser.hasMathPlayer = true; | |
} catch (err) {} | |
try { | |
if (browser.hasMathPlayer) { | |
var mathplayer = document.createElement("object"); | |
mathplayer.id = "mathplayer"; mathplayer.classid = "clsid:32F66A20-7614-11D4-BD11-00104BD3F987"; | |
HEAD.appendChild(mathplayer); | |
document.namespaces.add("m","http://www.w3.org/1998/Math/MathML"); | |
browser.mpNamespace = true; | |
if (document.readyState && (document.readyState === "loading" || | |
document.readyState === "interactive")) { | |
document.write('<?import namespace="m" implementation="#MathPlayer">'); | |
browser.mpImported = true; | |
} | |
} else { | |
// Adding any namespace avoids a crash in IE9 in IE9-standards mode | |
// (any reference to document.namespaces before document.readyState is | |
// "complete" causes an "unspecified error" to be thrown) | |
document.namespaces.add("mjx_IE_fix","http://www.w3.org/1999/xlink"); | |
} | |
} catch (err) {} | |
} | |
} | |
});} catch (err) { | |
console.error(err.message); | |
} | |
MathJax.Ajax.Preloading( | |
"[MathJax]/jax/element/mml/jax.js", | |
"[MathJax]/jax/element/mml/optable/Arrows.js", | |
"[MathJax]/jax/element/mml/optable/MiscMathSymbolsA.js", | |
"[MathJax]/jax/element/mml/optable/Dingbats.js", | |
"[MathJax]/jax/element/mml/optable/GeneralPunctuation.js", | |
"[MathJax]/jax/element/mml/optable/SpacingModLetters.js", | |
"[MathJax]/jax/element/mml/optable/MiscTechnical.js", | |
"[MathJax]/jax/element/mml/optable/SupplementalArrowsA.js", | |
"[MathJax]/jax/element/mml/optable/GreekAndCoptic.js", | |
"[MathJax]/jax/element/mml/optable/LetterlikeSymbols.js", | |
"[MathJax]/jax/element/mml/optable/SupplementalArrowsB.js", | |
"[MathJax]/jax/element/mml/optable/BasicLatin.js", | |
"[MathJax]/jax/element/mml/optable/MiscSymbolsAndArrows.js", | |
"[MathJax]/jax/element/mml/optable/CombDiacritMarks.js", | |
"[MathJax]/jax/element/mml/optable/GeometricShapes.js", | |
"[MathJax]/jax/element/mml/optable/MathOperators.js", | |
"[MathJax]/jax/element/mml/optable/MiscMathSymbolsB.js", | |
"[MathJax]/jax/element/mml/optable/SuppMathOperators.js", | |
"[MathJax]/jax/element/mml/optable/CombDiactForSymbols.js", | |
"[MathJax]/jax/element/mml/optable/Latin1Supplement.js", | |
"[MathJax]/extensions/MathEvents.js", | |
"[MathJax]/extensions/MathZoom.js", | |
"[MathJax]/extensions/MathMenu.js", | |
"[MathJax]/extensions/toMathML.js", | |
"[MathJax]/extensions/HelpDialog.js", | |
"[MathJax]/jax/input/TeX/config.js", | |
"[MathJax]/jax/input/TeX/jax.js", | |
"[MathJax]/jax/output/SVG/config.js", | |
"[MathJax]/jax/output/SVG/jax.js", | |
"[MathJax]/jax/output/SVG/autoload/mtable.js", | |
"[MathJax]/jax/output/SVG/autoload/mglyph.js", | |
"[MathJax]/jax/output/SVG/autoload/mmultiscripts.js", | |
"[MathJax]/jax/output/SVG/autoload/annotation-xml.js", | |
"[MathJax]/jax/output/SVG/autoload/maction.js", | |
"[MathJax]/jax/output/SVG/autoload/multiline.js", | |
"[MathJax]/jax/output/SVG/autoload/menclose.js", | |
"[MathJax]/jax/output/SVG/autoload/ms.js", | |
"[MathJax]/extensions/tex2jax.js", | |
"[MathJax]/extensions/TeX/AMScd.js", | |
"[MathJax]/extensions/TeX/AMSmath.js", | |
"[MathJax]/extensions/TeX/AMSsymbols.js", | |
"[MathJax]/extensions/TeX/HTML.js", | |
"[MathJax]/extensions/TeX/action.js", | |
"[MathJax]/extensions/TeX/autobold.js", | |
"[MathJax]/extensions/TeX/bbox.js", | |
"[MathJax]/extensions/TeX/boldsymbol.js", | |
"[MathJax]/extensions/TeX/cancel.js", | |
"[MathJax]/extensions/TeX/color.js", | |
"[MathJax]/extensions/TeX/enclose.js", | |
"[MathJax]/extensions/TeX/extpfeil.js", | |
"[MathJax]/extensions/TeX/mathchoice.js", | |
"[MathJax]/extensions/TeX/mediawiki-texvc.js", | |
"[MathJax]/extensions/TeX/mhchem.js", | |
"[MathJax]/extensions/TeX/newcommand.js", | |
"[MathJax]/extensions/TeX/unicode.js", | |
"[MathJax]/extensions/TeX/verb.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/fontdata.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/fontdata-extra.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/Arrows.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/BoxDrawing.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/CombDiacritMarks.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/Dingbats.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/EnclosedAlphanum.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/GeneralPunctuation.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/GeometricShapes.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/GreekAndCoptic.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/Latin1Supplement.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/LatinExtendedA.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/LetterlikeSymbols.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/MathOperators.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/MiscMathSymbolsB.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/MiscSymbols.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/MiscTechnical.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/PUA.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/SpacingModLetters.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/AMS/Regular/SuppMathOperators.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Caligraphic/Bold/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Caligraphic/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Fraktur/Bold/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Fraktur/Bold/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Fraktur/Bold/Other.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Fraktur/Bold/PUA.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Fraktur/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Fraktur/Regular/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Fraktur/Regular/Other.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Fraktur/Regular/PUA.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/Arrows.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/CombDiacritMarks.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/CombDiactForSymbols.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/GeneralPunctuation.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/GeometricShapes.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/GreekAndCoptic.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/Latin1Supplement.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/LatinExtendedA.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/LatinExtendedB.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/LetterlikeSymbols.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/MathOperators.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/MiscMathSymbolsA.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/MiscSymbols.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/MiscTechnical.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/SpacingModLetters.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/SupplementalArrowsA.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Bold/SuppMathOperators.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Italic/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Italic/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Italic/CombDiacritMarks.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Italic/GeneralPunctuation.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Italic/GreekAndCoptic.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Italic/LatinExtendedA.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Italic/LatinExtendedB.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Italic/LetterlikeSymbols.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/CombDiacritMarks.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/GeometricShapes.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/GreekAndCoptic.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/LatinExtendedA.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/LatinExtendedB.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/LetterlikeSymbols.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/MiscSymbols.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/SpacingModLetters.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Main/Regular/SuppMathOperators.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Math/BoldItalic/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Bold/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Bold/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Bold/CombDiacritMarks.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Bold/Other.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Italic/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Italic/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Italic/CombDiacritMarks.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Italic/Other.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Regular/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Regular/CombDiacritMarks.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/SansSerif/Regular/Other.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Script/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Script/Regular/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Size1/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Size2/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Size3/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Size4/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Typewriter/Regular/Main.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Typewriter/Regular/BasicLatin.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Typewriter/Regular/CombDiacritMarks.js", | |
"[MathJax]/jax/output/SVG/fonts/TeX/Typewriter/Regular/Other.js"); | |
MathJax.Hub.Config({"v1.0-compatible":false}); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/element/mml/jax.js | |
* | |
* Implements the MML ElementJax that holds the internal represetation | |
* of the mathematics on the page. Various InputJax will produce this | |
* format, and the OutputJax will display it in various formats. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2009-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.ElementJax.mml = MathJax.ElementJax({ | |
mimeType: "jax/mml" | |
},{ | |
id: "mml", | |
version: "2.7.1", | |
directory: MathJax.ElementJax.directory + "/mml", | |
extensionDir: MathJax.ElementJax.extensionDir + "/mml", | |
optableDir: MathJax.ElementJax.directory + "/mml/optable" | |
}); | |
MathJax.ElementJax.mml.Augment({ | |
Init: function () { | |
if (arguments.length === 1 && arguments[0].type === "math") {this.root = arguments[0]} | |
else {this.root = MathJax.ElementJax.mml.math.apply(this,arguments)} | |
if (this.root.attr && this.root.attr.mode) { | |
if (!this.root.display && this.root.attr.mode === "display") { | |
this.root.display = "block"; | |
this.root.attrNames.push("display"); | |
} | |
delete this.root.attr.mode; | |
for (var i = 0, m = this.root.attrNames.length; i < m; i++) { | |
if (this.root.attrNames[i] === "mode") {this.root.attrNames.splice(i,1); break} | |
} | |
} | |
} | |
},{ | |
INHERIT: "_inherit_", | |
AUTO: "_auto_", | |
SIZE: { | |
INFINITY: "infinity", | |
SMALL: "small", | |
NORMAL: "normal", | |
BIG: "big" | |
}, | |
COLOR: { | |
TRANSPARENT: "transparent" | |
}, | |
VARIANT: { | |
NORMAL: "normal", | |
BOLD: "bold", | |
ITALIC: "italic", | |
BOLDITALIC: "bold-italic", | |
DOUBLESTRUCK: "double-struck", | |
FRAKTUR: "fraktur", | |
BOLDFRAKTUR: "bold-fraktur", | |
SCRIPT: "script", | |
BOLDSCRIPT: "bold-script", | |
SANSSERIF: "sans-serif", | |
BOLDSANSSERIF: "bold-sans-serif", | |
SANSSERIFITALIC: "sans-serif-italic", | |
SANSSERIFBOLDITALIC: "sans-serif-bold-italic", | |
MONOSPACE: "monospace", | |
INITIAL: "inital", | |
TAILED: "tailed", | |
LOOPED: "looped", | |
STRETCHED: "stretched", | |
CALIGRAPHIC: "-tex-caligraphic", | |
OLDSTYLE: "-tex-oldstyle" | |
}, | |
FORM: { | |
PREFIX: "prefix", | |
INFIX: "infix", | |
POSTFIX: "postfix" | |
}, | |
LINEBREAK: { | |
AUTO: "auto", | |
NEWLINE: "newline", | |
NOBREAK: "nobreak", | |
GOODBREAK: "goodbreak", | |
BADBREAK: "badbreak" | |
}, | |
LINEBREAKSTYLE: { | |
BEFORE: "before", | |
AFTER: "after", | |
DUPLICATE: "duplicate", | |
INFIXLINBREAKSTYLE: "infixlinebreakstyle" | |
}, | |
INDENTALIGN: { | |
LEFT: "left", | |
CENTER: "center", | |
RIGHT: "right", | |
AUTO: "auto", | |
ID: "id", | |
INDENTALIGN: "indentalign" | |
}, | |
INDENTSHIFT: { | |
INDENTSHIFT: "indentshift" | |
}, | |
LINETHICKNESS: { | |
THIN: "thin", | |
MEDIUM: "medium", | |
THICK: "thick" | |
}, | |
NOTATION: { | |
LONGDIV: "longdiv", | |
ACTUARIAL: "actuarial", | |
RADICAL: "radical", | |
BOX: "box", | |
ROUNDEDBOX: "roundedbox", | |
CIRCLE: "circle", | |
LEFT: "left", | |
RIGHT: "right", | |
TOP: "top", | |
BOTTOM: "bottom", | |
UPDIAGONALSTRIKE: "updiagonalstrike", | |
DOWNDIAGONALSTRIKE: "downdiagonalstrike", | |
UPDIAGONALARROW: "updiagonalarrow", | |
VERTICALSTRIKE: "verticalstrike", | |
HORIZONTALSTRIKE: "horizontalstrike", | |
PHASORANGLE: "phasorangle", | |
MADRUWB: "madruwb" | |
}, | |
ALIGN: { | |
TOP: "top", | |
BOTTOM: "bottom", | |
CENTER: "center", | |
BASELINE: "baseline", | |
AXIS: "axis", | |
LEFT: "left", | |
RIGHT: "right" | |
}, | |
LINES: { | |
NONE: "none", | |
SOLID: "solid", | |
DASHED: "dashed" | |
}, | |
SIDE: { | |
LEFT: "left", | |
RIGHT: "right", | |
LEFTOVERLAP: "leftoverlap", | |
RIGHTOVERLAP: "rightoverlap" | |
}, | |
WIDTH: { | |
AUTO: "auto", | |
FIT: "fit" | |
}, | |
ACTIONTYPE: { | |
TOGGLE: "toggle", | |
STATUSLINE: "statusline", | |
TOOLTIP: "tooltip", | |
INPUT: "input" | |
}, | |
LENGTH: { | |
VERYVERYTHINMATHSPACE: "veryverythinmathspace", | |
VERYTHINMATHSPACE: "verythinmathspace", | |
THINMATHSPACE: "thinmathspace", | |
MEDIUMMATHSPACE: "mediummathspace", | |
THICKMATHSPACE: "thickmathspace", | |
VERYTHICKMATHSPACE: "verythickmathspace", | |
VERYVERYTHICKMATHSPACE: "veryverythickmathspace", | |
NEGATIVEVERYVERYTHINMATHSPACE: "negativeveryverythinmathspace", | |
NEGATIVEVERYTHINMATHSPACE: "negativeverythinmathspace", | |
NEGATIVETHINMATHSPACE: "negativethinmathspace", | |
NEGATIVEMEDIUMMATHSPACE: "negativemediummathspace", | |
NEGATIVETHICKMATHSPACE: "negativethickmathspace", | |
NEGATIVEVERYTHICKMATHSPACE: "negativeverythickmathspace", | |
NEGATIVEVERYVERYTHICKMATHSPACE: "negativeveryverythickmathspace" | |
}, | |
OVERFLOW: { | |
LINBREAK: "linebreak", | |
SCROLL: "scroll", | |
ELIDE: "elide", | |
TRUNCATE: "truncate", | |
SCALE: "scale" | |
}, | |
UNIT: { | |
EM: "em", | |
EX: "ex", | |
PX: "px", | |
IN: "in", | |
CM: "cm", | |
MM: "mm", | |
PT: "pt", | |
PC: "pc" | |
}, | |
TEXCLASS: { | |
ORD: 0, | |
OP: 1, | |
BIN: 2, | |
REL: 3, | |
OPEN: 4, | |
CLOSE: 5, | |
PUNCT: 6, | |
INNER: 7, | |
VCENTER: 8, | |
NONE: -1 | |
}, | |
TEXCLASSNAMES: ["ORD", "OP", "BIN", "REL", "OPEN", "CLOSE", "PUNCT", "INNER", "VCENTER"], | |
skipAttributes: { | |
texClass:true, useHeight:true, texprimestyle:true | |
}, | |
copyAttributes: { | |
displaystyle:1, scriptlevel:1, open:1, close:1, form:1, | |
actiontype: 1, | |
fontfamily:true, fontsize:true, fontweight:true, fontstyle:true, | |
color:true, background:true, | |
id:true, "class":1, href:true, style:true | |
}, | |
copyAttributeNames: [ | |
"displaystyle", "scriptlevel", "open", "close", "form", // force these to be copied | |
"actiontype", | |
"fontfamily", "fontsize", "fontweight", "fontstyle", | |
"color", "background", | |
"id", "class", "href", "style" | |
], | |
nocopyAttributes: { | |
fontfamily: true, fontsize: true, fontweight: true, fontstyle: true, | |
color: true, background: true, | |
id: true, 'class': true, href: true, style: true, | |
xmlns: true | |
}, | |
Error: function (message,def) { | |
var mml = this.merror(message), | |
dir = MathJax.Localization.fontDirection(), | |
font = MathJax.Localization.fontFamily(); | |
if (def) {mml = mml.With(def)} | |
if (dir || font) { | |
mml = this.mstyle(mml); | |
if (dir) {mml.dir = dir} | |
if (font) {mml.style.fontFamily = "font-family: "+font} | |
} | |
return mml; | |
} | |
}); | |
(function (MML) { | |
MML.mbase = MathJax.Object.Subclass({ | |
type: "base", isToken: false, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
dir: MML.INHERIT | |
}, | |
noInherit: {}, | |
noInheritAttribute: { | |
texClass: true | |
}, | |
getRemoved: {}, | |
linebreakContainer: false, | |
Init: function () { | |
this.data = []; | |
if (this.inferRow && !(arguments.length === 1 && arguments[0].inferred)) | |
{this.Append(MML.mrow().With({inferred: true, notParent: true}))} | |
this.Append.apply(this,arguments); | |
}, | |
With: function (def) { | |
for (var id in def) {if (def.hasOwnProperty(id)) {this[id] = def[id]}} | |
return this; | |
}, | |
Append: function () { | |
if (this.inferRow && this.data.length) { | |
this.data[0].Append.apply(this.data[0],arguments); | |
} else { | |
for (var i = 0, m = arguments.length; i < m; i++) | |
{this.SetData(this.data.length,arguments[i])} | |
} | |
}, | |
SetData: function (i,item) { | |
if (item != null) { | |
if (!(item instanceof MML.mbase)) | |
{item = (this.isToken || this.isChars ? MML.chars(item) : MML.mtext(item))} | |
item.parent = this; | |
item.setInherit(this.inheritFromMe ? this : this.inherit); | |
} | |
this.data[i] = item; | |
}, | |
Parent: function () { | |
var parent = this.parent; | |
while (parent && parent.notParent) {parent = parent.parent} | |
return parent; | |
}, | |
Get: function (name,nodefault,noself) { | |
if (!noself) { | |
if (this[name] != null) {return this[name]} | |
if (this.attr && this.attr[name] != null) {return this.attr[name]} | |
} | |
// FIXME: should cache these values and get from cache | |
// (clear cache when appended to a new object?) | |
var parent = this.Parent(); | |
if (parent && parent["adjustChild_"+name] != null) { | |
return (parent["adjustChild_"+name])(this.childPosition(),nodefault); | |
} | |
var obj = this.inherit; var root = obj; | |
while (obj) { | |
var value = obj[name]; if (value == null && obj.attr) {value = obj.attr[name]} | |
if (obj.removedStyles && obj.getRemoved[name] && value == null) value = obj.removedStyles[obj.getRemoved[name]]; | |
if (value != null && obj.noInheritAttribute && !obj.noInheritAttribute[name]) { | |
var noInherit = obj.noInherit[this.type]; | |
if (!(noInherit && noInherit[name])) {return value} | |
} | |
root = obj; obj = obj.inherit; | |
} | |
if (!nodefault) { | |
if (this.defaults[name] === MML.AUTO) {return this.autoDefault(name)} | |
if (this.defaults[name] !== MML.INHERIT && this.defaults[name] != null) | |
{return this.defaults[name]} | |
if (root) {return root.defaults[name]} | |
} | |
return null; | |
}, | |
hasValue: function (name) {return (this.Get(name,true) != null)}, | |
getValues: function () { | |
var values = {}; | |
for (var i = 0, m = arguments.length; i < m; i++) | |
{values[arguments[i]] = this.Get(arguments[i])} | |
return values; | |
}, | |
adjustChild_scriptlevel: function (i,nodef) {return this.Get("scriptlevel",nodef)}, // always inherit from parent | |
adjustChild_displaystyle: function (i,nodef) {return this.Get("displaystyle",nodef)}, // always inherit from parent | |
adjustChild_texprimestyle: function (i,nodef) {return this.Get("texprimestyle",nodef)}, // always inherit from parent | |
childPosition: function () { | |
var child = this, parent = child.parent; | |
while (parent.notParent) {child = parent; parent = child.parent} | |
for (var i = 0, m = parent.data.length; i < m; i++) {if (parent.data[i] === child) {return i}} | |
return null; | |
}, | |
setInherit: function (obj) { | |
if (obj !== this.inherit && this.inherit == null) { | |
this.inherit = obj; | |
for (var i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i] && this.data[i].setInherit) {this.data[i].setInherit(obj)} | |
} | |
} | |
}, | |
setTeXclass: function (prev) { | |
this.getPrevClass(prev); | |
return (typeof(this.texClass) !== "undefined" ? this : prev); | |
}, | |
getPrevClass: function (prev) { | |
if (prev) { | |
this.prevClass = prev.Get("texClass"); | |
this.prevLevel = prev.Get("scriptlevel"); | |
} | |
}, | |
updateTeXclass: function (core) { | |
if (core) { | |
this.prevClass = core.prevClass; delete core.prevClass; | |
this.prevLevel = core.prevLevel; delete core.prevLevel; | |
this.texClass = core.Get("texClass"); | |
} | |
}, | |
texSpacing: function () { | |
var prev = (this.prevClass != null ? this.prevClass : MML.TEXCLASS.NONE); | |
var tex = (this.Get("texClass") || MML.TEXCLASS.ORD); | |
if (prev === MML.TEXCLASS.NONE || tex === MML.TEXCLASS.NONE) {return ""} | |
if (prev === MML.TEXCLASS.VCENTER) {prev = MML.TEXCLASS.ORD} | |
if (tex === MML.TEXCLASS.VCENTER) {tex = MML.TEXCLASS.ORD} | |
var space = this.TEXSPACE[prev][tex]; | |
if ((this.prevLevel > 0 || this.Get("scriptlevel") > 0) && space >= 0) {return ""} | |
return this.TEXSPACELENGTH[Math.abs(space)]; | |
}, | |
TEXSPACELENGTH:[ | |
"", | |
MML.LENGTH.THINMATHSPACE, | |
MML.LENGTH.MEDIUMMATHSPACE, | |
MML.LENGTH.THICKMATHSPACE | |
], | |
// See TeXBook Chapter 18 (p. 170) | |
TEXSPACE: [ | |
[ 0,-1, 2, 3, 0, 0, 0, 1], // ORD | |
[-1,-1, 0, 3, 0, 0, 0, 1], // OP | |
[ 2, 2, 0, 0, 2, 0, 0, 2], // BIN | |
[ 3, 3, 0, 0, 3, 0, 0, 3], // REL | |
[ 0, 0, 0, 0, 0, 0, 0, 0], // OPEN | |
[ 0,-1, 2, 3, 0, 0, 0, 1], // CLOSE | |
[ 1, 1, 0, 1, 1, 1, 1, 1], // PUNCT | |
[ 1,-1, 2, 3, 1, 0, 1, 1] // INNER | |
], | |
autoDefault: function (name) {return ""}, | |
isSpacelike: function () {return false}, | |
isEmbellished: function () {return false}, | |
Core: function () {return this}, | |
CoreMO: function () {return this}, | |
childIndex: function(child) { | |
if (child == null) return; | |
for (var i = 0, m = this.data.length; i < m; i++) if (child === this.data[i]) return i; | |
}, | |
CoreIndex: function () { | |
return (this.inferRow ? this.data[0]||this : this).childIndex(this.Core()); | |
}, | |
hasNewline: function () { | |
if (this.isEmbellished()) {return this.CoreMO().hasNewline()} | |
if (this.isToken || this.linebreakContainer) {return false} | |
for (var i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i] && this.data[i].hasNewline()) {return true} | |
} | |
return false; | |
}, | |
array: function () {if (this.inferred) {return this.data} else {return [this]}}, | |
toString: function () {return this.type+"("+this.data.join(",")+")"}, | |
getAnnotation: function () {return null} | |
},{ | |
childrenSpacelike: function () { | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{if (!this.data[i].isSpacelike()) {return false}} | |
return true; | |
}, | |
childEmbellished: function () { | |
return (this.data[0] && this.data[0].isEmbellished()); | |
}, | |
childCore: function () {return (this.inferRow && this.data[0] ? this.data[0].Core() : this.data[0])}, | |
childCoreMO: function () {return (this.data[0] ? this.data[0].CoreMO() : null)}, | |
setChildTeXclass: function (prev) { | |
if (this.data[0]) { | |
prev = this.data[0].setTeXclass(prev); | |
this.updateTeXclass(this.data[0]); | |
} | |
return prev; | |
}, | |
setBaseTeXclasses: function (prev) { | |
this.getPrevClass(prev); this.texClass = null; | |
if (this.data[0]) { | |
if (this.isEmbellished() || this.data[0].isa(MML.mi)) { | |
prev = this.data[0].setTeXclass(prev); | |
this.updateTeXclass(this.Core()); | |
} else {this.data[0].setTeXclass(); prev = this} | |
} else {prev = this} | |
for (var i = 1, m = this.data.length; i < m; i++) | |
{if (this.data[i]) {this.data[i].setTeXclass()}} | |
return prev; | |
}, | |
setSeparateTeXclasses: function (prev) { | |
this.getPrevClass(prev); | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{if (this.data[i]) {this.data[i].setTeXclass()}} | |
if (this.isEmbellished()) {this.updateTeXclass(this.Core())} | |
return this; | |
} | |
}); | |
MML.mi = MML.mbase.Subclass({ | |
type: "mi", isToken: true, | |
texClass: MML.TEXCLASS.ORD, | |
defaults: { | |
mathvariant: MML.AUTO, | |
mathsize: MML.INHERIT, | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
dir: MML.INHERIT | |
}, | |
autoDefault: function (name) { | |
if (name === "mathvariant") { | |
var mi = (this.data[0]||"").toString(); | |
return (mi.length === 1 || | |
(mi.length === 2 && mi.charCodeAt(0) >= 0xD800 && mi.charCodeAt(0) < 0xDC00) ? | |
MML.VARIANT.ITALIC : MML.VARIANT.NORMAL); | |
} | |
return ""; | |
}, | |
setTeXclass: function (prev) { | |
this.getPrevClass(prev); | |
var name = this.data.join(""); | |
if (name.length > 1 && name.match(/^[a-z][a-z0-9]*$/i) && | |
this.texClass === MML.TEXCLASS.ORD) { | |
this.texClass = MML.TEXCLASS.OP; | |
this.autoOP = true; | |
} | |
return this; | |
} | |
}); | |
MML.mn = MML.mbase.Subclass({ | |
type: "mn", isToken: true, | |
texClass: MML.TEXCLASS.ORD, | |
defaults: { | |
mathvariant: MML.INHERIT, | |
mathsize: MML.INHERIT, | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
dir: MML.INHERIT | |
} | |
}); | |
MML.mo = MML.mbase.Subclass({ | |
type: "mo", isToken: true, | |
defaults: { | |
mathvariant: MML.INHERIT, | |
mathsize: MML.INHERIT, | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
dir: MML.INHERIT, | |
form: MML.AUTO, | |
fence: MML.AUTO, | |
separator: MML.AUTO, | |
lspace: MML.AUTO, | |
rspace: MML.AUTO, | |
stretchy: MML.AUTO, | |
symmetric: MML.AUTO, | |
maxsize: MML.AUTO, | |
minsize: MML.AUTO, | |
largeop: MML.AUTO, | |
movablelimits: MML.AUTO, | |
accent: MML.AUTO, | |
linebreak: MML.LINEBREAK.AUTO, | |
lineleading: MML.INHERIT, | |
linebreakstyle: MML.AUTO, | |
linebreakmultchar: MML.INHERIT, | |
indentalign: MML.INHERIT, | |
indentshift: MML.INHERIT, | |
indenttarget: MML.INHERIT, | |
indentalignfirst: MML.INHERIT, | |
indentshiftfirst: MML.INHERIT, | |
indentalignlast: MML.INHERIT, | |
indentshiftlast: MML.INHERIT, | |
texClass: MML.AUTO | |
}, | |
defaultDef: { | |
form: MML.FORM.INFIX, | |
fence: false, | |
separator: false, | |
lspace: MML.LENGTH.THICKMATHSPACE, | |
rspace: MML.LENGTH.THICKMATHSPACE, | |
stretchy: false, | |
symmetric: false, | |
maxsize: MML.SIZE.INFINITY, | |
minsize: '0em', //'1em', | |
largeop: false, | |
movablelimits: false, | |
accent: false, | |
linebreak: MML.LINEBREAK.AUTO, | |
lineleading: "1ex", | |
linebreakstyle: "before", | |
indentalign: MML.INDENTALIGN.AUTO, | |
indentshift: "0", | |
indenttarget: "", | |
indentalignfirst: MML.INDENTALIGN.INDENTALIGN, | |
indentshiftfirst: MML.INDENTSHIFT.INDENTSHIFT, | |
indentalignlast: MML.INDENTALIGN.INDENTALIGN, | |
indentshiftlast: MML.INDENTSHIFT.INDENTSHIFT, | |
texClass: MML.TEXCLASS.REL // for MML, but TeX sets ORD explicitly | |
}, | |
SPACE_ATTR: {lspace: 0x01, rspace: 0x02, form: 0x04}, | |
useMMLspacing: 0x07, | |
autoDefault: function (name,nodefault) { | |
var def = this.def; | |
if (!def) { | |
if (name === "form") {this.useMMLspacing &= ~this.SPACE_ATTR.form; return this.getForm()} | |
var mo = this.data.join(""); | |
var forms = [this.Get("form"),MML.FORM.INFIX,MML.FORM.POSTFIX,MML.FORM.PREFIX]; | |
for (var i = 0, m = forms.length; i < m; i++) { | |
var data = this.OPTABLE[forms[i]][mo]; | |
if (data) {def = this.makeDef(data); break} | |
} | |
if (!def) {def = this.CheckRange(mo)} | |
if (!def && nodefault) {def = {}} else { | |
if (!def) {def = MathJax.Hub.Insert({},this.defaultDef)} | |
if (this.parent) {this.def = def} else {def = MathJax.Hub.Insert({},def)} | |
def.form = forms[0]; | |
} | |
} | |
this.useMMLspacing &= ~(this.SPACE_ATTR[name] || 0); | |
if (def[name] != null) {return def[name]} | |
else if (!nodefault) {return this.defaultDef[name]} | |
return ""; | |
}, | |
CheckRange: function (mo) { | |
var n = mo.charCodeAt(0); | |
if (n >= 0xD800 && n < 0xDC00) {n = (((n-0xD800)<<10)+(mo.charCodeAt(1)-0xDC00))+0x10000} | |
for (var i = 0, m = this.RANGES.length; i < m && this.RANGES[i][0] <= n; i++) { | |
if (n <= this.RANGES[i][1]) { | |
if (this.RANGES[i][3]) { | |
var file = MML.optableDir+"/"+this.RANGES[i][3]+".js"; | |
this.RANGES[i][3] = null; | |
MathJax.Hub.RestartAfter(MathJax.Ajax.Require(file)); | |
} | |
var data = MML.TEXCLASSNAMES[this.RANGES[i][2]]; | |
data = this.OPTABLE.infix[mo] = MML.mo.OPTYPES[data === "BIN" ? "BIN3" : data]; | |
return this.makeDef(data); | |
} | |
} | |
return null; | |
}, | |
makeDef: function (data) { | |
if (data[2] == null) {data[2] = this.defaultDef.texClass} | |
if (!data[3]) {data[3] = {}} | |
var def = MathJax.Hub.Insert({},data[3]); | |
def.lspace = this.SPACE[data[0]]; def.rspace = this.SPACE[data[1]]; | |
def.texClass = data[2]; | |
if (def.texClass === MML.TEXCLASS.REL && | |
(this.movablelimits || this.data.join("").match(/^[a-z]+$/i))) | |
{def.texClass = MML.TEXCLASS.OP} // mark named operators as OP | |
return def; | |
}, | |
getForm: function () { | |
var core = this, parent = this.parent, Parent = this.Parent(); | |
while (Parent && Parent.isEmbellished()) | |
{core = parent; parent = Parent.parent; Parent = Parent.Parent()} | |
if (parent && parent.type === "mrow" && parent.NonSpaceLength() !== 1) { | |
if (parent.FirstNonSpace() === core) {return MML.FORM.PREFIX} | |
if (parent.LastNonSpace() === core) {return MML.FORM.POSTFIX} | |
} | |
return MML.FORM.INFIX; | |
}, | |
isEmbellished: function () {return true}, | |
hasNewline: function () {return (this.Get("linebreak") === MML.LINEBREAK.NEWLINE)}, | |
CoreParent: function () { | |
var parent = this; | |
while (parent && parent.isEmbellished() && | |
parent.CoreMO() === this && !parent.isa(MML.math)) {parent = parent.Parent()} | |
return parent; | |
}, | |
CoreText: function (parent) { | |
if (!parent) {return ""} | |
if (parent.isEmbellished()) {return parent.CoreMO().data.join("")} | |
while ((((parent.isa(MML.mrow) || parent.isa(MML.TeXAtom) || | |
parent.isa(MML.mstyle) || parent.isa(MML.mphantom)) && | |
parent.data.length === 1) || parent.isa(MML.munderover)) && | |
parent.data[0]) {parent = parent.data[0]} | |
if (!parent.isToken) {return ""} else {return parent.data.join("")} | |
}, | |
remapChars: { | |
'*':"\u2217", | |
'"':"\u2033", | |
"\u00B0":"\u2218", | |
"\u00B2":"2", | |
"\u00B3":"3", | |
"\u00B4":"\u2032", | |
"\u00B9":"1" | |
}, | |
remap: function (text,map) { | |
text = text.replace(/-/g,"\u2212"); | |
if (map) { | |
text = text.replace(/'/g,"\u2032").replace(/`/g,"\u2035"); | |
if (text.length === 1) {text = map[text]||text} | |
} | |
return text; | |
}, | |
setTeXclass: function (prev) { | |
var values = this.getValues("form","lspace","rspace","fence"); // sets useMMLspacing | |
if (this.useMMLspacing) {this.texClass = MML.TEXCLASS.NONE; return this} | |
if (values.fence && !this.texClass) { | |
if (values.form === MML.FORM.PREFIX) {this.texClass = MML.TEXCLASS.OPEN} | |
if (values.form === MML.FORM.POSTFIX) {this.texClass = MML.TEXCLASS.CLOSE} | |
} | |
this.texClass = this.Get("texClass"); | |
if (this.data.join("") === "\u2061") { | |
// force previous node to be texClass OP, and skip this node | |
if (prev) {prev.texClass = MML.TEXCLASS.OP; prev.fnOP = true} | |
this.texClass = this.prevClass = MML.TEXCLASS.NONE; | |
return prev; | |
} | |
return this.adjustTeXclass(prev); | |
}, | |
adjustTeXclass: function (prev) { | |
if (this.texClass === MML.TEXCLASS.NONE) {return prev} | |
if (prev) { | |
if (prev.autoOP && (this.texClass === MML.TEXCLASS.BIN || | |
this.texClass === MML.TEXCLASS.REL)) | |
{prev.texClass = MML.TEXCLASS.ORD} | |
this.prevClass = prev.texClass || MML.TEXCLASS.ORD; | |
this.prevLevel = prev.Get("scriptlevel") | |
} else {this.prevClass = MML.TEXCLASS.NONE} | |
if (this.texClass === MML.TEXCLASS.BIN && | |
(this.prevClass === MML.TEXCLASS.NONE || | |
this.prevClass === MML.TEXCLASS.BIN || | |
this.prevClass === MML.TEXCLASS.OP || | |
this.prevClass === MML.TEXCLASS.REL || | |
this.prevClass === MML.TEXCLASS.OPEN || | |
this.prevClass === MML.TEXCLASS.PUNCT)) { | |
this.texClass = MML.TEXCLASS.ORD; | |
} else if (this.prevClass === MML.TEXCLASS.BIN && | |
(this.texClass === MML.TEXCLASS.REL || | |
this.texClass === MML.TEXCLASS.CLOSE || | |
this.texClass === MML.TEXCLASS.PUNCT)) { | |
prev.texClass = this.prevClass = MML.TEXCLASS.ORD; | |
} else if (this.texClass === MML.TEXCLASS.BIN) { | |
// | |
// Check if node is the last one in its container since the rule | |
// above only takes effect if there is a node that follows. | |
// | |
var child = this, parent = this.parent; | |
while (parent && parent.parent && parent.isEmbellished() && | |
(parent.data.length === 1 || | |
(parent.type !== "mrow" && parent.Core() === child))) // handles msubsup and munderover | |
{child = parent; parent = parent.parent} | |
if (parent.data[parent.data.length-1] === child) this.texClass = MML.TEXCLASS.ORD; | |
} | |
return this; | |
} | |
}); | |
MML.mtext = MML.mbase.Subclass({ | |
type: "mtext", isToken: true, | |
isSpacelike: function () {return true}, | |
texClass: MML.TEXCLASS.ORD, | |
defaults: { | |
mathvariant: MML.INHERIT, | |
mathsize: MML.INHERIT, | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
dir: MML.INHERIT | |
} | |
}); | |
MML.mspace = MML.mbase.Subclass({ | |
type: "mspace", isToken: true, | |
isSpacelike: function () {return true}, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
width: "0em", | |
height: "0ex", | |
depth: "0ex", | |
linebreak: MML.LINEBREAK.AUTO | |
}, | |
hasDimAttr: function () { | |
return (this.hasValue("width") || this.hasValue("height") || | |
this.hasValue("depth")); | |
}, | |
hasNewline: function () { | |
// The MathML spec says that the linebreak attribute should be ignored | |
// if any dimensional attribute is set. | |
return (!this.hasDimAttr() && | |
this.Get("linebreak") === MML.LINEBREAK.NEWLINE); | |
} | |
}); | |
MML.ms = MML.mbase.Subclass({ | |
type: "ms", isToken: true, | |
texClass: MML.TEXCLASS.ORD, | |
defaults: { | |
mathvariant: MML.INHERIT, | |
mathsize: MML.INHERIT, | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
dir: MML.INHERIT, | |
lquote: '"', | |
rquote: '"' | |
} | |
}); | |
MML.mglyph = MML.mbase.Subclass({ | |
type: "mglyph", isToken: true, | |
texClass: MML.TEXCLASS.ORD, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
alt: "", | |
src: "", | |
width: MML.AUTO, | |
height: MML.AUTO, | |
valign: "0em" | |
} | |
}); | |
MML.mrow = MML.mbase.Subclass({ | |
type: "mrow", | |
isSpacelike: MML.mbase.childrenSpacelike, | |
inferred: false, notParent: false, | |
isEmbellished: function () { | |
var isEmbellished = false; | |
for (var i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i] == null) continue; | |
if (this.data[i].isEmbellished()) { | |
if (isEmbellished) {return false} | |
isEmbellished = true; this.core = i; | |
} else if (!this.data[i].isSpacelike()) {return false} | |
} | |
return isEmbellished; | |
}, | |
NonSpaceLength: function () { | |
var n = 0; | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{if (this.data[i] && !this.data[i].isSpacelike()) {n++}} | |
return n; | |
}, | |
FirstNonSpace: function () { | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{if (this.data[i] && !this.data[i].isSpacelike()) {return this.data[i]}} | |
return null; | |
}, | |
LastNonSpace: function () { | |
for (var i = this.data.length-1; i >= 0; i--) | |
{if (this.data[0] && !this.data[i].isSpacelike()) {return this.data[i]}} | |
return null; | |
}, | |
Core: function () { | |
if (!(this.isEmbellished()) || typeof(this.core) === "undefined") {return this} | |
return this.data[this.core]; | |
}, | |
CoreMO: function () { | |
if (!(this.isEmbellished()) || typeof(this.core) === "undefined") {return this} | |
return this.data[this.core].CoreMO(); | |
}, | |
toString: function () { | |
if (this.inferred) {return '[' + this.data.join(',') + ']'} | |
return this.SUPER(arguments).toString.call(this); | |
}, | |
setTeXclass: function (prev) { | |
var i, m = this.data.length; | |
if ((this.open || this.close) && (!prev || !prev.fnOP)) { | |
// | |
// <mrow> came from \left...\right | |
// so treat as subexpression (tex class INNER) | |
// | |
this.getPrevClass(prev); prev = null; | |
for (i = 0; i < m; i++) | |
{if (this.data[i]) {prev = this.data[i].setTeXclass(prev)}} | |
if (!this.hasOwnProperty("texClass")) this.texClass = MML.TEXCLASS.INNER; | |
return this; | |
} else { | |
// | |
// Normal <mrow>, so treat as | |
// thorugh mrow is not there | |
// | |
for (i = 0; i < m; i++) | |
{if (this.data[i]) {prev = this.data[i].setTeXclass(prev)}} | |
if (this.data[0]) {this.updateTeXclass(this.data[0])} | |
return prev; | |
} | |
}, | |
getAnnotation: function (name) { | |
if (this.data.length != 1) return null; | |
return this.data[0].getAnnotation(name); | |
} | |
}); | |
MML.mfrac = MML.mbase.Subclass({ | |
type: "mfrac", num: 0, den: 1, | |
linebreakContainer: true, | |
isEmbellished: MML.mbase.childEmbellished, | |
Core: MML.mbase.childCore, | |
CoreMO: MML.mbase.childCoreMO, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
linethickness: MML.LINETHICKNESS.MEDIUM, | |
numalign: MML.ALIGN.CENTER, | |
denomalign: MML.ALIGN.CENTER, | |
bevelled: false | |
}, | |
adjustChild_displaystyle: function (n) {return false}, | |
adjustChild_scriptlevel: function (n) { | |
var level = this.Get("scriptlevel"); | |
if (!this.Get("displaystyle") || level > 0) {level++} | |
return level; | |
}, | |
adjustChild_texprimestyle: function (n) { | |
if (n == this.den) {return true} | |
return this.Get("texprimestyle"); | |
}, | |
setTeXclass: MML.mbase.setSeparateTeXclasses | |
}); | |
MML.msqrt = MML.mbase.Subclass({ | |
type: "msqrt", | |
inferRow: true, | |
linebreakContainer: true, | |
texClass: MML.TEXCLASS.ORD, | |
setTeXclass: MML.mbase.setSeparateTeXclasses, | |
adjustChild_texprimestyle: function (n) {return true} | |
}); | |
MML.mroot = MML.mbase.Subclass({ | |
type: "mroot", | |
linebreakContainer: true, | |
texClass: MML.TEXCLASS.ORD, | |
adjustChild_displaystyle: function (n) { | |
if (n === 1) {return false} | |
return this.Get("displaystyle"); | |
}, | |
adjustChild_scriptlevel: function (n) { | |
var level = this.Get("scriptlevel"); | |
if (n === 1) {level += 2} | |
return level; | |
}, | |
adjustChild_texprimestyle: function (n) { | |
if (n === 0) {return true}; | |
return this.Get("texprimestyle"); | |
}, | |
setTeXclass: MML.mbase.setSeparateTeXclasses | |
}); | |
MML.mstyle = MML.mbase.Subclass({ | |
type: "mstyle", | |
isSpacelike: MML.mbase.childrenSpacelike, | |
isEmbellished: MML.mbase.childEmbellished, | |
Core: MML.mbase.childCore, | |
CoreMO: MML.mbase.childCoreMO, | |
inferRow: true, | |
defaults: { | |
scriptlevel: MML.INHERIT, | |
displaystyle: MML.INHERIT, | |
scriptsizemultiplier: Math.sqrt(1/2), | |
scriptminsize: "8pt", | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
dir: MML.INHERIT, | |
infixlinebreakstyle: MML.LINEBREAKSTYLE.BEFORE, | |
decimalseparator: "." | |
}, | |
adjustChild_scriptlevel: function (n) { | |
var level = this.scriptlevel; | |
if (level == null) { | |
level = this.Get("scriptlevel"); | |
} else if (String(level).match(/^ *[-+]/)) { | |
var LEVEL = this.Get("scriptlevel",null,true); | |
level = LEVEL + parseInt(level); | |
} | |
return level; | |
}, | |
inheritFromMe: true, | |
noInherit: { | |
mpadded: {width: true, height: true, depth: true, lspace: true, voffset: true}, | |
mtable: {width: true, height: true, depth: true, align: true} | |
}, | |
getRemoved: {fontfamily:"fontFamily", fontweight:"fontWeight", fontstyle:"fontStyle", fontsize:"fontSize"}, | |
setTeXclass: MML.mbase.setChildTeXclass | |
}); | |
MML.merror = MML.mbase.Subclass({ | |
type: "merror", | |
inferRow: true, | |
linebreakContainer: true, | |
texClass: MML.TEXCLASS.ORD | |
}); | |
MML.mpadded = MML.mbase.Subclass({ | |
type: "mpadded", | |
inferRow: true, | |
isSpacelike: MML.mbase.childrenSpacelike, | |
isEmbellished: MML.mbase.childEmbellished, | |
Core: MML.mbase.childCore, | |
CoreMO: MML.mbase.childCoreMO, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
width: "", | |
height: "", | |
depth: "", | |
lspace: 0, | |
voffset: 0 | |
}, | |
setTeXclass: MML.mbase.setChildTeXclass | |
}); | |
MML.mphantom = MML.mbase.Subclass({ | |
type: "mphantom", | |
texClass: MML.TEXCLASS.ORD, | |
inferRow: true, | |
isSpacelike: MML.mbase.childrenSpacelike, | |
isEmbellished: MML.mbase.childEmbellished, | |
Core: MML.mbase.childCore, | |
CoreMO: MML.mbase.childCoreMO, | |
setTeXclass: MML.mbase.setChildTeXclass | |
}); | |
MML.mfenced = MML.mbase.Subclass({ | |
type: "mfenced", | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
open: '(', | |
close: ')', | |
separators: ',' | |
}, | |
addFakeNodes: function () { | |
var values = this.getValues("open","close","separators"); | |
values.open = values.open.replace(/[ \t\n\r]/g,""); | |
values.close = values.close.replace(/[ \t\n\r]/g,""); | |
values.separators = values.separators.replace(/[ \t\n\r]/g,""); | |
// | |
// Create a fake node for the open item | |
// | |
if (values.open !== "") { | |
this.SetData("open",MML.mo(values.open).With({ | |
fence:true, form:MML.FORM.PREFIX, texClass:MML.TEXCLASS.OPEN | |
})); | |
// | |
// Clear flag for using MML spacing even though form is specified | |
// | |
this.data.open.useMMLspacing = 0; | |
} | |
// | |
// Create fake nodes for the separators | |
// | |
if (values.separators !== "") { | |
while (values.separators.length < this.data.length) | |
{values.separators += values.separators.charAt(values.separators.length-1)} | |
for (var i = 1, m = this.data.length; i < m; i++) { | |
if (this.data[i]) { | |
this.SetData("sep"+i,MML.mo(values.separators.charAt(i-1)).With({separator:true})) | |
this.data["sep"+i].useMMLspacing = 0; | |
} | |
} | |
} | |
// | |
// Create fake node for the close item | |
// | |
if (values.close !== "") { | |
this.SetData("close",MML.mo(values.close).With({ | |
fence:true, form:MML.FORM.POSTFIX, texClass:MML.TEXCLASS.CLOSE | |
})); | |
// | |
// Clear flag for using MML spacing even though form is specified | |
// | |
this.data.close.useMMLspacing = 0; | |
} | |
}, | |
texClass: MML.TEXCLASS.OPEN, | |
setTeXclass: function (prev) { | |
this.addFakeNodes(); | |
this.getPrevClass(prev); | |
if (this.data.open) {prev = this.data.open.setTeXclass(prev)} | |
if (this.data[0]) {prev = this.data[0].setTeXclass(prev)} | |
for (var i = 1, m = this.data.length; i < m; i++) { | |
if (this.data["sep"+i]) {prev = this.data["sep"+i].setTeXclass(prev)} | |
if (this.data[i]) {prev = this.data[i].setTeXclass(prev)} | |
} | |
if (this.data.close) {prev = this.data.close.setTeXclass(prev)} | |
this.updateTeXclass(this.data.open); | |
this.texClass = MML.TEXCLASS.INNER; | |
return prev; | |
} | |
}); | |
MML.menclose = MML.mbase.Subclass({ | |
type: "menclose", | |
inferRow: true, | |
linebreakContainer: true, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
notation: MML.NOTATION.LONGDIV, | |
texClass: MML.TEXCLASS.ORD | |
}, | |
setTeXclass: MML.mbase.setSeparateTeXclasses | |
}); | |
MML.msubsup = MML.mbase.Subclass({ | |
type: "msubsup", base: 0, sub: 1, sup: 2, | |
isEmbellished: MML.mbase.childEmbellished, | |
Core: MML.mbase.childCore, | |
CoreMO: MML.mbase.childCoreMO, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
subscriptshift: "", | |
superscriptshift: "", | |
texClass: MML.AUTO | |
}, | |
autoDefault: function (name) { | |
if (name === "texClass") | |
{return (this.isEmbellished() ? this.CoreMO().Get(name) : MML.TEXCLASS.ORD)} | |
return 0; | |
}, | |
adjustChild_displaystyle: function (n) { | |
if (n > 0) {return false} | |
return this.Get("displaystyle"); | |
}, | |
adjustChild_scriptlevel: function (n) { | |
var level = this.Get("scriptlevel"); | |
if (n > 0) {level++} | |
return level; | |
}, | |
adjustChild_texprimestyle: function (n) { | |
if (n === this.sub) {return true} | |
return this.Get("texprimestyle"); | |
}, | |
setTeXclass: MML.mbase.setBaseTeXclasses | |
}); | |
MML.msub = MML.msubsup.Subclass({type: "msub"}); | |
MML.msup = MML.msubsup.Subclass({type: "msup", sub:2, sup:1}); | |
MML.mmultiscripts = MML.msubsup.Subclass({ | |
type: "mmultiscripts", | |
adjustChild_texprimestyle: function (n) { | |
if (n % 2 === 1) {return true} | |
return this.Get("texprimestyle"); | |
} | |
}); | |
MML.mprescripts = MML.mbase.Subclass({type: "mprescripts"}); | |
MML.none = MML.mbase.Subclass({type: "none"}); | |
MML.munderover = MML.mbase.Subclass({ | |
type: "munderover", | |
base: 0, under: 1, over: 2, sub: 1, sup: 2, | |
ACCENTS: ["", "accentunder", "accent"], | |
linebreakContainer: true, | |
isEmbellished: MML.mbase.childEmbellished, | |
Core: MML.mbase.childCore, | |
CoreMO: MML.mbase.childCoreMO, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
accent: MML.AUTO, | |
accentunder: MML.AUTO, | |
align: MML.ALIGN.CENTER, | |
texClass: MML.AUTO, | |
subscriptshift: "", // when converted to msubsup by moveablelimits | |
superscriptshift: "" // when converted to msubsup by moveablelimits | |
}, | |
autoDefault: function (name) { | |
if (name === "texClass") | |
{return (this.isEmbellished() ? this.CoreMO().Get(name) : MML.TEXCLASS.ORD)} | |
if (name === "accent" && this.data[this.over]) {return this.data[this.over].CoreMO().Get("accent")} | |
if (name === "accentunder" && this.data[this.under]) {return this.data[this.under].CoreMO().Get("accent")} | |
return false; | |
}, | |
adjustChild_displaystyle: function (n) { | |
if (n > 0) {return false} | |
return this.Get("displaystyle"); | |
}, | |
adjustChild_scriptlevel: function (n) { | |
var level = this.Get("scriptlevel"); | |
var force = (this.data[this.base] && !this.Get("displaystyle") && | |
this.data[this.base].CoreMO().Get("movablelimits")); | |
if (n == this.under && (force || !this.Get("accentunder"))) {level++} | |
if (n == this.over && (force || !this.Get("accent"))) {level++} | |
return level; | |
}, | |
adjustChild_texprimestyle: function (n) { | |
if (n === this.base && this.data[this.over]) {return true} | |
return this.Get("texprimestyle"); | |
}, | |
setTeXclass: MML.mbase.setBaseTeXclasses | |
}); | |
MML.munder = MML.munderover.Subclass({type: "munder"}); | |
MML.mover = MML.munderover.Subclass({ | |
type: "mover", over: 1, under: 2, sup: 1, sub: 2, | |
ACCENTS: ["", "accent", "accentunder"] | |
}); | |
MML.mtable = MML.mbase.Subclass({ | |
type: "mtable", | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
align: MML.ALIGN.AXIS, | |
rowalign: MML.ALIGN.BASELINE, | |
columnalign: MML.ALIGN.CENTER, | |
groupalign: "{left}", | |
alignmentscope: true, | |
columnwidth: MML.WIDTH.AUTO, | |
width: MML.WIDTH.AUTO, | |
rowspacing: "1ex", | |
columnspacing: ".8em", | |
rowlines: MML.LINES.NONE, | |
columnlines: MML.LINES.NONE, | |
frame: MML.LINES.NONE, | |
framespacing: "0.4em 0.5ex", | |
equalrows: false, | |
equalcolumns: false, | |
displaystyle: false, | |
side: MML.SIDE.RIGHT, | |
minlabelspacing: "0.8em", | |
texClass: MML.TEXCLASS.ORD, | |
useHeight: 1 | |
}, | |
adjustChild_displaystyle: function () { | |
return (this.displaystyle != null ? this.displaystyle : this.defaults.displaystyle); | |
}, | |
inheritFromMe: true, | |
noInherit: { | |
mover: {align: true}, | |
munder: {align: true}, | |
munderover: {align: true}, | |
mtable: { | |
align: true, rowalign: true, columnalign: true, groupalign: true, | |
alignmentscope: true, columnwidth: true, width: true, rowspacing: true, | |
columnspacing: true, rowlines: true, columnlines: true, frame: true, | |
framespacing: true, equalrows: true, equalcolumns: true, displaystyle: true, | |
side: true, minlabelspacing: true, texClass: true, useHeight: 1 | |
} | |
}, | |
linebreakContainer: true, | |
Append: function () { | |
for (var i = 0, m = arguments.length; i < m; i++) { | |
if (!((arguments[i] instanceof MML.mtr) || | |
(arguments[i] instanceof MML.mlabeledtr))) {arguments[i] = MML.mtr(arguments[i])} | |
} | |
this.SUPER(arguments).Append.apply(this,arguments); | |
}, | |
setTeXclass: MML.mbase.setSeparateTeXclasses | |
}); | |
MML.mtr = MML.mbase.Subclass({ | |
type: "mtr", | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
rowalign: MML.INHERIT, | |
columnalign: MML.INHERIT, | |
groupalign: MML.INHERIT | |
}, | |
inheritFromMe: true, | |
noInherit: { | |
mrow: {rowalign: true, columnalign: true, groupalign: true}, | |
mtable: {rowalign: true, columnalign: true, groupalign: true} | |
}, | |
linebreakContainer: true, | |
Append: function () { | |
for (var i = 0, m = arguments.length; i < m; i++) { | |
if (!(arguments[i] instanceof MML.mtd)) {arguments[i] = MML.mtd(arguments[i])} | |
} | |
this.SUPER(arguments).Append.apply(this,arguments); | |
}, | |
setTeXclass: MML.mbase.setSeparateTeXclasses | |
}); | |
MML.mtd = MML.mbase.Subclass({ | |
type: "mtd", | |
inferRow: true, | |
linebreakContainer: true, | |
isEmbellished: MML.mbase.childEmbellished, | |
Core: MML.mbase.childCore, | |
CoreMO: MML.mbase.childCoreMO, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
rowspan: 1, | |
columnspan: 1, | |
rowalign: MML.INHERIT, | |
columnalign: MML.INHERIT, | |
groupalign: MML.INHERIT | |
}, | |
setTeXclass: MML.mbase.setSeparateTeXclasses | |
}); | |
MML.maligngroup = MML.mbase.Subclass({ | |
type: "maligngroup", | |
isSpacelike: function () {return true}, | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
groupalign: MML.INHERIT | |
}, | |
inheritFromMe: true, | |
noInherit: { | |
mrow: {groupalign: true}, | |
mtable: {groupalign: true} | |
} | |
}); | |
MML.malignmark = MML.mbase.Subclass({ | |
type: "malignmark", | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
edge: MML.SIDE.LEFT | |
}, | |
isSpacelike: function () {return true} | |
}); | |
MML.mlabeledtr = MML.mtr.Subclass({ | |
type: "mlabeledtr" | |
}); | |
MML.maction = MML.mbase.Subclass({ | |
type: "maction", | |
defaults: { | |
mathbackground: MML.INHERIT, | |
mathcolor: MML.INHERIT, | |
actiontype: MML.ACTIONTYPE.TOGGLE, | |
selection: 1 | |
}, | |
selected: function () {return this.data[this.Get("selection")-1] || MML.NULL}, | |
isEmbellished: function () {return this.selected().isEmbellished()}, | |
isSpacelike: function () {return this.selected().isSpacelike()}, | |
Core: function () {return this.selected().Core()}, | |
CoreMO: function () {return this.selected().CoreMO()}, | |
setTeXclass: function (prev) { | |
if (this.Get("actiontype") === MML.ACTIONTYPE.TOOLTIP && this.data[1]) { | |
// Make sure tooltip has proper spacing when typeset (see issue #412) | |
this.data[1].setTeXclass(); | |
} | |
var selected = this.selected(); | |
prev = selected.setTeXclass(prev); | |
this.updateTeXclass(selected); | |
return prev; | |
} | |
}); | |
MML.semantics = MML.mbase.Subclass({ | |
type: "semantics", notParent: true, | |
isEmbellished: MML.mbase.childEmbellished, | |
Core: MML.mbase.childCore, | |
CoreMO: MML.mbase.childCoreMO, | |
defaults: { | |
definitionURL: null, | |
encoding: null | |
}, | |
setTeXclass: MML.mbase.setChildTeXclass, | |
getAnnotation: function (name) { | |
var encodingList = MathJax.Hub.config.MathMenu.semanticsAnnotations[name]; | |
if (encodingList) { | |
for (var i = 0, m = this.data.length; i < m; i++) { | |
var encoding = this.data[i].Get("encoding"); | |
if (encoding) { | |
for (var j = 0, n = encodingList.length; j < n; j++) { | |
if (encodingList[j] === encoding) return this.data[i]; | |
} | |
} | |
} | |
} | |
return null; | |
} | |
}); | |
MML.annotation = MML.mbase.Subclass({ | |
type: "annotation", isChars: true, | |
linebreakContainer: true, | |
defaults: { | |
definitionURL: null, | |
encoding: null, | |
cd: "mathmlkeys", | |
name: "", | |
src: null | |
} | |
}); | |
MML["annotation-xml"] = MML.mbase.Subclass({ | |
type: "annotation-xml", | |
linebreakContainer: true, | |
defaults: { | |
definitionURL: null, | |
encoding: null, | |
cd: "mathmlkeys", | |
name: "", | |
src: null | |
} | |
}); | |
MML.math = MML.mstyle.Subclass({ | |
type: "math", | |
defaults: { | |
mathvariant: MML.VARIANT.NORMAL, | |
mathsize: MML.SIZE.NORMAL, | |
mathcolor: "", // should be "black", but allow it to inherit from surrounding text | |
mathbackground: MML.COLOR.TRANSPARENT, | |
dir: "ltr", | |
scriptlevel: 0, | |
displaystyle: MML.AUTO, | |
display: "inline", | |
maxwidth: "", | |
overflow: MML.OVERFLOW.LINEBREAK, | |
altimg: "", | |
'altimg-width': "", | |
'altimg-height': "", | |
'altimg-valign': "", | |
alttext: "", | |
cdgroup: "", | |
scriptsizemultiplier: Math.sqrt(1/2), | |
scriptminsize: "8px", // should be 8pt, but that's too big | |
infixlinebreakstyle: MML.LINEBREAKSTYLE.BEFORE, | |
lineleading: "1ex", | |
indentshift: "auto", // use user configuration | |
indentalign: MML.INDENTALIGN.AUTO, | |
indentalignfirst: MML.INDENTALIGN.INDENTALIGN, | |
indentshiftfirst: MML.INDENTSHIFT.INDENTSHIFT, | |
indentalignlast: MML.INDENTALIGN.INDENTALIGN, | |
indentshiftlast: MML.INDENTSHIFT.INDENTSHIFT, | |
decimalseparator: ".", | |
texprimestyle: false // is it in TeX's C' style? | |
}, | |
autoDefault: function (name) { | |
if (name === "displaystyle") {return this.Get("display") === "block"} | |
return ""; | |
}, | |
linebreakContainer: true, | |
setTeXclass: MML.mbase.setChildTeXclass, | |
getAnnotation: function (name) { | |
if (this.data.length != 1) return null; | |
return this.data[0].getAnnotation(name); | |
} | |
}); | |
MML.chars = MML.mbase.Subclass({ | |
type: "chars", | |
Append: function () {this.data.push.apply(this.data,arguments)}, | |
value: function () {return this.data.join("")}, | |
toString: function () {return this.data.join("")} | |
}); | |
MML.entity = MML.mbase.Subclass({ | |
type: "entity", | |
Append: function () {this.data.push.apply(this.data,arguments)}, | |
value: function () { | |
if (this.data[0].substr(0,2) === "#x") {return parseInt(this.data[0].substr(2),16)} | |
else if (this.data[0].substr(0,1) === "#") {return parseInt(this.data[0].substr(1))} | |
else {return 0} // FIXME: look up named entities from table | |
}, | |
toString: function () { | |
var n = this.value(); | |
if (n <= 0xFFFF) {return String.fromCharCode(n)} | |
n -= 0x10000; | |
return String.fromCharCode((n>>10)+0xD800) | |
+ String.fromCharCode((n&0x3FF)+0xDC00); | |
} | |
}); | |
MML.xml = MML.mbase.Subclass({ | |
type: "xml", | |
Init: function () { | |
this.div = document.createElement("div"); | |
return this.SUPER(arguments).Init.apply(this,arguments); | |
}, | |
Append: function () { | |
for (var i = 0, m = arguments.length; i < m; i++) { | |
var node = this.Import(arguments[i]); | |
this.data.push(node); | |
this.div.appendChild(node); | |
} | |
}, | |
Import: function (node) { | |
if (document.importNode) {return document.importNode(node,true)} | |
// | |
// IE < 9 doesn't have importNode, so fake it. | |
// | |
var nNode, i, m; | |
if (node.nodeType === 1) { // ELEMENT_NODE | |
nNode = document.createElement(node.nodeName); | |
for (i = 0, m = node.attributes.length; i < m; i++) { | |
var attribute = node.attributes[i]; | |
if (attribute.specified && attribute.nodeValue != null && attribute.nodeValue != '') | |
{nNode.setAttribute(attribute.nodeName,attribute.nodeValue)} | |
if (attribute.nodeName === "style") {nNode.style.cssText = attribute.nodeValue} | |
} | |
if (node.className) {nNode.className = node.className} | |
} else if (node.nodeType === 3 || node.nodeType === 4) { // TEXT_NODE or CDATA_SECTION_NODE | |
nNode = document.createTextNode(node.nodeValue); | |
} else if (node.nodeType === 8) { // COMMENT_NODE | |
nNode = document.createComment(node.nodeValue); | |
} else { | |
return document.createTextNode(''); | |
} | |
for (i = 0, m = node.childNodes.length; i < m; i++) | |
{nNode.appendChild(this.Import(node.childNodes[i]))} | |
return nNode; | |
}, | |
value: function () {return this.div}, | |
toString: function () {return this.div.innerHTML} | |
}); | |
MML.TeXAtom = MML.mbase.Subclass({ | |
type: "texatom", | |
linebreakContainer: true, | |
inferRow: true, notParent: true, | |
texClass: MML.TEXCLASS.ORD, | |
Core: MML.mbase.childCore, | |
CoreMO: MML.mbase.childCoreMO, | |
isEmbellished: MML.mbase.childEmbellished, | |
setTeXclass: function (prev) { | |
this.data[0].setTeXclass(); | |
return this.adjustTeXclass(prev); | |
}, | |
adjustTeXclass: MML.mo.prototype.adjustTeXclass | |
}); | |
MML.NULL = MML.mbase().With({type:"null"}); | |
var TEXCLASS = MML.TEXCLASS; | |
var MO = { | |
ORD: [0,0,TEXCLASS.ORD], | |
ORD11: [1,1,TEXCLASS.ORD], | |
ORD21: [2,1,TEXCLASS.ORD], | |
ORD02: [0,2,TEXCLASS.ORD], | |
ORD55: [5,5,TEXCLASS.ORD], | |
OP: [1,2,TEXCLASS.OP,{largeop: true, movablelimits: true, symmetric: true}], | |
OPFIXED: [1,2,TEXCLASS.OP,{largeop: true, movablelimits: true}], | |
INTEGRAL: [0,1,TEXCLASS.OP,{largeop: true, symmetric: true}], | |
INTEGRAL2: [1,2,TEXCLASS.OP,{largeop: true, symmetric: true}], | |
BIN3: [3,3,TEXCLASS.BIN], | |
BIN4: [4,4,TEXCLASS.BIN], | |
BIN01: [0,1,TEXCLASS.BIN], | |
BIN5: [5,5,TEXCLASS.BIN], | |
TALLBIN: [4,4,TEXCLASS.BIN,{stretchy: true}], | |
BINOP: [4,4,TEXCLASS.BIN,{largeop: true, movablelimits: true}], | |
REL: [5,5,TEXCLASS.REL], | |
REL1: [1,1,TEXCLASS.REL,{stretchy: true}], | |
REL4: [4,4,TEXCLASS.REL], | |
RELSTRETCH: [5,5,TEXCLASS.REL,{stretchy: true}], | |
RELACCENT: [5,5,TEXCLASS.REL,{accent: true}], | |
WIDEREL: [5,5,TEXCLASS.REL,{accent: true, stretchy: true}], | |
OPEN: [0,0,TEXCLASS.OPEN,{fence: true, stretchy: true, symmetric: true}], | |
CLOSE: [0,0,TEXCLASS.CLOSE,{fence: true, stretchy: true, symmetric: true}], | |
INNER: [0,0,TEXCLASS.INNER], | |
PUNCT: [0,3,TEXCLASS.PUNCT], | |
ACCENT: [0,0,TEXCLASS.ORD,{accent: true}], | |
WIDEACCENT: [0,0,TEXCLASS.ORD,{accent: true, stretchy: true}] | |
}; | |
MML.mo.Augment({ | |
SPACE: [ | |
'0em', | |
'0.1111em', | |
'0.1667em', | |
'0.2222em', | |
'0.2667em', | |
'0.3333em' | |
], | |
RANGES: [ | |
[0x20,0x7F,TEXCLASS.REL,"BasicLatin"], | |
[0xA0,0xFF,TEXCLASS.ORD,"Latin1Supplement"], | |
[0x100,0x17F,TEXCLASS.ORD], | |
[0x180,0x24F,TEXCLASS.ORD], | |
[0x2B0,0x2FF,TEXCLASS.ORD,"SpacingModLetters"], | |
[0x300,0x36F,TEXCLASS.ORD,"CombDiacritMarks"], | |
[0x370,0x3FF,TEXCLASS.ORD,"GreekAndCoptic"], | |
[0x1E00,0x1EFF,TEXCLASS.ORD], | |
[0x2000,0x206F,TEXCLASS.PUNCT,"GeneralPunctuation"], | |
[0x2070,0x209F,TEXCLASS.ORD], | |
[0x20A0,0x20CF,TEXCLASS.ORD], | |
[0x20D0,0x20FF,TEXCLASS.ORD,"CombDiactForSymbols"], | |
[0x2100,0x214F,TEXCLASS.ORD,"LetterlikeSymbols"], | |
[0x2150,0x218F,TEXCLASS.ORD], | |
[0x2190,0x21FF,TEXCLASS.REL,"Arrows"], | |
[0x2200,0x22FF,TEXCLASS.BIN,"MathOperators"], | |
[0x2300,0x23FF,TEXCLASS.ORD,"MiscTechnical"], | |
[0x2460,0x24FF,TEXCLASS.ORD], | |
[0x2500,0x259F,TEXCLASS.ORD], | |
[0x25A0,0x25FF,TEXCLASS.ORD,"GeometricShapes"], | |
[0x2700,0x27BF,TEXCLASS.ORD,"Dingbats"], | |
[0x27C0,0x27EF,TEXCLASS.ORD,"MiscMathSymbolsA"], | |
[0x27F0,0x27FF,TEXCLASS.REL,"SupplementalArrowsA"], | |
[0x2900,0x297F,TEXCLASS.REL,"SupplementalArrowsB"], | |
[0x2980,0x29FF,TEXCLASS.ORD,"MiscMathSymbolsB"], | |
[0x2A00,0x2AFF,TEXCLASS.BIN,"SuppMathOperators"], | |
[0x2B00,0x2BFF,TEXCLASS.ORD,"MiscSymbolsAndArrows"], | |
[0x1D400,0x1D7FF,TEXCLASS.ORD] | |
], | |
OPTABLE: { | |
prefix: { | |
'\u2200': MO.ORD21, // for all | |
'\u2202': MO.ORD21, // partial differential | |
'\u2203': MO.ORD21, // there exists | |
'\u2207': MO.ORD21, // nabla | |
'\u220F': MO.OP, // n-ary product | |
'\u2210': MO.OP, // n-ary coproduct | |
'\u2211': MO.OP, // n-ary summation | |
'\u2212': MO.BIN01, // minus sign | |
'\u2213': MO.BIN01, // minus-or-plus sign | |
'\u221A': [1,1,TEXCLASS.ORD,{stretchy: true}], // square root | |
'\u2220': MO.ORD, // angle | |
'\u222B': MO.INTEGRAL, // integral | |
'\u222E': MO.INTEGRAL, // contour integral | |
'\u22C0': MO.OP, // n-ary logical and | |
'\u22C1': MO.OP, // n-ary logical or | |
'\u22C2': MO.OP, // n-ary intersection | |
'\u22C3': MO.OP, // n-ary union | |
'\u2308': MO.OPEN, // left ceiling | |
'\u230A': MO.OPEN, // left floor | |
'\u27E8': MO.OPEN, // mathematical left angle bracket | |
'\u27EE': MO.OPEN, // mathematical left flattened parenthesis | |
'\u2A00': MO.OP, // n-ary circled dot operator | |
'\u2A01': MO.OP, // n-ary circled plus operator | |
'\u2A02': MO.OP, // n-ary circled times operator | |
'\u2A04': MO.OP, // n-ary union operator with plus | |
'\u2A06': MO.OP, // n-ary square union operator | |
'\u00AC': MO.ORD21, // not sign | |
'\u00B1': MO.BIN01, // plus-minus sign | |
'(': MO.OPEN, // left parenthesis | |
'+': MO.BIN01, // plus sign | |
'-': MO.BIN01, // hyphen-minus | |
'[': MO.OPEN, // left square bracket | |
'{': MO.OPEN, // left curly bracket | |
'|': MO.OPEN // vertical line | |
}, | |
postfix: { | |
'!': [1,0,TEXCLASS.CLOSE], // exclamation mark | |
'&': MO.ORD, // ampersand | |
'\u2032': MO.ORD02, // prime | |
'\u203E': MO.WIDEACCENT, // overline | |
'\u2309': MO.CLOSE, // right ceiling | |
'\u230B': MO.CLOSE, // right floor | |
'\u23DE': MO.WIDEACCENT, // top curly bracket | |
'\u23DF': MO.WIDEACCENT, // bottom curly bracket | |
'\u266D': MO.ORD02, // music flat sign | |
'\u266E': MO.ORD02, // music natural sign | |
'\u266F': MO.ORD02, // music sharp sign | |
'\u27E9': MO.CLOSE, // mathematical right angle bracket | |
'\u27EF': MO.CLOSE, // mathematical right flattened parenthesis | |
'\u02C6': MO.WIDEACCENT, // modifier letter circumflex accent | |
'\u02C7': MO.WIDEACCENT, // caron | |
'\u02C9': MO.WIDEACCENT, // modifier letter macron | |
'\u02CA': MO.ACCENT, // modifier letter acute accent | |
'\u02CB': MO.ACCENT, // modifier letter grave accent | |
'\u02D8': MO.ACCENT, // breve | |
'\u02D9': MO.ACCENT, // dot above | |
'\u02DC': MO.WIDEACCENT, // small tilde | |
'\u0302': MO.WIDEACCENT, // combining circumflex accent | |
'\u00A8': MO.ACCENT, // diaeresis | |
'\u00AF': MO.WIDEACCENT, // macron | |
')': MO.CLOSE, // right parenthesis | |
']': MO.CLOSE, // right square bracket | |
'^': MO.WIDEACCENT, // circumflex accent | |
'_': MO.WIDEACCENT, // low line | |
'`': MO.ACCENT, // grave accent | |
'|': MO.CLOSE, // vertical line | |
'}': MO.CLOSE, // right curly bracket | |
'~': MO.WIDEACCENT // tilde | |
}, | |
infix: { | |
'': MO.ORD, // empty <mo> | |
'%': [3,3,TEXCLASS.ORD], // percent sign | |
'\u2022': MO.BIN4, // bullet | |
'\u2026': MO.INNER, // horizontal ellipsis | |
'\u2044': MO.TALLBIN, // fraction slash | |
'\u2061': MO.ORD, // function application | |
'\u2062': MO.ORD, // invisible times | |
'\u2063': [0,0,TEXCLASS.ORD,{linebreakstyle:"after", separator: true}], // invisible separator | |
'\u2064': MO.ORD, // invisible plus | |
'\u2190': MO.WIDEREL, // leftwards arrow | |
'\u2191': MO.RELSTRETCH, // upwards arrow | |
'\u2192': MO.WIDEREL, // rightwards arrow | |
'\u2193': MO.RELSTRETCH, // downwards arrow | |
'\u2194': MO.WIDEREL, // left right arrow | |
'\u2195': MO.RELSTRETCH, // up down arrow | |
'\u2196': MO.RELSTRETCH, // north west arrow | |
'\u2197': MO.RELSTRETCH, // north east arrow | |
'\u2198': MO.RELSTRETCH, // south east arrow | |
'\u2199': MO.RELSTRETCH, // south west arrow | |
'\u21A6': MO.WIDEREL, // rightwards arrow from bar | |
'\u21A9': MO.WIDEREL, // leftwards arrow with hook | |
'\u21AA': MO.WIDEREL, // rightwards arrow with hook | |
'\u21BC': MO.WIDEREL, // leftwards harpoon with barb upwards | |
'\u21BD': MO.WIDEREL, // leftwards harpoon with barb downwards | |
'\u21C0': MO.WIDEREL, // rightwards harpoon with barb upwards | |
'\u21C1': MO.WIDEREL, // rightwards harpoon with barb downwards | |
'\u21CC': MO.WIDEREL, // rightwards harpoon over leftwards harpoon | |
'\u21D0': MO.WIDEREL, // leftwards double arrow | |
'\u21D1': MO.RELSTRETCH, // upwards double arrow | |
'\u21D2': MO.WIDEREL, // rightwards double arrow | |
'\u21D3': MO.RELSTRETCH, // downwards double arrow | |
'\u21D4': MO.WIDEREL, // left right double arrow | |
'\u21D5': MO.RELSTRETCH, // up down double arrow | |
'\u2208': MO.REL, // element of | |
'\u2209': MO.REL, // not an element of | |
'\u220B': MO.REL, // contains as member | |
'\u2212': MO.BIN4, // minus sign | |
'\u2213': MO.BIN4, // minus-or-plus sign | |
'\u2215': MO.TALLBIN, // division slash | |
'\u2216': MO.BIN4, // set minus | |
'\u2217': MO.BIN4, // asterisk operator | |
'\u2218': MO.BIN4, // ring operator | |
'\u2219': MO.BIN4, // bullet operator | |
'\u221D': MO.REL, // proportional to | |
'\u2223': MO.REL, // divides | |
'\u2225': MO.REL, // parallel to | |
'\u2227': MO.BIN4, // logical and | |
'\u2228': MO.BIN4, // logical or | |
'\u2229': MO.BIN4, // intersection | |
'\u222A': MO.BIN4, // union | |
'\u223C': MO.REL, // tilde operator | |
'\u2240': MO.BIN4, // wreath product | |
'\u2243': MO.REL, // asymptotically equal to | |
'\u2245': MO.REL, // approximately equal to | |
'\u2248': MO.REL, // almost equal to | |
'\u224D': MO.REL, // equivalent to | |
'\u2250': MO.REL, // approaches the limit | |
'\u2260': MO.REL, // not equal to | |
'\u2261': MO.REL, // identical to | |
'\u2264': MO.REL, // less-than or equal to | |
'\u2265': MO.REL, // greater-than or equal to | |
'\u226A': MO.REL, // much less-than | |
'\u226B': MO.REL, // much greater-than | |
'\u227A': MO.REL, // precedes | |
'\u227B': MO.REL, // succeeds | |
'\u2282': MO.REL, // subset of | |
'\u2283': MO.REL, // superset of | |
'\u2286': MO.REL, // subset of or equal to | |
'\u2287': MO.REL, // superset of or equal to | |
'\u228E': MO.BIN4, // multiset union | |
'\u2291': MO.REL, // square image of or equal to | |
'\u2292': MO.REL, // square original of or equal to | |
'\u2293': MO.BIN4, // square cap | |
'\u2294': MO.BIN4, // square cup | |
'\u2295': MO.BIN4, // circled plus | |
'\u2296': MO.BIN4, // circled minus | |
'\u2297': MO.BIN4, // circled times | |
'\u2298': MO.BIN4, // circled division slash | |
'\u2299': MO.BIN4, // circled dot operator | |
'\u22A2': MO.REL, // right tack | |
'\u22A3': MO.REL, // left tack | |
'\u22A4': MO.ORD55, // down tack | |
'\u22A5': MO.REL, // up tack | |
'\u22A8': MO.REL, // true | |
'\u22C4': MO.BIN4, // diamond operator | |
'\u22C5': MO.BIN4, // dot operator | |
'\u22C6': MO.BIN4, // star operator | |
'\u22C8': MO.REL, // bowtie | |
'\u22EE': MO.ORD55, // vertical ellipsis | |
'\u22EF': MO.INNER, // midline horizontal ellipsis | |
'\u22F1': [5,5,TEXCLASS.INNER], // down right diagonal ellipsis | |
'\u25B3': MO.BIN4, // white up-pointing triangle | |
'\u25B5': MO.BIN4, // white up-pointing small triangle | |
'\u25B9': MO.BIN4, // white right-pointing small triangle | |
'\u25BD': MO.BIN4, // white down-pointing triangle | |
'\u25BF': MO.BIN4, // white down-pointing small triangle | |
'\u25C3': MO.BIN4, // white left-pointing small triangle | |
'\u2758': MO.REL, // light vertical bar | |
'\u27F5': MO.WIDEREL, // long leftwards arrow | |
'\u27F6': MO.WIDEREL, // long rightwards arrow | |
'\u27F7': MO.WIDEREL, // long left right arrow | |
'\u27F8': MO.WIDEREL, // long leftwards double arrow | |
'\u27F9': MO.WIDEREL, // long rightwards double arrow | |
'\u27FA': MO.WIDEREL, // long left right double arrow | |
'\u27FC': MO.WIDEREL, // long rightwards arrow from bar | |
'\u2A2F': MO.BIN4, // vector or cross product | |
'\u2A3F': MO.BIN4, // amalgamation or coproduct | |
'\u2AAF': MO.REL, // precedes above single-line equals sign | |
'\u2AB0': MO.REL, // succeeds above single-line equals sign | |
'\u00B1': MO.BIN4, // plus-minus sign | |
'\u00B7': MO.BIN4, // middle dot | |
'\u00D7': MO.BIN4, // multiplication sign | |
'\u00F7': MO.BIN4, // division sign | |
'*': MO.BIN3, // asterisk | |
'+': MO.BIN4, // plus sign | |
',': [0,3,TEXCLASS.PUNCT,{linebreakstyle:"after", separator: true}], // comma | |
'-': MO.BIN4, // hyphen-minus | |
'.': [3,3,TEXCLASS.ORD], // full stop | |
'/': MO.ORD11, // solidus | |
':': [1,2,TEXCLASS.REL], // colon | |
';': [0,3,TEXCLASS.PUNCT,{linebreakstyle:"after", separator: true}], // semicolon | |
'<': MO.REL, // less-than sign | |
'=': MO.REL, // equals sign | |
'>': MO.REL, // greater-than sign | |
'?': [1,1,TEXCLASS.CLOSE], // question mark | |
'\\': MO.ORD, // reverse solidus | |
'^': MO.ORD11, // circumflex accent | |
'_': MO.ORD11, // low line | |
'|': [2,2,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}], // vertical line | |
'#': MO.ORD, // # | |
'$': MO.ORD, // $ | |
'\u002E': [0,3,TEXCLASS.PUNCT,{separator: true}], // \ldotp | |
'\u02B9': MO.ORD, // prime | |
'\u0300': MO.ACCENT, // \grave | |
'\u0301': MO.ACCENT, // \acute | |
'\u0303': MO.WIDEACCENT, // \tilde | |
'\u0304': MO.ACCENT, // \bar | |
'\u0306': MO.ACCENT, // \breve | |
'\u0307': MO.ACCENT, // \dot | |
'\u0308': MO.ACCENT, // \ddot | |
'\u030C': MO.ACCENT, // \check | |
'\u0332': MO.WIDEACCENT, // horizontal line | |
'\u0338': MO.REL4, // \not | |
'\u2015': [0,0,TEXCLASS.ORD,{stretchy: true}], // horizontal line | |
'\u2017': [0,0,TEXCLASS.ORD,{stretchy: true}], // horizontal line | |
'\u2020': MO.BIN3, // \dagger | |
'\u2021': MO.BIN3, // \ddagger | |
'\u20D7': MO.ACCENT, // \vec | |
'\u2111': MO.ORD, // \Im | |
'\u2113': MO.ORD, // \ell | |
'\u2118': MO.ORD, // \wp | |
'\u211C': MO.ORD, // \Re | |
'\u2205': MO.ORD, // \emptyset | |
'\u221E': MO.ORD, // \infty | |
'\u2305': MO.BIN3, // barwedge | |
'\u2306': MO.BIN3, // doublebarwedge | |
'\u2322': MO.REL4, // \frown | |
'\u2323': MO.REL4, // \smile | |
'\u2329': MO.OPEN, // langle | |
'\u232A': MO.CLOSE, // rangle | |
'\u23AA': MO.ORD, // \bracevert | |
'\u23AF': [0,0,TEXCLASS.ORD,{stretchy: true}], // \underline | |
'\u23B0': MO.OPEN, // \lmoustache | |
'\u23B1': MO.CLOSE, // \rmoustache | |
'\u2500': MO.ORD, // horizontal line | |
'\u25EF': MO.BIN3, // \bigcirc | |
'\u2660': MO.ORD, // \spadesuit | |
'\u2661': MO.ORD, // \heartsuit | |
'\u2662': MO.ORD, // \diamondsuit | |
'\u2663': MO.ORD, // \clubsuit | |
'\u3008': MO.OPEN, // langle | |
'\u3009': MO.CLOSE, // rangle | |
'\uFE37': MO.WIDEACCENT, // horizontal brace down | |
'\uFE38': MO.WIDEACCENT // horizontal brace up | |
} | |
} | |
},{ | |
OPTYPES: MO | |
}); | |
// | |
// These are not in the W3C table, but FF works this way, | |
// and it makes sense, so add it here | |
// | |
var OPTABLE = MML.mo.prototype.OPTABLE; | |
OPTABLE.infix["^"] = MO.WIDEREL; | |
OPTABLE.infix["_"] = MO.WIDEREL; | |
OPTABLE.prefix["\u2223"] = MO.OPEN; | |
OPTABLE.prefix["\u2225"] = MO.OPEN; | |
OPTABLE.postfix["\u2223"] = MO.CLOSE; | |
OPTABLE.postfix["\u2225"] = MO.CLOSE; | |
})(MathJax.ElementJax.mml); | |
MathJax.ElementJax.mml.loadComplete("jax.js"); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/Arrows.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
infix: { | |
'\u219A': MO.RELACCENT, // leftwards arrow with stroke | |
'\u219B': MO.RELACCENT, // rightwards arrow with stroke | |
'\u219C': MO.WIDEREL, // leftwards wave arrow | |
'\u219D': MO.WIDEREL, // rightwards wave arrow | |
'\u219E': MO.WIDEREL, // leftwards two headed arrow | |
'\u219F': MO.WIDEREL, // upwards two headed arrow | |
'\u21A0': MO.WIDEREL, // rightwards two headed arrow | |
'\u21A1': MO.RELSTRETCH, // downwards two headed arrow | |
'\u21A2': MO.WIDEREL, // leftwards arrow with tail | |
'\u21A3': MO.WIDEREL, // rightwards arrow with tail | |
'\u21A4': MO.WIDEREL, // leftwards arrow from bar | |
'\u21A5': MO.RELSTRETCH, // upwards arrow from bar | |
'\u21A7': MO.RELSTRETCH, // downwards arrow from bar | |
'\u21A8': MO.RELSTRETCH, // up down arrow with base | |
'\u21AB': MO.WIDEREL, // leftwards arrow with loop | |
'\u21AC': MO.WIDEREL, // rightwards arrow with loop | |
'\u21AD': MO.WIDEREL, // left right wave arrow | |
'\u21AE': MO.RELACCENT, // left right arrow with stroke | |
'\u21AF': MO.RELSTRETCH, // downwards zigzag arrow | |
'\u21B0': MO.RELSTRETCH, // upwards arrow with tip leftwards | |
'\u21B1': MO.RELSTRETCH, // upwards arrow with tip rightwards | |
'\u21B2': MO.RELSTRETCH, // downwards arrow with tip leftwards | |
'\u21B3': MO.RELSTRETCH, // downwards arrow with tip rightwards | |
'\u21B4': MO.RELSTRETCH, // rightwards arrow with corner downwards | |
'\u21B5': MO.RELSTRETCH, // downwards arrow with corner leftwards | |
'\u21B6': MO.RELACCENT, // anticlockwise top semicircle arrow | |
'\u21B7': MO.RELACCENT, // clockwise top semicircle arrow | |
'\u21B8': MO.REL, // north west arrow to long bar | |
'\u21B9': MO.WIDEREL, // leftwards arrow to bar over rightwards arrow to bar | |
'\u21BA': MO.REL, // anticlockwise open circle arrow | |
'\u21BB': MO.REL, // clockwise open circle arrow | |
'\u21BE': MO.RELSTRETCH, // upwards harpoon with barb rightwards | |
'\u21BF': MO.RELSTRETCH, // upwards harpoon with barb leftwards | |
'\u21C2': MO.RELSTRETCH, // downwards harpoon with barb rightwards | |
'\u21C3': MO.RELSTRETCH, // downwards harpoon with barb leftwards | |
'\u21C4': MO.WIDEREL, // rightwards arrow over leftwards arrow | |
'\u21C5': MO.RELSTRETCH, // upwards arrow leftwards of downwards arrow | |
'\u21C6': MO.WIDEREL, // leftwards arrow over rightwards arrow | |
'\u21C7': MO.WIDEREL, // leftwards paired arrows | |
'\u21C8': MO.RELSTRETCH, // upwards paired arrows | |
'\u21C9': MO.WIDEREL, // rightwards paired arrows | |
'\u21CA': MO.RELSTRETCH, // downwards paired arrows | |
'\u21CB': MO.WIDEREL, // leftwards harpoon over rightwards harpoon | |
'\u21CD': MO.RELACCENT, // leftwards double arrow with stroke | |
'\u21CE': MO.RELACCENT, // left right double arrow with stroke | |
'\u21CF': MO.RELACCENT, // rightwards double arrow with stroke | |
'\u21D6': MO.RELSTRETCH, // north west double arrow | |
'\u21D7': MO.RELSTRETCH, // north east double arrow | |
'\u21D8': MO.RELSTRETCH, // south east double arrow | |
'\u21D9': MO.RELSTRETCH, // south west double arrow | |
'\u21DA': MO.WIDEREL, // leftwards triple arrow | |
'\u21DB': MO.WIDEREL, // rightwards triple arrow | |
'\u21DC': MO.WIDEREL, // leftwards squiggle arrow | |
'\u21DD': MO.WIDEREL, // rightwards squiggle arrow | |
'\u21DE': MO.REL, // upwards arrow with double stroke | |
'\u21DF': MO.REL, // downwards arrow with double stroke | |
'\u21E0': MO.WIDEREL, // leftwards dashed arrow | |
'\u21E1': MO.RELSTRETCH, // upwards dashed arrow | |
'\u21E2': MO.WIDEREL, // rightwards dashed arrow | |
'\u21E3': MO.RELSTRETCH, // downwards dashed arrow | |
'\u21E4': MO.WIDEREL, // leftwards arrow to bar | |
'\u21E5': MO.WIDEREL, // rightwards arrow to bar | |
'\u21E6': MO.WIDEREL, // leftwards white arrow | |
'\u21E7': MO.RELSTRETCH, // upwards white arrow | |
'\u21E8': MO.WIDEREL, // rightwards white arrow | |
'\u21E9': MO.RELSTRETCH, // downwards white arrow | |
'\u21EA': MO.RELSTRETCH, // upwards white arrow from bar | |
'\u21EB': MO.RELSTRETCH, // upwards white arrow on pedestal | |
'\u21EC': MO.RELSTRETCH, // upwards white arrow on pedestal with horizontal bar | |
'\u21ED': MO.RELSTRETCH, // upwards white arrow on pedestal with vertical bar | |
'\u21EE': MO.RELSTRETCH, // upwards white double arrow | |
'\u21EF': MO.RELSTRETCH, // upwards white double arrow on pedestal | |
'\u21F0': MO.WIDEREL, // rightwards white arrow from wall | |
'\u21F1': MO.REL, // north west arrow to corner | |
'\u21F2': MO.REL, // south east arrow to corner | |
'\u21F3': MO.RELSTRETCH, // up down white arrow | |
'\u21F4': MO.RELACCENT, // right arrow with small circle | |
'\u21F5': MO.RELSTRETCH, // downwards arrow leftwards of upwards arrow | |
'\u21F6': MO.WIDEREL, // three rightwards arrows | |
'\u21F7': MO.RELACCENT, // leftwards arrow with vertical stroke | |
'\u21F8': MO.RELACCENT, // rightwards arrow with vertical stroke | |
'\u21F9': MO.RELACCENT, // left right arrow with vertical stroke | |
'\u21FA': MO.RELACCENT, // leftwards arrow with double vertical stroke | |
'\u21FB': MO.RELACCENT, // rightwards arrow with double vertical stroke | |
'\u21FC': MO.RELACCENT, // left right arrow with double vertical stroke | |
'\u21FD': MO.WIDEREL, // leftwards open-headed arrow | |
'\u21FE': MO.WIDEREL, // rightwards open-headed arrow | |
'\u21FF': MO.WIDEREL // left right open-headed arrow | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/Arrows.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/MiscMathSymbolsA.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
prefix: { | |
'\u27E6': MO.OPEN, // mathematical left white square bracket | |
'\u27EA': MO.OPEN, // mathematical left double angle bracket | |
'\u27EC': MO.OPEN // mathematical left white tortoise shell bracket | |
}, | |
postfix: { | |
'\u27E7': MO.CLOSE, // mathematical right white square bracket | |
'\u27EB': MO.CLOSE, // mathematical right double angle bracket | |
'\u27ED': MO.CLOSE // mathematical right white tortoise shell bracket | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/MiscMathSymbolsA.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/Dingbats.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
prefix: { | |
'\u2772': MO.OPEN // light left tortoise shell bracket ornament | |
}, | |
postfix: { | |
'\u2773': MO.CLOSE // light right tortoise shell bracket ornament | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/Dingbats.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/GeneralPunctuation.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
prefix: { | |
'\u2016': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true}], // double vertical line | |
'\u2018': [0,0,TEXCLASS.OPEN,{fence: true}], // left single quotation mark | |
'\u201C': [0,0,TEXCLASS.OPEN,{fence: true}] // left double quotation mark | |
}, | |
postfix: { | |
'\u2016': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true}], // double vertical line | |
'\u2019': [0,0,TEXCLASS.CLOSE,{fence: true}], // right single quotation mark | |
'\u201D': [0,0,TEXCLASS.CLOSE,{fence: true}] // right double quotation mark | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/GeneralPunctuation.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/SpacingModLetters.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
postfix: { | |
'\u02CD': MO.WIDEACCENT, // modifier letter low macron | |
'\u02DA': MO.ACCENT, // ring above | |
'\u02DD': MO.ACCENT, // double acute accent | |
'\u02F7': MO.WIDEACCENT // modifier letter low tilde | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/SpacingModLetters.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/MiscTechnical.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
postfix: { | |
'\u23B4': MO.WIDEACCENT, // top square bracket | |
'\u23B5': MO.WIDEACCENT, // bottom square bracket | |
'\u23DC': MO.WIDEACCENT, // top parenthesis | |
'\u23DD': MO.WIDEACCENT, // bottom parenthesis | |
'\u23E0': MO.WIDEACCENT, // top tortoise shell bracket | |
'\u23E1': MO.WIDEACCENT // bottom tortoise shell bracket | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/MiscTechnical.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/SupplementalArrowsA.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
infix: { | |
'\u27F0': MO.RELSTRETCH, // upwards quadruple arrow | |
'\u27F1': MO.RELSTRETCH, // downwards quadruple arrow | |
'\u27FB': MO.WIDEREL, // long leftwards arrow from bar | |
'\u27FD': MO.WIDEREL, // long leftwards double arrow from bar | |
'\u27FE': MO.WIDEREL, // long rightwards double arrow from bar | |
'\u27FF': MO.WIDEREL // long rightwards squiggle arrow | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/SupplementalArrowsA.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/GreekAndCoptic.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
infix: { | |
'\u03F6': MO.REL // greek reversed lunate epsilon symbol | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/GreekAndCoptic.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/LetterlikeSymbols.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
prefix: { | |
'\u2145': MO.ORD21, // double-struck italic capital d | |
'\u2146': [2,0,TEXCLASS.ORD] // double-struck italic small d | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/LetterlikeSymbols.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/SupplementalArrowsB.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
infix: { | |
'\u2900': MO.RELACCENT, // rightwards two-headed arrow with vertical stroke | |
'\u2901': MO.RELACCENT, // rightwards two-headed arrow with double vertical stroke | |
'\u2902': MO.RELACCENT, // leftwards double arrow with vertical stroke | |
'\u2903': MO.RELACCENT, // rightwards double arrow with vertical stroke | |
'\u2904': MO.RELACCENT, // left right double arrow with vertical stroke | |
'\u2905': MO.RELACCENT, // rightwards two-headed arrow from bar | |
'\u2906': MO.RELACCENT, // leftwards double arrow from bar | |
'\u2907': MO.RELACCENT, // rightwards double arrow from bar | |
'\u2908': MO.REL, // downwards arrow with horizontal stroke | |
'\u2909': MO.REL, // upwards arrow with horizontal stroke | |
'\u290A': MO.RELSTRETCH, // upwards triple arrow | |
'\u290B': MO.RELSTRETCH, // downwards triple arrow | |
'\u290C': MO.WIDEREL, // leftwards double dash arrow | |
'\u290D': MO.WIDEREL, // rightwards double dash arrow | |
'\u290E': MO.WIDEREL, // leftwards triple dash arrow | |
'\u290F': MO.WIDEREL, // rightwards triple dash arrow | |
'\u2910': MO.WIDEREL, // rightwards two-headed triple dash arrow | |
'\u2911': MO.RELACCENT, // rightwards arrow with dotted stem | |
'\u2912': MO.RELSTRETCH, // upwards arrow to bar | |
'\u2913': MO.RELSTRETCH, // downwards arrow to bar | |
'\u2914': MO.RELACCENT, // rightwards arrow with tail with vertical stroke | |
'\u2915': MO.RELACCENT, // rightwards arrow with tail with double vertical stroke | |
'\u2916': MO.RELACCENT, // rightwards two-headed arrow with tail | |
'\u2917': MO.RELACCENT, // rightwards two-headed arrow with tail with vertical stroke | |
'\u2918': MO.RELACCENT, // rightwards two-headed arrow with tail with double vertical stroke | |
'\u2919': MO.RELACCENT, // leftwards arrow-tail | |
'\u291A': MO.RELACCENT, // rightwards arrow-tail | |
'\u291B': MO.RELACCENT, // leftwards double arrow-tail | |
'\u291C': MO.RELACCENT, // rightwards double arrow-tail | |
'\u291D': MO.RELACCENT, // leftwards arrow to black diamond | |
'\u291E': MO.RELACCENT, // rightwards arrow to black diamond | |
'\u291F': MO.RELACCENT, // leftwards arrow from bar to black diamond | |
'\u2920': MO.RELACCENT, // rightwards arrow from bar to black diamond | |
'\u2921': MO.RELSTRETCH, // north west and south east arrow | |
'\u2922': MO.RELSTRETCH, // north east and south west arrow | |
'\u2923': MO.REL, // north west arrow with hook | |
'\u2924': MO.REL, // north east arrow with hook | |
'\u2925': MO.REL, // south east arrow with hook | |
'\u2926': MO.REL, // south west arrow with hook | |
'\u2927': MO.REL, // north west arrow and north east arrow | |
'\u2928': MO.REL, // north east arrow and south east arrow | |
'\u2929': MO.REL, // south east arrow and south west arrow | |
'\u292A': MO.REL, // south west arrow and north west arrow | |
'\u292B': MO.REL, // rising diagonal crossing falling diagonal | |
'\u292C': MO.REL, // falling diagonal crossing rising diagonal | |
'\u292D': MO.REL, // south east arrow crossing north east arrow | |
'\u292E': MO.REL, // north east arrow crossing south east arrow | |
'\u292F': MO.REL, // falling diagonal crossing north east arrow | |
'\u2930': MO.REL, // rising diagonal crossing south east arrow | |
'\u2931': MO.REL, // north east arrow crossing north west arrow | |
'\u2932': MO.REL, // north west arrow crossing north east arrow | |
'\u2933': MO.RELACCENT, // wave arrow pointing directly right | |
'\u2934': MO.REL, // arrow pointing rightwards then curving upwards | |
'\u2935': MO.REL, // arrow pointing rightwards then curving downwards | |
'\u2936': MO.REL, // arrow pointing downwards then curving leftwards | |
'\u2937': MO.REL, // arrow pointing downwards then curving rightwards | |
'\u2938': MO.REL, // right-side arc clockwise arrow | |
'\u2939': MO.REL, // left-side arc anticlockwise arrow | |
'\u293A': MO.RELACCENT, // top arc anticlockwise arrow | |
'\u293B': MO.RELACCENT, // bottom arc anticlockwise arrow | |
'\u293C': MO.RELACCENT, // top arc clockwise arrow with minus | |
'\u293D': MO.RELACCENT, // top arc anticlockwise arrow with plus | |
'\u293E': MO.REL, // lower right semicircular clockwise arrow | |
'\u293F': MO.REL, // lower left semicircular anticlockwise arrow | |
'\u2940': MO.REL, // anticlockwise closed circle arrow | |
'\u2941': MO.REL, // clockwise closed circle arrow | |
'\u2942': MO.RELACCENT, // rightwards arrow above short leftwards arrow | |
'\u2943': MO.RELACCENT, // leftwards arrow above short rightwards arrow | |
'\u2944': MO.RELACCENT, // short rightwards arrow above leftwards arrow | |
'\u2945': MO.RELACCENT, // rightwards arrow with plus below | |
'\u2946': MO.RELACCENT, // leftwards arrow with plus below | |
'\u2947': MO.RELACCENT, // rightwards arrow through x | |
'\u2948': MO.RELACCENT, // left right arrow through small circle | |
'\u2949': MO.REL, // upwards two-headed arrow from small circle | |
'\u294A': MO.RELACCENT, // left barb up right barb down harpoon | |
'\u294B': MO.RELACCENT, // left barb down right barb up harpoon | |
'\u294C': MO.REL, // up barb right down barb left harpoon | |
'\u294D': MO.REL, // up barb left down barb right harpoon | |
'\u294E': MO.WIDEREL, // left barb up right barb up harpoon | |
'\u294F': MO.RELSTRETCH, // up barb right down barb right harpoon | |
'\u2950': MO.WIDEREL, // left barb down right barb down harpoon | |
'\u2951': MO.RELSTRETCH, // up barb left down barb left harpoon | |
'\u2952': MO.WIDEREL, // leftwards harpoon with barb up to bar | |
'\u2953': MO.WIDEREL, // rightwards harpoon with barb up to bar | |
'\u2954': MO.RELSTRETCH, // upwards harpoon with barb right to bar | |
'\u2955': MO.RELSTRETCH, // downwards harpoon with barb right to bar | |
'\u2956': MO.RELSTRETCH, // leftwards harpoon with barb down to bar | |
'\u2957': MO.RELSTRETCH, // rightwards harpoon with barb down to bar | |
'\u2958': MO.RELSTRETCH, // upwards harpoon with barb left to bar | |
'\u2959': MO.RELSTRETCH, // downwards harpoon with barb left to bar | |
'\u295A': MO.WIDEREL, // leftwards harpoon with barb up from bar | |
'\u295B': MO.WIDEREL, // rightwards harpoon with barb up from bar | |
'\u295C': MO.RELSTRETCH, // upwards harpoon with barb right from bar | |
'\u295D': MO.RELSTRETCH, // downwards harpoon with barb right from bar | |
'\u295E': MO.WIDEREL, // leftwards harpoon with barb down from bar | |
'\u295F': MO.WIDEREL, // rightwards harpoon with barb down from bar | |
'\u2960': MO.RELSTRETCH, // upwards harpoon with barb left from bar | |
'\u2961': MO.RELSTRETCH, // downwards harpoon with barb left from bar | |
'\u2962': MO.RELACCENT, // leftwards harpoon with barb up above leftwards harpoon with barb down | |
'\u2963': MO.REL, // upwards harpoon with barb left beside upwards harpoon with barb right | |
'\u2964': MO.RELACCENT, // rightwards harpoon with barb up above rightwards harpoon with barb down | |
'\u2965': MO.REL, // downwards harpoon with barb left beside downwards harpoon with barb right | |
'\u2966': MO.RELACCENT, // leftwards harpoon with barb up above rightwards harpoon with barb up | |
'\u2967': MO.RELACCENT, // leftwards harpoon with barb down above rightwards harpoon with barb down | |
'\u2968': MO.RELACCENT, // rightwards harpoon with barb up above leftwards harpoon with barb up | |
'\u2969': MO.RELACCENT, // rightwards harpoon with barb down above leftwards harpoon with barb down | |
'\u296A': MO.RELACCENT, // leftwards harpoon with barb up above long dash | |
'\u296B': MO.RELACCENT, // leftwards harpoon with barb down below long dash | |
'\u296C': MO.RELACCENT, // rightwards harpoon with barb up above long dash | |
'\u296D': MO.RELACCENT, // rightwards harpoon with barb down below long dash | |
'\u296E': MO.RELSTRETCH, // upwards harpoon with barb left beside downwards harpoon with barb right | |
'\u296F': MO.RELSTRETCH, // downwards harpoon with barb left beside upwards harpoon with barb right | |
'\u2970': MO.RELACCENT, // right double arrow with rounded head | |
'\u2971': MO.RELACCENT, // equals sign above rightwards arrow | |
'\u2972': MO.RELACCENT, // tilde operator above rightwards arrow | |
'\u2973': MO.RELACCENT, // leftwards arrow above tilde operator | |
'\u2974': MO.RELACCENT, // rightwards arrow above tilde operator | |
'\u2975': MO.RELACCENT, // rightwards arrow above almost equal to | |
'\u2976': MO.RELACCENT, // less-than above leftwards arrow | |
'\u2977': MO.RELACCENT, // leftwards arrow through less-than | |
'\u2978': MO.RELACCENT, // greater-than above rightwards arrow | |
'\u2979': MO.RELACCENT, // subset above rightwards arrow | |
'\u297A': MO.RELACCENT, // leftwards arrow through subset | |
'\u297B': MO.RELACCENT, // superset above leftwards arrow | |
'\u297C': MO.RELACCENT, // left fish tail | |
'\u297D': MO.RELACCENT, // right fish tail | |
'\u297E': MO.REL, // up fish tail | |
'\u297F': MO.REL // down fish tail | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/SupplementalArrowsB.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/BasicLatin.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
prefix: { | |
'||': [0,0,TEXCLASS.BIN,{fence: true, stretchy: true, symmetric: true}], // multiple character operator: || | |
'|||': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}] // multiple character operator: ||| | |
}, | |
postfix: { | |
'!!': [1,0,TEXCLASS.BIN], // multiple character operator: !! | |
'\'': MO.ACCENT, // apostrophe | |
'++': [0,0,TEXCLASS.BIN], // multiple character operator: ++ | |
'--': [0,0,TEXCLASS.BIN], // multiple character operator: -- | |
'..': [0,0,TEXCLASS.BIN], // multiple character operator: .. | |
'...': MO.ORD, // multiple character operator: ... | |
'||': [0,0,TEXCLASS.BIN,{fence: true, stretchy: true, symmetric: true}], // multiple character operator: || | |
'|||': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}] // multiple character operator: ||| | |
}, | |
infix: { | |
'!=': MO.BIN4, // multiple character operator: != | |
'&&': MO.BIN4, // multiple character operator: && | |
'**': [1,1,TEXCLASS.BIN], // multiple character operator: ** | |
'*=': MO.BIN4, // multiple character operator: *= | |
'+=': MO.BIN4, // multiple character operator: += | |
'-=': MO.BIN4, // multiple character operator: -= | |
'->': MO.BIN5, // multiple character operator: -> | |
'//': [1,1,TEXCLASS.BIN], // multiple character operator: // | |
'/=': MO.BIN4, // multiple character operator: /= | |
':=': MO.BIN4, // multiple character operator: := | |
'<=': MO.BIN5, // multiple character operator: <= | |
'<>': [1,1,TEXCLASS.BIN], // multiple character operator: <> | |
'==': MO.BIN4, // multiple character operator: == | |
'>=': MO.BIN5, // multiple character operator: >= | |
'@': MO.ORD11, // commercial at | |
'||': [2,2,TEXCLASS.BIN,{fence: true, stretchy: true, symmetric: true}], // multiple character operator: || | |
'|||': [2,2,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}] // multiple character operator: ||| | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/BasicLatin.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/MiscSymbolsAndArrows.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
infix: { | |
'\u2B45': MO.RELSTRETCH, // leftwards quadruple arrow | |
'\u2B46': MO.RELSTRETCH // rightwards quadruple arrow | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/MiscSymbolsAndArrows.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/CombDiacritMarks.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
postfix: { | |
'\u0311': MO.ACCENT // combining inverted breve | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/CombDiacritMarks.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/GeometricShapes.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
infix: { | |
'\u25A0': MO.BIN3, // black square | |
'\u25A1': MO.BIN3, // white square | |
'\u25AA': MO.BIN3, // black small square | |
'\u25AB': MO.BIN3, // white small square | |
'\u25AD': MO.BIN3, // white rectangle | |
'\u25AE': MO.BIN3, // black vertical rectangle | |
'\u25AF': MO.BIN3, // white vertical rectangle | |
'\u25B0': MO.BIN3, // black parallelogram | |
'\u25B1': MO.BIN3, // white parallelogram | |
'\u25B2': MO.BIN4, // black up-pointing triangle | |
'\u25B4': MO.BIN4, // black up-pointing small triangle | |
'\u25B6': MO.BIN4, // black right-pointing triangle | |
'\u25B7': MO.BIN4, // white right-pointing triangle | |
'\u25B8': MO.BIN4, // black right-pointing small triangle | |
'\u25BC': MO.BIN4, // black down-pointing triangle | |
'\u25BE': MO.BIN4, // black down-pointing small triangle | |
'\u25C0': MO.BIN4, // black left-pointing triangle | |
'\u25C1': MO.BIN4, // white left-pointing triangle | |
'\u25C2': MO.BIN4, // black left-pointing small triangle | |
'\u25C4': MO.BIN4, // black left-pointing pointer | |
'\u25C5': MO.BIN4, // white left-pointing pointer | |
'\u25C6': MO.BIN4, // black diamond | |
'\u25C7': MO.BIN4, // white diamond | |
'\u25C8': MO.BIN4, // white diamond containing black small diamond | |
'\u25C9': MO.BIN4, // fisheye | |
'\u25CC': MO.BIN4, // dotted circle | |
'\u25CD': MO.BIN4, // circle with vertical fill | |
'\u25CE': MO.BIN4, // bullseye | |
'\u25CF': MO.BIN4, // black circle | |
'\u25D6': MO.BIN4, // left half black circle | |
'\u25D7': MO.BIN4, // right half black circle | |
'\u25E6': MO.BIN4 // white bullet | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/GeometricShapes.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/MathOperators.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
prefix: { | |
'\u2204': MO.ORD21, // there does not exist | |
'\u221B': MO.ORD11, // cube root | |
'\u221C': MO.ORD11, // fourth root | |
'\u2221': MO.ORD, // measured angle | |
'\u2222': MO.ORD, // spherical angle | |
'\u222C': MO.INTEGRAL, // double integral | |
'\u222D': MO.INTEGRAL, // triple integral | |
'\u222F': MO.INTEGRAL, // surface integral | |
'\u2230': MO.INTEGRAL, // volume integral | |
'\u2231': MO.INTEGRAL, // clockwise integral | |
'\u2232': MO.INTEGRAL, // clockwise contour integral | |
'\u2233': MO.INTEGRAL // anticlockwise contour integral | |
}, | |
infix: { | |
'\u2201': [1,2,TEXCLASS.ORD], // complement | |
'\u2206': MO.BIN3, // increment | |
'\u220A': MO.REL, // small element of | |
'\u220C': MO.REL, // does not contain as member | |
'\u220D': MO.REL, // small contains as member | |
'\u220E': MO.BIN3, // end of proof | |
'\u2214': MO.BIN4, // dot plus | |
'\u221F': MO.REL, // right angle | |
'\u2224': MO.REL, // does not divide | |
'\u2226': MO.REL, // not parallel to | |
'\u2234': MO.REL, // therefore | |
'\u2235': MO.REL, // because | |
'\u2236': MO.REL, // ratio | |
'\u2237': MO.REL, // proportion | |
'\u2238': MO.BIN4, // dot minus | |
'\u2239': MO.REL, // excess | |
'\u223A': MO.BIN4, // geometric proportion | |
'\u223B': MO.REL, // homothetic | |
'\u223D': MO.REL, // reversed tilde | |
'\u223D\u0331': MO.BIN3, // reversed tilde with underline | |
'\u223E': MO.REL, // inverted lazy s | |
'\u223F': MO.BIN3, // sine wave | |
'\u2241': MO.REL, // not tilde | |
'\u2242': MO.REL, // minus tilde | |
'\u2242\u0338': MO.REL, // minus tilde with slash | |
'\u2244': MO.REL, // not asymptotically equal to | |
'\u2246': MO.REL, // approximately but not actually equal to | |
'\u2247': MO.REL, // neither approximately nor actually equal to | |
'\u2249': MO.REL, // not almost equal to | |
'\u224A': MO.REL, // almost equal or equal to | |
'\u224B': MO.REL, // triple tilde | |
'\u224C': MO.REL, // all equal to | |
'\u224E': MO.REL, // geometrically equivalent to | |
'\u224E\u0338': MO.REL, // geometrically equivalent to with slash | |
'\u224F': MO.REL, // difference between | |
'\u224F\u0338': MO.REL, // difference between with slash | |
'\u2251': MO.REL, // geometrically equal to | |
'\u2252': MO.REL, // approximately equal to or the image of | |
'\u2253': MO.REL, // image of or approximately equal to | |
'\u2254': MO.REL, // colon equals | |
'\u2255': MO.REL, // equals colon | |
'\u2256': MO.REL, // ring in equal to | |
'\u2257': MO.REL, // ring equal to | |
'\u2258': MO.REL, // corresponds to | |
'\u2259': MO.REL, // estimates | |
'\u225A': MO.REL, // equiangular to | |
'\u225C': MO.REL, // delta equal to | |
'\u225D': MO.REL, // equal to by definition | |
'\u225E': MO.REL, // measured by | |
'\u225F': MO.REL, // questioned equal to | |
'\u2262': MO.REL, // not identical to | |
'\u2263': MO.REL, // strictly equivalent to | |
'\u2266': MO.REL, // less-than over equal to | |
'\u2266\u0338': MO.REL, // less-than over equal to with slash | |
'\u2267': MO.REL, // greater-than over equal to | |
'\u2268': MO.REL, // less-than but not equal to | |
'\u2269': MO.REL, // greater-than but not equal to | |
'\u226A\u0338': MO.REL, // much less than with slash | |
'\u226B\u0338': MO.REL, // much greater than with slash | |
'\u226C': MO.REL, // between | |
'\u226D': MO.REL, // not equivalent to | |
'\u226E': MO.REL, // not less-than | |
'\u226F': MO.REL, // not greater-than | |
'\u2270': MO.REL, // neither less-than nor equal to | |
'\u2271': MO.REL, // neither greater-than nor equal to | |
'\u2272': MO.REL, // less-than or equivalent to | |
'\u2273': MO.REL, // greater-than or equivalent to | |
'\u2274': MO.REL, // neither less-than nor equivalent to | |
'\u2275': MO.REL, // neither greater-than nor equivalent to | |
'\u2276': MO.REL, // less-than or greater-than | |
'\u2277': MO.REL, // greater-than or less-than | |
'\u2278': MO.REL, // neither less-than nor greater-than | |
'\u2279': MO.REL, // neither greater-than nor less-than | |
'\u227C': MO.REL, // precedes or equal to | |
'\u227D': MO.REL, // succeeds or equal to | |
'\u227E': MO.REL, // precedes or equivalent to | |
'\u227F': MO.REL, // succeeds or equivalent to | |
'\u227F\u0338': MO.REL, // succeeds or equivalent to with slash | |
'\u2280': MO.REL, // does not precede | |
'\u2281': MO.REL, // does not succeed | |
'\u2282\u20D2': MO.REL, // subset of with vertical line | |
'\u2283\u20D2': MO.REL, // superset of with vertical line | |
'\u2284': MO.REL, // not a subset of | |
'\u2285': MO.REL, // not a superset of | |
'\u2288': MO.REL, // neither a subset of nor equal to | |
'\u2289': MO.REL, // neither a superset of nor equal to | |
'\u228A': MO.REL, // subset of with not equal to | |
'\u228B': MO.REL, // superset of with not equal to | |
'\u228C': MO.BIN4, // multiset | |
'\u228D': MO.BIN4, // multiset multiplication | |
'\u228F': MO.REL, // square image of | |
'\u228F\u0338': MO.REL, // square image of with slash | |
'\u2290': MO.REL, // square original of | |
'\u2290\u0338': MO.REL, // square original of with slash | |
'\u229A': MO.BIN4, // circled ring operator | |
'\u229B': MO.BIN4, // circled asterisk operator | |
'\u229C': MO.BIN4, // circled equals | |
'\u229D': MO.BIN4, // circled dash | |
'\u229E': MO.BIN4, // squared plus | |
'\u229F': MO.BIN4, // squared minus | |
'\u22A0': MO.BIN4, // squared times | |
'\u22A1': MO.BIN4, // squared dot operator | |
'\u22A6': MO.REL, // assertion | |
'\u22A7': MO.REL, // models | |
'\u22A9': MO.REL, // forces | |
'\u22AA': MO.REL, // triple vertical bar right turnstile | |
'\u22AB': MO.REL, // double vertical bar double right turnstile | |
'\u22AC': MO.REL, // does not prove | |
'\u22AD': MO.REL, // not true | |
'\u22AE': MO.REL, // does not force | |
'\u22AF': MO.REL, // negated double vertical bar double right turnstile | |
'\u22B0': MO.REL, // precedes under relation | |
'\u22B1': MO.REL, // succeeds under relation | |
'\u22B2': MO.REL, // normal subgroup of | |
'\u22B3': MO.REL, // contains as normal subgroup | |
'\u22B4': MO.REL, // normal subgroup of or equal to | |
'\u22B5': MO.REL, // contains as normal subgroup or equal to | |
'\u22B6': MO.REL, // original of | |
'\u22B7': MO.REL, // image of | |
'\u22B8': MO.REL, // multimap | |
'\u22B9': MO.REL, // hermitian conjugate matrix | |
'\u22BA': MO.BIN4, // intercalate | |
'\u22BB': MO.BIN4, // xor | |
'\u22BC': MO.BIN4, // nand | |
'\u22BD': MO.BIN4, // nor | |
'\u22BE': MO.BIN3, // right angle with arc | |
'\u22BF': MO.BIN3, // right triangle | |
'\u22C7': MO.BIN4, // division times | |
'\u22C9': MO.BIN4, // left normal factor semidirect product | |
'\u22CA': MO.BIN4, // right normal factor semidirect product | |
'\u22CB': MO.BIN4, // left semidirect product | |
'\u22CC': MO.BIN4, // right semidirect product | |
'\u22CD': MO.REL, // reversed tilde equals | |
'\u22CE': MO.BIN4, // curly logical or | |
'\u22CF': MO.BIN4, // curly logical and | |
'\u22D0': MO.REL, // double subset | |
'\u22D1': MO.REL, // double superset | |
'\u22D2': MO.BIN4, // double intersection | |
'\u22D3': MO.BIN4, // double union | |
'\u22D4': MO.REL, // pitchfork | |
'\u22D5': MO.REL, // equal and parallel to | |
'\u22D6': MO.REL, // less-than with dot | |
'\u22D7': MO.REL, // greater-than with dot | |
'\u22D8': MO.REL, // very much less-than | |
'\u22D9': MO.REL, // very much greater-than | |
'\u22DA': MO.REL, // less-than equal to or greater-than | |
'\u22DB': MO.REL, // greater-than equal to or less-than | |
'\u22DC': MO.REL, // equal to or less-than | |
'\u22DD': MO.REL, // equal to or greater-than | |
'\u22DE': MO.REL, // equal to or precedes | |
'\u22DF': MO.REL, // equal to or succeeds | |
'\u22E0': MO.REL, // does not precede or equal | |
'\u22E1': MO.REL, // does not succeed or equal | |
'\u22E2': MO.REL, // not square image of or equal to | |
'\u22E3': MO.REL, // not square original of or equal to | |
'\u22E4': MO.REL, // square image of or not equal to | |
'\u22E5': MO.REL, // square original of or not equal to | |
'\u22E6': MO.REL, // less-than but not equivalent to | |
'\u22E7': MO.REL, // greater-than but not equivalent to | |
'\u22E8': MO.REL, // precedes but not equivalent to | |
'\u22E9': MO.REL, // succeeds but not equivalent to | |
'\u22EA': MO.REL, // not normal subgroup of | |
'\u22EB': MO.REL, // does not contain as normal subgroup | |
'\u22EC': MO.REL, // not normal subgroup of or equal to | |
'\u22ED': MO.REL, // does not contain as normal subgroup or equal | |
'\u22F0': MO.REL, // up right diagonal ellipsis | |
'\u22F2': MO.REL, // element of with long horizontal stroke | |
'\u22F3': MO.REL, // element of with vertical bar at end of horizontal stroke | |
'\u22F4': MO.REL, // small element of with vertical bar at end of horizontal stroke | |
'\u22F5': MO.REL, // element of with dot above | |
'\u22F6': MO.REL, // element of with overbar | |
'\u22F7': MO.REL, // small element of with overbar | |
'\u22F8': MO.REL, // element of with underbar | |
'\u22F9': MO.REL, // element of with two horizontal strokes | |
'\u22FA': MO.REL, // contains with long horizontal stroke | |
'\u22FB': MO.REL, // contains with vertical bar at end of horizontal stroke | |
'\u22FC': MO.REL, // small contains with vertical bar at end of horizontal stroke | |
'\u22FD': MO.REL, // contains with overbar | |
'\u22FE': MO.REL, // small contains with overbar | |
'\u22FF': MO.REL // z notation bag membership | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/MathOperators.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/MiscMathSymbolsB.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
prefix: { | |
'\u2980': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true}], // triple vertical bar delimiter | |
'\u2983': MO.OPEN, // left white curly bracket | |
'\u2985': MO.OPEN, // left white parenthesis | |
'\u2987': MO.OPEN, // z notation left image bracket | |
'\u2989': MO.OPEN, // z notation left binding bracket | |
'\u298B': MO.OPEN, // left square bracket with underbar | |
'\u298D': MO.OPEN, // left square bracket with tick in top corner | |
'\u298F': MO.OPEN, // left square bracket with tick in bottom corner | |
'\u2991': MO.OPEN, // left angle bracket with dot | |
'\u2993': MO.OPEN, // left arc less-than bracket | |
'\u2995': MO.OPEN, // double left arc greater-than bracket | |
'\u2997': MO.OPEN, // left black tortoise shell bracket | |
'\u29FC': MO.OPEN // left-pointing curved angle bracket | |
}, | |
postfix: { | |
'\u2980': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true}], // triple vertical bar delimiter | |
'\u2984': MO.CLOSE, // right white curly bracket | |
'\u2986': MO.CLOSE, // right white parenthesis | |
'\u2988': MO.CLOSE, // z notation right image bracket | |
'\u298A': MO.CLOSE, // z notation right binding bracket | |
'\u298C': MO.CLOSE, // right square bracket with underbar | |
'\u298E': MO.CLOSE, // right square bracket with tick in bottom corner | |
'\u2990': MO.CLOSE, // right square bracket with tick in top corner | |
'\u2992': MO.CLOSE, // right angle bracket with dot | |
'\u2994': MO.CLOSE, // right arc greater-than bracket | |
'\u2996': MO.CLOSE, // double right arc less-than bracket | |
'\u2998': MO.CLOSE, // right black tortoise shell bracket | |
'\u29FD': MO.CLOSE // right-pointing curved angle bracket | |
}, | |
infix: { | |
'\u2981': MO.BIN3, // z notation spot | |
'\u2982': MO.BIN3, // z notation type colon | |
'\u2999': MO.BIN3, // dotted fence | |
'\u299A': MO.BIN3, // vertical zigzag line | |
'\u299B': MO.BIN3, // measured angle opening left | |
'\u299C': MO.BIN3, // right angle variant with square | |
'\u299D': MO.BIN3, // measured right angle with dot | |
'\u299E': MO.BIN3, // angle with s inside | |
'\u299F': MO.BIN3, // acute angle | |
'\u29A0': MO.BIN3, // spherical angle opening left | |
'\u29A1': MO.BIN3, // spherical angle opening up | |
'\u29A2': MO.BIN3, // turned angle | |
'\u29A3': MO.BIN3, // reversed angle | |
'\u29A4': MO.BIN3, // angle with underbar | |
'\u29A5': MO.BIN3, // reversed angle with underbar | |
'\u29A6': MO.BIN3, // oblique angle opening up | |
'\u29A7': MO.BIN3, // oblique angle opening down | |
'\u29A8': MO.BIN3, // measured angle with open arm ending in arrow pointing up and right | |
'\u29A9': MO.BIN3, // measured angle with open arm ending in arrow pointing up and left | |
'\u29AA': MO.BIN3, // measured angle with open arm ending in arrow pointing down and right | |
'\u29AB': MO.BIN3, // measured angle with open arm ending in arrow pointing down and left | |
'\u29AC': MO.BIN3, // measured angle with open arm ending in arrow pointing right and up | |
'\u29AD': MO.BIN3, // measured angle with open arm ending in arrow pointing left and up | |
'\u29AE': MO.BIN3, // measured angle with open arm ending in arrow pointing right and down | |
'\u29AF': MO.BIN3, // measured angle with open arm ending in arrow pointing left and down | |
'\u29B0': MO.BIN3, // reversed empty set | |
'\u29B1': MO.BIN3, // empty set with overbar | |
'\u29B2': MO.BIN3, // empty set with small circle above | |
'\u29B3': MO.BIN3, // empty set with right arrow above | |
'\u29B4': MO.BIN3, // empty set with left arrow above | |
'\u29B5': MO.BIN3, // circle with horizontal bar | |
'\u29B6': MO.BIN4, // circled vertical bar | |
'\u29B7': MO.BIN4, // circled parallel | |
'\u29B8': MO.BIN4, // circled reverse solidus | |
'\u29B9': MO.BIN4, // circled perpendicular | |
'\u29BA': MO.BIN4, // circle divided by horizontal bar and top half divided by vertical bar | |
'\u29BB': MO.BIN4, // circle with superimposed x | |
'\u29BC': MO.BIN4, // circled anticlockwise-rotated division sign | |
'\u29BD': MO.BIN4, // up arrow through circle | |
'\u29BE': MO.BIN4, // circled white bullet | |
'\u29BF': MO.BIN4, // circled bullet | |
'\u29C0': MO.REL, // circled less-than | |
'\u29C1': MO.REL, // circled greater-than | |
'\u29C2': MO.BIN3, // circle with small circle to the right | |
'\u29C3': MO.BIN3, // circle with two horizontal strokes to the right | |
'\u29C4': MO.BIN4, // squared rising diagonal slash | |
'\u29C5': MO.BIN4, // squared falling diagonal slash | |
'\u29C6': MO.BIN4, // squared asterisk | |
'\u29C7': MO.BIN4, // squared small circle | |
'\u29C8': MO.BIN4, // squared square | |
'\u29C9': MO.BIN3, // two joined squares | |
'\u29CA': MO.BIN3, // triangle with dot above | |
'\u29CB': MO.BIN3, // triangle with underbar | |
'\u29CC': MO.BIN3, // s in triangle | |
'\u29CD': MO.BIN3, // triangle with serifs at bottom | |
'\u29CE': MO.REL, // right triangle above left triangle | |
'\u29CF': MO.REL, // left triangle beside vertical bar | |
'\u29CF\u0338': MO.REL, // left triangle beside vertical bar with slash | |
'\u29D0': MO.REL, // vertical bar beside right triangle | |
'\u29D0\u0338': MO.REL, // vertical bar beside right triangle with slash | |
'\u29D1': MO.REL, // bowtie with left half black | |
'\u29D2': MO.REL, // bowtie with right half black | |
'\u29D3': MO.REL, // black bowtie | |
'\u29D4': MO.REL, // times with left half black | |
'\u29D5': MO.REL, // times with right half black | |
'\u29D6': MO.BIN4, // white hourglass | |
'\u29D7': MO.BIN4, // black hourglass | |
'\u29D8': MO.BIN3, // left wiggly fence | |
'\u29D9': MO.BIN3, // right wiggly fence | |
'\u29DB': MO.BIN3, // right double wiggly fence | |
'\u29DC': MO.BIN3, // incomplete infinity | |
'\u29DD': MO.BIN3, // tie over infinity | |
'\u29DE': MO.REL, // infinity negated with vertical bar | |
'\u29DF': MO.BIN3, // double-ended multimap | |
'\u29E0': MO.BIN3, // square with contoured outline | |
'\u29E1': MO.REL, // increases as | |
'\u29E2': MO.BIN4, // shuffle product | |
'\u29E3': MO.REL, // equals sign and slanted parallel | |
'\u29E4': MO.REL, // equals sign and slanted parallel with tilde above | |
'\u29E5': MO.REL, // identical to and slanted parallel | |
'\u29E6': MO.REL, // gleich stark | |
'\u29E7': MO.BIN3, // thermodynamic | |
'\u29E8': MO.BIN3, // down-pointing triangle with left half black | |
'\u29E9': MO.BIN3, // down-pointing triangle with right half black | |
'\u29EA': MO.BIN3, // black diamond with down arrow | |
'\u29EB': MO.BIN3, // black lozenge | |
'\u29EC': MO.BIN3, // white circle with down arrow | |
'\u29ED': MO.BIN3, // black circle with down arrow | |
'\u29EE': MO.BIN3, // error-barred white square | |
'\u29EF': MO.BIN3, // error-barred black square | |
'\u29F0': MO.BIN3, // error-barred white diamond | |
'\u29F1': MO.BIN3, // error-barred black diamond | |
'\u29F2': MO.BIN3, // error-barred white circle | |
'\u29F3': MO.BIN3, // error-barred black circle | |
'\u29F4': MO.REL, // rule-delayed | |
'\u29F5': MO.BIN4, // reverse solidus operator | |
'\u29F6': MO.BIN4, // solidus with overbar | |
'\u29F7': MO.BIN4, // reverse solidus with horizontal stroke | |
'\u29F8': MO.BIN3, // big solidus | |
'\u29F9': MO.BIN3, // big reverse solidus | |
'\u29FA': MO.BIN3, // double plus | |
'\u29FB': MO.BIN3, // triple plus | |
'\u29FE': MO.BIN4, // tiny | |
'\u29FF': MO.BIN4 // miny | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/MiscMathSymbolsB.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/SuppMathOperators.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
prefix: { | |
'\u2A03': MO.OP, // n-ary union operator with dot | |
'\u2A05': MO.OP, // n-ary square intersection operator | |
'\u2A07': MO.OP, // two logical and operator | |
'\u2A08': MO.OP, // two logical or operator | |
'\u2A09': MO.OP, // n-ary times operator | |
'\u2A0A': MO.OP, // modulo two sum | |
'\u2A0B': MO.INTEGRAL2, // summation with integral | |
'\u2A0C': MO.INTEGRAL, // quadruple integral operator | |
'\u2A0D': MO.INTEGRAL2, // finite part integral | |
'\u2A0E': MO.INTEGRAL2, // integral with double stroke | |
'\u2A0F': MO.INTEGRAL2, // integral average with slash | |
'\u2A10': MO.OP, // circulation function | |
'\u2A11': MO.OP, // anticlockwise integration | |
'\u2A12': MO.OP, // line integration with rectangular path around pole | |
'\u2A13': MO.OP, // line integration with semicircular path around pole | |
'\u2A14': MO.OP, // line integration not including the pole | |
'\u2A15': MO.INTEGRAL2, // integral around a point operator | |
'\u2A16': MO.INTEGRAL2, // quaternion integral operator | |
'\u2A17': MO.INTEGRAL2, // integral with leftwards arrow with hook | |
'\u2A18': MO.INTEGRAL2, // integral with times sign | |
'\u2A19': MO.INTEGRAL2, // integral with intersection | |
'\u2A1A': MO.INTEGRAL2, // integral with union | |
'\u2A1B': MO.INTEGRAL2, // integral with overbar | |
'\u2A1C': MO.INTEGRAL2, // integral with underbar | |
'\u2AFC': MO.OP, // large triple vertical bar operator | |
'\u2AFF': MO.OP // n-ary white vertical bar | |
}, | |
infix: { | |
'\u2A1D': MO.BIN3, // join | |
'\u2A1E': MO.BIN3, // large left triangle operator | |
'\u2A1F': MO.BIN3, // z notation schema composition | |
'\u2A20': MO.BIN3, // z notation schema piping | |
'\u2A21': MO.BIN3, // z notation schema projection | |
'\u2A22': MO.BIN4, // plus sign with small circle above | |
'\u2A23': MO.BIN4, // plus sign with circumflex accent above | |
'\u2A24': MO.BIN4, // plus sign with tilde above | |
'\u2A25': MO.BIN4, // plus sign with dot below | |
'\u2A26': MO.BIN4, // plus sign with tilde below | |
'\u2A27': MO.BIN4, // plus sign with subscript two | |
'\u2A28': MO.BIN4, // plus sign with black triangle | |
'\u2A29': MO.BIN4, // minus sign with comma above | |
'\u2A2A': MO.BIN4, // minus sign with dot below | |
'\u2A2B': MO.BIN4, // minus sign with falling dots | |
'\u2A2C': MO.BIN4, // minus sign with rising dots | |
'\u2A2D': MO.BIN4, // plus sign in left half circle | |
'\u2A2E': MO.BIN4, // plus sign in right half circle | |
'\u2A30': MO.BIN4, // multiplication sign with dot above | |
'\u2A31': MO.BIN4, // multiplication sign with underbar | |
'\u2A32': MO.BIN4, // semidirect product with bottom closed | |
'\u2A33': MO.BIN4, // smash product | |
'\u2A34': MO.BIN4, // multiplication sign in left half circle | |
'\u2A35': MO.BIN4, // multiplication sign in right half circle | |
'\u2A36': MO.BIN4, // circled multiplication sign with circumflex accent | |
'\u2A37': MO.BIN4, // multiplication sign in double circle | |
'\u2A38': MO.BIN4, // circled division sign | |
'\u2A39': MO.BIN4, // plus sign in triangle | |
'\u2A3A': MO.BIN4, // minus sign in triangle | |
'\u2A3B': MO.BIN4, // multiplication sign in triangle | |
'\u2A3C': MO.BIN4, // interior product | |
'\u2A3D': MO.BIN4, // righthand interior product | |
'\u2A3E': MO.BIN4, // z notation relational composition | |
'\u2A40': MO.BIN4, // intersection with dot | |
'\u2A41': MO.BIN4, // union with minus sign | |
'\u2A42': MO.BIN4, // union with overbar | |
'\u2A43': MO.BIN4, // intersection with overbar | |
'\u2A44': MO.BIN4, // intersection with logical and | |
'\u2A45': MO.BIN4, // union with logical or | |
'\u2A46': MO.BIN4, // union above intersection | |
'\u2A47': MO.BIN4, // intersection above union | |
'\u2A48': MO.BIN4, // union above bar above intersection | |
'\u2A49': MO.BIN4, // intersection above bar above union | |
'\u2A4A': MO.BIN4, // union beside and joined with union | |
'\u2A4B': MO.BIN4, // intersection beside and joined with intersection | |
'\u2A4C': MO.BIN4, // closed union with serifs | |
'\u2A4D': MO.BIN4, // closed intersection with serifs | |
'\u2A4E': MO.BIN4, // double square intersection | |
'\u2A4F': MO.BIN4, // double square union | |
'\u2A50': MO.BIN4, // closed union with serifs and smash product | |
'\u2A51': MO.BIN4, // logical and with dot above | |
'\u2A52': MO.BIN4, // logical or with dot above | |
'\u2A53': MO.BIN4, // double logical and | |
'\u2A54': MO.BIN4, // double logical or | |
'\u2A55': MO.BIN4, // two intersecting logical and | |
'\u2A56': MO.BIN4, // two intersecting logical or | |
'\u2A57': MO.BIN4, // sloping large or | |
'\u2A58': MO.BIN4, // sloping large and | |
'\u2A59': MO.REL, // logical or overlapping logical and | |
'\u2A5A': MO.BIN4, // logical and with middle stem | |
'\u2A5B': MO.BIN4, // logical or with middle stem | |
'\u2A5C': MO.BIN4, // logical and with horizontal dash | |
'\u2A5D': MO.BIN4, // logical or with horizontal dash | |
'\u2A5E': MO.BIN4, // logical and with double overbar | |
'\u2A5F': MO.BIN4, // logical and with underbar | |
'\u2A60': MO.BIN4, // logical and with double underbar | |
'\u2A61': MO.BIN4, // small vee with underbar | |
'\u2A62': MO.BIN4, // logical or with double overbar | |
'\u2A63': MO.BIN4, // logical or with double underbar | |
'\u2A64': MO.BIN4, // z notation domain antirestriction | |
'\u2A65': MO.BIN4, // z notation range antirestriction | |
'\u2A66': MO.REL, // equals sign with dot below | |
'\u2A67': MO.REL, // identical with dot above | |
'\u2A68': MO.REL, // triple horizontal bar with double vertical stroke | |
'\u2A69': MO.REL, // triple horizontal bar with triple vertical stroke | |
'\u2A6A': MO.REL, // tilde operator with dot above | |
'\u2A6B': MO.REL, // tilde operator with rising dots | |
'\u2A6C': MO.REL, // similar minus similar | |
'\u2A6D': MO.REL, // congruent with dot above | |
'\u2A6E': MO.REL, // equals with asterisk | |
'\u2A6F': MO.REL, // almost equal to with circumflex accent | |
'\u2A70': MO.REL, // approximately equal or equal to | |
'\u2A71': MO.BIN4, // equals sign above plus sign | |
'\u2A72': MO.BIN4, // plus sign above equals sign | |
'\u2A73': MO.REL, // equals sign above tilde operator | |
'\u2A74': MO.REL, // double colon equal | |
'\u2A75': MO.REL, // two consecutive equals signs | |
'\u2A76': MO.REL, // three consecutive equals signs | |
'\u2A77': MO.REL, // equals sign with two dots above and two dots below | |
'\u2A78': MO.REL, // equivalent with four dots above | |
'\u2A79': MO.REL, // less-than with circle inside | |
'\u2A7A': MO.REL, // greater-than with circle inside | |
'\u2A7B': MO.REL, // less-than with question mark above | |
'\u2A7C': MO.REL, // greater-than with question mark above | |
'\u2A7D': MO.REL, // less-than or slanted equal to | |
'\u2A7D\u0338': MO.REL, // less-than or slanted equal to with slash | |
'\u2A7E': MO.REL, // greater-than or slanted equal to | |
'\u2A7E\u0338': MO.REL, // greater-than or slanted equal to with slash | |
'\u2A7F': MO.REL, // less-than or slanted equal to with dot inside | |
'\u2A80': MO.REL, // greater-than or slanted equal to with dot inside | |
'\u2A81': MO.REL, // less-than or slanted equal to with dot above | |
'\u2A82': MO.REL, // greater-than or slanted equal to with dot above | |
'\u2A83': MO.REL, // less-than or slanted equal to with dot above right | |
'\u2A84': MO.REL, // greater-than or slanted equal to with dot above left | |
'\u2A85': MO.REL, // less-than or approximate | |
'\u2A86': MO.REL, // greater-than or approximate | |
'\u2A87': MO.REL, // less-than and single-line not equal to | |
'\u2A88': MO.REL, // greater-than and single-line not equal to | |
'\u2A89': MO.REL, // less-than and not approximate | |
'\u2A8A': MO.REL, // greater-than and not approximate | |
'\u2A8B': MO.REL, // less-than above double-line equal above greater-than | |
'\u2A8C': MO.REL, // greater-than above double-line equal above less-than | |
'\u2A8D': MO.REL, // less-than above similar or equal | |
'\u2A8E': MO.REL, // greater-than above similar or equal | |
'\u2A8F': MO.REL, // less-than above similar above greater-than | |
'\u2A90': MO.REL, // greater-than above similar above less-than | |
'\u2A91': MO.REL, // less-than above greater-than above double-line equal | |
'\u2A92': MO.REL, // greater-than above less-than above double-line equal | |
'\u2A93': MO.REL, // less-than above slanted equal above greater-than above slanted equal | |
'\u2A94': MO.REL, // greater-than above slanted equal above less-than above slanted equal | |
'\u2A95': MO.REL, // slanted equal to or less-than | |
'\u2A96': MO.REL, // slanted equal to or greater-than | |
'\u2A97': MO.REL, // slanted equal to or less-than with dot inside | |
'\u2A98': MO.REL, // slanted equal to or greater-than with dot inside | |
'\u2A99': MO.REL, // double-line equal to or less-than | |
'\u2A9A': MO.REL, // double-line equal to or greater-than | |
'\u2A9B': MO.REL, // double-line slanted equal to or less-than | |
'\u2A9C': MO.REL, // double-line slanted equal to or greater-than | |
'\u2A9D': MO.REL, // similar or less-than | |
'\u2A9E': MO.REL, // similar or greater-than | |
'\u2A9F': MO.REL, // similar above less-than above equals sign | |
'\u2AA0': MO.REL, // similar above greater-than above equals sign | |
'\u2AA1': MO.REL, // double nested less-than | |
'\u2AA1\u0338': MO.REL, // double nested less-than with slash | |
'\u2AA2': MO.REL, // double nested greater-than | |
'\u2AA2\u0338': MO.REL, // double nested greater-than with slash | |
'\u2AA3': MO.REL, // double nested less-than with underbar | |
'\u2AA4': MO.REL, // greater-than overlapping less-than | |
'\u2AA5': MO.REL, // greater-than beside less-than | |
'\u2AA6': MO.REL, // less-than closed by curve | |
'\u2AA7': MO.REL, // greater-than closed by curve | |
'\u2AA8': MO.REL, // less-than closed by curve above slanted equal | |
'\u2AA9': MO.REL, // greater-than closed by curve above slanted equal | |
'\u2AAA': MO.REL, // smaller than | |
'\u2AAB': MO.REL, // larger than | |
'\u2AAC': MO.REL, // smaller than or equal to | |
'\u2AAD': MO.REL, // larger than or equal to | |
'\u2AAE': MO.REL, // equals sign with bumpy above | |
'\u2AAF\u0338': MO.REL, // precedes above single-line equals sign with slash | |
'\u2AB0\u0338': MO.REL, // succeeds above single-line equals sign with slash | |
'\u2AB1': MO.REL, // precedes above single-line not equal to | |
'\u2AB2': MO.REL, // succeeds above single-line not equal to | |
'\u2AB3': MO.REL, // precedes above equals sign | |
'\u2AB4': MO.REL, // succeeds above equals sign | |
'\u2AB5': MO.REL, // precedes above not equal to | |
'\u2AB6': MO.REL, // succeeds above not equal to | |
'\u2AB7': MO.REL, // precedes above almost equal to | |
'\u2AB8': MO.REL, // succeeds above almost equal to | |
'\u2AB9': MO.REL, // precedes above not almost equal to | |
'\u2ABA': MO.REL, // succeeds above not almost equal to | |
'\u2ABB': MO.REL, // double precedes | |
'\u2ABC': MO.REL, // double succeeds | |
'\u2ABD': MO.REL, // subset with dot | |
'\u2ABE': MO.REL, // superset with dot | |
'\u2ABF': MO.REL, // subset with plus sign below | |
'\u2AC0': MO.REL, // superset with plus sign below | |
'\u2AC1': MO.REL, // subset with multiplication sign below | |
'\u2AC2': MO.REL, // superset with multiplication sign below | |
'\u2AC3': MO.REL, // subset of or equal to with dot above | |
'\u2AC4': MO.REL, // superset of or equal to with dot above | |
'\u2AC5': MO.REL, // subset of above equals sign | |
'\u2AC6': MO.REL, // superset of above equals sign | |
'\u2AC7': MO.REL, // subset of above tilde operator | |
'\u2AC8': MO.REL, // superset of above tilde operator | |
'\u2AC9': MO.REL, // subset of above almost equal to | |
'\u2ACA': MO.REL, // superset of above almost equal to | |
'\u2ACB': MO.REL, // subset of above not equal to | |
'\u2ACC': MO.REL, // superset of above not equal to | |
'\u2ACD': MO.REL, // square left open box operator | |
'\u2ACE': MO.REL, // square right open box operator | |
'\u2ACF': MO.REL, // closed subset | |
'\u2AD0': MO.REL, // closed superset | |
'\u2AD1': MO.REL, // closed subset or equal to | |
'\u2AD2': MO.REL, // closed superset or equal to | |
'\u2AD3': MO.REL, // subset above superset | |
'\u2AD4': MO.REL, // superset above subset | |
'\u2AD5': MO.REL, // subset above subset | |
'\u2AD6': MO.REL, // superset above superset | |
'\u2AD7': MO.REL, // superset beside subset | |
'\u2AD8': MO.REL, // superset beside and joined by dash with subset | |
'\u2AD9': MO.REL, // element of opening downwards | |
'\u2ADA': MO.REL, // pitchfork with tee top | |
'\u2ADB': MO.REL, // transversal intersection | |
'\u2ADC': MO.REL, // forking | |
'\u2ADD': MO.REL, // nonforking | |
'\u2ADE': MO.REL, // short left tack | |
'\u2ADF': MO.REL, // short down tack | |
'\u2AE0': MO.REL, // short up tack | |
'\u2AE1': MO.REL, // perpendicular with s | |
'\u2AE2': MO.REL, // vertical bar triple right turnstile | |
'\u2AE3': MO.REL, // double vertical bar left turnstile | |
'\u2AE4': MO.REL, // vertical bar double left turnstile | |
'\u2AE5': MO.REL, // double vertical bar double left turnstile | |
'\u2AE6': MO.REL, // long dash from left member of double vertical | |
'\u2AE7': MO.REL, // short down tack with overbar | |
'\u2AE8': MO.REL, // short up tack with underbar | |
'\u2AE9': MO.REL, // short up tack above short down tack | |
'\u2AEA': MO.REL, // double down tack | |
'\u2AEB': MO.REL, // double up tack | |
'\u2AEC': MO.REL, // double stroke not sign | |
'\u2AED': MO.REL, // reversed double stroke not sign | |
'\u2AEE': MO.REL, // does not divide with reversed negation slash | |
'\u2AEF': MO.REL, // vertical line with circle above | |
'\u2AF0': MO.REL, // vertical line with circle below | |
'\u2AF1': MO.REL, // down tack with circle below | |
'\u2AF2': MO.REL, // parallel with horizontal stroke | |
'\u2AF3': MO.REL, // parallel with tilde operator | |
'\u2AF4': MO.BIN4, // triple vertical bar binary relation | |
'\u2AF5': MO.BIN4, // triple vertical bar with horizontal stroke | |
'\u2AF6': MO.BIN4, // triple colon operator | |
'\u2AF7': MO.REL, // triple nested less-than | |
'\u2AF8': MO.REL, // triple nested greater-than | |
'\u2AF9': MO.REL, // double-line slanted less-than or equal to | |
'\u2AFA': MO.REL, // double-line slanted greater-than or equal to | |
'\u2AFB': MO.BIN4, // triple solidus binary relation | |
'\u2AFD': MO.BIN4, // double solidus operator | |
'\u2AFE': MO.BIN3 // white vertical bar | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/SuppMathOperators.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/CombDiactForSymbols.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
postfix: { | |
'\u20DB': MO.ACCENT, // combining three dots above | |
'\u20DC': MO.ACCENT // combining four dots above | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/CombDiactForSymbols.js"); | |
})(MathJax.ElementJax.mml); | |
/************************************************************* | |
* | |
* MathJax/jax/output/HTML-CSS/optable/Latin1Supplement.js | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
(function (MML) { | |
var MO = MML.mo.OPTYPES; | |
var TEXCLASS = MML.TEXCLASS; | |
MathJax.Hub.Insert(MML.mo.prototype,{ | |
OPTABLE: { | |
postfix: { | |
'\u00B0': MO.ORD, // degree sign | |
'\u00B4': MO.ACCENT, // acute accent | |
'\u00B8': MO.ACCENT // cedilla | |
} | |
} | |
}); | |
MathJax.Ajax.loadComplete(MML.optableDir+"/Latin1Supplement.js"); | |
})(MathJax.ElementJax.mml); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/extensions/MathEvents.js | |
* | |
* Implements the event handlers needed by the output jax to perform | |
* menu, hover, and other events. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2011-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
(function (HUB,HTML,AJAX,CALLBACK,LOCALE,OUTPUT,INPUT) { | |
var VERSION = "2.7.1"; | |
var EXTENSION = MathJax.Extension; | |
var ME = EXTENSION.MathEvents = {version: VERSION}; | |
var SETTINGS = HUB.config.menuSettings; | |
var CONFIG = { | |
hover: 500, // time required to be considered a hover | |
frame: { | |
x: 3.5, y: 5, // frame padding and | |
bwidth: 1, // frame border width (in pixels) | |
bcolor: "#A6D", // frame border color | |
hwidth: "15px", // haze width | |
hcolor: "#83A" // haze color | |
}, | |
button: { | |
x: -6, y: -3, // menu button offsets | |
wx: -2 // button offset for full-width equations | |
}, | |
fadeinInc: .2, // increment for fade-in | |
fadeoutInc: .05, // increment for fade-out | |
fadeDelay: 50, // delay between fade-in or fade-out steps | |
fadeoutStart: 400, // delay before fade-out after mouseout | |
fadeoutDelay: 15*1000, // delay before automatic fade-out | |
styles: { | |
".MathJax_Hover_Frame": { | |
"border-radius": ".25em", // Opera 10.5 and IE9 | |
"-webkit-border-radius": ".25em", // Safari and Chrome | |
"-moz-border-radius": ".25em", // Firefox | |
"-khtml-border-radius": ".25em", // Konqueror | |
"box-shadow": "0px 0px 15px #83A", // Opera 10.5 and IE9 | |
"-webkit-box-shadow": "0px 0px 15px #83A", // Safari and Chrome | |
"-moz-box-shadow": "0px 0px 15px #83A", // Forefox | |
"-khtml-box-shadow": "0px 0px 15px #83A", // Konqueror | |
border: "1px solid #A6D ! important", | |
display: "inline-block", position:"absolute" | |
}, | |
".MathJax_Menu_Button .MathJax_Hover_Arrow": { | |
position:"absolute", | |
cursor:"pointer", | |
display:"inline-block", | |
border:"2px solid #AAA", | |
"border-radius":"4px", | |
"-webkit-border-radius": "4px", // Safari and Chrome | |
"-moz-border-radius": "4px", // Firefox | |
"-khtml-border-radius": "4px", // Konqueror | |
"font-family":"'Courier New',Courier", | |
"font-size":"9px", | |
color:"#F0F0F0" | |
}, | |
".MathJax_Menu_Button .MathJax_Hover_Arrow span": { | |
display:"block", | |
"background-color":"#AAA", | |
border:"1px solid", | |
"border-radius":"3px", | |
"line-height":0, | |
padding:"4px" | |
}, | |
".MathJax_Hover_Arrow:hover": { | |
color:"white!important", | |
border:"2px solid #CCC!important" | |
}, | |
".MathJax_Hover_Arrow:hover span": { | |
"background-color":"#CCC!important" | |
} | |
} | |
}; | |
// | |
// Common event-handling code | |
// | |
var EVENT = ME.Event = { | |
LEFTBUTTON: 0, // the event.button value for left button | |
RIGHTBUTTON: 2, // the event.button value for right button | |
MENUKEY: "altKey", // the event value for alternate context menu | |
/*************************************************************/ | |
/* | |
* Enum element for key codes. | |
*/ | |
KEY: { | |
RETURN: 13, | |
ESCAPE: 27, | |
SPACE: 32, | |
LEFT: 37, | |
UP: 38, | |
RIGHT: 39, | |
DOWN: 40 | |
}, | |
Mousedown: function (event) {return EVENT.Handler(event,"Mousedown",this)}, | |
Mouseup: function (event) {return EVENT.Handler(event,"Mouseup",this)}, | |
Mousemove: function (event) {return EVENT.Handler(event,"Mousemove",this)}, | |
Mouseover: function (event) {return EVENT.Handler(event,"Mouseover",this)}, | |
Mouseout: function (event) {return EVENT.Handler(event,"Mouseout",this)}, | |
Click: function (event) {return EVENT.Handler(event,"Click",this)}, | |
DblClick: function (event) {return EVENT.Handler(event,"DblClick",this)}, | |
Menu: function (event) {return EVENT.Handler(event,"ContextMenu",this)}, | |
// | |
// Call the output jax's event handler or the zoom handler | |
// | |
Handler: function (event,type,math) { | |
if (AJAX.loadingMathMenu) {return EVENT.False(event)} | |
var jax = OUTPUT[math.jaxID]; | |
if (!event) {event = window.event} | |
event.isContextMenu = (type === "ContextMenu"); | |
if (jax[type]) {return jax[type](event,math)} | |
if (EXTENSION.MathZoom) {return EXTENSION.MathZoom.HandleEvent(event,type,math)} | |
}, | |
// | |
// Try to cancel the event in every way we can | |
// | |
False: function (event) { | |
if (!event) {event = window.event} | |
if (event) { | |
if (event.preventDefault) {event.preventDefault()} else {event.returnValue = false} | |
if (event.stopPropagation) {event.stopPropagation()} | |
event.cancelBubble = true; | |
} | |
return false; | |
}, | |
// | |
// Keydown event handler. Should only fire on Space key. | |
// | |
Keydown: function (event, math) { | |
if (!event) event = window.event; | |
if (event.keyCode === EVENT.KEY.SPACE) { | |
EVENT.ContextMenu(event, this); | |
}; | |
}, | |
// | |
// Load the contextual menu code, if needed, and post the menu | |
// | |
ContextMenu: function (event,math,force) { | |
// | |
// Check if we are showing menus | |
// | |
var JAX = OUTPUT[math.jaxID], jax = JAX.getJaxFromMath(math); | |
var show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu; | |
if (!show || (SETTINGS.context !== "MathJax" && !force)) return; | |
// | |
// Remove selections, remove hover fades | |
// | |
if (ME.msieEventBug) {event = window.event || event} | |
EVENT.ClearSelection(); HOVER.ClearHoverTimer(); | |
if (jax.hover) { | |
if (jax.hover.remove) {clearTimeout(jax.hover.remove); delete jax.hover.remove} | |
jax.hover.nofade = true; | |
} | |
// | |
// If the menu code is loaded, | |
// Check if localization needs loading; | |
// If not, post the menu, and return. | |
// Otherwise wait for the localization to load | |
// Otherwse load the menu code. | |
// Try again after the file is loaded. | |
// | |
var MENU = MathJax.Menu; var load, fn; | |
if (MENU) { | |
if (MENU.loadingDomain) {return EVENT.False(event)} | |
load = LOCALE.loadDomain("MathMenu"); | |
if (!load) { | |
MENU.jax = jax; | |
var source = MENU.menu.Find("Show Math As").submenu; | |
source.items[0].name = jax.sourceMenuTitle; | |
source.items[0].format = (jax.sourceMenuFormat||"MathML"); | |
source.items[1].name = INPUT[jax.inputJax].sourceMenuTitle; | |
source.items[5].disabled = !INPUT[jax.inputJax].annotationEncoding; | |
// | |
// Try and find each known annotation format and enable the menu | |
// items accordingly. | |
// | |
var annotations = source.items[2]; annotations.disabled = true; | |
var annotationItems = annotations.submenu.items; | |
annotationList = MathJax.Hub.Config.semanticsAnnotations; | |
for (var i = 0, m = annotationItems.length; i < m; i++) { | |
var name = annotationItems[i].name[1] | |
if (jax.root && jax.root.getAnnotation(name) !== null) { | |
annotations.disabled = false; | |
annotationItems[i].hidden = false; | |
} else { | |
annotationItems[i].hidden = true; | |
} | |
} | |
var MathPlayer = MENU.menu.Find("Math Settings","MathPlayer"); | |
MathPlayer.hidden = !(jax.outputJax === "NativeMML" && HUB.Browser.hasMathPlayer); | |
return MENU.menu.Post(event); | |
} | |
MENU.loadingDomain = true; | |
fn = function () {delete MENU.loadingDomain}; | |
} else { | |
if (AJAX.loadingMathMenu) {return EVENT.False(event)} | |
AJAX.loadingMathMenu = true; | |
load = AJAX.Require("[MathJax]/extensions/MathMenu.js"); | |
fn = function () { | |
delete AJAX.loadingMathMenu; | |
if (!MathJax.Menu) {MathJax.Menu = {}} | |
} | |
} | |
var ev = { | |
pageX:event.pageX, pageY:event.pageY, | |
clientX:event.clientX, clientY:event.clientY | |
}; | |
CALLBACK.Queue( | |
load, fn, // load the file and delete the marker when done | |
["ContextMenu",EVENT,ev,math,force] // call this function again | |
); | |
return EVENT.False(event); | |
}, | |
// | |
// Mousedown handler for alternate means of accessing menu | |
// | |
AltContextMenu: function (event,math) { | |
var JAX = OUTPUT[math.jaxID]; | |
var show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu; | |
if (show) { | |
show = (JAX.config.showMathMenuMSIE != null ? JAX : HUB).config.showMathMenuMSIE; | |
if (SETTINGS.context === "MathJax" && !SETTINGS.mpContext && show) { | |
if (!ME.noContextMenuBug || event.button !== EVENT.RIGHTBUTTON) return; | |
} else { | |
if (!event[EVENT.MENUKEY] || event.button !== EVENT.LEFTBUTTON) return; | |
} | |
return JAX.ContextMenu(event,math,true); | |
} | |
}, | |
ClearSelection: function () { | |
if (ME.safariContextMenuBug) {setTimeout("window.getSelection().empty()",0)} | |
if (document.selection) {setTimeout("document.selection.empty()",0)} | |
}, | |
getBBox: function (span) { | |
span.appendChild(ME.topImg); | |
var h = ME.topImg.offsetTop, d = span.offsetHeight-h, w = span.offsetWidth; | |
span.removeChild(ME.topImg); | |
return {w:w, h:h, d:d}; | |
} | |
}; | |
// | |
// Handle hover "discoverability" | |
// | |
var HOVER = ME.Hover = { | |
// | |
// Check if we are moving from a non-MathJax element to a MathJax one | |
// and either start fading in again (if it is fading out) or start the | |
// timer for the hover | |
// | |
Mouseover: function (event,math) { | |
if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") { | |
var from = event.fromElement || event.relatedTarget, | |
to = event.toElement || event.target; | |
if (from && to && (HUB.isMathJaxNode(from) !== HUB.isMathJaxNode(to) || | |
HUB.getJaxFor(from) !== HUB.getJaxFor(to))) { | |
var jax = this.getJaxFromMath(math); | |
if (jax.hover) {HOVER.ReHover(jax)} else {HOVER.HoverTimer(jax,math)} | |
return EVENT.False(event); | |
} | |
} | |
}, | |
// | |
// Check if we are moving from a MathJax element to a non-MathJax one | |
// and either start fading out, or clear the timer if we haven't | |
// hovered yet | |
// | |
Mouseout: function (event,math) { | |
if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") { | |
var from = event.fromElement || event.relatedTarget, | |
to = event.toElement || event.target; | |
if (from && to && (HUB.isMathJaxNode(from) !== HUB.isMathJaxNode(to) || | |
HUB.getJaxFor(from) !== HUB.getJaxFor(to))) { | |
var jax = this.getJaxFromMath(math); | |
if (jax.hover) {HOVER.UnHover(jax)} else {HOVER.ClearHoverTimer()} | |
return EVENT.False(event); | |
} | |
} | |
}, | |
// | |
// Restart hover timer if the mouse moves | |
// | |
Mousemove: function (event,math) { | |
if (SETTINGS.discoverable || SETTINGS.zoom === "Hover") { | |
var jax = this.getJaxFromMath(math); if (jax.hover) return; | |
if (HOVER.lastX == event.clientX && HOVER.lastY == event.clientY) return; | |
HOVER.lastX = event.clientX; HOVER.lastY = event.clientY; | |
HOVER.HoverTimer(jax,math); | |
return EVENT.False(event); | |
} | |
}, | |
// | |
// Clear the old timer and start a new one | |
// | |
HoverTimer: function (jax,math) { | |
this.ClearHoverTimer(); | |
this.hoverTimer = setTimeout(CALLBACK(["Hover",this,jax,math]),CONFIG.hover); | |
}, | |
ClearHoverTimer: function () { | |
if (this.hoverTimer) {clearTimeout(this.hoverTimer); delete this.hoverTimer} | |
}, | |
// | |
// Handle putting up the hover frame | |
// | |
Hover: function (jax,math) { | |
// | |
// Check if Zoom handles the hover event | |
// | |
if (EXTENSION.MathZoom && EXTENSION.MathZoom.Hover({},math)) return; | |
// | |
// Get the hover data | |
// | |
var JAX = OUTPUT[jax.outputJax], | |
span = JAX.getHoverSpan(jax,math), | |
bbox = JAX.getHoverBBox(jax,span,math), | |
show = (JAX.config.showMathMenu != null ? JAX : HUB).config.showMathMenu; | |
var dx = CONFIG.frame.x, dy = CONFIG.frame.y, dd = CONFIG.frame.bwidth; // frame size | |
if (ME.msieBorderWidthBug) {dd = 0} | |
jax.hover = {opacity:0, id:jax.inputID+"-Hover"}; | |
// | |
// The frame and menu button | |
// | |
var frame = HTML.Element("span",{ | |
id:jax.hover.id, isMathJax: true, | |
style:{display:"inline-block", width:0, height:0, position:"relative"} | |
},[["span",{ | |
className:"MathJax_Hover_Frame", isMathJax: true, | |
style:{ | |
display:"inline-block", position:"absolute", | |
top:this.Px(-bbox.h-dy-dd-(bbox.y||0)), left:this.Px(-dx-dd+(bbox.x||0)), | |
width:this.Px(bbox.w+2*dx), height:this.Px(bbox.h+bbox.d+2*dy), | |
opacity:0, filter:"alpha(opacity=0)" | |
}} | |
]] | |
); | |
var button = HTML.Element("span",{ | |
isMathJax: true, id:jax.hover.id+"Menu", className:"MathJax_Menu_Button", | |
style:{display:"inline-block", "z-index": 1, width:0, height:0, position:"relative"} | |
},[["span",{ | |
className: "MathJax_Hover_Arrow", isMathJax: true, math: math, | |
onclick: this.HoverMenu, jax:JAX.id, | |
style: { | |
left:this.Px(bbox.w+dx+dd+(bbox.x||0)+CONFIG.button.x), | |
top:this.Px(-bbox.h-dy-dd-(bbox.y||0)-CONFIG.button.y), | |
opacity:0, filter:"alpha(opacity=0)" | |
} | |
},[["span",{isMathJax:true},"\u25BC"]]]] | |
); | |
if (bbox.width) { | |
frame.style.width = button.style.width = bbox.width; | |
frame.style.marginRight = button.style.marginRight = "-"+bbox.width; | |
frame.firstChild.style.width = bbox.width; | |
button.firstChild.style.left = ""; | |
button.firstChild.style.right = this.Px(CONFIG.button.wx); | |
} | |
// | |
// Add the frame and button | |
// | |
span.parentNode.insertBefore(frame,span); | |
if (show) {span.parentNode.insertBefore(button,span)} | |
if (span.style) {span.style.position = "relative"} // so math is on top of hover frame | |
// | |
// Start the hover fade-in | |
// | |
this.ReHover(jax); | |
}, | |
// | |
// Restart the hover fade in and fade-out timers | |
// | |
ReHover: function (jax) { | |
if (jax.hover.remove) {clearTimeout(jax.hover.remove)} | |
jax.hover.remove = setTimeout(CALLBACK(["UnHover",this,jax]),CONFIG.fadeoutDelay); | |
this.HoverFadeTimer(jax,CONFIG.fadeinInc); | |
}, | |
// | |
// Start the fade-out | |
// | |
UnHover: function (jax) { | |
if (!jax.hover.nofade) {this.HoverFadeTimer(jax,-CONFIG.fadeoutInc,CONFIG.fadeoutStart)} | |
}, | |
// | |
// Handle the fade-in and fade-out | |
// | |
HoverFade: function (jax) { | |
delete jax.hover.timer; | |
jax.hover.opacity = Math.max(0,Math.min(1,jax.hover.opacity + jax.hover.inc)); | |
jax.hover.opacity = Math.floor(1000*jax.hover.opacity)/1000; | |
var frame = document.getElementById(jax.hover.id), | |
button = document.getElementById(jax.hover.id+"Menu"); | |
frame.firstChild.style.opacity = jax.hover.opacity; | |
frame.firstChild.style.filter = "alpha(opacity="+Math.floor(100*jax.hover.opacity)+")"; | |
if (button) { | |
button.firstChild.style.opacity = jax.hover.opacity; | |
button.firstChild.style.filter = frame.style.filter; | |
} | |
if (jax.hover.opacity === 1) {return} | |
if (jax.hover.opacity > 0) {this.HoverFadeTimer(jax,jax.hover.inc); return} | |
frame.parentNode.removeChild(frame); | |
if (button) {button.parentNode.removeChild(button)} | |
if (jax.hover.remove) {clearTimeout(jax.hover.remove)} | |
delete jax.hover; | |
}, | |
// | |
// Set the fade to in or out (via inc) and start the timer, if needed | |
// | |
HoverFadeTimer: function (jax,inc,delay) { | |
jax.hover.inc = inc; | |
if (!jax.hover.timer) { | |
jax.hover.timer = setTimeout(CALLBACK(["HoverFade",this,jax]),(delay||CONFIG.fadeDelay)); | |
} | |
}, | |
// | |
// Handle a click on the menu button | |
// | |
HoverMenu: function (event) { | |
if (!event) {event = window.event} | |
return OUTPUT[this.jax].ContextMenu(event,this.math,true); | |
}, | |
// | |
// Clear all hover timers | |
// | |
ClearHover: function (jax) { | |
if (jax.hover.remove) {clearTimeout(jax.hover.remove)} | |
if (jax.hover.timer) {clearTimeout(jax.hover.timer)} | |
HOVER.ClearHoverTimer(); | |
delete jax.hover; | |
}, | |
// | |
// Make a measurement in pixels | |
// | |
Px: function (m) { | |
if (Math.abs(m) < .006) {return "0px"} | |
return m.toFixed(2).replace(/\.?0+$/,"") + "px"; | |
}, | |
// | |
// Preload images so they show up with the menu | |
// | |
getImages: function () { | |
if (SETTINGS.discoverable) { | |
var menu = new Image(); | |
menu.src = CONFIG.button.src; | |
} | |
} | |
}; | |
// | |
// Handle touch events. | |
// | |
// Use double-tap-and-hold as a replacement for context menu event. | |
// Use double-tap as a replacement for double click. | |
// | |
var TOUCH = ME.Touch = { | |
last: 0, // time of last tap event | |
delay: 500, // delay time for double-click | |
// | |
// Check if this is a double-tap, and if so, start the timer | |
// for the double-tap and hold (to trigger the contextual menu) | |
// | |
start: function (event) { | |
var now = new Date().getTime(); | |
var dblTap = (now - TOUCH.last < TOUCH.delay && TOUCH.up); | |
TOUCH.last = now; TOUCH.up = false; | |
if (dblTap) { | |
TOUCH.timeout = setTimeout(TOUCH.menu,TOUCH.delay,event,this); | |
event.preventDefault(); | |
} | |
}, | |
// | |
// Check if there is a timeout pending, i.e., we have a | |
// double-tap and were waiting to see if it is held long | |
// enough for the menu. Since we got the end before the | |
// timeout, it is a double-click, not a double-tap-and-hold. | |
// Prevent the default action and issue a double click. | |
// | |
end: function (event) { | |
var now = new Date().getTime(); | |
TOUCH.up = (now - TOUCH.last < TOUCH.delay); | |
if (TOUCH.timeout) { | |
clearTimeout(TOUCH.timeout); | |
delete TOUCH.timeout; TOUCH.last = 0; TOUCH.up = false; | |
event.preventDefault(); | |
return EVENT.Handler((event.touches[0]||event.touch),"DblClick",this); | |
} | |
}, | |
// | |
// If the timeout passes without an end event, we issue | |
// the contextual menu event. | |
// | |
menu: function (event,math) { | |
delete TOUCH.timeout; TOUCH.last = 0; TOUCH.up = false; | |
return EVENT.Handler((event.touches[0]||event.touch),"ContextMenu",math); | |
} | |
}; | |
/* | |
* // | |
* // Mobile screens are small, so use larger version of arrow | |
* // | |
* if (HUB.Browser.isMobile) { | |
* var arrow = CONFIG.styles[".MathJax_Hover_Arrow"]; | |
* arrow.width = "25px"; arrow.height = "18px"; | |
* CONFIG.button.x = -6; | |
* } | |
*/ | |
// | |
// Set up browser-specific values | |
// | |
HUB.Browser.Select({ | |
MSIE: function (browser) { | |
var mode = (document.documentMode || 0); | |
var isIE8 = browser.versionAtLeast("8.0"); | |
ME.msieBorderWidthBug = (document.compatMode === "BackCompat"); // borders are inside offsetWidth/Height | |
ME.msieEventBug = browser.isIE9; // must get event from window even though event is passed | |
ME.msieAlignBug = (!isIE8 || mode < 8); // inline-block spans don't rest on baseline | |
if (mode < 9) {EVENT.LEFTBUTTON = 1} // IE < 9 has wrong event.button values | |
}, | |
Safari: function (browser) { | |
ME.safariContextMenuBug = true; // selection can be started by contextmenu event | |
}, | |
Opera: function (browser) { | |
ME.operaPositionBug = true; // position is wrong unless border is used | |
}, | |
Konqueror: function (browser) { | |
ME.noContextMenuBug = true; // doesn't produce contextmenu event | |
} | |
}); | |
// | |
// Used in measuring zoom and hover positions | |
// | |
ME.topImg = (ME.msieAlignBug ? | |
HTML.Element("img",{style:{width:0,height:0,position:"relative"},src:"about:blank"}) : | |
HTML.Element("span",{style:{width:0,height:0,display:"inline-block"}}) | |
); | |
if (ME.operaPositionBug) {ME.topImg.style.border="1px solid"} | |
// | |
// Get configuration from user | |
// | |
ME.config = CONFIG = HUB.CombineConfig("MathEvents",CONFIG); | |
var SETFRAME = function () { | |
var haze = CONFIG.styles[".MathJax_Hover_Frame"]; | |
haze.border = CONFIG.frame.bwidth+"px solid "+CONFIG.frame.bcolor+" ! important"; | |
haze["box-shadow"] = haze["-webkit-box-shadow"] = | |
haze["-moz-box-shadow"] = haze["-khtml-box-shadow"] = | |
"0px 0px "+CONFIG.frame.hwidth+" "+CONFIG.frame.hcolor; | |
}; | |
// | |
// Queue the events needed for startup | |
// | |
CALLBACK.Queue( | |
HUB.Register.StartupHook("End Config",{}), // wait until config is complete | |
[SETFRAME], | |
["getImages",HOVER], | |
["Styles",AJAX,CONFIG.styles], | |
["Post",HUB.Startup.signal,"MathEvents Ready"], | |
["loadComplete",AJAX,"[MathJax]/extensions/MathEvents.js"] | |
); | |
})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.Callback, | |
MathJax.Localization,MathJax.OutputJax,MathJax.InputJax); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/extensions/MathZoom.js | |
* | |
* Implements the zoom feature for enlarging math expressions. It is | |
* loaded automatically when the Zoom menu selection changes from "None". | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
(function (HUB,HTML,AJAX,HTMLCSS,nMML) { | |
var VERSION = "2.7.1"; | |
var CONFIG = HUB.CombineConfig("MathZoom",{ | |
styles: { | |
// | |
// The styles for the MathZoom display box | |
// | |
"#MathJax_Zoom": { | |
position:"absolute", "background-color":"#F0F0F0", overflow:"auto", | |
display:"block", "z-index":301, padding:".5em", border:"1px solid black", margin:0, | |
"font-weight":"normal", "font-style":"normal", | |
"text-align":"left", "text-indent":0, "text-transform":"none", | |
"line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal", | |
"word-wrap":"normal", "white-space":"nowrap", "float":"none", | |
"-webkit-box-sizing":"content-box", // Android ≤ 2.3, iOS ≤ 4 | |
"-moz-box-sizing":"content-box", // Firefox ≤ 28 | |
"box-sizing":"content-box", // Chrome, Firefox 29+, IE 8+, Opera, Safari 5.1 | |
"box-shadow":"5px 5px 15px #AAAAAA", // Opera 10.5 and IE9 | |
"-webkit-box-shadow":"5px 5px 15px #AAAAAA", // Safari 3 and Chrome | |
"-moz-box-shadow":"5px 5px 15px #AAAAAA", // Forefox 3.5 | |
"-khtml-box-shadow":"5px 5px 15px #AAAAAA", // Konqueror | |
filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE | |
}, | |
// | |
// The styles for the hidden overlay (should not need to be adjusted by the page author) | |
// | |
"#MathJax_ZoomOverlay": { | |
position:"absolute", left:0, top:0, "z-index":300, display:"inline-block", | |
width:"100%", height:"100%", border:0, padding:0, margin:0, | |
"background-color":"white", opacity:0, filter:"alpha(opacity=0)" | |
}, | |
"#MathJax_ZoomFrame": { | |
position:"relative", display:"inline-block", | |
height:0, width:0 | |
}, | |
"#MathJax_ZoomEventTrap": { | |
position:"absolute", left:0, top:0, "z-index":302, | |
display:"inline-block", border:0, padding:0, margin:0, | |
"background-color":"white", opacity:0, filter:"alpha(opacity=0)" | |
} | |
} | |
}); | |
var FALSE, HOVER, EVENT; | |
MathJax.Hub.Register.StartupHook("MathEvents Ready",function () { | |
EVENT = MathJax.Extension.MathEvents.Event; | |
FALSE = MathJax.Extension.MathEvents.Event.False; | |
HOVER = MathJax.Extension.MathEvents.Hover; | |
}); | |
/*************************************************************/ | |
var ZOOM = MathJax.Extension.MathZoom = { | |
version: VERSION, | |
settings: HUB.config.menuSettings, | |
scrollSize: 18, // width of scrool bars | |
// | |
// Process events passed from output jax | |
// | |
HandleEvent: function (event,type,math) { | |
if (ZOOM.settings.CTRL && !event.ctrlKey) return true; | |
if (ZOOM.settings.ALT && !event.altKey) return true; | |
if (ZOOM.settings.CMD && !event.metaKey) return true; | |
if (ZOOM.settings.Shift && !event.shiftKey) return true; | |
if (!ZOOM[type]) return true; | |
return ZOOM[type](event,math); | |
}, | |
// | |
// Zoom on click | |
// | |
Click: function (event,math) { | |
if (this.settings.zoom === "Click") {return this.Zoom(event,math)} | |
}, | |
// | |
// Zoom on double click | |
// | |
DblClick: function (event,math) { | |
if (this.settings.zoom === "Double-Click" || this.settings.zoom === "DoubleClick") {return this.Zoom(event,math)} | |
}, | |
// | |
// Zoom on hover (called by MathEvents.Hover) | |
// | |
Hover: function (event,math) { | |
if (this.settings.zoom === "Hover") {this.Zoom(event,math); return true} | |
return false; | |
}, | |
// | |
// Handle the actual zooming | |
// | |
Zoom: function (event,math) { | |
// | |
// Remove any other zoom and clear timers | |
// | |
this.Remove(); HOVER.ClearHoverTimer(); EVENT.ClearSelection(); | |
// | |
// Find the jax | |
// | |
var JAX = MathJax.OutputJax[math.jaxID]; | |
var jax = JAX.getJaxFromMath(math); | |
if (jax.hover) {HOVER.UnHover(jax)} | |
// | |
// Create the DOM elements for the zoom box | |
// | |
var container = this.findContainer(math); | |
var Mw = Math.floor(.85*container.clientWidth), | |
Mh = Math.max(document.body.clientHeight,document.documentElement.clientHeight); | |
if (this.getOverflow(container) !== "visible") {Mh = Math.min(container.clientHeight,Mh)} | |
Mh = Math.floor(.85*Mh); | |
var div = HTML.Element( | |
"span",{id:"MathJax_ZoomFrame"},[ | |
["span",{id:"MathJax_ZoomOverlay", onmousedown:this.Remove}], | |
["span",{ | |
id:"MathJax_Zoom", onclick:this.Remove, | |
style:{visibility:"hidden", fontSize:this.settings.zscale} | |
},[["span",{style:{display:"inline-block", "white-space":"nowrap"}}]] | |
]] | |
); | |
var zoom = div.lastChild, span = zoom.firstChild, overlay = div.firstChild; | |
math.parentNode.insertBefore(div,math); math.parentNode.insertBefore(math,div); // put div after math | |
if (span.addEventListener) {span.addEventListener("mousedown",this.Remove,true)} | |
var eW = zoom.offsetWidth || zoom.clientWidth; Mw -= eW; Mh -= eW; | |
zoom.style.maxWidth = Mw+"px"; zoom.style.maxHeight = Mh+"px"; | |
if (this.msieTrapEventBug) { | |
var trap = HTML.Element("span",{id:"MathJax_ZoomEventTrap", onmousedown:this.Remove}); | |
div.insertBefore(trap,zoom); | |
} | |
// | |
// Display the zoomed math | |
// | |
if (this.msieZIndexBug) { | |
// MSIE doesn't do z-index properly, so move the div to the document.body, | |
// and use an image as a tracker for the usual position | |
var tracker = HTML.addElement(document.body,"img",{ | |
src:"about:blank", id:"MathJax_ZoomTracker", width:0, height:0, | |
style:{width:0, height:0, position:"relative"} | |
}); | |
div.style.position = "relative"; | |
div.style.zIndex = CONFIG.styles["#MathJax_ZoomOverlay"]["z-index"]; | |
div = tracker; | |
} | |
var bbox = JAX.Zoom(jax,span,math,Mw,Mh); | |
// | |
// Fix up size and position for browsers with bugs (IE) | |
// | |
if (this.msiePositionBug) { | |
if (this.msieSizeBug) | |
{zoom.style.height = bbox.zH+"px"; zoom.style.width = bbox.zW+"px"} // IE8 gets the dimensions completely wrong | |
if (zoom.offsetHeight > Mh) {zoom.style.height = Mh+"px"; zoom.style.width = (bbox.zW+this.scrollSize)+"px"} // IE doesn't do max-height? | |
if (zoom.offsetWidth > Mw) {zoom.style.width = Mw+"px"; zoom.style.height = (bbox.zH+this.scrollSize)+"px"} | |
} | |
if (this.operaPositionBug) {zoom.style.width = Math.min(Mw,bbox.zW)+"px"} // Opera gets width as 0? | |
if (zoom.offsetWidth > eW && zoom.offsetWidth-eW < Mw && zoom.offsetHeight-eW < Mh) | |
{zoom.style.overflow = "visible"} // don't show scroll bars if we don't need to | |
this.Position(zoom,bbox); | |
if (this.msieTrapEventBug) { | |
trap.style.height = zoom.clientHeight+"px"; trap.style.width = zoom.clientWidth+"px"; | |
trap.style.left = (parseFloat(zoom.style.left)+zoom.clientLeft)+"px"; | |
trap.style.top = (parseFloat(zoom.style.top)+zoom.clientTop)+"px"; | |
} | |
zoom.style.visibility = ""; | |
// | |
// Add event handlers | |
// | |
if (this.settings.zoom === "Hover") {overlay.onmouseover = this.Remove} | |
if (window.addEventListener) {addEventListener("resize",this.Resize,false)} | |
else if (window.attachEvent) {attachEvent("onresize",this.Resize)} | |
else {this.onresize = window.onresize; window.onresize = this.Resize} | |
// | |
// Let others know about the zoomed math | |
// | |
HUB.signal.Post(["math zoomed",jax]); | |
// | |
// Canel further actions | |
// | |
return FALSE(event); | |
}, | |
// | |
// Set the position of the zoom box and overlay | |
// | |
Position: function (zoom,bbox) { | |
zoom.style.display = "none"; // avoids getting excessive width in Resize() | |
var XY = this.Resize(), x = XY.x, y = XY.y, W = bbox.mW; | |
zoom.style.display = ""; | |
var dx = -W-Math.floor((zoom.offsetWidth-W)/2), dy = bbox.Y; | |
zoom.style.left = Math.max(dx,10-x)+"px"; zoom.style.top = Math.max(dy,10-y)+"px"; | |
if (!ZOOM.msiePositionBug) {ZOOM.SetWH()} // refigure overlay width/height | |
}, | |
// | |
// Handle resizing of overlay while zoom is displayed | |
// | |
Resize: function (event) { | |
if (ZOOM.onresize) {ZOOM.onresize(event)} | |
var div = document.getElementById("MathJax_ZoomFrame"), | |
overlay = document.getElementById("MathJax_ZoomOverlay"); | |
var xy = ZOOM.getXY(div), obj = ZOOM.findContainer(div); | |
if (ZOOM.getOverflow(obj) !== "visible") { | |
overlay.scroll_parent = obj; // Save this for future reference. | |
var XY = ZOOM.getXY(obj); // Remove container position | |
xy.x -= XY.x; xy.y -= XY.y; | |
XY = ZOOM.getBorder(obj); // Remove container border | |
xy.x -= XY.x; xy.y -= XY.y; | |
} | |
overlay.style.left = (-xy.x)+"px"; overlay.style.top = (-xy.y)+"px"; | |
if (ZOOM.msiePositionBug) {setTimeout(ZOOM.SetWH,0)} else {ZOOM.SetWH()} | |
return xy; | |
}, | |
SetWH: function () { | |
var overlay = document.getElementById("MathJax_ZoomOverlay"); | |
if (!overlay) return; | |
overlay.style.display = "none"; // so scrollWidth/Height will be right below | |
var doc = overlay.scroll_parent || document.documentElement || document.body; | |
overlay.style.width = doc.scrollWidth + "px"; | |
overlay.style.height = Math.max(doc.clientHeight,doc.scrollHeight) + "px"; | |
overlay.style.display = ""; | |
}, | |
findContainer: function (obj) { | |
obj = obj.parentNode; | |
while (obj.parentNode && obj !== document.body && ZOOM.getOverflow(obj) === "visible") | |
{obj = obj.parentNode} | |
return obj; | |
}, | |
// | |
// Look up CSS properties (use getComputeStyle if available, or currentStyle if not) | |
// | |
getOverflow: (window.getComputedStyle ? | |
function (obj) {return getComputedStyle(obj).overflow} : | |
function (obj) {return (obj.currentStyle||{overflow:"visible"}).overflow}), | |
getBorder: function (obj) { | |
var size = {thin: 1, medium: 2, thick: 3}; | |
var style = (window.getComputedStyle ? getComputedStyle(obj) : | |
(obj.currentStyle || {borderLeftWidth:0,borderTopWidth:0})); | |
var x = style.borderLeftWidth, y = style.borderTopWidth; | |
if (size[x]) {x = size[x]} else {x = parseInt(x)} | |
if (size[y]) {y = size[y]} else {y = parseInt(y)} | |
return {x:x, y:y}; | |
}, | |
// | |
// Get the position of an element on the page | |
// | |
getXY: function (div) { | |
var x = 0, y = 0, obj; | |
obj = div; while (obj.offsetParent) {x += obj.offsetLeft; obj = obj.offsetParent} | |
if (ZOOM.operaPositionBug) {div.style.border = "1px solid"} // to get vertical position right | |
obj = div; while (obj.offsetParent) {y += obj.offsetTop; obj = obj.offsetParent} | |
if (ZOOM.operaPositionBug) {div.style.border = ""} | |
return {x:x, y:y}; | |
}, | |
// | |
// Remove zoom display and event handlers | |
// | |
Remove: function (event) { | |
var div = document.getElementById("MathJax_ZoomFrame"); | |
if (div) { | |
var JAX = MathJax.OutputJax[div.previousSibling.jaxID]; | |
var jax = JAX.getJaxFromMath(div.previousSibling); | |
HUB.signal.Post(["math unzoomed",jax]); | |
div.parentNode.removeChild(div); | |
div = document.getElementById("MathJax_ZoomTracker"); | |
if (div) {div.parentNode.removeChild(div)} | |
if (ZOOM.operaRefreshBug) { | |
// force a redisplay of the page | |
// (Opera doesn't refresh properly after the zoom is removed) | |
var overlay = HTML.addElement(document.body,"div",{ | |
style:{position:"fixed", left:0, top:0, width:"100%", height:"100%", | |
backgroundColor:"white", opacity:0}, | |
id: "MathJax_OperaDiv" | |
}); | |
document.body.removeChild(overlay); | |
} | |
if (window.removeEventListener) {removeEventListener("resize",ZOOM.Resize,false)} | |
else if (window.detachEvent) {detachEvent("onresize",ZOOM.Resize)} | |
else {window.onresize = ZOOM.onresize; delete ZOOM.onresize} | |
} | |
return FALSE(event); | |
} | |
}; | |
/*************************************************************/ | |
HUB.Browser.Select({ | |
MSIE: function (browser) { | |
var mode = (document.documentMode || 0); | |
var isIE9 = (mode >= 9); | |
ZOOM.msiePositionBug = !isIE9; | |
ZOOM.msieSizeBug = browser.versionAtLeast("7.0") && | |
(!document.documentMode || mode === 7 || mode === 8); | |
ZOOM.msieZIndexBug = (mode <= 7); | |
ZOOM.msieInlineBlockAlignBug = (mode <= 7); | |
ZOOM.msieTrapEventBug = !window.addEventListener; | |
if (document.compatMode === "BackCompat") {ZOOM.scrollSize = 52} // don't know why this is so far off | |
if (isIE9) {delete CONFIG.styles["#MathJax_Zoom"].filter} | |
}, | |
Opera: function (browser) { | |
ZOOM.operaPositionBug = true; | |
ZOOM.operaRefreshBug = true; | |
} | |
}); | |
ZOOM.topImg = (ZOOM.msieInlineBlockAlignBug ? | |
HTML.Element("img",{style:{width:0,height:0,position:"relative"},src:"about:blank"}) : | |
HTML.Element("span",{style:{width:0,height:0,display:"inline-block"}}) | |
); | |
if (ZOOM.operaPositionBug || ZOOM.msieTopBug) {ZOOM.topImg.style.border="1px solid"} | |
/*************************************************************/ | |
MathJax.Callback.Queue( | |
["StartupHook",MathJax.Hub.Register,"Begin Styles",{}], | |
["Styles",AJAX,CONFIG.styles], | |
["Post",HUB.Startup.signal,"MathZoom Ready"], | |
["loadComplete",AJAX,"[MathJax]/extensions/MathZoom.js"] | |
); | |
})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.OutputJax["HTML-CSS"],MathJax.OutputJax.NativeMML); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/extensions/MathMenu.js | |
* | |
* Implements a right-mouse (or CTRL-click) menu over mathematics | |
* elements that gives the user the ability to copy the source, | |
* change the math size, and zoom settings. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
(function (HUB,HTML,AJAX,CALLBACK,OUTPUT) { | |
var VERSION = "2.7.1"; | |
var SIGNAL = MathJax.Callback.Signal("menu"); // signal for menu events | |
MathJax.Extension.MathMenu = { | |
version: VERSION, | |
signal: SIGNAL | |
}; | |
var _ = function (id) { | |
return MathJax.Localization._.apply( | |
MathJax.Localization, | |
[["MathMenu",id]].concat([].slice.call(arguments,1)) | |
); | |
}; | |
var isArray = MathJax.Object.isArray; | |
var isPC = HUB.Browser.isPC, isMSIE = HUB.Browser.isMSIE, isIE9 = ((document.documentMode||0) > 8); | |
var ROUND = (isPC ? null : "5px"); | |
var CONFIG = HUB.CombineConfig("MathMenu",{ | |
delay: 150, // the delay for submenus | |
showRenderer: true, // show the "Math Renderer" menu? | |
showMathPlayer: true, // show the "MathPlayer" menu? | |
showFontMenu: false, // show the "Font Preference" menu? | |
showContext: false, // show the "Context Menu" menu? | |
showDiscoverable: false, // show the "Discoverable" menu? | |
showLocale: true, // show the "Locale" menu? | |
showLocaleURL: false, // show the "Load from URL" menu? | |
semanticsAnnotations: { | |
"TeX": ["TeX", "LaTeX", "application/x-tex"], | |
"StarMath": ["StarMath 5.0"], | |
"Maple": ["Maple"], | |
"ContentMathML": ["MathML-Content", "application/mathml-content+xml"], | |
"OpenMath": ["OpenMath"] | |
}, | |
windowSettings: { // for source window | |
status: "no", toolbar: "no", locationbar: "no", menubar: "no", | |
directories: "no", personalbar: "no", resizable: "yes", scrollbars: "yes", | |
width: 400, height: 300, | |
left: Math.round((screen.width - 400)/2), | |
top: Math.round((screen.height - 300)/3) | |
}, | |
styles: { | |
"#MathJax_About": { | |
position:"fixed", left:"50%", width:"auto", "text-align":"center", | |
border:"3px outset", padding:"1em 2em", "background-color":"#DDDDDD", color:"black", | |
cursor: "default", "font-family":"message-box", "font-size":"120%", | |
"font-style":"normal", "text-indent":0, "text-transform":"none", | |
"line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal", | |
"word-wrap":"normal", "white-space":"nowrap", "float":"none", "z-index":201, | |
"border-radius": "15px", // Opera 10.5 and IE9 | |
"-webkit-border-radius": "15px", // Safari and Chrome | |
"-moz-border-radius": "15px", // Firefox | |
"-khtml-border-radius": "15px", // Konqueror | |
"box-shadow":"0px 10px 20px #808080", // Opera 10.5 and IE9 | |
"-webkit-box-shadow":"0px 10px 20px #808080", // Safari 3 and Chrome | |
"-moz-box-shadow":"0px 10px 20px #808080", // Forefox 3.5 | |
"-khtml-box-shadow":"0px 10px 20px #808080", // Konqueror | |
filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE | |
}, | |
"#MathJax_About.MathJax_MousePost": { | |
outline:"none" | |
}, | |
".MathJax_Menu": { | |
position:"absolute", "background-color":"white", color:"black", | |
width:"auto", padding:(isPC ? "2px" : "5px 0px"), | |
border:"1px solid #CCCCCC", margin:0, cursor:"default", | |
font: "menu", "text-align":"left", "text-indent":0, "text-transform":"none", | |
"line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal", | |
"word-wrap":"normal", "white-space":"nowrap", "float":"none", "z-index":201, | |
"border-radius": ROUND, // Opera 10.5 and IE9 | |
"-webkit-border-radius": ROUND, // Safari and Chrome | |
"-moz-border-radius": ROUND, // Firefox | |
"-khtml-border-radius": ROUND, // Konqueror | |
"box-shadow":"0px 10px 20px #808080", // Opera 10.5 and IE9 | |
"-webkit-box-shadow":"0px 10px 20px #808080", // Safari 3 and Chrome | |
"-moz-box-shadow":"0px 10px 20px #808080", // Forefox 3.5 | |
"-khtml-box-shadow":"0px 10px 20px #808080", // Konqueror | |
filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE | |
}, | |
".MathJax_MenuItem": { | |
padding: (isPC ? "2px 2em" : "1px 2em"), | |
background:"transparent" | |
}, | |
".MathJax_MenuArrow": { | |
position:"absolute", right:".5em", "padding-top":".25em", color:"#666666", | |
"font-family": (isMSIE ? "'Arial unicode MS'" : null), "font-size": ".75em" | |
}, | |
".MathJax_MenuActive .MathJax_MenuArrow": {color:"white"}, | |
".MathJax_MenuArrow.RTL": {left:".5em", right:"auto"}, | |
".MathJax_MenuCheck": { | |
position:"absolute", left:".7em", | |
"font-family": (isMSIE ? "'Arial unicode MS'" : null) | |
}, | |
".MathJax_MenuCheck.RTL": {right:".7em", left:"auto"}, | |
".MathJax_MenuRadioCheck": { | |
position:"absolute", left: (isPC ? "1em" : ".7em") | |
}, | |
".MathJax_MenuRadioCheck.RTL": { | |
right: (isPC ? "1em" : ".7em"), left:"auto" | |
}, | |
".MathJax_MenuLabel": { | |
padding: (isPC ? "2px 2em 4px 1.33em" : "1px 2em 3px 1.33em"), | |
"font-style":"italic" | |
}, | |
".MathJax_MenuRule": { | |
"border-top": (isPC ? "1px solid #CCCCCC" : "1px solid #DDDDDD"), | |
margin: (isPC ? "4px 1px 0px" : "4px 3px") | |
}, | |
".MathJax_MenuDisabled": { | |
color:"GrayText" | |
}, | |
".MathJax_MenuActive": { | |
"background-color": (isPC ? "Highlight" : "#606872"), | |
color: (isPC ? "HighlightText" : "white") | |
}, | |
".MathJax_MenuDisabled:focus, .MathJax_MenuLabel:focus": { | |
"background-color": "#E8E8E8" | |
}, | |
".MathJax_ContextMenu:focus": { | |
outline:"none" | |
}, | |
".MathJax_ContextMenu .MathJax_MenuItem:focus": { | |
outline:"none" | |
}, | |
"#MathJax_AboutClose": { | |
top:".2em", right:".2em" | |
}, | |
".MathJax_Menu .MathJax_MenuClose": { | |
top:"-10px", left:"-10px" | |
}, | |
".MathJax_MenuClose": { | |
position:"absolute", | |
cursor:"pointer", | |
display:"inline-block", | |
border:"2px solid #AAA", | |
"border-radius":"18px", | |
"-webkit-border-radius": "18px", // Safari and Chrome | |
"-moz-border-radius": "18px", // Firefox | |
"-khtml-border-radius": "18px", // Konqueror | |
"font-family":"'Courier New',Courier", | |
"font-size":"24px", | |
color:"#F0F0F0" | |
}, | |
".MathJax_MenuClose span": { | |
display:"block", "background-color":"#AAA", border:"1.5px solid", | |
"border-radius":"18px", | |
"-webkit-border-radius": "18px", // Safari and Chrome | |
"-moz-border-radius": "18px", // Firefox | |
"-khtml-border-radius": "18px", // Konqueror | |
"line-height":0, | |
padding:"8px 0 6px" // may need to be browser-specific | |
}, | |
".MathJax_MenuClose:hover": { | |
color:"white!important", | |
border:"2px solid #CCC!important" | |
}, | |
".MathJax_MenuClose:hover span": { | |
"background-color":"#CCC!important" | |
}, | |
".MathJax_MenuClose:hover:focus": { | |
outline:"none" | |
} | |
} | |
}); | |
var FALSE, HOVER, KEY; | |
HUB.Register.StartupHook("MathEvents Ready",function () { | |
FALSE = MathJax.Extension.MathEvents.Event.False; | |
HOVER = MathJax.Extension.MathEvents.Hover; | |
KEY = MathJax.Extension.MathEvents.Event.KEY; | |
}); | |
/*************************************************************/ | |
/* | |
* Abstract class of all keyboard navigatable objects. | |
*/ | |
var NAV = MathJax.Object.Subclass({ | |
/* | |
* Moving in the list of items. | |
*/ | |
Keydown: function(event, menu) { | |
switch (event.keyCode) { | |
case KEY.ESCAPE: | |
this.Remove(event, menu); | |
break; | |
case KEY.RIGHT: | |
this.Right(event, menu); | |
break; | |
case KEY.LEFT: | |
this.Left(event, menu); | |
break; | |
case KEY.UP: | |
this.Up(event, menu); | |
break; | |
case KEY.DOWN: | |
this.Down(event, menu); | |
break; | |
case KEY.RETURN: | |
case KEY.SPACE: | |
this.Space(event, menu); | |
break; | |
default: | |
return; | |
break; | |
} | |
return FALSE(event); | |
}, | |
Escape: function(event, menu) { }, | |
Right: function(event, menu) { }, | |
Left: function(event, menu) { }, | |
Up: function(event, menu) { }, | |
Down: function(event, menu) { }, | |
Space: function(event, menu) { } | |
}, {}); | |
/*************************************************************/ | |
/* | |
* The main menu class | |
*/ | |
var MENU = MathJax.Menu = NAV.Subclass({ | |
version: VERSION, | |
items: [], | |
posted: false, | |
title: null, | |
margin: 5, | |
Init: function (def) {this.items = [].slice.call(arguments,0)}, | |
With: function (def) {if (def) {HUB.Insert(this,def)}; return this}, | |
/* | |
* Display the menu | |
*/ | |
Post: function (event,parent,forceLTR) { | |
if (!event) {event = window.event||{}} | |
var div = document.getElementById("MathJax_MenuFrame"); | |
if (!div) { | |
div = MENU.Background(this); | |
delete ITEM.lastItem; delete ITEM.lastMenu; | |
delete MENU.skipUp; | |
SIGNAL.Post(["post",MENU.jax]); | |
MENU.isRTL = (MathJax.Localization.fontDirection() === "rtl"); | |
} | |
var menu = HTML.Element("div",{ | |
onmouseup: MENU.Mouseup, ondblclick: FALSE, | |
ondragstart: FALSE, onselectstart: FALSE, oncontextmenu: FALSE, | |
menuItem: this, className: "MathJax_Menu", onkeydown: MENU.Keydown, | |
role: "menu" | |
}); | |
if (event.type === "contextmenu" || event.type === "mouseover") | |
menu.className += " MathJax_ContextMenu"; | |
if (!forceLTR) {MathJax.Localization.setCSS(menu)} | |
for (var i = 0, m = this.items.length; i < m; i++) {this.items[i].Create(menu)} | |
if (MENU.isMobile) { | |
HTML.addElement(menu,"span",{ | |
className: "MathJax_MenuClose", menu: parent, | |
ontouchstart: MENU.Close, ontouchend: FALSE, onmousedown: MENU.Close, onmouseup: FALSE | |
},[["span",{},"\u00D7"]]); | |
} | |
div.appendChild(menu); | |
this.posted = true; | |
if (menu.offsetWidth) menu.style.width = (menu.offsetWidth+2) + "px"; | |
var x = event.pageX, y = event.pageY; | |
var bbox = document.body.getBoundingClientRect(); | |
var styles = (window.getComputedStyle ? window.getComputedStyle(document.body) : {marginLeft: "0px"}); | |
var bodyRight = bbox.right - Math.min(0,bbox.left) + parseFloat(styles.marginLeft); | |
if (!x && !y && "clientX" in event) { | |
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; | |
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; | |
} | |
if (!parent) { | |
var node = MENU.CurrentNode() || event.target; | |
if ((event.type === "keydown" || (!x && !y)) && node) { | |
var offsetX = window.pageXOffset || document.documentElement.scrollLeft; | |
var offsetY = window.pageYOffset || document.documentElement.scrollTop; | |
var rect = node.getBoundingClientRect(); | |
x = (rect.right + rect.left) / 2 + offsetX; | |
y = (rect.bottom + rect.top) / 2 + offsetY; | |
} | |
if (x + menu.offsetWidth > bodyRight - this.margin) | |
{x = bodyRight - menu.offsetWidth - this.margin} | |
if (MENU.isMobile) {x = Math.max(5,x-Math.floor(menu.offsetWidth/2)); y -= 20} | |
MENU.skipUp = event.isContextMenu; | |
} else { | |
var side = "left", mw = parent.offsetWidth; | |
x = (MENU.isMobile ? 30 : mw - 2); y = 0; | |
while (parent && parent !== div) { | |
x += parent.offsetLeft; y += parent.offsetTop; | |
parent = parent.parentNode; | |
} | |
if (!MENU.isMobile) { | |
if ((MENU.isRTL && x - mw - menu.offsetWidth > this.margin) || | |
(!MENU.isRTL && x + menu.offsetWidth > bodyRight - this.margin)) | |
{side = "right"; x = Math.max(this.margin,x - mw - menu.offsetWidth + 6)} | |
} | |
if (!isPC) { | |
// in case these ever get implemented | |
menu.style["borderRadiusTop"+side] = 0; // Opera 10.5 | |
menu.style["WebkitBorderRadiusTop"+side] = 0; // Safari and Chrome | |
menu.style["MozBorderRadiusTop"+side] = 0; // Firefox | |
menu.style["KhtmlBorderRadiusTop"+side] = 0; // Konqueror | |
} | |
} | |
menu.style.left = x+"px"; menu.style.top = y+"px"; | |
if (document.selection && document.selection.empty) {document.selection.empty()} | |
// Focusing while keeping the scroll position. | |
var oldX = window.pageXOffset || document.documentElement.scrollLeft; | |
var oldY = window.pageYOffset || document.documentElement.scrollTop; | |
MENU.Focus(menu); | |
if (event.type === "keydown") { | |
MENU.skipMouseoverFromKey = true; | |
setTimeout(function() {delete MENU.skipMouseoverFromKey;}, CONFIG.delay); | |
} | |
window.scrollTo(oldX, oldY); | |
return FALSE(event); | |
}, | |
/* | |
* Remove the menu from the screen | |
*/ | |
Remove: function (event,menu) { | |
SIGNAL.Post(["unpost",MENU.jax]); | |
var div = document.getElementById("MathJax_MenuFrame"); | |
if (div) { | |
div.parentNode.removeChild(div); | |
if (this.msieFixedPositionBug) {detachEvent("onresize",MENU.Resize)} | |
} | |
if (MENU.jax.hover) { | |
delete MENU.jax.hover.nofade; | |
HOVER.UnHover(MENU.jax); | |
} | |
MENU.Unfocus(menu); | |
if (event.type === "mousedown") MENU.CurrentNode().blur(); | |
return FALSE(event); | |
}, | |
/* | |
* Find an item in a menu (or submenu) by name (Find) or ID (FindID). | |
* A list of names or IDs means descend into submenus. | |
*/ | |
Find: function (name) {return this.FindN(1,name,[].slice.call(arguments,1))}, | |
FindId: function (name) {return this.FindN(0,name,[].slice.call(arguments,1))}, | |
FindN: function (n,name,names) { | |
for (var i = 0, m = this.items.length; i < m; i++) { | |
if (this.items[i].name[n] === name) { | |
if (names.length) { | |
if (!this.items[i].submenu) {return null} | |
return this.items[i].submenu.FindN(n,names[0],names.slice(1)); | |
} | |
return this.items[i]; | |
} | |
} | |
return null; | |
}, | |
/* | |
* Find the index of a menu item (so we can insert before or after it) | |
*/ | |
IndexOf: function (name) {return this.IndexOfN(1,name)}, | |
IndexOfId: function (name) {return this.IndexOfN(0,name)}, | |
IndexOfN: function (n,name) { | |
for (var i = 0, m = this.items.length; i < m; i++) | |
{if (this.items[i].name[n] === name) {return i}} | |
return null; | |
}, | |
Right: function(event, menu) { | |
MENU.Right(event, menu); | |
}, | |
Left: function(event, menu) { | |
MENU.Left(event, menu); | |
}, | |
Up: function(event, menu) { | |
var node = menu.lastChild; | |
node.menuItem.Activate(event, node); | |
}, | |
Down: function(event, menu) { | |
var node = menu.firstChild; | |
node.menuItem.Activate(event, node); | |
}, | |
Space: function(event, menu) { | |
this.Remove(event, menu); | |
} | |
},{ | |
config: CONFIG, | |
Remove: function (event) {return MENU.Event(event,this,"Remove")}, | |
Mouseover: function (event) {return MENU.Event(event,this,"Mouseover")}, | |
Mouseout: function (event) {return MENU.Event(event,this,"Mouseout")}, | |
Mousedown: function (event) {return MENU.Event(event,this,"Mousedown")}, | |
Mouseup: function (event) {return MENU.Event(event,this,"Mouseup")}, | |
Keydown: function (event) {return MENU.Event(event,this,"Keydown")}, | |
/* | |
* Events for mobile devices. | |
*/ | |
Touchstart: function (event) {return MENU.Event(event,this,"Touchstart")}, | |
Touchend: function (event) {return MENU.Event(event,this,"Touchend")}, | |
Close: function (event) { | |
return MENU.Event(event,this.menu||this.parentNode,(this.menu?"Touchend":"Remove")); | |
}, | |
Event: function (event,menu,type,force) { | |
if (MENU.skipMouseover && type === "Mouseover" && !force) {return FALSE(event)} | |
if (MENU.skipMouseoverFromKey && type === "Mouseover") { | |
delete MENU.skipMouseoverFromKey; | |
return FALSE(event); | |
} | |
if (MENU.skipUp) { | |
if (type.match(/Mouseup|Touchend/)) {delete MENU.skipUp; return FALSE(event)} | |
if (type === "Touchstart" || | |
(type === "Mousedown" && !MENU.skipMousedown)) {delete MENU.skipUp} | |
} | |
if (!event) {event = window.event} | |
var item = menu.menuItem; | |
if (item && item[type]) {return item[type](event,menu)} | |
return null; | |
}, | |
/* | |
* Style for the background DIV | |
*/ | |
BGSTYLE: { | |
position:"absolute", left:0, top:0, "z-index":200, | |
width:"100%", height:"100%", border:0, padding:0, margin:0 | |
}, | |
Background: function (menu) { | |
var div = HTML.addElement(document.body,"div", | |
{style:this.BGSTYLE, id:"MathJax_MenuFrame"}, | |
[["div",{style: this.BGSTYLE, menuItem: menu, onmousedown: this.Remove}]]); | |
var bg = div.firstChild; | |
if (MENU.msieBackgroundBug) { | |
// MSIE doesn't allow transparent background to be hit boxes, so | |
// fake it using opacity with solid background color | |
bg.style.backgroundColor = "white"; bg.style.filter = "alpha(opacity=0)"; | |
} | |
if (MENU.msieFixedPositionBug) { | |
// MSIE can't do fixed position, so use a full-sized background | |
// and an onresize handler to update it (stupid, but necessary) | |
div.width = div.height = 0; this.Resize(); | |
attachEvent("onresize",this.Resize); | |
} else { | |
// otherwise, use a fixed position DIV to cover the viewport | |
bg.style.position = "fixed"; | |
} | |
return div; | |
}, | |
Resize: function () {setTimeout(MENU.SetWH,0)}, | |
SetWH: function () { | |
var bg = document.getElementById("MathJax_MenuFrame"); | |
if (bg) { | |
bg = bg.firstChild; | |
bg.style.width = bg.style.height = "1px"; // so scrollWidth/Height will be right below | |
bg.style.width = document.body.scrollWidth + "px"; | |
bg.style.height = document.body.scrollHeight + "px"; | |
} | |
}, | |
/*************************************************************/ | |
/* | |
* Keyboard navigation of menu. | |
*/ | |
posted: false, // Is a menu open? | |
active: null, // The focused in HTML node in the menu. | |
GetNode: function(jax) { | |
var node = document.getElementById(jax.inputID + "-Frame"); | |
return node.isMathJax ? node : node.firstChild; | |
}, | |
CurrentNode: function() { | |
return MENU.GetNode(MENU.jax); | |
}, | |
AllNodes: function() { | |
var jaxs = MathJax.Hub.getAllJax(); | |
var nodes = []; | |
for (var i = 0, jax; jax = jaxs[i]; i++) { | |
nodes.push(MENU.GetNode(jax)); | |
} | |
return nodes; | |
}, | |
ActiveNode: function() { | |
return MENU.active; | |
}, | |
FocusNode: function(node) { | |
MENU.active = node; | |
node.focus(); | |
}, | |
// | |
// Focus is a global affair, since we only ever want a single focused item. | |
// | |
Focus: function(menu) { | |
!MENU.posted ? MENU.Activate(menu) : MENU.ActiveNode().tabIndex = -1; | |
menu.tabIndex = 0; | |
MENU.FocusNode(menu); | |
}, | |
Activate: function(event, menu) { | |
MENU.UnsetTabIndex(); | |
MENU.posted = true; | |
}, | |
Unfocus: function() { | |
MENU.ActiveNode().tabIndex = -1; | |
MENU.SetTabIndex(); | |
MENU.FocusNode(MENU.CurrentNode()); | |
MENU.posted = false; | |
}, | |
MoveHorizontal: function(event, menu, move) { | |
if (!event.shiftKey) return; | |
var jaxs = MENU.AllNodes(); | |
var len = jaxs.length; | |
if (len === 0) return; | |
var next = jaxs[MENU.Mod(move(MENU.IndexOf(jaxs, MENU.CurrentNode())), len)]; | |
if (next === MENU.CurrentNode()) return; | |
MENU.menu.Remove(event, menu); | |
MENU.jax = MathJax.Hub.getJaxFor(next); | |
MENU.FocusNode(next); | |
MENU.menu.Post(null); | |
}, | |
Right: function(event, menu) { | |
MENU.MoveHorizontal(event, menu, function(x) {return x + 1;}); | |
}, | |
Left: function(event, menu) { | |
MENU.MoveHorizontal(event, menu, function(x) {return x - 1;}); | |
}, | |
UnsetTabIndex: function () { | |
var jaxs = MENU.AllNodes(); | |
for (var j = 0, jax; jax = jaxs[j]; j++) { | |
if (jax.tabIndex > 0) { | |
jax.oldTabIndex = jax.tabIndex; | |
} | |
jax.tabIndex = -1; | |
} | |
}, | |
SetTabIndex: function () { | |
var jaxs = MENU.AllNodes(); | |
for (var j = 0, jax; jax = jaxs[j]; j++) { | |
if (jax.oldTabIndex !== undefined) { | |
jax.tabIndex = jax.oldTabIndex | |
delete jax.oldTabIndex; | |
} else { | |
jax.tabIndex = HUB.getTabOrder(jax); | |
} | |
} | |
}, | |
//TODO: Move to utility class. | |
// Computes a mod n. | |
Mod: function(a, n) { | |
return ((a % n) + n) % n; | |
}, | |
IndexOf: (Array.prototype.indexOf ? | |
function (A, item, start) {return A.indexOf(item, start);} : | |
function (A, item, start) { | |
for (var i = (start || 0), j = A.length; i < j; i++) { | |
if (item === A[i]) return i; | |
} | |
return -1; | |
}), | |
saveCookie: function () {HTML.Cookie.Set("menu",this.cookie)}, | |
getCookie: function () {this.cookie = HTML.Cookie.Get("menu")} | |
}); | |
MathJax.Menu.NAV = NAV; | |
/*************************************************************/ | |
/* | |
* Abstract class of menu items. | |
*/ | |
var ITEM = MENU.ITEM = NAV.Subclass({ | |
name: "", // The menu item's label as [id,label] pair. | |
node: null, // The HTML node of the item. | |
menu: null, // The parent menu containing that item. HTML node. | |
Attributes: function(def) { | |
return HUB.Insert( | |
{onmouseup: MENU.Mouseup, | |
ondragstart: FALSE, onselectstart: FALSE, onselectend: FALSE, | |
ontouchstart: MENU.Touchstart, ontouchend: MENU.Touchend, | |
className: "MathJax_MenuItem", role: this.role, | |
menuItem: this}, | |
def); | |
}, | |
Create: function (menu) { | |
if (!this.hidden) { | |
var def = this.Attributes(); | |
var label = this.Label(def,menu); | |
HTML.addElement(menu, "div", def, label); | |
} | |
}, | |
Name: function () {return _(this.name[0],this.name[1])}, | |
Mouseover: function (event,menu) { | |
if (menu.parentNode === MENU.ActiveNode().parentNode) { | |
this.Deactivate(MENU.ActiveNode()); | |
} | |
this.Activate(event, menu); | |
}, | |
Mouseout: function (event,menu) { | |
this.Deactivate(menu); | |
}, | |
Mouseup: function (event,menu) {return this.Remove(event,menu)}, | |
DeactivateSubmenus: function(menu) { | |
var menus = document.getElementById("MathJax_MenuFrame").childNodes, | |
items = ITEM.GetMenuNode(menu).childNodes; | |
for (var i = 0, m = items.length; i < m; i++) { | |
var item = items[i].menuItem; | |
// Deactivates submenu items. | |
if (item && item.submenu && item.submenu.posted && | |
item !== menu.menuItem) { | |
item.Deactivate(items[i]); | |
} | |
} | |
this.RemoveSubmenus(menu, menus); | |
}, | |
RemoveSubmenus: function(menu, menus) { | |
menus = menus || document.getElementById("MathJax_MenuFrame").childNodes; | |
var m = menus.length-1; | |
while (m >= 0 && ITEM.GetMenuNode(menu).menuItem !== menus[m].menuItem) { | |
menus[m].menuItem.posted = false; | |
menus[m].parentNode.removeChild(menus[m]); | |
m--; | |
} | |
}, | |
Touchstart: function (event,menu) {return this.TouchEvent(event,menu,"Mousedown")}, | |
Touchend: function (event,menu) {return this.TouchEvent(event,menu,"Mouseup")}, | |
TouchEvent: function (event,menu,type) { | |
if (this !== ITEM.lastItem) { | |
if (ITEM.lastMenu) {MENU.Event(event,ITEM.lastMenu,"Mouseout")} | |
MENU.Event(event,menu,"Mouseover",true); | |
ITEM.lastItem = this; ITEM.lastMenu = menu; | |
} | |
if (this.nativeTouch) {return null} | |
MENU.Event(event,menu,type); | |
return false; | |
}, | |
Remove: function (event,menu) { | |
menu = menu.parentNode.menuItem; | |
return menu.Remove(event,menu); | |
}, | |
With: function (def) {if (def) {HUB.Insert(this,def)}; return this}, | |
isRTL: function () {return MENU.isRTL}, | |
rtlClass: function () {return (this.isRTL() ? " RTL" : "")} | |
}, { | |
GetMenuNode: function(item) { | |
return item.parentNode; | |
} | |
}); | |
/*************************************************************/ | |
/* | |
* Abstract class of menu items that are focusable and perform some action | |
*/ | |
MENU.ENTRY = MENU.ITEM.Subclass({ | |
role: "menuitem", // Aria role. | |
Attributes: function(def) { | |
def = HUB.Insert( | |
{onmouseover: MENU.Mouseover, onmouseout: MENU.Mouseout, | |
onmousedown: MENU.Mousedown, onkeydown: MENU.Keydown, | |
"aria-disabled": !!this.disabled}, | |
def); | |
def = this.SUPER(arguments).Attributes.call(this, def); | |
if (this.disabled) { | |
def.className += " MathJax_MenuDisabled"; | |
} | |
return def; | |
}, | |
MoveVertical: function(event, item, move) { | |
var menuNode = ITEM.GetMenuNode(item); | |
var items = []; | |
for (var i = 0, allItems = menuNode.menuItem.items, it; | |
it = allItems[i]; i++) { | |
if (!it.hidden) { | |
items.push(it); | |
} | |
} | |
var index = MENU.IndexOf(items, this); | |
if (index === -1) return; | |
var len = items.length; | |
var children = menuNode.childNodes; | |
do { | |
index = MENU.Mod(move(index), len); | |
} while (items[index].hidden || !children[index].role || | |
children[index].role === "separator"); | |
this.Deactivate(item); | |
items[index].Activate(event, children[index]); | |
}, | |
Up: function(event, item) { | |
this.MoveVertical(event, item, function(x) { return x - 1; }); | |
}, | |
Down: function(event, item) { | |
this.MoveVertical(event, item, function(x) { return x + 1; }); | |
}, | |
Right: function(event, item) { | |
this.MoveHorizontal(event, item, MENU.Right, !this.isRTL()); | |
}, | |
Left: function(event, item) { | |
this.MoveHorizontal(event, item, MENU.Left, this.isRTL()); | |
}, | |
MoveHorizontal: function(event, item, move, rtl) { | |
var menuNode = ITEM.GetMenuNode(item); | |
if (menuNode.menuItem === MENU.menu && event.shiftKey) { | |
move(event, item); | |
} | |
if (rtl) return; | |
if (menuNode.menuItem !== MENU.menu) { | |
this.Deactivate(item); | |
} | |
var parentNodes = menuNode.previousSibling.childNodes; | |
var length = parentNodes.length; | |
while (length--) { | |
var parent = parentNodes[length]; | |
if (parent.menuItem.submenu && | |
parent.menuItem.submenu === menuNode.menuItem) { | |
MENU.Focus(parent); | |
break; | |
} | |
} | |
this.RemoveSubmenus(item); | |
}, | |
Space: function (event, menu) { | |
this.Mouseup(event, menu); | |
}, | |
Activate: function (event, menu) { | |
this.Deactivate(menu); | |
if (!this.disabled) { | |
menu.className += " MathJax_MenuActive"; | |
} | |
this.DeactivateSubmenus(menu); | |
MENU.Focus(menu); | |
}, | |
Deactivate: function (menu) { | |
menu.className = menu.className.replace(/ MathJax_MenuActive/,""); | |
} | |
}); | |
/*************************************************************/ | |
/* | |
* A menu item that performs a command when selected | |
*/ | |
MENU.ITEM.COMMAND = MENU.ENTRY.Subclass({ | |
action: function () {}, | |
Init: function (name,action,def) { | |
if (!isArray(name)) {name = [name,name]} // make [id,label] pair | |
this.name = name; this.action = action; | |
this.With(def); | |
}, | |
Label: function (def,menu) {return [this.Name()]}, | |
Mouseup: function (event,menu) { | |
if (!this.disabled) { | |
this.Remove(event,menu); | |
SIGNAL.Post(["command",this]); | |
this.action.call(this,event); | |
} | |
return FALSE(event); | |
} | |
}); | |
/*************************************************************/ | |
/* | |
* A menu item that posts a submenu | |
*/ | |
MENU.ITEM.SUBMENU = MENU.ENTRY.Subclass({ | |
submenu: null, // the submenu | |
marker: "\u25BA", // the submenu arrow | |
markerRTL: "\u25C4", // the submenu arrow for RTL | |
Attributes: function(def) { | |
def = HUB.Insert({"aria-haspopup": "true"}, def); | |
def = this.SUPER(arguments).Attributes.call(this, def); | |
return def; | |
}, | |
Init: function (name,def) { | |
if (!isArray(name)) {name = [name,name]} // make [id,label] pair | |
this.name = name; var i = 1; | |
if (!(def instanceof MENU.ITEM)) {this.With(def), i++} | |
this.submenu = MENU.apply(MENU,[].slice.call(arguments,i)); | |
}, | |
Label: function (def,menu) { | |
this.submenu.posted = false; | |
return [this.Name()+" ",["span",{ | |
className:"MathJax_MenuArrow" + this.rtlClass() | |
},[this.isRTL() ? this.markerRTL : this.marker]]]; | |
}, | |
Timer: function (event,menu) { | |
this.ClearTimer(); | |
event = {type: event.type, | |
clientX: event.clientX, clientY: event.clientY}; // MSIE can't pass the event below | |
this.timer = setTimeout(CALLBACK(["Mouseup",this,event,menu]),CONFIG.delay); | |
}, | |
ClearTimer: function() { | |
if (this.timer) { | |
clearTimeout(this.timer); | |
} | |
}, | |
Touchend: function (event,menu) { | |
var forceout = this.submenu.posted; | |
var result = this.SUPER(arguments).Touchend.apply(this,arguments); | |
if (forceout) {this.Deactivate(menu); delete ITEM.lastItem; delete ITEM.lastMenu} | |
return result; | |
}, | |
Mouseout: function(event, menu) { | |
if (!this.submenu.posted) { | |
this.Deactivate(menu); | |
} | |
this.ClearTimer(); | |
}, | |
Mouseover: function(event, menu) { | |
this.Activate(event, menu); | |
}, | |
Mouseup: function (event,menu) { | |
if (!this.disabled) { | |
if (!this.submenu.posted) { | |
this.ClearTimer(); | |
this.submenu.Post(event, menu, this.ltr); | |
MENU.Focus(menu); | |
} else { | |
this.DeactivateSubmenus(menu); | |
} | |
} | |
return FALSE(event); | |
}, | |
Activate: function (event, menu) { | |
if (!this.disabled) { | |
this.Deactivate(menu); | |
menu.className += " MathJax_MenuActive"; | |
} | |
if (!this.submenu.posted) { | |
this.DeactivateSubmenus(menu); | |
if (!MENU.isMobile) { | |
this.Timer(event,menu); | |
} | |
} | |
MENU.Focus(menu); | |
}, | |
MoveVertical: function(event, item, move) { | |
this.ClearTimer(); | |
this.SUPER(arguments).MoveVertical.apply(this, arguments); | |
}, | |
MoveHorizontal: function(event, menu, move, rtl) { | |
if (!rtl) { | |
this.SUPER(arguments).MoveHorizontal.apply(this, arguments); | |
return; | |
} | |
if (this.disabled) return; | |
if (!this.submenu.posted) { | |
this.Activate(event, menu); | |
return; | |
} | |
var submenuNodes = ITEM.GetMenuNode(menu).nextSibling.childNodes; | |
if (submenuNodes.length > 0) { | |
this.submenu.items[0].Activate(event, submenuNodes[0]); | |
} | |
} | |
}); | |
/*************************************************************/ | |
/* | |
* A menu item that is one of several radio buttons | |
*/ | |
MENU.ITEM.RADIO = MENU.ENTRY.Subclass({ | |
variable: null, // the variable name | |
marker: (isPC ? "\u25CF" : "\u2713"), // the checkmark | |
role: "menuitemradio", | |
Attributes: function(def) { | |
var checked = CONFIG.settings[this.variable] === this.value ? "true" : "false"; | |
def = HUB.Insert({"aria-checked": checked}, def); | |
def = this.SUPER(arguments).Attributes.call(this, def); | |
return def; | |
}, | |
Init: function (name,variable,def) { | |
if (!isArray(name)) {name = [name,name]} // make [id,label] pair | |
this.name = name; this.variable = variable; this.With(def); | |
if (this.value == null) {this.value = this.name[0]} | |
}, | |
Label: function (def,menu) { | |
var span = {className:"MathJax_MenuRadioCheck" + this.rtlClass()}; | |
if (CONFIG.settings[this.variable] !== this.value) { | |
span = {style:{display:"none"}}; | |
} | |
return [["span",span,[this.marker]]," "+this.Name()]; | |
}, | |
Mouseup: function (event,menu) { | |
if (!this.disabled) { | |
var child = menu.parentNode.childNodes; | |
for (var i = 0, m = child.length; i < m; i++) { | |
var item = child[i].menuItem; | |
if (item && item.variable === this.variable) { | |
child[i].firstChild.style.display = "none"; | |
} | |
} | |
menu.firstChild.display = ""; | |
CONFIG.settings[this.variable] = this.value; | |
MENU.cookie[this.variable] = CONFIG.settings[this.variable]; MENU.saveCookie(); | |
SIGNAL.Post(["radio button",this]); | |
} | |
this.Remove(event,menu); | |
if (this.action && !this.disabled) {this.action.call(MENU,this)} | |
return FALSE(event); | |
} | |
}); | |
/*************************************************************/ | |
/* | |
* A menu item that is checkable | |
*/ | |
MENU.ITEM.CHECKBOX = MENU.ENTRY.Subclass({ | |
variable: null, // the variable name | |
marker: "\u2713", // the checkmark | |
role: "menuitemcheckbox", | |
Attributes: function(def) { | |
var checked = CONFIG.settings[this.variable] ? "true" : "false"; | |
def = HUB.Insert({"aria-checked": checked}, def); | |
def = this.SUPER(arguments).Attributes.call(this, def); | |
return def; | |
}, | |
Init: function (name,variable,def) { | |
if (!isArray(name)) {name = [name,name]} // make [id,label] pair | |
this.name = name; this.variable = variable; this.With(def); | |
}, | |
Label: function (def,menu) { | |
var span = {className:"MathJax_MenuCheck" + this.rtlClass()}; | |
if (!CONFIG.settings[this.variable]) {span = {style:{display:"none"}}} | |
return [["span",span,[this.marker]]," "+this.Name()]; | |
}, | |
Mouseup: function (event,menu) { | |
if (!this.disabled) { | |
menu.firstChild.display = (CONFIG.settings[this.variable] ? "none" : ""); | |
CONFIG.settings[this.variable] = !CONFIG.settings[this.variable]; | |
MENU.cookie[this.variable] = CONFIG.settings[this.variable]; MENU.saveCookie(); | |
SIGNAL.Post(["checkbox",this]); | |
} | |
this.Remove(event,menu); | |
if (this.action && !this.disabled) {this.action.call(MENU,this)} | |
return FALSE(event); | |
} | |
}); | |
/*************************************************************/ | |
/* | |
* A menu item that is a label | |
*/ | |
MENU.ITEM.LABEL = MENU.ENTRY.Subclass({ | |
role: "menuitem", // Aria role. | |
Init: function (name,def) { | |
if (!isArray(name)) {name = [name,name]} // make [id,label] pair | |
this.name = name; this.With(def); | |
}, | |
Label: function (def,menu) { | |
def.className += " MathJax_MenuLabel"; | |
return [this.Name()]; | |
}, | |
Activate: function(event, menu) { | |
this.Deactivate(menu); | |
MENU.Focus(menu); | |
}, | |
Mouseup: function (event,menu) { } | |
}); | |
/*************************************************************/ | |
/* | |
* A rule in a menu | |
*/ | |
MENU.ITEM.RULE = MENU.ITEM.Subclass({ | |
role: "separator", | |
Attributes: function(def) { | |
def = HUB.Insert({"aria-orientation": "vertical"}, def); | |
def = this.SUPER(arguments).Attributes.call(this, def); | |
return def; | |
}, | |
Label: function (def,menu) { | |
def.className += " MathJax_MenuRule"; | |
return null; | |
} | |
}); | |
/*************************************************************/ | |
/*************************************************************/ | |
/* | |
* Handle the ABOUT box | |
*/ | |
MENU.About = function (event) { | |
var font = MENU.About.GetFont(); | |
var format = MENU.About.GetFormat(); | |
var jax = ["MathJax.js v"+MathJax.fileversion,["br"]]; | |
jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}]); | |
MENU.About.GetJax(jax,MathJax.InputJax,["InputJax","%1 Input Jax v%2"]); | |
MENU.About.GetJax(jax,MathJax.OutputJax,["OutputJax","%1 Output Jax v%2"]); | |
MENU.About.GetJax(jax,MathJax.ElementJax,["ElementJax","%1 Element Jax v%2"]); | |
jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}]); | |
MENU.About.GetJax(jax,MathJax.Extension,["Extension","%1 Extension v%2"],true); | |
jax.push(["div",{style:{"border-top":"groove 2px",margin:".25em 0"}}],["center",{},[ | |
HUB.Browser + " v"+HUB.Browser.version + (format ? | |
" \u2014 " + _(format.replace(/ /g,""),format) : "") | |
]]); | |
MENU.About.div = MENU.Background(MENU.About); | |
var about = HTML.addElement(MENU.About.div,"div",{ | |
id: "MathJax_About", tabIndex: 0, onkeydown: MENU.About.Keydown | |
},[ | |
["b",{style:{fontSize:"120%"}},["MathJax"]]," v"+MathJax.version,["br"], | |
_(font.replace(/ /g,""),"using "+font),["br"],["br"], | |
["span",{style:{ | |
display:"inline-block", "text-align":"left", "font-size":"80%", | |
"max-height":"20em", overflow:"auto", | |
"background-color":"#E4E4E4", padding:".4em .6em", border:"1px inset" | |
}, tabIndex: 0},jax],["br"],["br"], | |
["a",{href:"http://www.mathjax.org/"},["www.mathjax.org"]], | |
["span",{className:"MathJax_MenuClose",id:"MathJax_AboutClose", | |
onclick:MENU.About.Remove, | |
onkeydown: MENU.About.Keydown, tabIndex: 0, role: "button", | |
"aria-label": _("CloseAboutDialog","Close about MathJax dialog")}, | |
[["span",{},"\u00D7"]]] | |
]); | |
if (event.type === "mouseup") about.className += " MathJax_MousePost"; | |
about.focus(); | |
MathJax.Localization.setCSS(about); | |
var doc = (document.documentElement||{}); | |
var H = window.innerHeight || doc.clientHeight || doc.scrollHeight || 0; | |
if (MENU.prototype.msieAboutBug) { | |
about.style.width = "20em"; about.style.position = "absolute"; | |
about.style.left = Math.floor((document.documentElement.scrollWidth - about.offsetWidth)/2)+"px"; | |
about.style.top = (Math.floor((H-about.offsetHeight)/3)+document.body.scrollTop)+"px"; | |
} else { | |
about.style.marginLeft = Math.floor(-about.offsetWidth/2)+"px"; | |
about.style.top = Math.floor((H-about.offsetHeight)/3)+"px"; | |
} | |
}; | |
MENU.About.Remove = function (event) { | |
if (MENU.About.div) {document.body.removeChild(MENU.About.div); delete MENU.About.div} | |
}; | |
MENU.About.Keydown = function(event) { | |
if (event.keyCode === KEY.ESCAPE || | |
(this.id === "MathJax_AboutClose" && | |
(event.keyCode === KEY.SPACE || event.keyCode === KEY.RETURN))) { | |
MENU.About.Remove(event); | |
MENU.CurrentNode().focus(); | |
FALSE(event); | |
} | |
}, | |
MENU.About.GetJax = function (jax,JAX,type,noTypeCheck) { | |
var info = []; | |
for (var id in JAX) {if (JAX.hasOwnProperty(id) && JAX[id]) { | |
if ((noTypeCheck && JAX[id].version) || (JAX[id].isa && JAX[id].isa(JAX))) | |
{info.push(_(type[0],type[1],(JAX[id].id||id),JAX[id].version))} | |
}} | |
info.sort(); | |
for (var i = 0, m = info.length; i < m; i++) {jax.push(info[i],["br"])} | |
return jax; | |
}; | |
MENU.About.GetFont = function () { | |
var jax = MathJax.Hub.outputJax["jax/mml"][0] || {}; | |
var font = { | |
SVG: "web SVG", | |
CommonHTML: "web TeX", | |
"HTML-CSS": (jax.imgFonts ? "image" : (jax.webFonts ? "web" : "local")+" "+jax.fontInUse) | |
}[jax.id] || "generic"; | |
return font + " fonts"; | |
}; | |
MENU.About.GetFormat = function () { | |
var jax = MathJax.Hub.outputJax["jax/mml"][0] || {}; | |
if (jax.id !== "HTML-CSS"|| !jax.webFonts || jax.imgFonts) return; | |
return jax.allowWebFonts.replace(/otf/,"woff or otf") + " fonts"; | |
}; | |
/* | |
* Handle the MathJax HELP menu | |
*/ | |
MENU.Help = function (event) { | |
AJAX.Require("[MathJax]/extensions/HelpDialog.js", | |
function () {MathJax.Extension.Help.Dialog({type:event.type})}); | |
}; | |
/* | |
* Handle showing of element's source | |
*/ | |
MENU.ShowSource = function (event) { | |
if (!event) {event = window.event} | |
var EVENT = {screenX:event.screenX, screenY:event.screenY}; | |
if (!MENU.jax) return; | |
if (this.format === "MathML") { | |
var MML = MathJax.ElementJax.mml; | |
if (MML && typeof(MML.mbase.prototype.toMathML) !== "undefined") { | |
// toMathML() can call MathJax.Hub.RestartAfter, so trap errors and check | |
try {MENU.ShowSource.Text(MENU.jax.root.toMathML("",MENU.jax),event)} catch (err) { | |
if (!err.restart) {throw err} | |
CALLBACK.After([this,MENU.ShowSource,EVENT],err.restart); | |
} | |
} else if (!AJAX.loadingToMathML) { | |
AJAX.loadingToMathML = true; | |
MENU.ShowSource.Window(event); // WeBKit needs to open window on click event | |
CALLBACK.Queue( | |
AJAX.Require("[MathJax]/extensions/toMathML.js"), | |
function () { | |
delete AJAX.loadingToMathML; | |
if (!MML.mbase.prototype.toMathML) {MML.mbase.prototype.toMathML = function () {}} | |
}, | |
[this,MENU.ShowSource,EVENT] // call this function again | |
); | |
return; | |
} | |
} else if (this.format === "Error") { | |
MENU.ShowSource.Text(MENU.jax.errorText,event); | |
} else if (CONFIG.semanticsAnnotations[this.format]) { | |
var annotation = MENU.jax.root.getAnnotation(this.format); | |
if (annotation.data[0]) MENU.ShowSource.Text(annotation.data[0].toString()); | |
} else { | |
if (MENU.jax.originalText == null) { | |
alert(_("NoOriginalForm","No original form available")); | |
return; | |
} | |
MENU.ShowSource.Text(MENU.jax.originalText,event); | |
} | |
}; | |
MENU.ShowSource.Window = function (event) { | |
if (!MENU.ShowSource.w) { | |
var def = [], DEF = CONFIG.windowSettings; | |
for (var id in DEF) {if (DEF.hasOwnProperty(id)) {def.push(id+"="+DEF[id])}} | |
MENU.ShowSource.w = window.open("","_blank",def.join(",")); | |
} | |
return MENU.ShowSource.w; | |
}; | |
MENU.ShowSource.Text = function (text,event) { | |
var w = MENU.ShowSource.Window(event); delete MENU.ShowSource.w; | |
text = text.replace(/^\s*/,"").replace(/\s*$/,""); | |
text = text.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | |
var title = _("EqSource","MathJax Equation Source"); | |
if (MENU.isMobile) { | |
w.document.open(); | |
w.document.write("<html><head><meta name='viewport' content='width=device-width, initial-scale=1.0' /><title>"+title+"</title></head><body style='font-size:85%'>"); | |
w.document.write("<pre>"+text+"</pre>"); | |
w.document.write("<hr><input type='button' value='"+_("Close","Close")+"' onclick='window.close()' />"); | |
w.document.write("</body></html>"); | |
w.document.close(); | |
} else { | |
w.document.open(); | |
w.document.write("<html><head><title>"+title+"</title></head><body style='font-size:85%'>"); | |
w.document.write("<table><tr><td><pre>"+text+"</pre></td></tr></table>"); | |
w.document.write("</body></html>"); | |
w.document.close(); | |
var table = w.document.body.firstChild; | |
setTimeout(function () { | |
var H = (w.outerHeight-w.innerHeight)||30, W = (w.outerWidth-w.innerWidth)||30, x, y; | |
W = Math.max(140,Math.min(Math.floor(.5*screen.width),table.offsetWidth+W+25)); | |
H = Math.max(40,Math.min(Math.floor(.5*screen.height),table.offsetHeight+H+25)); | |
if (MENU.prototype.msieHeightBug) {H += 35}; // for title bar in XP | |
w.resizeTo(W,H); | |
var X; try {X = event.screenX} catch (e) {}; // IE8 throws an error accessing screenX | |
if (event && X != null) { | |
x = Math.max(0,Math.min(event.screenX-Math.floor(W/2), screen.width-W-20)); | |
y = Math.max(0,Math.min(event.screenY-Math.floor(H/2), screen.height-H-20)); | |
w.moveTo(x,y); | |
} | |
},50); | |
} | |
}; | |
/* | |
* Handle rescaling all the math | |
*/ | |
MENU.Scale = function () { | |
var JAX = ["CommonHTML","HTML-CSS","SVG","NativeMML","PreviewHTML"], m = JAX.length, | |
SCALE = 100, i, jax; | |
for (i = 0; i < m; i++) { | |
jax = OUTPUT[JAX[i]]; | |
if (jax) {SCALE = jax.config.scale; break} | |
} | |
var scale = prompt(_("ScaleMath","Scale all mathematics (compared to surrounding text) by"),SCALE+"%"); | |
if (scale) { | |
if (scale.match(/^\s*\d+(\.\d*)?\s*%?\s*$/)) { | |
scale = parseFloat(scale); | |
if (scale) { | |
if (scale !== SCALE) { | |
for (i = 0; i < m; i++) { | |
jax = OUTPUT[JAX[i]]; | |
if (jax) jax.config.scale = scale; | |
} | |
MENU.cookie.scale = HUB.config.scale = scale; | |
MENU.saveCookie(); | |
HUB.Queue(["Rerender",HUB]); | |
} | |
} else {alert(_("NonZeroScale","The scale should not be zero"))} | |
} else {alert(_("PercentScale", | |
"The scale should be a percentage (e.g., 120%%)"))} | |
} | |
}; | |
/* | |
* Handle loading the zoom code | |
*/ | |
MENU.Zoom = function () { | |
if (!MathJax.Extension.MathZoom) {AJAX.Require("[MathJax]/extensions/MathZoom.js")} | |
}; | |
/* | |
* Handle changing the renderer | |
*/ | |
MENU.Renderer = function () { | |
var jax = HUB.outputJax["jax/mml"]; | |
if (jax[0] !== CONFIG.settings.renderer) { | |
var BROWSER = HUB.Browser, message, MESSAGE = MENU.Renderer.Messages, warned; | |
// | |
// Check that the new renderer is appropriate for the browser | |
// | |
switch (CONFIG.settings.renderer) { | |
case "NativeMML": | |
if (!CONFIG.settings.warnedMML) { | |
if (BROWSER.isChrome && BROWSER.version.substr(0,3) !== "24.") {message = MESSAGE.MML.WebKit} | |
else if (BROWSER.isSafari && !BROWSER.versionAtLeast("5.0")) {message = MESSAGE.MML.WebKit} | |
else if (BROWSER.isMSIE) {if (!BROWSER.hasMathPlayer) {message = MESSAGE.MML.MSIE}} | |
else if (BROWSER.isEdge) {message = MESSAGE.MML.WebKit} | |
else {message = MESSAGE.MML[BROWSER]} | |
warned = "warnedMML"; | |
} | |
break; | |
case "SVG": | |
if (!CONFIG.settings.warnedSVG) { | |
if (BROWSER.isMSIE && !isIE9) {message = MESSAGE.SVG.MSIE} | |
} | |
break; | |
} | |
if (message) { | |
message = _(message[0],message[1]); | |
message += "\n\n"; | |
message += _("SwitchAnyway", | |
"Switch the renderer anyway?\n\n" + | |
"(Press OK to switch, CANCEL to continue with the current renderer)"); | |
MENU.cookie.renderer = jax[0].id; MENU.saveCookie(); | |
if (!confirm(message)) { | |
MENU.cookie.renderer = CONFIG.settings.renderer = HTML.Cookie.Get("menu").renderer; | |
MENU.saveCookie(); | |
return; | |
} | |
if (warned) {MENU.cookie.warned = CONFIG.settings.warned = true} | |
MENU.cookie.renderer = CONFIG.settings.renderer; MENU.saveCookie(); | |
} | |
HUB.Queue( | |
["setRenderer",HUB,CONFIG.settings.renderer,"jax/mml"], | |
["Rerender",HUB] | |
); | |
} | |
}; | |
MENU.Renderer.Messages = { | |
MML: { | |
WebKit: ["WebkitNativeMMLWarning", | |
"Your browser doesn't seem to support MathML natively, " + | |
"so switching to MathML output may cause the mathematics " + | |
"on the page to become unreadable."], | |
MSIE: ["MSIENativeMMLWarning", | |
"Internet Explorer requires the MathPlayer plugin " + | |
"in order to process MathML output."], | |
Opera: ["OperaNativeMMLWarning", | |
"Opera's support for MathML is limited, so switching to " + | |
"MathML output may cause some expressions to render poorly."], | |
Safari: ["SafariNativeMMLWarning", | |
"Your browser's native MathML does not implement all the features " + | |
"used by MathJax, so some expressions may not render properly."], | |
Firefox: ["FirefoxNativeMMLWarning", | |
"Your browser's native MathML does not implement all the features " + | |
"used by MathJax, so some expressions may not render properly."] | |
}, | |
SVG: { | |
MSIE: ["MSIESVGWarning", | |
"SVG is not implemented in Internet Explorer prior to " + | |
"IE9 or when it is emulating IE8 or below. " + | |
"Switching to SVG output will cause the mathematics to " + | |
"not display properly."] | |
} | |
}; | |
/* | |
* Toggle assistive MML settings | |
*/ | |
MENU.AssistiveMML = function (item,restart) { | |
var AMML = MathJax.Extension.AssistiveMML; | |
if (!AMML) { | |
// Try to load the extension, but only try once. | |
if (!restart) | |
AJAX.Require("[MathJax]/extensions/AssistiveMML.js",["AssistiveMML",MENU,item,true]); | |
return; | |
} | |
MathJax.Hub.Queue([(CONFIG.settings.assistiveMML ? "Add" : "Remove")+"AssistiveMathML",AMML]); | |
}; | |
/* | |
* Handle setting the HTMLCSS fonts | |
*/ | |
MENU.Font = function () { | |
var HTMLCSS = OUTPUT["HTML-CSS"]; if (!HTMLCSS) return; | |
document.location.reload(); | |
}; | |
/* | |
* Handle selection of locale and rerender the page | |
*/ | |
MENU.Locale = function () { | |
MathJax.Localization.setLocale(CONFIG.settings.locale); | |
MathJax.Hub.Queue(["Reprocess",MathJax.Hub]); // FIXME: Just reprocess error messages? | |
}; | |
MENU.LoadLocale = function () { | |
var url = prompt(_("LoadURL","Load translation data from this URL:")); | |
if (url) { | |
if (!url.match(/\.js$/)) { | |
alert(_("BadURL", | |
"The URL should be for a javascript file that defines MathJax translation data. " + | |
"Javascript file names should end with '.js'" | |
)); | |
} | |
AJAX.Require(url,function (status) { | |
if (status != AJAX.STATUS.OK) {alert(_("BadData","Failed to load translation data from %1",url))} | |
}); | |
} | |
}; | |
/* | |
* Handle setting MathPlayer events | |
*/ | |
MENU.MPEvents = function (item) { | |
var discoverable = CONFIG.settings.discoverable, | |
MESSAGE = MENU.MPEvents.Messages; | |
if (!isIE9) { | |
if (CONFIG.settings.mpMouse && !confirm(_.apply(_,MESSAGE.IE8warning))) { | |
delete MENU.cookie.mpContext; delete CONFIG.settings.mpContext; | |
delete MENU.cookie.mpMouse; delete CONFIG.settings.mpMouse; | |
MENU.saveCookie(); | |
return; | |
} | |
CONFIG.settings.mpContext = CONFIG.settings.mpMouse; | |
MENU.cookie.mpContext = MENU.cookie.mpMouse = CONFIG.settings.mpMouse; | |
MENU.saveCookie(); | |
MathJax.Hub.Queue(["Rerender",MathJax.Hub]) | |
} else if (!discoverable && item.name[1] === "Menu Events" && CONFIG.settings.mpContext) { | |
alert(_.apply(_,MESSAGE.IE9warning)); | |
} | |
}; | |
MENU.MPEvents.Messages = { | |
IE8warning: ["IE8warning", | |
"This will disable the MathJax menu and zoom features, " + | |
"but you can Alt-Click on an expression to obtain the MathJax " + | |
"menu instead.\n\nReally change the MathPlayer settings?"], | |
IE9warning: ["IE9warning", | |
"The MathJax contextual menu will be disabled, but you can " + | |
"Alt-Click on an expression to obtain the MathJax menu instead."] | |
}; | |
/*************************************************************/ | |
/*************************************************************/ | |
HUB.Browser.Select({ | |
MSIE: function (browser) { | |
var quirks = (document.compatMode === "BackCompat"); | |
var isIE8 = browser.versionAtLeast("8.0") && document.documentMode > 7; | |
MENU.Augment({ | |
margin: 20, | |
msieBackgroundBug: ((document.documentMode||0) < 9), | |
msieFixedPositionBug: (quirks || !isIE8), | |
msieAboutBug: quirks, | |
msieHeightBug: ((document.documentMode||0) < 9) | |
// height of window doesn't include title bar in XP | |
}); | |
if (isIE9) { | |
delete CONFIG.styles["#MathJax_About"].filter; | |
delete CONFIG.styles[".MathJax_Menu"].filter; | |
} | |
}, | |
Firefox: function (browser) { | |
MENU.skipMouseover = browser.isMobile && browser.versionAtLeast("6.0"); | |
MENU.skipMousedown = browser.isMobile; | |
} | |
}); | |
MENU.isMobile = HUB.Browser.isMobile; | |
MENU.noContextMenu = HUB.Browser.noContextMenu; | |
/*************************************************************/ | |
// | |
// Creates the locale menu from the list of locales in MathJax.Localization.strings | |
// | |
MENU.CreateLocaleMenu = function () { | |
if (!MENU.menu) return; | |
var menu = MENU.menu.Find("Language").submenu, items = menu.items; | |
// | |
// Get the names of the languages and sort them | |
// | |
var locales = [], LOCALE = MathJax.Localization.strings; | |
for (var id in LOCALE) {if (LOCALE.hasOwnProperty(id)) {locales.push(id)}} | |
locales = locales.sort(); menu.items = []; | |
// | |
// Add a menu item for each | |
// | |
for (var i = 0, m = locales.length; i < m; i++) { | |
var title = LOCALE[locales[i]].menuTitle; | |
if (title) {title += " ("+locales[i]+")"} else {title = locales[i]} | |
menu.items.push(ITEM.RADIO([locales[i],title],"locale",{action:MENU.Locale})); | |
} | |
// | |
// Add the rule and "Load from URL" items | |
// | |
menu.items.push(items[items.length-2],items[items.length-1]); | |
}; | |
// | |
// Create the annotation menu from MathJax.Hub.config.semanticsAnnotations | |
// | |
MENU.CreateAnnotationMenu = function () { | |
if (!MENU.menu) return; | |
var menu = MENU.menu.Find("Show Math As","Annotation").submenu; | |
var annotations = CONFIG.semanticsAnnotations; | |
for (var a in annotations) { | |
if (annotations.hasOwnProperty(a)) { | |
menu.items.push(ITEM.COMMAND([a,a], MENU.ShowSource, {hidden: true, nativeTouch: true, format: a})); | |
} | |
} | |
}; | |
/*************************************************************/ | |
HUB.Register.StartupHook("End Config",function () { | |
/* | |
* Get the menu settings from the HUB (which includes the | |
* data from the cookie already), and add the format, if | |
* it wasn't set in the cookie. | |
*/ | |
CONFIG.settings = HUB.config.menuSettings; | |
if (typeof(CONFIG.settings.showRenderer) !== "undefined") {CONFIG.showRenderer = CONFIG.settings.showRenderer} | |
if (typeof(CONFIG.settings.showFontMenu) !== "undefined") {CONFIG.showFontMenu = CONFIG.settings.showFontMenu} | |
if (typeof(CONFIG.settings.showContext) !== "undefined") {CONFIG.showContext = CONFIG.settings.showContext} | |
MENU.getCookie(); | |
/* | |
* The main menu | |
*/ | |
// Localization: items used as key, should be refactored. | |
MENU.menu = MENU( | |
ITEM.SUBMENU(["Show","Show Math As"], | |
ITEM.COMMAND(["MathMLcode","MathML Code"], MENU.ShowSource, {nativeTouch: true, format: "MathML"}), | |
ITEM.COMMAND(["Original","Original Form"], MENU.ShowSource, {nativeTouch: true}), | |
ITEM.SUBMENU(["Annotation","Annotation"], {disabled:true}), | |
ITEM.RULE(), | |
ITEM.CHECKBOX(["texHints","Show TeX hints in MathML"], "texHints"), | |
ITEM.CHECKBOX(["semantics","Add original form as annotation"], "semantics") | |
), | |
ITEM.RULE(), | |
ITEM.SUBMENU(["Settings","Math Settings"], | |
ITEM.SUBMENU(["ZoomTrigger","Zoom Trigger"], | |
ITEM.RADIO(["Hover","Hover"], "zoom", {action: MENU.Zoom}), | |
ITEM.RADIO(["Click","Click"], "zoom", {action: MENU.Zoom}), | |
ITEM.RADIO(["DoubleClick","Double-Click"], "zoom", {action: MENU.Zoom}), | |
ITEM.RADIO(["NoZoom","No Zoom"], "zoom", {value: "None"}), | |
ITEM.RULE(), | |
ITEM.LABEL(["TriggerRequires","Trigger Requires:"]), | |
ITEM.CHECKBOX((HUB.Browser.isMac ? ["Option","Option"] : ["Alt","Alt"]), "ALT"), | |
ITEM.CHECKBOX(["Command","Command"], "CMD", {hidden: !HUB.Browser.isMac}), | |
ITEM.CHECKBOX(["Control","Control"], "CTRL", {hidden: HUB.Browser.isMac}), | |
ITEM.CHECKBOX(["Shift","Shift"], "Shift") | |
), | |
ITEM.SUBMENU(["ZoomFactor","Zoom Factor"], | |
ITEM.RADIO("125%", "zscale"), | |
ITEM.RADIO("133%", "zscale"), | |
ITEM.RADIO("150%", "zscale"), | |
ITEM.RADIO("175%", "zscale"), | |
ITEM.RADIO("200%", "zscale"), | |
ITEM.RADIO("250%", "zscale"), | |
ITEM.RADIO("300%", "zscale"), | |
ITEM.RADIO("400%", "zscale") | |
), | |
ITEM.RULE(), | |
ITEM.SUBMENU(["Renderer","Math Renderer"], {hidden:!CONFIG.showRenderer}, | |
ITEM.RADIO(["HTML-CSS","HTML-CSS"], "renderer", {action: MENU.Renderer}), | |
ITEM.RADIO(["CommonHTML","Common HTML"], "renderer", {action: MENU.Renderer, value:"CommonHTML"}), | |
ITEM.RADIO(["PreviewHTML","Preview HTML"],"renderer", {action: MENU.Renderer, value:"PreviewHTML"}), | |
ITEM.RADIO(["MathML","MathML"], "renderer", {action: MENU.Renderer, value:"NativeMML"}), | |
ITEM.RADIO(["SVG","SVG"], "renderer", {action: MENU.Renderer}), | |
ITEM.RADIO(["PlainSource","Plain Source"],"renderer", {action: MENU.Renderer, value:"PlainSource"}), | |
ITEM.RULE(), | |
ITEM.CHECKBOX(["FastPreview","Fast Preview"], "FastPreview") | |
), | |
ITEM.SUBMENU("MathPlayer", {hidden:!HUB.Browser.isMSIE || !CONFIG.showMathPlayer, | |
disabled:!HUB.Browser.hasMathPlayer}, | |
ITEM.LABEL(["MPHandles","Let MathPlayer Handle:"]), | |
ITEM.CHECKBOX(["MenuEvents","Menu Events"], "mpContext", {action: MENU.MPEvents, hidden:!isIE9}), | |
ITEM.CHECKBOX(["MouseEvents","Mouse Events"], "mpMouse", {action: MENU.MPEvents, hidden:!isIE9}), | |
ITEM.CHECKBOX(["MenuAndMouse","Mouse and Menu Events"], "mpMouse", {action: MENU.MPEvents, hidden:isIE9}) | |
), | |
ITEM.SUBMENU(["FontPrefs","Font Preference"], {hidden:!CONFIG.showFontMenu}, | |
ITEM.LABEL(["ForHTMLCSS","For HTML-CSS:"]), | |
ITEM.RADIO(["Auto","Auto"], "font", {action: MENU.Font}), | |
ITEM.RULE(), | |
ITEM.RADIO(["TeXLocal","TeX (local)"], "font", {action: MENU.Font}), | |
ITEM.RADIO(["TeXWeb","TeX (web)"], "font", {action: MENU.Font}), | |
ITEM.RADIO(["TeXImage","TeX (image)"], "font", {action: MENU.Font}), | |
ITEM.RULE(), | |
ITEM.RADIO(["STIXLocal","STIX (local)"], "font", {action: MENU.Font}), | |
ITEM.RADIO(["STIXWeb","STIX (web)"], "font", {action: MENU.Font}), | |
ITEM.RULE(), | |
ITEM.RADIO(["AsanaMathWeb","Asana Math (web)"], "font", {action: MENU.Font}), | |
ITEM.RADIO(["GyrePagellaWeb","Gyre Pagella (web)"], "font", {action: MENU.Font}), | |
ITEM.RADIO(["GyreTermesWeb","Gyre Termes (web)"], "font", {action: MENU.Font}), | |
ITEM.RADIO(["LatinModernWeb","Latin Modern (web)"], "font", {action: MENU.Font}), | |
ITEM.RADIO(["NeoEulerWeb","Neo Euler (web)"], "font", {action: MENU.Font}) | |
), | |
ITEM.SUBMENU(["ContextMenu","Contextual Menu"], {hidden:!CONFIG.showContext}, | |
ITEM.RADIO(["MathJax","MathJax"], "context"), | |
ITEM.RADIO(["Browser","Browser"], "context") | |
), | |
ITEM.COMMAND(["Scale","Scale All Math ..."],MENU.Scale), | |
ITEM.RULE().With({hidden:!CONFIG.showDiscoverable, name:["","discover_rule"]}), | |
ITEM.CHECKBOX(["Discoverable","Highlight on Hover"], "discoverable", {hidden:!CONFIG.showDiscoverable}) | |
), | |
ITEM.SUBMENU(["Accessibility","Accessibility"], | |
ITEM.CHECKBOX(["AssistiveMML","Assistive MathML"], "assistiveMML", {action:MENU.AssistiveMML}), | |
ITEM.CHECKBOX(["InTabOrder","Include in Tab Order"], "inTabOrder") | |
), | |
ITEM.SUBMENU(["Locale","Language"], {hidden:!CONFIG.showLocale, ltr:true}, | |
ITEM.RADIO("en", "locale", {action: MENU.Locale}), | |
ITEM.RULE().With({hidden:!CONFIG.showLocaleURL, name:["","localURL_rule"]}), | |
ITEM.COMMAND(["LoadLocale","Load from URL ..."], MENU.LoadLocale, {hidden:!CONFIG.showLocaleURL}) | |
), | |
ITEM.RULE(), | |
ITEM.COMMAND(["About","About MathJax"],MENU.About), | |
ITEM.COMMAND(["Help","MathJax Help"],MENU.Help) | |
); | |
if (MENU.isMobile) { | |
(function () { | |
var settings = CONFIG.settings; | |
var trigger = MENU.menu.Find("Math Settings","Zoom Trigger").submenu; | |
trigger.items[0].disabled = trigger.items[1].disabled = true; | |
if (settings.zoom === "Hover" || settings.zoom == "Click") {settings.zoom = "None"} | |
trigger.items = trigger.items.slice(0,4); | |
if (navigator.appVersion.match(/[ (]Android[) ]/)) { | |
MENU.ITEM.SUBMENU.Augment({marker: "\u00BB"}); | |
} | |
})(); | |
} | |
MENU.CreateLocaleMenu(); | |
MENU.CreateAnnotationMenu(); | |
}); | |
MENU.showRenderer = function (show) { | |
MENU.cookie.showRenderer = CONFIG.showRenderer = show; MENU.saveCookie(); | |
MENU.menu.Find("Math Settings","Math Renderer").hidden = !show; | |
}; | |
MENU.showMathPlayer = function (show) { | |
MENU.cookie.showMathPlayer = CONFIG.showMathPlayer = show; MENU.saveCookie(); | |
MENU.menu.Find("Math Settings","MathPlayer").hidden = !show; | |
}; | |
MENU.showFontMenu = function (show) { | |
MENU.cookie.showFontMenu = CONFIG.showFontMenu = show; MENU.saveCookie(); | |
MENU.menu.Find("Math Settings","Font Preference").hidden = !show; | |
}; | |
MENU.showContext = function (show) { | |
MENU.cookie.showContext = CONFIG.showContext = show; MENU.saveCookie(); | |
MENU.menu.Find("Math Settings","Contextual Menu").hidden = !show; | |
}; | |
MENU.showDiscoverable = function (show) { | |
MENU.cookie.showDiscoverable = CONFIG.showDiscoverable = show; MENU.saveCookie(); | |
MENU.menu.Find("Math Settings","Highlight on Hover").hidden = !show; | |
MENU.menu.Find("Math Settings","discover_rule").hidden = !show; | |
}; | |
MENU.showLocale = function (show) { | |
MENU.cookie.showLocale = CONFIG.showLocale = show; MENU.saveCookie(); | |
MENU.menu.Find("Language").hidden = !show; | |
}; | |
MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () { | |
if (!MathJax.OutputJax["HTML-CSS"].config.imageFont) | |
{MENU.menu.Find("Math Settings","Font Preference","TeX (image)").disabled = true} | |
}); | |
/*************************************************************/ | |
CALLBACK.Queue( | |
HUB.Register.StartupHook("End Config",{}), // wait until config is complete | |
["Styles",AJAX,CONFIG.styles], | |
["Post",HUB.Startup.signal,"MathMenu Ready"], | |
["loadComplete",AJAX,"[MathJax]/extensions/MathMenu.js"] | |
); | |
})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.CallBack,MathJax.OutputJax); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/extensions/toMathML.js | |
* | |
* Implements a toMathML() method for the mml Element Jax that returns | |
* a MathML string from a given math expression. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2010-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.Hub.Register.LoadHook("[MathJax]/jax/element/mml/jax.js",function () { | |
var VERSION = "2.7.1"; | |
var MML = MathJax.ElementJax.mml, | |
SETTINGS = MathJax.Hub.config.menuSettings; | |
MML.mbase.Augment({ | |
toMathML: function (space) { | |
var inferred = (this.inferred && this.parent.inferRow); | |
if (space == null) {space = ""} | |
var tag = this.type, attr = this.toMathMLattributes(); | |
if (tag === "mspace") {return space + "<"+tag+attr+" />"} | |
var data = [], SPACE = (this.isToken ? "" : space+(inferred ? "" : " ")); | |
for (var i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))} | |
else if (!this.isToken && !this.isChars) {data.push(SPACE+"<mrow />")} | |
} | |
if (this.isToken || this.isChars) {return space + "<"+tag+attr+">"+data.join("")+"</"+tag+">"} | |
if (inferred) {return data.join("\n")} | |
if (data.length === 0 || (data.length === 1 && data[0] === "")) | |
{return space + "<"+tag+attr+" />"} | |
return space + "<"+tag+attr+">\n"+data.join("\n")+"\n"+ space +"</"+tag+">"; | |
}, | |
toMathMLattributes: function () { | |
var defaults = (this.type === "mstyle" ? MML.math.prototype.defaults : this.defaults); | |
var names = (this.attrNames||MML.copyAttributeNames), | |
skip = MML.skipAttributes, copy = MML.copyAttributes; | |
var attr = []; | |
if (this.type === "math" && (!this.attr || !this.attr.xmlns)) | |
{attr.push('xmlns="http://www.w3.org/1998/Math/MathML"')} | |
if (!this.attrNames) { | |
for (var id in defaults) {if (!skip[id] && !copy[id] && defaults.hasOwnProperty(id)) { | |
if (this[id] != null && this[id] !== defaults[id]) { | |
if (this.Get(id,null,1) !== this[id]) | |
attr.push(id+'="'+this.toMathMLattribute(this[id])+'"'); | |
} | |
}} | |
} | |
for (var i = 0, m = names.length; i < m; i++) { | |
if (copy[names[i]] === 1 && !defaults.hasOwnProperty(names[i])) continue; | |
value = (this.attr||{})[names[i]]; if (value == null) {value = this[names[i]]} | |
if (value != null) {attr.push(names[i]+'="'+this.toMathMLquote(value)+'"')} | |
} | |
this.toMathMLclass(attr); | |
if (attr.length) {return " "+attr.join(" ")} else {return ""} | |
}, | |
toMathMLclass: function (attr) { | |
var CLASS = []; if (this["class"]) {CLASS.push(this["class"])} | |
if (this.isa(MML.TeXAtom) && SETTINGS.texHints) { | |
var TEXCLASS = ["ORD","OP","BIN","REL","OPEN","CLOSE","PUNCT","INNER","VCENTER"][this.texClass]; | |
if (TEXCLASS) { | |
CLASS.push("MJX-TeXAtom-"+TEXCLASS) | |
if (TEXCLASS === "OP" && !this.movablelimits) CLASS.push("MJX-fixedlimits"); | |
} | |
} | |
if (this.mathvariant && this.toMathMLvariants[this.mathvariant]) | |
{CLASS.push("MJX"+this.mathvariant)} | |
if (this.variantForm) {CLASS.push("MJX-variant")} | |
if (CLASS.length) {attr.unshift('class="'+CLASS.join(" ")+'"')} | |
}, | |
toMathMLattribute: function (value) { | |
if (typeof(value) === "string" && | |
value.replace(/ /g,"").match(/^(([-+])?(\d+(\.\d*)?|\.\d+))mu$/)) { | |
// FIXME: should take scriptlevel into account | |
return (RegExp.$2||"")+((1/18)*RegExp.$3).toFixed(3).replace(/\.?0+$/,"")+"em"; | |
} | |
else if (this.toMathMLvariants[value]) {return this.toMathMLvariants[value]} | |
return this.toMathMLquote(value); | |
}, | |
toMathMLvariants: { | |
"-tex-caligraphic": MML.VARIANT.SCRIPT, | |
"-tex-caligraphic-bold": MML.VARIANT.BOLDSCRIPT, | |
"-tex-oldstyle": MML.VARIANT.NORMAL, | |
"-tex-oldstyle-bold": MML.VARIANT.BOLD, | |
"-tex-mathit": MML.VARIANT.ITALIC | |
}, | |
toMathMLquote: function (string) { | |
string = String(string).split(""); | |
for (var i = 0, m = string.length; i < m; i++) { | |
var n = string[i].charCodeAt(0); | |
if (n <= 0xD7FF || 0xE000 <= n) { | |
// Code points U+0000 to U+D7FF and U+E000 to U+FFFF. | |
// They are directly represented by n. | |
if (n > 0x7E || (n < 0x20 && n !== 0x0A && n !== 0x0D && n !== 0x09)) { | |
string[i] = "&#x"+n.toString(16).toUpperCase()+";"; | |
} else { | |
var c = | |
{'&':'&', '<':'<', '>':'>', '"':'"'}[string[i]]; | |
if (c) {string[i] = c} | |
} | |
} else if (i+1 < m) { | |
// Code points U+10000 to U+10FFFF. | |
// n is the lead surrogate, let's read the trail surrogate. | |
var trailSurrogate = string[i+1].charCodeAt(0); | |
var codePoint = (((n-0xD800)<<10)+(trailSurrogate-0xDC00)+0x10000); | |
string[i] = "&#x"+codePoint.toString(16).toUpperCase()+";"; | |
string[i+1] = ""; | |
i++; | |
} else { | |
// n is a lead surrogate without corresponding trail surrogate: | |
// remove that character. | |
string[i] = ""; | |
} | |
} | |
return string.join(""); | |
} | |
}); | |
// | |
// Override math.toMathML in order to add semantics tag | |
// for the input format, if the user requests that in the | |
// Show As menu. | |
// | |
MML.math.Augment({ | |
toMathML: function (space,jax) { | |
var annotation; | |
if (space == null) {space = ""} | |
if (jax && jax.originalText && SETTINGS.semantics) | |
{annotation = MathJax.InputJax[jax.inputJax].annotationEncoding} | |
var nested = (this.data[0] && this.data[0].data.length > 1); | |
var tag = this.type, attr = this.toMathMLattributes(); | |
var data = [], SPACE = space + (annotation ? " " + (nested ? " " : "") : "") + " "; | |
for (var i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))} | |
else {data.push(SPACE+"<mrow />")} | |
} | |
if (data.length === 0 || (data.length === 1 && data[0] === "")) { | |
if (!annotation) {return "<"+tag+attr+" />"} | |
data.push(SPACE+"<mrow />"); | |
} | |
if (annotation) { | |
if (nested) {data.unshift(space+" <mrow>"); data.push(space+" </mrow>")} | |
data.unshift(space+" <semantics>"); | |
var xmlEscapedTex = jax.originalText.replace(/[&<>]/g, function(item) { | |
return { '>': '>', '<': '<','&': '&' }[item] | |
}); | |
data.push(space+' <annotation encoding="'+annotation+'">'+xmlEscapedTex+"</annotation>"); | |
data.push(space+" </semantics>"); | |
} | |
return space+"<"+tag+attr+">\n"+data.join("\n")+"\n"+space+"</"+tag+">"; | |
} | |
}); | |
MML.msubsup.Augment({ | |
toMathML: function (space) { | |
var tag = this.type; | |
if (this.data[this.sup] == null) {tag = "msub"} | |
if (this.data[this.sub] == null) {tag = "msup"} | |
var attr = this.toMathMLattributes(); | |
delete this.data[0].inferred; | |
var data = []; | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{if (this.data[i]) {data.push(this.data[i].toMathML(space+" "))}} | |
return space + "<"+tag+attr+">\n" + data.join("\n") + "\n" + space + "</"+tag+">"; | |
} | |
}); | |
MML.munderover.Augment({ | |
toMathML: function (space) { | |
var tag = this.type; | |
var base = this.data[this.base]; | |
if (base && base.isa(MML.TeXAtom) && base.movablelimits && !base.Get("displaystyle")) { | |
type = "msubsup"; | |
if (this.data[this.under] == null) {tag = "msup"} | |
if (this.data[this.over] == null) {tag = "msub"} | |
} else { | |
if (this.data[this.under] == null) {tag = "mover"} | |
if (this.data[this.over] == null) {tag = "munder"} | |
} | |
var attr = this.toMathMLattributes(); | |
delete this.data[0].inferred; | |
var data = []; | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{if (this.data[i]) {data.push(this.data[i].toMathML(space+" "))}} | |
return space + "<"+tag+attr+">\n" + data.join("\n") + "\n" + space + "</"+tag+">"; | |
} | |
}); | |
MML.TeXAtom.Augment({ | |
toMathML: function (space) { | |
// FIXME: Handle spacing using mpadded? | |
var attr = this.toMathMLattributes(); | |
if (!attr && this.data[0].data.length === 1) {return space.substr(2) + this.data[0].toMathML(space)} | |
return space+"<mrow"+attr+">\n" + this.data[0].toMathML(space+" ")+"\n"+space+"</mrow>"; | |
} | |
}); | |
MML.chars.Augment({ | |
toMathML: function (space) {return (space||"") + this.toMathMLquote(this.toString())} | |
}); | |
MML.entity.Augment({ | |
toMathML: function (space) {return (space||"") + "&"+this.data[0]+";<!-- "+this.toString()+" -->"} | |
}); | |
MML.xml.Augment({ | |
toMathML: function (space) {return (space||"") + this.toString()} | |
}); | |
MathJax.Hub.Register.StartupHook("TeX mathchoice Ready",function () { | |
MML.TeXmathchoice.Augment({ | |
toMathML: function (space) {return this.Core().toMathML(space)} | |
}); | |
}); | |
MathJax.Hub.Startup.signal.Post("toMathML Ready"); | |
}); | |
MathJax.Ajax.loadComplete("[MathJax]/extensions/toMathML.js"); | |
/************************************************************* | |
* | |
* MathJax/extensions/HelpDialog.js | |
* | |
* Implements the MathJax Help dialog box. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2013-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
(function (HUB,HTML,AJAX,OUTPUT,LOCALE) { | |
var HELP = MathJax.Extension.Help = { | |
version: "2.7.1" | |
}; | |
var STIXURL = "http://www.stixfonts.org/"; | |
var MENU = MathJax.Menu; | |
var FALSE, KEY; | |
HUB.Register.StartupHook("MathEvents Ready",function () { | |
FALSE = MathJax.Extension.MathEvents.Event.False; | |
KEY = MathJax.Extension.MathEvents.Event.KEY; | |
}); | |
var CONFIG = HUB.CombineConfig("HelpDialog",{ | |
styles: { | |
"#MathJax_Help": { | |
position:"fixed", left:"50%", width:"auto", "max-width": "90%", "text-align":"center", | |
border:"3px outset", padding:"1em 2em", "background-color":"#DDDDDD", color:"black", | |
cursor: "default", "font-family":"message-box", "font-size":"120%", | |
"font-style":"normal", "text-indent":0, "text-transform":"none", | |
"line-height":"normal", "letter-spacing":"normal", "word-spacing":"normal", | |
"word-wrap":"normal", "white-space":"wrap", "float":"none", "z-index":201, | |
"border-radius": "15px", // Opera 10.5 and IE9 | |
"-webkit-border-radius": "15px", // Safari and Chrome | |
"-moz-border-radius": "15px", // Firefox | |
"-khtml-border-radius": "15px", // Konqueror | |
"box-shadow":"0px 10px 20px #808080", // Opera 10.5 and IE9 | |
"-webkit-box-shadow":"0px 10px 20px #808080", // Safari 3 and Chrome | |
"-moz-box-shadow":"0px 10px 20px #808080", // Forefox 3.5 | |
"-khtml-box-shadow":"0px 10px 20px #808080", // Konqueror | |
filter: "progid:DXImageTransform.Microsoft.dropshadow(OffX=2, OffY=2, Color='gray', Positive='true')" // IE | |
}, | |
"#MathJax_Help.MathJax_MousePost": { | |
outline:"none" | |
}, | |
"#MathJax_HelpContent": { | |
overflow:"auto", "text-align":"left", "font-size":"80%", | |
padding:".4em .6em", border:"1px inset", margin:"1em 0px", | |
"max-height":"20em", "max-width":"30em", "background-color":"#EEEEEE" | |
}, | |
"#MathJax_HelpClose": { | |
position:"absolute", top:".2em", right:".2em", | |
cursor:"pointer", | |
display:"inline-block", | |
border:"2px solid #AAA", | |
"border-radius":"18px", | |
"-webkit-border-radius": "18px", // Safari and Chrome | |
"-moz-border-radius": "18px", // Firefox | |
"-khtml-border-radius": "18px", // Konqueror | |
"font-family":"'Courier New',Courier", | |
"font-size":"24px", | |
color:"#F0F0F0" | |
}, | |
"#MathJax_HelpClose span": { | |
display:"block", "background-color":"#AAA", border:"1.5px solid", | |
"border-radius":"18px", | |
"-webkit-border-radius": "18px", // Safari and Chrome | |
"-moz-border-radius": "18px", // Firefox | |
"-khtml-border-radius": "18px", // Konqueror | |
"line-height":0, | |
padding:"8px 0 6px" // may need to be browser-specific | |
}, | |
"#MathJax_HelpClose:hover": { | |
color:"white!important", | |
border:"2px solid #CCC!important" | |
}, | |
"#MathJax_HelpClose:hover span": { | |
"background-color":"#CCC!important" | |
}, | |
"#MathJax_HelpClose:hover:focus": { | |
outline:"none" | |
} | |
} | |
}); | |
/* | |
* Handle the Help Dialog box | |
*/ | |
HELP.Dialog = function (event) { | |
LOCALE.loadDomain("HelpDialog",["Post",HELP,event]); | |
}; | |
HELP.Post = function (event) { | |
this.div = MENU.Background(this); | |
var help = HTML.addElement(this.div,"div",{ | |
id: "MathJax_Help", tabIndex: 0, onkeydown: HELP.Keydown | |
},LOCALE._("HelpDialog",[ | |
["b",{style:{fontSize:"120%"}},[["Help","MathJax Help"]]], | |
["div",{id: "MathJax_HelpContent", tabIndex: 0},[ | |
["p",{},[["MathJax", | |
"*MathJax* is a JavaScript library that allows page authors to include " + | |
"mathematics within their web pages. As a reader, you don't need to do " + | |
"anything to make that happen."]] | |
], | |
["p",{},[["Browsers", | |
"*Browsers*: MathJax works with all modern browsers including IE6+, Firefox 3+, " + | |
"Chrome 0.2+, Safari 2+, Opera 9.6+ and most mobile browsers."]] | |
], | |
["p",{},[["Menu", | |
"*Math Menu*: MathJax adds a contextual menu to equations. Right-click or " + | |
"CTRL-click on any mathematics to access the menu."]] | |
], | |
["div",{style:{"margin-left":"1em"}},[ | |
["p",{},[["ShowMath", | |
"*Show Math As* allows you to view the formula's source markup " + | |
"for copy & paste (as MathML or in its original format)."]] | |
], | |
["p",{},[["Settings", | |
"*Settings* gives you control over features of MathJax, such as the " + | |
"size of the mathematics, and the mechanism used to display equations."]] | |
], | |
["p",{},[["Language", | |
"*Language* lets you select the language used by MathJax for its menus " + | |
"and warning messages."]] | |
], | |
]], | |
["p",{},[["Zoom", | |
"*Math Zoom*: If you are having difficulty reading an equation, MathJax can " + | |
"enlarge it to help you see it better."]] | |
], | |
["p",{},[["Accessibilty", | |
"*Accessibility*: MathJax will automatically work with screen readers to make " + | |
"mathematics accessible to the visually impaired."]] | |
], | |
["p",{},[["Fonts", | |
"*Fonts*: MathJax will use certain math fonts if they are installed on your " + | |
"computer; otherwise, it will use web-based fonts. Although not required, " + | |
"locally installed fonts will speed up typesetting. We suggest installing " + | |
"the [STIX fonts](%1).",STIXURL]] | |
] | |
]], | |
["a",{href:"http://www.mathjax.org/"},["www.mathjax.org"]], | |
["span",{id: "MathJax_HelpClose", onclick: HELP.Remove, | |
onkeydown: HELP.Keydown, tabIndex: 0, role: "button", | |
"aria-label": LOCALE._(["HelpDialog","CloseDialog"],"Close help dialog")}, | |
[["span",{},["\u00D7"]]] | |
] | |
])); | |
if (event.type === "mouseup") help.className += " MathJax_MousePost"; | |
help.focus(); | |
LOCALE.setCSS(help); | |
var doc = (document.documentElement||{}); | |
var H = window.innerHeight || doc.clientHeight || doc.scrollHeight || 0; | |
if (MENU.prototype.msieAboutBug) { | |
help.style.width = "20em"; help.style.position = "absolute"; | |
help.style.left = Math.floor((document.documentElement.scrollWidth - help.offsetWidth)/2)+"px"; | |
help.style.top = (Math.floor((H-help.offsetHeight)/3)+document.body.scrollTop)+"px"; | |
} else { | |
help.style.marginLeft = Math.floor(-help.offsetWidth/2)+"px"; | |
help.style.top = Math.floor((H-help.offsetHeight)/3)+"px"; | |
} | |
}; | |
HELP.Remove = function (event) { | |
if (HELP.div) {document.body.removeChild(HELP.div); delete HELP.div} | |
}; | |
HELP.Keydown = function(event) { | |
if (event.keyCode === KEY.ESCAPE || | |
(this.id === "MathJax_HelpClose" && | |
(event.keyCode === KEY.SPACE || event.keyCode === KEY.RETURN))) { | |
HELP.Remove(event); | |
MENU.CurrentNode().focus(); | |
FALSE(event); | |
} | |
}, | |
MathJax.Callback.Queue( | |
HUB.Register.StartupHook("End Config",{}), // wait until config is complete | |
["Styles",AJAX,CONFIG.styles], | |
["Post",HUB.Startup.signal,"HelpDialig Ready"], | |
["loadComplete",AJAX,"[MathJax]/extensions/HelpDialog.js"] | |
); | |
})(MathJax.Hub,MathJax.HTML,MathJax.Ajax,MathJax.OutputJax,MathJax.Localization); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/input/TeX/config.js | |
* | |
* Initializes the TeX InputJax (the main definition is in | |
* MathJax/jax/input/TeX/jax.js, which is loaded when needed). | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2009-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.InputJax.TeX = MathJax.InputJax({ | |
id: "TeX", | |
version: "2.7.1", | |
directory: MathJax.InputJax.directory + "/TeX", | |
extensionDir: MathJax.InputJax.extensionDir + "/TeX", | |
config: { | |
TagSide: "right", | |
TagIndent: "0.8em", | |
MultLineWidth: "85%", | |
equationNumbers: { | |
autoNumber: "none", // "AMS" for standard AMS numbering, | |
// or "all" for all displayed equations | |
formatNumber: function (n) {return n}, | |
formatTag: function (n) {return '('+n+')'}, | |
formatID: function (n) {return 'mjx-eqn-'+String(n).replace(/[:"'<>&]/g,"")}, | |
formatURL: function (id,base) {return base+'#'+escape(id)}, | |
useLabelIds: true | |
} | |
}, | |
resetEquationNumbers: function () {} // filled in by AMSmath extension | |
}); | |
MathJax.InputJax.TeX.Register("math/tex"); | |
MathJax.InputJax.TeX.loadComplete("config.js"); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/input/TeX/jax.js | |
* | |
* Implements the TeX InputJax that reads mathematics in | |
* TeX and LaTeX format and converts it to the MML ElementJax | |
* internal format. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2009-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
(function (TEX,HUB,AJAX) { | |
var MML, NBSP = "\u00A0"; | |
var _ = function (id) { | |
return MathJax.Localization._.apply(MathJax.Localization, | |
[["TeX", id]].concat([].slice.call(arguments,1))); | |
}; | |
var isArray = MathJax.Object.isArray; | |
var STACK = MathJax.Object.Subclass({ | |
Init: function (env,inner) { | |
this.global = {isInner: inner}; | |
this.data = [STACKITEM.start(this.global)]; | |
if (env) {this.data[0].env = env} | |
this.env = this.data[0].env; | |
}, | |
Push: function () { | |
var i, m, item, top; | |
for (i = 0, m = arguments.length; i < m; i++) { | |
item = arguments[i]; if (!item) continue; | |
if (item instanceof MML.mbase) {item = STACKITEM.mml(item)} | |
item.global = this.global; | |
top = (this.data.length ? this.Top().checkItem(item) : true); | |
if (top instanceof Array) {this.Pop(); this.Push.apply(this,top)} | |
else if (top instanceof STACKITEM) {this.Pop(); this.Push(top)} | |
else if (top) { | |
this.data.push(item); | |
if (item.env) { | |
if (item.copyEnv !== false) { | |
for (var id in this.env) | |
{if (this.env.hasOwnProperty(id)) {item.env[id] = this.env[id]}} | |
} | |
this.env = item.env; | |
} else {item.env = this.env} | |
} | |
} | |
}, | |
Pop: function () { | |
var item = this.data.pop(); if (!item.isOpen) {delete item.env} | |
this.env = (this.data.length ? this.Top().env : {}); | |
return item; | |
}, | |
Top: function (n) { | |
if (n == null) {n = 1} | |
if (this.data.length < n) {return null} | |
return this.data[this.data.length-n]; | |
}, | |
Prev: function (noPop) { | |
var top = this.Top(); | |
if (noPop) {return top.data[top.data.length-1]} | |
else {return top.Pop()} | |
}, | |
toString: function () {return "stack[\n "+this.data.join("\n ")+"\n]"} | |
}); | |
var STACKITEM = STACK.Item = MathJax.Object.Subclass({ | |
type: "base", | |
endError: /*_()*/ ["ExtraOpenMissingClose","Extra open brace or missing close brace"], | |
closeError: /*_()*/ ["ExtraCloseMissingOpen","Extra close brace or missing open brace"], | |
rightError: /*_()*/ ["MissingLeftExtraRight","Missing \\left or extra \\right"], | |
Init: function () { | |
if (this.isOpen) {this.env = {}} | |
this.data = []; | |
this.Push.apply(this,arguments); | |
}, | |
Push: function () {this.data.push.apply(this.data,arguments)}, | |
Pop: function () {return this.data.pop()}, | |
mmlData: function (inferred,forceRow) { | |
if (inferred == null) {inferred = true} | |
if (this.data.length === 1 && !forceRow) {return this.data[0]} | |
return MML.mrow.apply(MML,this.data).With((inferred ? {inferred: true}: {})); | |
}, | |
checkItem: function (item) { | |
if (item.type === "over" && this.isOpen) {item.num = this.mmlData(false); this.data = []} | |
if (item.type === "cell" && this.isOpen) { | |
if (item.linebreak) {return false} | |
TEX.Error(["Misplaced","Misplaced %1",item.name]); | |
} | |
if (item.isClose && this[item.type+"Error"]) {TEX.Error(this[item.type+"Error"])} | |
if (!item.isNotStack) {return true} | |
this.Push(item.data[0]); return false; | |
}, | |
With: function (def) { | |
for (var id in def) {if (def.hasOwnProperty(id)) {this[id] = def[id]}} | |
return this; | |
}, | |
toString: function () {return this.type+"["+this.data.join("; ")+"]"} | |
}); | |
STACKITEM.start = STACKITEM.Subclass({ | |
type: "start", isOpen: true, | |
Init: function (global) { | |
this.SUPER(arguments).Init.call(this); | |
this.global = global; | |
}, | |
checkItem: function (item) { | |
if (item.type === "stop") {return STACKITEM.mml(this.mmlData())} | |
return this.SUPER(arguments).checkItem.call(this,item); | |
} | |
}); | |
STACKITEM.stop = STACKITEM.Subclass({ | |
type: "stop", isClose: true | |
}); | |
STACKITEM.open = STACKITEM.Subclass({ | |
type: "open", isOpen: true, | |
stopError: /*_()*/ ["ExtraOpenMissingClose","Extra open brace or missing close brace"], | |
checkItem: function (item) { | |
if (item.type === "close") { | |
var mml = this.mmlData(); | |
return STACKITEM.mml(MML.TeXAtom(mml)); // TeXAtom make it an ORD to prevent spacing (FIXME: should be another way) | |
} | |
return this.SUPER(arguments).checkItem.call(this,item); | |
} | |
}); | |
STACKITEM.close = STACKITEM.Subclass({ | |
type: "close", isClose: true | |
}); | |
STACKITEM.prime = STACKITEM.Subclass({ | |
type: "prime", | |
checkItem: function (item) { | |
if (this.data[0].type !== "msubsup") | |
{return [MML.msup(this.data[0],this.data[1]),item]} | |
this.data[0].SetData(this.data[0].sup,this.data[1]); | |
return [this.data[0],item]; | |
} | |
}); | |
STACKITEM.subsup = STACKITEM.Subclass({ | |
type: "subsup", | |
stopError: /*_()*/ ["MissingScript","Missing superscript or subscript argument"], | |
supError: /*_()*/ ["MissingOpenForSup","Missing open brace for superscript"], | |
subError: /*_()*/ ["MissingOpenForSub","Missing open brace for subscript"], | |
checkItem: function (item) { | |
if (item.type === "open" || item.type === "left") {return true} | |
if (item.type === "mml") { | |
if (this.primes) { | |
if (this.position !== 2) {this.data[0].SetData(2,this.primes)} | |
else {item.data[0] = MML.mrow(this.primes.With({variantForm:true}),item.data[0])} | |
} | |
this.data[0].SetData(this.position,item.data[0]); | |
if (this.movesupsub != null) {this.data[0].movesupsub = this.movesupsub} | |
return STACKITEM.mml(this.data[0]); | |
} | |
if (this.SUPER(arguments).checkItem.call(this,item)) | |
{TEX.Error(this[["","subError","supError"][this.position]])} | |
}, | |
Pop: function () {} | |
}); | |
STACKITEM.over = STACKITEM.Subclass({ | |
type: "over", isClose: true, name: "\\over", | |
checkItem: function (item,stack) { | |
if (item.type === "over") | |
{TEX.Error(["AmbiguousUseOf","Ambiguous use of %1",item.name])} | |
if (item.isClose) { | |
var mml = MML.mfrac(this.num,this.mmlData(false)); | |
if (this.thickness != null) {mml.linethickness = this.thickness} | |
if (this.open || this.close) { | |
mml.texWithDelims = true; | |
mml = TEX.fixedFence(this.open,mml,this.close); | |
} | |
return [STACKITEM.mml(mml), item]; | |
} | |
return this.SUPER(arguments).checkItem.call(this,item); | |
}, | |
toString: function () {return "over["+this.num+" / "+this.data.join("; ")+"]"} | |
}); | |
STACKITEM.left = STACKITEM.Subclass({ | |
type: "left", isOpen: true, delim: '(', | |
stopError: /*_()*/ ["ExtraLeftMissingRight", "Extra \\left or missing \\right"], | |
checkItem: function (item) { | |
if (item.type === "right") | |
{return STACKITEM.mml(TEX.fenced(this.delim,this.mmlData(),item.delim))} | |
return this.SUPER(arguments).checkItem.call(this,item); | |
} | |
}); | |
STACKITEM.right = STACKITEM.Subclass({ | |
type: "right", isClose: true, delim: ')' | |
}); | |
STACKITEM.begin = STACKITEM.Subclass({ | |
type: "begin", isOpen: true, | |
checkItem: function (item) { | |
if (item.type === "end") { | |
if (item.name !== this.name) | |
{TEX.Error(["EnvBadEnd","\\begin{%1} ended with \\end{%2}",this.name,item.name])} | |
if (!this.end) {return STACKITEM.mml(this.mmlData())} | |
return this.parse[this.end].call(this.parse,this,this.data); | |
} | |
if (item.type === "stop") | |
{TEX.Error(["EnvMissingEnd","Missing \\end{%1}",this.name])} | |
return this.SUPER(arguments).checkItem.call(this,item); | |
} | |
}); | |
STACKITEM.end = STACKITEM.Subclass({ | |
type: "end", isClose: true | |
}); | |
STACKITEM.style = STACKITEM.Subclass({ | |
type: "style", | |
checkItem: function (item) { | |
if (!item.isClose) {return this.SUPER(arguments).checkItem.call(this,item)} | |
var mml = MML.mstyle.apply(MML,this.data).With(this.styles); | |
return [STACKITEM.mml(mml),item]; | |
} | |
}); | |
STACKITEM.position = STACKITEM.Subclass({ | |
type: "position", | |
checkItem: function (item) { | |
if (item.isClose) {TEX.Error(["MissingBoxFor","Missing box for %1",this.name])} | |
if (item.isNotStack) { | |
var mml = item.mmlData(); | |
switch (this.move) { | |
case 'vertical': | |
mml = MML.mpadded(mml).With({height: this.dh, depth: this.dd, voffset: this.dh}); | |
return [STACKITEM.mml(mml)]; | |
case 'horizontal': | |
return [STACKITEM.mml(this.left),item,STACKITEM.mml(this.right)]; | |
} | |
} | |
return this.SUPER(arguments).checkItem.call(this,item); | |
} | |
}); | |
STACKITEM.array = STACKITEM.Subclass({ | |
type: "array", isOpen: true, copyEnv: false, arraydef: {}, | |
Init: function () { | |
this.table = []; this.row = []; this.frame = []; this.hfill = []; | |
this.SUPER(arguments).Init.apply(this,arguments); | |
}, | |
checkItem: function (item) { | |
if (item.isClose && item.type !== "over") { | |
if (item.isEntry) {this.EndEntry(); this.clearEnv(); return false} | |
if (item.isCR) {this.EndEntry(); this.EndRow(); this.clearEnv(); return false} | |
this.EndTable(); this.clearEnv(); | |
var scriptlevel = this.arraydef.scriptlevel; delete this.arraydef.scriptlevel; | |
var mml = MML.mtable.apply(MML,this.table).With(this.arraydef); | |
if (this.frame.length === 4) { | |
mml.frame = (this.frame.dashed ? "dashed" : "solid"); | |
} else if (this.frame.length) { | |
mml.hasFrame = true; | |
if (this.arraydef.rowlines) {this.arraydef.rowlines = this.arraydef.rowlines.replace(/none( none)+$/,"none")} | |
mml = MML.menclose(mml).With({notation: this.frame.join(" "), isFrame: true}); | |
if ((this.arraydef.columnlines||"none") != "none" || | |
(this.arraydef.rowlines||"none") != "none") {mml.padding = 0} // HTML-CSS jax implements this | |
} | |
if (scriptlevel) {mml = MML.mstyle(mml).With({scriptlevel: scriptlevel})} | |
if (this.open || this.close) {mml = TEX.fenced(this.open,mml,this.close)} | |
mml = STACKITEM.mml(mml); | |
if (this.requireClose) { | |
if (item.type === 'close') {return mml} | |
TEX.Error(["MissingCloseBrace","Missing close brace"]); | |
} | |
return [mml,item]; | |
} | |
return this.SUPER(arguments).checkItem.call(this,item); | |
}, | |
EndEntry: function () { | |
var mtd = MML.mtd.apply(MML,this.data); | |
if (this.hfill.length) { | |
if (this.hfill[0] === 0) mtd.columnalign = "right"; | |
if (this.hfill[this.hfill.length-1] === this.data.length) | |
mtd.columnalign = (mtd.columnalign ? "center" : "left"); | |
} | |
this.row.push(mtd); this.data = []; this.hfill = []; | |
}, | |
EndRow: function () { | |
var mtr = MML.mtr; | |
if (this.isNumbered && this.row.length === 3) { | |
this.row.unshift(this.row.pop()); // move equation number to first position | |
mtr = MML.mlabeledtr; | |
} | |
this.table.push(mtr.apply(MML,this.row)); this.row = []; | |
}, | |
EndTable: function () { | |
if (this.data.length || this.row.length) {this.EndEntry(); this.EndRow()} | |
this.checkLines(); | |
}, | |
checkLines: function () { | |
if (this.arraydef.rowlines) { | |
var lines = this.arraydef.rowlines.split(/ /); | |
if (lines.length === this.table.length) { | |
this.frame.push("bottom"); lines.pop(); | |
this.arraydef.rowlines = lines.join(' '); | |
} else if (lines.length < this.table.length-1) { | |
this.arraydef.rowlines += " none"; | |
} | |
} | |
if (this.rowspacing) { | |
var rows = this.arraydef.rowspacing.split(/ /); | |
while (rows.length < this.table.length) {rows.push(this.rowspacing+"em")} | |
this.arraydef.rowspacing = rows.join(' '); | |
} | |
}, | |
clearEnv: function () { | |
for (var id in this.env) {if (this.env.hasOwnProperty(id)) {delete this.env[id]}} | |
} | |
}); | |
STACKITEM.cell = STACKITEM.Subclass({ | |
type: "cell", isClose: true | |
}); | |
STACKITEM.mml = STACKITEM.Subclass({ | |
type: "mml", isNotStack: true, | |
Add: function () {this.data.push.apply(this.data,arguments); return this} | |
}); | |
STACKITEM.fn = STACKITEM.Subclass({ | |
type: "fn", | |
checkItem: function (item) { | |
if (this.data[0]) { | |
if (item.isOpen) {return true} | |
if (item.type !== "fn") { | |
if (item.type !== "mml" || !item.data[0]) {return [this.data[0],item]} | |
if (item.data[0].isa(MML.mspace)) {return [this.data[0],item]} | |
var mml = item.data[0]; if (mml.isEmbellished()) {mml = mml.CoreMO()} | |
if ([0,0,1,1,0,1,1,0,0,0][mml.Get("texClass")]) {return [this.data[0],item]} | |
} | |
return [this.data[0],MML.mo(MML.entity("#x2061")).With({texClass:MML.TEXCLASS.NONE}),item]; | |
} | |
return this.SUPER(arguments).checkItem.apply(this,arguments); | |
} | |
}); | |
STACKITEM.not = STACKITEM.Subclass({ | |
type: "not", | |
checkItem: function (item) { | |
var mml, c; | |
if (item.type === "open" || item.type === "left") {return true} | |
if (item.type === "mml" && item.data[0].type.match(/^(mo|mi|mtext)$/)) { | |
mml = item.data[0], c = mml.data.join(""); | |
if (c.length === 1 && !mml.movesupsub) { | |
c = STACKITEM.not.remap[c.charCodeAt(0)]; | |
if (c) {mml.SetData(0,MML.chars(String.fromCharCode(c)))} | |
else {mml.Append(MML.chars("\u0338"))} | |
return item; | |
} | |
} | |
// \mathrel{\rlap{\notChar}} | |
mml = MML.mpadded(MML.mtext("\u29F8")).With({width:0}); | |
mml = MML.TeXAtom(mml).With({texClass:MML.TEXCLASS.REL}); | |
return [mml,item]; | |
} | |
}); | |
STACKITEM.not.remap = { | |
0x2190:0x219A, 0x2192:0x219B, 0x2194:0x21AE, | |
0x21D0:0x21CD, 0x21D2:0x21CF, 0x21D4:0x21CE, | |
0x2208:0x2209, 0x220B:0x220C, 0x2223:0x2224, 0x2225:0x2226, | |
0x223C:0x2241, 0x007E:0x2241, 0x2243:0x2244, 0x2245:0x2247, | |
0x2248:0x2249, 0x224D:0x226D, 0x003D:0x2260, 0x2261:0x2262, | |
0x003C:0x226E, 0x003E:0x226F, 0x2264:0x2270, 0x2265:0x2271, | |
0x2272:0x2274, 0x2273:0x2275, 0x2276:0x2278, 0x2277:0x2279, | |
0x227A:0x2280, 0x227B:0x2281, 0x2282:0x2284, 0x2283:0x2285, | |
0x2286:0x2288, 0x2287:0x2289, 0x22A2:0x22AC, 0x22A8:0x22AD, | |
0x22A9:0x22AE, 0x22AB:0x22AF, 0x227C:0x22E0, 0x227D:0x22E1, | |
0x2291:0x22E2, 0x2292:0x22E3, 0x22B2:0x22EA, 0x22B3:0x22EB, | |
0x22B4:0x22EC, 0x22B5:0x22ED, 0x2203:0x2204 | |
}; | |
STACKITEM.dots = STACKITEM.Subclass({ | |
type: "dots", | |
checkItem: function (item) { | |
if (item.type === "open" || item.type === "left") {return true} | |
var dots = this.ldots; | |
if (item.type === "mml" && item.data[0].isEmbellished()) { | |
var tclass = item.data[0].CoreMO().Get("texClass"); | |
if (tclass === MML.TEXCLASS.BIN || tclass === MML.TEXCLASS.REL) {dots = this.cdots} | |
} | |
return [dots,item]; | |
} | |
}); | |
var TEXDEF = { | |
// | |
// Add new definitions without overriding user-defined ones | |
// | |
Add: function (src,dst,nouser) { | |
if (!dst) {dst = this} | |
for (var id in src) {if (src.hasOwnProperty(id)) { | |
if (typeof src[id] === 'object' && !isArray(src[id]) && | |
(typeof dst[id] === 'object' || typeof dst[id] === 'function')) | |
{this.Add(src[id],dst[id],src[id],nouser)} | |
else if (!dst[id] || !dst[id].isUser || !nouser) {dst[id] = src[id]} | |
}} | |
return dst; | |
} | |
}; | |
var STARTUP = function () { | |
MML = MathJax.ElementJax.mml; | |
HUB.Insert(TEXDEF,{ | |
// patterns for letters and numbers | |
letter: /[a-z]/i, | |
digit: /[0-9.]/, | |
number: /^(?:[0-9]+(?:\{,\}[0-9]{3})*(?:\.[0-9]*)*|\.[0-9]+)/, | |
special: { | |
'\\': 'ControlSequence', | |
'{': 'Open', | |
'}': 'Close', | |
'~': 'Tilde', | |
'^': 'Superscript', | |
'_': 'Subscript', | |
' ': 'Space', | |
"\t": 'Space', | |
"\r": 'Space', | |
"\n": 'Space', | |
"'": 'Prime', | |
'%': 'Comment', | |
'&': 'Entry', | |
'#': 'Hash', | |
'\u00A0': 'Space', | |
'\u2019': 'Prime' | |
}, | |
remap: { | |
'-': '2212', | |
'*': '2217', | |
'`': '2018' // map ` to back quote | |
}, | |
mathchar0mi: { | |
// Lower-case greek | |
alpha: '03B1', | |
beta: '03B2', | |
gamma: '03B3', | |
delta: '03B4', | |
epsilon: '03F5', | |
zeta: '03B6', | |
eta: '03B7', | |
theta: '03B8', | |
iota: '03B9', | |
kappa: '03BA', | |
lambda: '03BB', | |
mu: '03BC', | |
nu: '03BD', | |
xi: '03BE', | |
omicron: '03BF', // added for completeness | |
pi: '03C0', | |
rho: '03C1', | |
sigma: '03C3', | |
tau: '03C4', | |
upsilon: '03C5', | |
phi: '03D5', | |
chi: '03C7', | |
psi: '03C8', | |
omega: '03C9', | |
varepsilon: '03B5', | |
vartheta: '03D1', | |
varpi: '03D6', | |
varrho: '03F1', | |
varsigma: '03C2', | |
varphi: '03C6', | |
// Ord symbols | |
S: ['00A7',{mathvariant: MML.VARIANT.NORMAL}], | |
aleph: ['2135',{mathvariant: MML.VARIANT.NORMAL}], | |
hbar: ['210F',{variantForm:true}], | |
imath: '0131', | |
jmath: '0237', | |
ell: '2113', | |
wp: ['2118',{mathvariant: MML.VARIANT.NORMAL}], | |
Re: ['211C',{mathvariant: MML.VARIANT.NORMAL}], | |
Im: ['2111',{mathvariant: MML.VARIANT.NORMAL}], | |
partial: ['2202',{mathvariant: MML.VARIANT.NORMAL}], | |
infty: ['221E',{mathvariant: MML.VARIANT.NORMAL}], | |
prime: ['2032',{mathvariant: MML.VARIANT.NORMAL, variantForm:true}], | |
emptyset: ['2205',{mathvariant: MML.VARIANT.NORMAL}], | |
nabla: ['2207',{mathvariant: MML.VARIANT.NORMAL}], | |
top: ['22A4',{mathvariant: MML.VARIANT.NORMAL}], | |
bot: ['22A5',{mathvariant: MML.VARIANT.NORMAL}], | |
angle: ['2220',{mathvariant: MML.VARIANT.NORMAL}], | |
triangle: ['25B3',{mathvariant: MML.VARIANT.NORMAL}], | |
backslash: ['2216',{mathvariant: MML.VARIANT.NORMAL, variantForm:true}], | |
forall: ['2200',{mathvariant: MML.VARIANT.NORMAL}], | |
exists: ['2203',{mathvariant: MML.VARIANT.NORMAL}], | |
neg: ['00AC',{mathvariant: MML.VARIANT.NORMAL}], | |
lnot: ['00AC',{mathvariant: MML.VARIANT.NORMAL}], | |
flat: ['266D',{mathvariant: MML.VARIANT.NORMAL}], | |
natural: ['266E',{mathvariant: MML.VARIANT.NORMAL}], | |
sharp: ['266F',{mathvariant: MML.VARIANT.NORMAL}], | |
clubsuit: ['2663',{mathvariant: MML.VARIANT.NORMAL}], | |
diamondsuit: ['2662',{mathvariant: MML.VARIANT.NORMAL}], | |
heartsuit: ['2661',{mathvariant: MML.VARIANT.NORMAL}], | |
spadesuit: ['2660',{mathvariant: MML.VARIANT.NORMAL}] | |
}, | |
mathchar0mo: { | |
surd: '221A', | |
// big ops | |
coprod: ['2210',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
bigvee: ['22C1',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
bigwedge: ['22C0',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
biguplus: ['2A04',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
bigcap: ['22C2',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
bigcup: ['22C3',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
'int': ['222B',{texClass: MML.TEXCLASS.OP}], | |
intop: ['222B',{texClass: MML.TEXCLASS.OP, movesupsub:true, movablelimits:true}], | |
iint: ['222C',{texClass: MML.TEXCLASS.OP}], | |
iiint: ['222D',{texClass: MML.TEXCLASS.OP}], | |
prod: ['220F',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
sum: ['2211',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
bigotimes: ['2A02',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
bigoplus: ['2A01',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
bigodot: ['2A00',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
oint: ['222E',{texClass: MML.TEXCLASS.OP}], | |
bigsqcup: ['2A06',{texClass: MML.TEXCLASS.OP, movesupsub:true}], | |
smallint: ['222B',{largeop:false}], | |
// binary operations | |
triangleleft: '25C3', | |
triangleright: '25B9', | |
bigtriangleup: '25B3', | |
bigtriangledown: '25BD', | |
wedge: '2227', | |
land: '2227', | |
vee: '2228', | |
lor: '2228', | |
cap: '2229', | |
cup: '222A', | |
ddagger: '2021', | |
dagger: '2020', | |
sqcap: '2293', | |
sqcup: '2294', | |
uplus: '228E', | |
amalg: '2A3F', | |
diamond: '22C4', | |
bullet: '2219', | |
wr: '2240', | |
div: '00F7', | |
odot: ['2299',{largeop: false}], | |
oslash: ['2298',{largeop: false}], | |
otimes: ['2297',{largeop: false}], | |
ominus: ['2296',{largeop: false}], | |
oplus: ['2295',{largeop: false}], | |
mp: '2213', | |
pm: '00B1', | |
circ: '2218', | |
bigcirc: '25EF', | |
setminus: ['2216',{variantForm:true}], | |
cdot: '22C5', | |
ast: '2217', | |
times: '00D7', | |
star: '22C6', | |
// Relations | |
propto: '221D', | |
sqsubseteq: '2291', | |
sqsupseteq: '2292', | |
parallel: '2225', | |
mid: '2223', | |
dashv: '22A3', | |
vdash: '22A2', | |
leq: '2264', | |
le: '2264', | |
geq: '2265', | |
ge: '2265', | |
lt: '003C', | |
gt: '003E', | |
succ: '227B', | |
prec: '227A', | |
approx: '2248', | |
succeq: '2AB0', // or '227C', | |
preceq: '2AAF', // or '227D', | |
supset: '2283', | |
subset: '2282', | |
supseteq: '2287', | |
subseteq: '2286', | |
'in': '2208', | |
ni: '220B', | |
notin: '2209', | |
owns: '220B', | |
gg: '226B', | |
ll: '226A', | |
sim: '223C', | |
simeq: '2243', | |
perp: '22A5', | |
equiv: '2261', | |
asymp: '224D', | |
smile: '2323', | |
frown: '2322', | |
ne: '2260', | |
neq: '2260', | |
cong: '2245', | |
doteq: '2250', | |
bowtie: '22C8', | |
models: '22A8', | |
notChar: '29F8', | |
// Arrows | |
Leftrightarrow: '21D4', | |
Leftarrow: '21D0', | |
Rightarrow: '21D2', | |
leftrightarrow: '2194', | |
leftarrow: '2190', | |
gets: '2190', | |
rightarrow: '2192', | |
to: '2192', | |
mapsto: '21A6', | |
leftharpoonup: '21BC', | |
leftharpoondown: '21BD', | |
rightharpoonup: '21C0', | |
rightharpoondown: '21C1', | |
nearrow: '2197', | |
searrow: '2198', | |
nwarrow: '2196', | |
swarrow: '2199', | |
rightleftharpoons: '21CC', | |
hookrightarrow: '21AA', | |
hookleftarrow: '21A9', | |
longleftarrow: '27F5', | |
Longleftarrow: '27F8', | |
longrightarrow: '27F6', | |
Longrightarrow: '27F9', | |
Longleftrightarrow: '27FA', | |
longleftrightarrow: '27F7', | |
longmapsto: '27FC', | |
// Misc. | |
ldots: '2026', | |
cdots: '22EF', | |
vdots: '22EE', | |
ddots: '22F1', | |
dotsc: '2026', // dots with commas | |
dotsb: '22EF', // dots with binary ops and relations | |
dotsm: '22EF', // dots with multiplication | |
dotsi: '22EF', // dots with integrals | |
dotso: '2026', // other dots | |
ldotp: ['002E', {texClass: MML.TEXCLASS.PUNCT}], | |
cdotp: ['22C5', {texClass: MML.TEXCLASS.PUNCT}], | |
colon: ['003A', {texClass: MML.TEXCLASS.PUNCT}] | |
}, | |
mathchar7: { | |
Gamma: '0393', | |
Delta: '0394', | |
Theta: '0398', | |
Lambda: '039B', | |
Xi: '039E', | |
Pi: '03A0', | |
Sigma: '03A3', | |
Upsilon: '03A5', | |
Phi: '03A6', | |
Psi: '03A8', | |
Omega: '03A9', | |
'_': '005F', | |
'#': '0023', | |
'$': '0024', | |
'%': '0025', | |
'&': '0026', | |
And: '0026' | |
}, | |
delimiter: { | |
'(': '(', | |
')': ')', | |
'[': '[', | |
']': ']', | |
'<': '27E8', | |
'>': '27E9', | |
'\\lt': '27E8', | |
'\\gt': '27E9', | |
'/': '/', | |
'|': ['|',{texClass:MML.TEXCLASS.ORD}], | |
'.': '', | |
'\\\\': '\\', | |
'\\lmoustache': '23B0', // non-standard | |
'\\rmoustache': '23B1', // non-standard | |
'\\lgroup': '27EE', // non-standard | |
'\\rgroup': '27EF', // non-standard | |
'\\arrowvert': '23D0', | |
'\\Arrowvert': '2016', | |
'\\bracevert': '23AA', // non-standard | |
'\\Vert': ['2016',{texClass:MML.TEXCLASS.ORD}], | |
'\\|': ['2016',{texClass:MML.TEXCLASS.ORD}], | |
'\\vert': ['|',{texClass:MML.TEXCLASS.ORD}], | |
'\\uparrow': '2191', | |
'\\downarrow': '2193', | |
'\\updownarrow': '2195', | |
'\\Uparrow': '21D1', | |
'\\Downarrow': '21D3', | |
'\\Updownarrow': '21D5', | |
'\\backslash': '\\', | |
'\\rangle': '27E9', | |
'\\langle': '27E8', | |
'\\rbrace': '}', | |
'\\lbrace': '{', | |
'\\}': '}', | |
'\\{': '{', | |
'\\rceil': '2309', | |
'\\lceil': '2308', | |
'\\rfloor': '230B', | |
'\\lfloor': '230A', | |
'\\lbrack': '[', | |
'\\rbrack': ']' | |
}, | |
macros: { | |
displaystyle: ['SetStyle','D',true,0], | |
textstyle: ['SetStyle','T',false,0], | |
scriptstyle: ['SetStyle','S',false,1], | |
scriptscriptstyle: ['SetStyle','SS',false,2], | |
rm: ['SetFont',MML.VARIANT.NORMAL], | |
mit: ['SetFont',MML.VARIANT.ITALIC], | |
oldstyle: ['SetFont',MML.VARIANT.OLDSTYLE], | |
cal: ['SetFont',MML.VARIANT.CALIGRAPHIC], | |
it: ['SetFont',"-tex-mathit"], // needs special handling | |
bf: ['SetFont',MML.VARIANT.BOLD], | |
bbFont: ['SetFont',MML.VARIANT.DOUBLESTRUCK], | |
scr: ['SetFont',MML.VARIANT.SCRIPT], | |
frak: ['SetFont',MML.VARIANT.FRAKTUR], | |
sf: ['SetFont',MML.VARIANT.SANSSERIF], | |
tt: ['SetFont',MML.VARIANT.MONOSPACE], | |
// font: | |
tiny: ['SetSize',0.5], | |
Tiny: ['SetSize',0.6], // non-standard | |
scriptsize: ['SetSize',0.7], | |
small: ['SetSize',0.85], | |
normalsize: ['SetSize',1.0], | |
large: ['SetSize',1.2], | |
Large: ['SetSize',1.44], | |
LARGE: ['SetSize',1.73], | |
huge: ['SetSize',2.07], | |
Huge: ['SetSize',2.49], | |
arcsin: ['NamedFn'], | |
arccos: ['NamedFn'], | |
arctan: ['NamedFn'], | |
arg: ['NamedFn'], | |
cos: ['NamedFn'], | |
cosh: ['NamedFn'], | |
cot: ['NamedFn'], | |
coth: ['NamedFn'], | |
csc: ['NamedFn'], | |
deg: ['NamedFn'], | |
det: 'NamedOp', | |
dim: ['NamedFn'], | |
exp: ['NamedFn'], | |
gcd: 'NamedOp', | |
hom: ['NamedFn'], | |
inf: 'NamedOp', | |
ker: ['NamedFn'], | |
lg: ['NamedFn'], | |
lim: 'NamedOp', | |
liminf: ['NamedOp','lim inf'], | |
limsup: ['NamedOp','lim sup'], | |
ln: ['NamedFn'], | |
log: ['NamedFn'], | |
max: 'NamedOp', | |
min: 'NamedOp', | |
Pr: 'NamedOp', | |
sec: ['NamedFn'], | |
sin: ['NamedFn'], | |
sinh: ['NamedFn'], | |
sup: 'NamedOp', | |
tan: ['NamedFn'], | |
tanh: ['NamedFn'], | |
limits: ['Limits',1], | |
nolimits: ['Limits',0], | |
overline: ['UnderOver','00AF',null,1], | |
underline: ['UnderOver','005F'], | |
overbrace: ['UnderOver','23DE',1], | |
underbrace: ['UnderOver','23DF',1], | |
overparen: ['UnderOver','23DC'], | |
underparen: ['UnderOver','23DD'], | |
overrightarrow: ['UnderOver','2192'], | |
underrightarrow: ['UnderOver','2192'], | |
overleftarrow: ['UnderOver','2190'], | |
underleftarrow: ['UnderOver','2190'], | |
overleftrightarrow: ['UnderOver','2194'], | |
underleftrightarrow: ['UnderOver','2194'], | |
overset: 'Overset', | |
underset: 'Underset', | |
stackrel: ['Macro','\\mathrel{\\mathop{#2}\\limits^{#1}}',2], | |
over: 'Over', | |
overwithdelims: 'Over', | |
atop: 'Over', | |
atopwithdelims: 'Over', | |
above: 'Over', | |
abovewithdelims: 'Over', | |
brace: ['Over','{','}'], | |
brack: ['Over','[',']'], | |
choose: ['Over','(',')'], | |
frac: 'Frac', | |
sqrt: 'Sqrt', | |
root: 'Root', | |
uproot: ['MoveRoot','upRoot'], | |
leftroot: ['MoveRoot','leftRoot'], | |
left: 'LeftRight', | |
right: 'LeftRight', | |
middle: 'Middle', | |
llap: 'Lap', | |
rlap: 'Lap', | |
raise: 'RaiseLower', | |
lower: 'RaiseLower', | |
moveleft: 'MoveLeftRight', | |
moveright: 'MoveLeftRight', | |
',': ['Spacer',MML.LENGTH.THINMATHSPACE], | |
':': ['Spacer',MML.LENGTH.MEDIUMMATHSPACE], // for LaTeX | |
'>': ['Spacer',MML.LENGTH.MEDIUMMATHSPACE], | |
';': ['Spacer',MML.LENGTH.THICKMATHSPACE], | |
'!': ['Spacer',MML.LENGTH.NEGATIVETHINMATHSPACE], | |
enspace: ['Spacer',".5em"], | |
quad: ['Spacer',"1em"], | |
qquad: ['Spacer',"2em"], | |
thinspace: ['Spacer',MML.LENGTH.THINMATHSPACE], | |
negthinspace: ['Spacer',MML.LENGTH.NEGATIVETHINMATHSPACE], | |
hskip: 'Hskip', | |
hspace: 'Hskip', | |
kern: 'Hskip', | |
mskip: 'Hskip', | |
mspace: 'Hskip', | |
mkern: 'Hskip', | |
Rule: ['Rule'], | |
Space: ['Rule','blank'], | |
big: ['MakeBig',MML.TEXCLASS.ORD,0.85], | |
Big: ['MakeBig',MML.TEXCLASS.ORD,1.15], | |
bigg: ['MakeBig',MML.TEXCLASS.ORD,1.45], | |
Bigg: ['MakeBig',MML.TEXCLASS.ORD,1.75], | |
bigl: ['MakeBig',MML.TEXCLASS.OPEN,0.85], | |
Bigl: ['MakeBig',MML.TEXCLASS.OPEN,1.15], | |
biggl: ['MakeBig',MML.TEXCLASS.OPEN,1.45], | |
Biggl: ['MakeBig',MML.TEXCLASS.OPEN,1.75], | |
bigr: ['MakeBig',MML.TEXCLASS.CLOSE,0.85], | |
Bigr: ['MakeBig',MML.TEXCLASS.CLOSE,1.15], | |
biggr: ['MakeBig',MML.TEXCLASS.CLOSE,1.45], | |
Biggr: ['MakeBig',MML.TEXCLASS.CLOSE,1.75], | |
bigm: ['MakeBig',MML.TEXCLASS.REL,0.85], | |
Bigm: ['MakeBig',MML.TEXCLASS.REL,1.15], | |
biggm: ['MakeBig',MML.TEXCLASS.REL,1.45], | |
Biggm: ['MakeBig',MML.TEXCLASS.REL,1.75], | |
mathord: ['TeXAtom',MML.TEXCLASS.ORD], | |
mathop: ['TeXAtom',MML.TEXCLASS.OP], | |
mathopen: ['TeXAtom',MML.TEXCLASS.OPEN], | |
mathclose: ['TeXAtom',MML.TEXCLASS.CLOSE], | |
mathbin: ['TeXAtom',MML.TEXCLASS.BIN], | |
mathrel: ['TeXAtom',MML.TEXCLASS.REL], | |
mathpunct: ['TeXAtom',MML.TEXCLASS.PUNCT], | |
mathinner: ['TeXAtom',MML.TEXCLASS.INNER], | |
vcenter: ['TeXAtom',MML.TEXCLASS.VCENTER], | |
mathchoice: ['Extension','mathchoice'], | |
buildrel: 'BuildRel', | |
hbox: ['HBox',0], | |
text: 'HBox', | |
mbox: ['HBox',0], | |
fbox: 'FBox', | |
strut: 'Strut', | |
mathstrut: ['Macro','\\vphantom{(}'], | |
phantom: 'Phantom', | |
vphantom: ['Phantom',1,0], | |
hphantom: ['Phantom',0,1], | |
smash: 'Smash', | |
acute: ['Accent', "00B4"], // or 0301 or 02CA | |
grave: ['Accent', "0060"], // or 0300 or 02CB | |
ddot: ['Accent', "00A8"], // or 0308 | |
tilde: ['Accent', "007E"], // or 0303 or 02DC | |
bar: ['Accent', "00AF"], // or 0304 or 02C9 | |
breve: ['Accent', "02D8"], // or 0306 | |
check: ['Accent', "02C7"], // or 030C | |
hat: ['Accent', "005E"], // or 0302 or 02C6 | |
vec: ['Accent', "2192"], // or 20D7 | |
dot: ['Accent', "02D9"], // or 0307 | |
widetilde: ['Accent', "007E",1], // or 0303 or 02DC | |
widehat: ['Accent', "005E",1], // or 0302 or 02C6 | |
matrix: 'Matrix', | |
array: 'Matrix', | |
pmatrix: ['Matrix','(',')'], | |
cases: ['Matrix','{','',"left left",null,".1em",null,true], | |
eqalign: ['Matrix',null,null,"right left",MML.LENGTH.THICKMATHSPACE,".5em",'D'], | |
displaylines: ['Matrix',null,null,"center",null,".5em",'D'], | |
cr: 'Cr', | |
'\\': 'CrLaTeX', | |
newline: 'Cr', | |
hline: ['HLine','solid'], | |
hdashline: ['HLine','dashed'], | |
// noalign: 'HandleNoAlign', | |
eqalignno: ['Matrix',null,null,"right left",MML.LENGTH.THICKMATHSPACE,".5em",'D',null,"right"], | |
leqalignno: ['Matrix',null,null,"right left",MML.LENGTH.THICKMATHSPACE,".5em",'D',null,"left"], | |
hfill: 'HFill', | |
hfil: 'HFill', // \hfil treated as \hfill for now | |
hfilll: 'HFill', // \hfilll treated as \hfill for now | |
// TeX substitution macros | |
bmod: ['Macro','\\mmlToken{mo}[lspace="thickmathspace" rspace="thickmathspace"]{mod}'], | |
pmod: ['Macro','\\pod{\\mmlToken{mi}{mod}\\kern 6mu #1}',1], | |
mod: ['Macro','\\mathchoice{\\kern18mu}{\\kern12mu}{\\kern12mu}{\\kern12mu}\\mmlToken{mi}{mod}\\,\\,#1',1], | |
pod: ['Macro','\\mathchoice{\\kern18mu}{\\kern8mu}{\\kern8mu}{\\kern8mu}(#1)',1], | |
iff: ['Macro','\\;\\Longleftrightarrow\\;'], | |
skew: ['Macro','{{#2{#3\\mkern#1mu}\\mkern-#1mu}{}}',3], | |
mathcal: ['Macro','{\\cal #1}',1], | |
mathscr: ['Macro','{\\scr #1}',1], | |
mathrm: ['Macro','{\\rm #1}',1], | |
mathbf: ['Macro','{\\bf #1}',1], | |
mathbb: ['Macro','{\\bbFont #1}',1], | |
Bbb: ['Macro','{\\bbFont #1}',1], | |
mathit: ['Macro','{\\it #1}',1], | |
mathfrak: ['Macro','{\\frak #1}',1], | |
mathsf: ['Macro','{\\sf #1}',1], | |
mathtt: ['Macro','{\\tt #1}',1], | |
textrm: ['Macro','\\mathord{\\rm\\text{#1}}',1], | |
textit: ['Macro','\\mathord{\\it\\text{#1}}',1], | |
textbf: ['Macro','\\mathord{\\bf\\text{#1}}',1], | |
textsf: ['Macro','\\mathord{\\sf\\text{#1}}',1], | |
texttt: ['Macro','\\mathord{\\tt\\text{#1}}',1], | |
pmb: ['Macro','\\rlap{#1}\\kern1px{#1}',1], | |
TeX: ['Macro','T\\kern-.14em\\lower.5ex{E}\\kern-.115em X'], | |
LaTeX: ['Macro','L\\kern-.325em\\raise.21em{\\scriptstyle{A}}\\kern-.17em\\TeX'], | |
' ': ['Macro','\\text{ }'], | |
// Specially handled | |
not: 'Not', | |
dots: 'Dots', | |
space: 'Tilde', | |
'\u00A0': 'Tilde', | |
// LaTeX | |
begin: 'BeginEnd', | |
end: 'BeginEnd', | |
newcommand: ['Extension','newcommand'], | |
renewcommand: ['Extension','newcommand'], | |
newenvironment: ['Extension','newcommand'], | |
renewenvironment: ['Extension','newcommand'], | |
def: ['Extension','newcommand'], | |
let: ['Extension','newcommand'], | |
verb: ['Extension','verb'], | |
boldsymbol: ['Extension','boldsymbol'], | |
tag: ['Extension','AMSmath'], | |
notag: ['Extension','AMSmath'], | |
label: ['Extension','AMSmath'], | |
ref: ['Extension','AMSmath'], | |
eqref: ['Extension','AMSmath'], | |
nonumber: ['Macro','\\notag'], | |
// Extensions to TeX | |
unicode: ['Extension','unicode'], | |
color: 'Color', | |
href: ['Extension','HTML'], | |
'class': ['Extension','HTML'], | |
style: ['Extension','HTML'], | |
cssId: ['Extension','HTML'], | |
bbox: ['Extension','bbox'], | |
mmlToken: 'MmlToken', | |
require: 'Require' | |
}, | |
environment: { | |
array: ['AlignedArray'], | |
matrix: ['Array',null,null,null,'c'], | |
pmatrix: ['Array',null,'(',')','c'], | |
bmatrix: ['Array',null,'[',']','c'], | |
Bmatrix: ['Array',null,'\\{','\\}','c'], | |
vmatrix: ['Array',null,'\\vert','\\vert','c'], | |
Vmatrix: ['Array',null,'\\Vert','\\Vert','c'], | |
cases: ['Array',null,'\\{','.','ll',null,".2em",'T'], | |
equation: [null,'Equation'], | |
'equation*': [null,'Equation'], | |
eqnarray: ['ExtensionEnv',null,'AMSmath'], | |
'eqnarray*': ['ExtensionEnv',null,'AMSmath'], | |
align: ['ExtensionEnv',null,'AMSmath'], | |
'align*': ['ExtensionEnv',null,'AMSmath'], | |
aligned: ['ExtensionEnv',null,'AMSmath'], | |
multline: ['ExtensionEnv',null,'AMSmath'], | |
'multline*': ['ExtensionEnv',null,'AMSmath'], | |
split: ['ExtensionEnv',null,'AMSmath'], | |
gather: ['ExtensionEnv',null,'AMSmath'], | |
'gather*': ['ExtensionEnv',null,'AMSmath'], | |
gathered: ['ExtensionEnv',null,'AMSmath'], | |
alignat: ['ExtensionEnv',null,'AMSmath'], | |
'alignat*': ['ExtensionEnv',null,'AMSmath'], | |
alignedat: ['ExtensionEnv',null,'AMSmath'] | |
}, | |
p_height: 1.2 / .85 // cmex10 height plus depth over .85 | |
}); | |
// | |
// Add macros defined in the configuration | |
// | |
if (this.config.Macros) { | |
var MACROS = this.config.Macros; | |
for (var id in MACROS) {if (MACROS.hasOwnProperty(id)) { | |
if (typeof(MACROS[id]) === "string") {TEXDEF.macros[id] = ['Macro',MACROS[id]]} | |
else {TEXDEF.macros[id] = ["Macro"].concat(MACROS[id])} | |
TEXDEF.macros[id].isUser = true; | |
}} | |
} | |
}; | |
/************************************************************************/ | |
/* | |
* The TeX Parser | |
*/ | |
var PARSE = MathJax.Object.Subclass({ | |
Init: function (string,env) { | |
this.string = string; this.i = 0; this.macroCount = 0; | |
var ENV; if (env) {ENV = {}; for (var id in env) {if (env.hasOwnProperty(id)) {ENV[id] = env[id]}}} | |
this.stack = TEX.Stack(ENV,!!env); | |
this.Parse(); this.Push(STACKITEM.stop()); | |
}, | |
Parse: function () { | |
var c, n; | |
while (this.i < this.string.length) { | |
c = this.string.charAt(this.i++); n = c.charCodeAt(0); | |
if (n >= 0xD800 && n < 0xDC00) {c += this.string.charAt(this.i++)} | |
if (TEXDEF.special[c]) {this[TEXDEF.special[c]](c)} | |
else if (TEXDEF.letter.test(c)) {this.Variable(c)} | |
else if (TEXDEF.digit.test(c)) {this.Number(c)} | |
else {this.Other(c)} | |
} | |
}, | |
Push: function () {this.stack.Push.apply(this.stack,arguments)}, | |
mml: function () { | |
if (this.stack.Top().type !== "mml") {return null} | |
return this.stack.Top().data[0]; | |
}, | |
mmlToken: function (token) {return token}, // used by boldsymbol extension | |
/************************************************************************/ | |
/* | |
* Handle various token classes | |
*/ | |
/* | |
* Lookup a control-sequence and process it | |
*/ | |
ControlSequence: function (c) { | |
var name = this.GetCS(), macro = this.csFindMacro(name); | |
if (macro) { | |
if (!isArray(macro)) {macro = [macro]} | |
var fn = macro[0]; if (!(fn instanceof Function)) {fn = this[fn]} | |
fn.apply(this,[c+name].concat(macro.slice(1))); | |
} else if (TEXDEF.mathchar0mi[name]) {this.csMathchar0mi(name,TEXDEF.mathchar0mi[name])} | |
else if (TEXDEF.mathchar0mo[name]) {this.csMathchar0mo(name,TEXDEF.mathchar0mo[name])} | |
else if (TEXDEF.mathchar7[name]) {this.csMathchar7(name,TEXDEF.mathchar7[name])} | |
else if (TEXDEF.delimiter["\\"+name] != null) {this.csDelimiter(name,TEXDEF.delimiter["\\"+name])} | |
else {this.csUndefined(c+name)} | |
}, | |
// | |
// Look up a macro in the macros list | |
// (overridden in begingroup extension) | |
// | |
csFindMacro: function (name) {return TEXDEF.macros[name]}, | |
// | |
// Handle normal mathchar (as an mi) | |
// | |
csMathchar0mi: function (name,mchar) { | |
var def = {mathvariant: MML.VARIANT.ITALIC}; | |
if (isArray(mchar)) {def = mchar[1]; mchar = mchar[0]} | |
this.Push(this.mmlToken(MML.mi(MML.entity("#x"+mchar)).With(def))); | |
}, | |
// | |
// Handle normal mathchar (as an mo) | |
// | |
csMathchar0mo: function (name,mchar) { | |
var def = {stretchy: false}; | |
if (isArray(mchar)) {def = mchar[1]; def.stretchy = false; mchar = mchar[0]} | |
this.Push(this.mmlToken(MML.mo(MML.entity("#x"+mchar)).With(def))); | |
}, | |
// | |
// Handle mathchar in current family | |
// | |
csMathchar7: function (name,mchar) { | |
var def = {mathvariant: MML.VARIANT.NORMAL}; | |
if (isArray(mchar)) {def = mchar[1]; mchar = mchar[0]} | |
if (this.stack.env.font) {def.mathvariant = this.stack.env.font} | |
this.Push(this.mmlToken(MML.mi(MML.entity("#x"+mchar)).With(def))); | |
}, | |
// | |
// Handle delimiter | |
// | |
csDelimiter: function (name,delim) { | |
var def = {}; | |
if (isArray(delim)) {def = delim[1]; delim = delim[0]} | |
if (delim.length === 4) {delim = MML.entity('#x'+delim)} else {delim = MML.chars(delim)} | |
this.Push(this.mmlToken(MML.mo(delim).With({fence: false, stretchy: false}).With(def))); | |
}, | |
// | |
// Handle undefined control sequence | |
// (overridden in noUndefined extension) | |
// | |
csUndefined: function (name) { | |
TEX.Error(["UndefinedControlSequence","Undefined control sequence %1",name]); | |
}, | |
/* | |
* Handle a variable (a single letter) | |
*/ | |
Variable: function (c) { | |
var def = {}; if (this.stack.env.font) {def.mathvariant = this.stack.env.font} | |
this.Push(this.mmlToken(MML.mi(MML.chars(c)).With(def))); | |
}, | |
/* | |
* Determine the extent of a number (pattern may need work) | |
*/ | |
Number: function (c) { | |
var mml, n = this.string.slice(this.i-1).match(TEXDEF.number); | |
if (n) {mml = MML.mn(n[0].replace(/[{}]/g,"")); this.i += n[0].length - 1} | |
else {mml = MML.mo(MML.chars(c))} | |
if (this.stack.env.font) {mml.mathvariant = this.stack.env.font} | |
this.Push(this.mmlToken(mml)); | |
}, | |
/* | |
* Handle { and } | |
*/ | |
Open: function (c) {this.Push(STACKITEM.open())}, | |
Close: function (c) {this.Push(STACKITEM.close())}, | |
/* | |
* Handle tilde and spaces | |
*/ | |
Tilde: function (c) {this.Push(MML.mtext(MML.chars(NBSP)))}, | |
Space: function (c) {}, | |
/* | |
* Handle ^, _, and ' | |
*/ | |
Superscript: function (c) { | |
if (this.GetNext().match(/\d/)) // don't treat numbers as a unit | |
{this.string = this.string.substr(0,this.i+1)+" "+this.string.substr(this.i+1)} | |
var primes, base, top = this.stack.Top(); | |
if (top.type === "prime") {base = top.data[0]; primes = top.data[1]; this.stack.Pop()} | |
else {base = this.stack.Prev(); if (!base) {base = MML.mi("")}} | |
if (base.isEmbellishedWrapper) {base = base.data[0].data[0]} | |
var movesupsub = base.movesupsub, position = base.sup; | |
if ((base.type === "msubsup" && base.data[base.sup]) || | |
(base.type === "munderover" && base.data[base.over] && !base.subsupOK)) | |
{TEX.Error(["DoubleExponent","Double exponent: use braces to clarify"])} | |
if (base.type !== "msubsup") { | |
if (movesupsub) { | |
if (base.type !== "munderover" || base.data[base.over]) { | |
if (base.movablelimits && base.isa(MML.mi)) {base = this.mi2mo(base)} | |
base = MML.munderover(base,null,null).With({movesupsub:true}) | |
} | |
position = base.over; | |
} else { | |
base = MML.msubsup(base,null,null); | |
position = base.sup; | |
} | |
} | |
this.Push(STACKITEM.subsup(base).With({ | |
position: position, primes: primes, movesupsub: movesupsub | |
})); | |
}, | |
Subscript: function (c) { | |
if (this.GetNext().match(/\d/)) // don't treat numbers as a unit | |
{this.string = this.string.substr(0,this.i+1)+" "+this.string.substr(this.i+1)} | |
var primes, base, top = this.stack.Top(); | |
if (top.type === "prime") {base = top.data[0]; primes = top.data[1]; this.stack.Pop()} | |
else {base = this.stack.Prev(); if (!base) {base = MML.mi("")}} | |
if (base.isEmbellishedWrapper) {base = base.data[0].data[0]} | |
var movesupsub = base.movesupsub, position = base.sub; | |
if ((base.type === "msubsup" && base.data[base.sub]) || | |
(base.type === "munderover" && base.data[base.under] && !base.subsupOK)) | |
{TEX.Error(["DoubleSubscripts","Double subscripts: use braces to clarify"])} | |
if (base.type !== "msubsup") { | |
if (movesupsub) { | |
if (base.type !== "munderover" || base.data[base.under]) { | |
if (base.movablelimits && base.isa(MML.mi)) {base = this.mi2mo(base)} | |
base = MML.munderover(base,null,null).With({movesupsub:true}) | |
} | |
position = base.under; | |
} else { | |
base = MML.msubsup(base,null,null); | |
position = base.sub; | |
} | |
} | |
this.Push(STACKITEM.subsup(base).With({ | |
position: position, primes: primes, movesupsub: movesupsub | |
})); | |
}, | |
PRIME: "\u2032", SMARTQUOTE: "\u2019", | |
Prime: function (c) { | |
var base = this.stack.Prev(); if (!base) {base = MML.mi()} | |
if (base.type === "msubsup" && base.data[base.sup]) { | |
TEX.Error(["DoubleExponentPrime", | |
"Prime causes double exponent: use braces to clarify"]); | |
} | |
var sup = ""; this.i--; | |
do {sup += this.PRIME; this.i++, c = this.GetNext()} | |
while (c === "'" || c === this.SMARTQUOTE); | |
sup = ["","\u2032","\u2033","\u2034","\u2057"][sup.length] || sup; | |
this.Push(STACKITEM.prime(base,this.mmlToken(MML.mo(sup)))); | |
}, | |
mi2mo: function (mi) { | |
var mo = MML.mo(); mo.Append.apply(mo,mi.data); var id; | |
for (id in mo.defaults) | |
{if (mo.defaults.hasOwnProperty(id) && mi[id] != null) {mo[id] = mi[id]}} | |
for (id in MML.copyAttributes) | |
{if (MML.copyAttributes.hasOwnProperty(id) && mi[id] != null) {mo[id] = mi[id]}} | |
mo.lspace = mo.rspace = "0"; // prevent mo from having space in NativeMML | |
mo.useMMLspacing &= ~(mo.SPACE_ATTR.lspace | mo.SPACE_ATTR.rspace); // don't count these explicit settings | |
return mo; | |
}, | |
/* | |
* Handle comments | |
*/ | |
Comment: function (c) { | |
while (this.i < this.string.length && this.string.charAt(this.i) != "\n") {this.i++} | |
}, | |
/* | |
* Handle hash marks outside of definitions | |
*/ | |
Hash: function (c) { | |
TEX.Error(["CantUseHash1", | |
"You can't use 'macro parameter character #' in math mode"]); | |
}, | |
/* | |
* Handle other characters (as <mo> elements) | |
*/ | |
Other: function (c) { | |
var def, mo; | |
if (this.stack.env.font) {def = {mathvariant: this.stack.env.font}} | |
if (TEXDEF.remap[c]) { | |
c = TEXDEF.remap[c]; | |
if (isArray(c)) {def = c[1]; c = c[0]} | |
mo = MML.mo(MML.entity('#x'+c)).With(def); | |
} else { | |
mo = MML.mo(c).With(def); | |
} | |
if (mo.autoDefault("stretchy",true)) {mo.stretchy = false} | |
if (mo.autoDefault("texClass",true) == "") {mo = MML.TeXAtom(mo)} | |
this.Push(this.mmlToken(mo)); | |
}, | |
/************************************************************************/ | |
/* | |
* Macros | |
*/ | |
SetFont: function (name,font) {this.stack.env.font = font}, | |
SetStyle: function (name,texStyle,style,level) { | |
this.stack.env.style = texStyle; this.stack.env.level = level; | |
this.Push(STACKITEM.style().With({styles: {displaystyle: style, scriptlevel: level}})); | |
}, | |
SetSize: function (name,size) { | |
this.stack.env.size = size; | |
this.Push(STACKITEM.style().With({styles: {mathsize: size+"em"}})); // convert to absolute? | |
}, | |
Color: function (name) { | |
var color = this.GetArgument(name); | |
var old = this.stack.env.color; this.stack.env.color = color; | |
var math = this.ParseArg(name); | |
if (old) {this.stack.env.color} else {delete this.stack.env.color} | |
this.Push(MML.mstyle(math).With({mathcolor: color})); | |
}, | |
Spacer: function (name,space) { | |
this.Push(MML.mspace().With({width: space, mathsize: MML.SIZE.NORMAL, scriptlevel:0})); | |
}, | |
LeftRight: function (name) { | |
this.Push(STACKITEM[name.substr(1)]().With({delim: this.GetDelimiter(name)})); | |
}, | |
Middle: function (name) { | |
var delim = this.GetDelimiter(name); | |
this.Push(MML.TeXAtom().With({texClass:MML.TEXCLASS.CLOSE})); | |
if (this.stack.Top().type !== "left") | |
{TEX.Error(["MisplacedMiddle","%1 must be within \\left and \\right",name])} | |
this.Push(MML.mo(delim).With({stretchy:true})); | |
this.Push(MML.TeXAtom().With({texClass:MML.TEXCLASS.OPEN})); | |
}, | |
NamedFn: function (name,id) { | |
if (!id) {id = name.substr(1)}; | |
var mml = MML.mi(id).With({texClass: MML.TEXCLASS.OP}); | |
this.Push(STACKITEM.fn(this.mmlToken(mml))); | |
}, | |
NamedOp: function (name,id) { | |
if (!id) {id = name.substr(1)}; | |
id = id.replace(/ /,"\u2006"); | |
var mml = MML.mo(id).With({ | |
movablelimits: true, | |
movesupsub: true, | |
form: MML.FORM.PREFIX, | |
texClass: MML.TEXCLASS.OP | |
}); | |
mml.useMMLspacing &= ~mml.SPACE_ATTR.form; // don't count this explicit form setting | |
this.Push(this.mmlToken(mml)); | |
}, | |
Limits: function (name,limits) { | |
var op = this.stack.Prev("nopop"); | |
if (!op || (op.Get("texClass") !== MML.TEXCLASS.OP && op.movesupsub == null)) | |
{TEX.Error(["MisplacedLimits","%1 is allowed only on operators",name])} | |
var top = this.stack.Top(); | |
if (op.type === "munderover" && !limits) { | |
op = top.data[top.data.length-1] = MML.msubsup.apply(MML.subsup,op.data); | |
} else if (op.type === "msubsup" && limits) { | |
op = top.data[top.data.length-1] = MML.munderover.apply(MML.underover,op.data); | |
} | |
op.movesupsub = (limits ? true : false); | |
op.Core().movablelimits = false; | |
if (op.movablelimits) op.movablelimits = false; | |
}, | |
Over: function (name,open,close) { | |
var mml = STACKITEM.over().With({name: name}); | |
if (open || close) { | |
mml.open = open; mml.close = close; | |
} else if (name.match(/withdelims$/)) { | |
mml.open = this.GetDelimiter(name); | |
mml.close = this.GetDelimiter(name); | |
} | |
if (name.match(/^\\above/)) {mml.thickness = this.GetDimen(name)} | |
else if (name.match(/^\\atop/) || open || close) {mml.thickness = 0} | |
this.Push(mml); | |
}, | |
Frac: function (name) { | |
var num = this.ParseArg(name); | |
var den = this.ParseArg(name); | |
this.Push(MML.mfrac(num,den)); | |
}, | |
Sqrt: function (name) { | |
var n = this.GetBrackets(name), arg = this.GetArgument(name); | |
if (arg === "\\frac") {arg += "{"+this.GetArgument(arg)+"}{"+this.GetArgument(arg)+"}"} | |
var mml = TEX.Parse(arg,this.stack.env).mml(); | |
if (!n) {mml = MML.msqrt.apply(MML,mml.array())} | |
else {mml = MML.mroot(mml,this.parseRoot(n))} | |
this.Push(mml); | |
}, | |
Root: function (name) { | |
var n = this.GetUpTo(name,"\\of"); | |
var arg = this.ParseArg(name); | |
this.Push(MML.mroot(arg,this.parseRoot(n))); | |
}, | |
parseRoot: function (n) { | |
var env = this.stack.env, inRoot = env.inRoot; env.inRoot = true; | |
var parser = TEX.Parse(n,env); n = parser.mml(); var global = parser.stack.global; | |
if (global.leftRoot || global.upRoot) { | |
n = MML.mpadded(n); | |
if (global.leftRoot) {n.width = global.leftRoot} | |
if (global.upRoot) {n.voffset = global.upRoot; n.height = global.upRoot} | |
} | |
env.inRoot = inRoot; | |
return n; | |
}, | |
MoveRoot: function (name,id) { | |
if (!this.stack.env.inRoot) | |
{TEX.Error(["MisplacedMoveRoot","%1 can appear only within a root",name])} | |
if (this.stack.global[id]) | |
{TEX.Error(["MultipleMoveRoot","Multiple use of %1",name])} | |
var n = this.GetArgument(name); | |
if (!n.match(/-?[0-9]+/)) | |
{TEX.Error(["IntegerArg","The argument to %1 must be an integer",name])} | |
n = (n/15)+"em"; | |
if (n.substr(0,1) !== "-") {n = "+"+n} | |
this.stack.global[id] = n; | |
}, | |
Accent: function (name,accent,stretchy) { | |
var c = this.ParseArg(name); | |
var def = {accent: true}; if (this.stack.env.font) {def.mathvariant = this.stack.env.font} | |
var mml = this.mmlToken(MML.mo(MML.entity("#x"+accent)).With(def)); | |
mml.stretchy = (stretchy ? true : false); | |
var mo = (c.isEmbellished() ? c.CoreMO() : c); | |
if (mo.isa(MML.mo)) mo.movablelimits = false; | |
this.Push(MML.TeXAtom(MML.munderover(c,null,mml).With({accent: true}))); | |
}, | |
UnderOver: function (name,c,stack,noaccent) { | |
var pos = {o: "over", u: "under"}[name.charAt(1)]; | |
var base = this.ParseArg(name); | |
if (base.Get("movablelimits")) {base.movablelimits = false} | |
if (base.isa(MML.munderover) && base.isEmbellished()) { | |
base.Core().With({lspace:0,rspace:0}); // get spacing right for NativeMML | |
base = MML.mrow(MML.mo().With({rspace:0}),base); // add an empty <mi> so it's not embellished any more | |
} | |
var mml = MML.munderover(base,null,null); | |
mml.SetData( | |
mml[pos], | |
this.mmlToken(MML.mo(MML.entity("#x"+c)).With({stretchy:true, accent:!noaccent})) | |
); | |
if (stack) {mml = MML.TeXAtom(mml).With({texClass:MML.TEXCLASS.OP, movesupsub:true})} | |
this.Push(mml.With({subsupOK:true})); | |
}, | |
Overset: function (name) { | |
var top = this.ParseArg(name), base = this.ParseArg(name); | |
if (base.movablelimits) base.movablelimits = false; | |
this.Push(MML.mover(base,top)); | |
}, | |
Underset: function (name) { | |
var bot = this.ParseArg(name), base = this.ParseArg(name); | |
if (base.movablelimits) base.movablelimits = false; | |
this.Push(MML.munder(base,bot)); | |
}, | |
TeXAtom: function (name,mclass) { | |
var def = {texClass: mclass}, mml; | |
if (mclass == MML.TEXCLASS.OP) { | |
def.movesupsub = def.movablelimits = true; | |
var arg = this.GetArgument(name); | |
var match = arg.match(/^\s*\\rm\s+([a-zA-Z0-9 ]+)$/); | |
if (match) { | |
def.mathvariant = MML.VARIANT.NORMAL; | |
mml = STACKITEM.fn(this.mmlToken(MML.mi(match[1]).With(def))); | |
} else { | |
mml = STACKITEM.fn(MML.TeXAtom(TEX.Parse(arg,this.stack.env).mml()).With(def)); | |
} | |
} else {mml = MML.TeXAtom(this.ParseArg(name)).With(def)} | |
this.Push(mml); | |
}, | |
MmlToken: function (name) { | |
var type = this.GetArgument(name), | |
attr = this.GetBrackets(name,"").replace(/^\s+/,""), | |
data = this.GetArgument(name), | |
def = {attrNames:[]}, match; | |
if (!MML[type] || !MML[type].prototype.isToken) | |
{TEX.Error(["NotMathMLToken","%1 is not a token element",type])} | |
while (attr !== "") { | |
match = attr.match(/^([a-z]+)\s*=\s*('[^']*'|"[^"]*"|[^ ,]*)\s*,?\s*/i); | |
if (!match) | |
{TEX.Error(["InvalidMathMLAttr","Invalid MathML attribute: %1",attr])} | |
if (MML[type].prototype.defaults[match[1]] == null && !this.MmlTokenAllow[match[1]]) { | |
TEX.Error(["UnknownAttrForElement", | |
"%1 is not a recognized attribute for %2", | |
match[1],type]); | |
} | |
var value = this.MmlFilterAttribute(match[1],match[2].replace(/^(['"])(.*)\1$/,"$2")); | |
if (value) { | |
if (value.toLowerCase() === "true") {value = true} | |
else if (value.toLowerCase() === "false") {value = false} | |
def[match[1]] = value; | |
def.attrNames.push(match[1]); | |
} | |
attr = attr.substr(match[0].length); | |
} | |
this.Push(this.mmlToken(MML[type](data).With(def))); | |
}, | |
MmlFilterAttribute: function (name,value) {return value}, | |
MmlTokenAllow: { | |
fontfamily:1, fontsize:1, fontweight:1, fontstyle:1, | |
color:1, background:1, | |
id:1, "class":1, href:1, style:1 | |
}, | |
Strut: function (name) { | |
this.Push(MML.mpadded(MML.mrow()).With({height: "8.6pt", depth: "3pt", width: 0})); | |
}, | |
Phantom: function (name,v,h) { | |
var box = MML.mphantom(this.ParseArg(name)); | |
if (v || h) { | |
box = MML.mpadded(box); | |
if (h) {box.height = box.depth = 0} | |
if (v) {box.width = 0} | |
} | |
this.Push(MML.TeXAtom(box)); | |
}, | |
Smash: function (name) { | |
var bt = this.trimSpaces(this.GetBrackets(name,"")); | |
var smash = MML.mpadded(this.ParseArg(name)); | |
switch (bt) { | |
case "b": smash.depth = 0; break; | |
case "t": smash.height = 0; break; | |
default: smash.height = smash.depth = 0; | |
} | |
this.Push(MML.TeXAtom(smash)); | |
}, | |
Lap: function (name) { | |
var mml = MML.mpadded(this.ParseArg(name)).With({width: 0}); | |
if (name === "\\llap") {mml.lspace = "-1width"} | |
this.Push(MML.TeXAtom(mml)); | |
}, | |
RaiseLower: function (name) { | |
var h = this.GetDimen(name); | |
var item = STACKITEM.position().With({name: name, move: 'vertical'}); | |
if (h.charAt(0) === '-') {h = h.slice(1); name = {raise: "\\lower", lower: "\\raise"}[name.substr(1)]} | |
if (name === "\\lower") {item.dh = '-'+h; item.dd = '+'+h} else {item.dh = '+'+h; item.dd = '-'+h} | |
this.Push(item); | |
}, | |
MoveLeftRight: function (name) { | |
var h = this.GetDimen(name); | |
var nh = (h.charAt(0) === '-' ? h.slice(1) : '-'+h); | |
if (name === "\\moveleft") {var tmp = h; h = nh; nh = tmp} | |
this.Push(STACKITEM.position().With({ | |
name: name, move: 'horizontal', | |
left: MML.mspace().With({width: h, mathsize: MML.SIZE.NORMAL}), | |
right: MML.mspace().With({width: nh, mathsize: MML.SIZE.NORMAL}) | |
})); | |
}, | |
Hskip: function (name) { | |
this.Push(MML.mspace().With({width: this.GetDimen(name), mathsize: MML.SIZE.NORMAL})); | |
}, | |
Rule: function (name,style) { | |
var w = this.GetDimen(name), | |
h = this.GetDimen(name), | |
d = this.GetDimen(name); | |
var mml, def = {width:w, height:h, depth:d}; | |
if (style !== 'blank') { | |
if (parseFloat(w) && parseFloat(h)+parseFloat(d)) | |
{def.mathbackground = (this.stack.env.color || "black")} | |
mml = MML.mpadded(MML.mrow()).With(def); | |
} else { | |
mml = MML.mspace().With(def); | |
} | |
this.Push(mml); | |
}, | |
MakeBig: function (name,mclass,size) { | |
size *= TEXDEF.p_height; | |
size = String(size).replace(/(\.\d\d\d).+/,'$1')+"em"; | |
var delim = this.GetDelimiter(name,true); | |
this.Push(MML.TeXAtom(MML.mo(delim).With({ | |
minsize: size, maxsize: size, | |
fence: true, stretchy: true, symmetric: true | |
})).With({texClass: mclass})); | |
}, | |
BuildRel: function (name) { | |
var top = this.ParseUpTo(name,"\\over"); | |
var bot = this.ParseArg(name); | |
this.Push(MML.TeXAtom(MML.munderover(bot,null,top)).With({texClass: MML.TEXCLASS.REL})); | |
}, | |
HBox: function (name,style) { | |
this.Push.apply(this,this.InternalMath(this.GetArgument(name),style)); | |
}, | |
FBox: function (name) { | |
this.Push(MML.menclose.apply(MML,this.InternalMath(this.GetArgument(name))).With({notation:"box"})); | |
}, | |
Not: function (name) { | |
this.Push(STACKITEM.not()); | |
}, | |
Dots: function (name) { | |
this.Push(STACKITEM.dots().With({ | |
ldots: this.mmlToken(MML.mo(MML.entity("#x2026")).With({stretchy:false})), | |
cdots: this.mmlToken(MML.mo(MML.entity("#x22EF")).With({stretchy:false})) | |
})); | |
}, | |
Require: function (name) { | |
var file = this.GetArgument(name) | |
.replace(/.*\//,"") // remove any leading path | |
.replace(/[^a-z0-9_.-]/ig,""); // remove illegal characters | |
this.Extension(null,file); | |
}, | |
Extension: function (name,file,array) { | |
if (name && !typeof(name) === "string") {name = name.name} | |
file = TEX.extensionDir+"/"+file; | |
if (!file.match(/\.js$/)) {file += ".js"} | |
if (!AJAX.loaded[AJAX.fileURL(file)]) { | |
if (name != null) {delete TEXDEF[array || 'macros'][name.replace(/^\\/,"")]} | |
HUB.RestartAfter(AJAX.Require(file)); | |
} | |
}, | |
Macro: function (name,macro,argcount,def) { | |
if (argcount) { | |
var args = []; | |
if (def != null) { | |
var optional = this.GetBrackets(name); | |
args.push(optional == null ? def : optional); | |
} | |
for (var i = args.length; i < argcount; i++) {args.push(this.GetArgument(name))} | |
macro = this.SubstituteArgs(args,macro); | |
} | |
this.string = this.AddArgs(macro,this.string.slice(this.i)); | |
this.i = 0; | |
if (++this.macroCount > TEX.config.MAXMACROS) { | |
TEX.Error(["MaxMacroSub1", | |
"MathJax maximum macro substitution count exceeded; " + | |
"is there a recursive macro call?"]); | |
} | |
}, | |
Matrix: function (name,open,close,align,spacing,vspacing,style,cases,numbered) { | |
var c = this.GetNext(); | |
if (c === "") | |
{TEX.Error(["MissingArgFor","Missing argument for %1",name])} | |
if (c === "{") {this.i++} else {this.string = c+"}"+this.string.slice(this.i+1); this.i = 0} | |
var array = STACKITEM.array().With({ | |
requireClose: true, | |
arraydef: { | |
rowspacing: (vspacing||"4pt"), | |
columnspacing: (spacing||"1em") | |
} | |
}); | |
if (cases) {array.isCases = true} | |
if (numbered) {array.isNumbered = true; array.arraydef.side = numbered} | |
if (open || close) {array.open = open; array.close = close} | |
if (style === "D") {array.arraydef.displaystyle = true} | |
if (align != null) {array.arraydef.columnalign = align} | |
this.Push(array); | |
}, | |
Entry: function (name) { | |
this.Push(STACKITEM.cell().With({isEntry: true, name: name})); | |
if (this.stack.Top().isCases) { | |
var string = this.string; | |
var braces = 0, i = this.i, m = string.length; | |
while (i < m) { | |
var c = string.charAt(i); | |
if (c === "{") {braces++; i++} | |
else if (c === "}") {if (braces === 0) {m = 0} else {braces--; i++}} | |
else if (c === "&" && braces === 0) { | |
TEX.Error(["ExtraAlignTab","Extra alignment tab in \\cases text"]); | |
} else if (c === "\\") { | |
if (string.substr(i).match(/^((\\cr)[^a-zA-Z]|\\\\)/)) {m = 0} else {i += 2} | |
} else {i++} | |
} | |
var text = string.substr(this.i,i-this.i); | |
if (!text.match(/^\s*\\text[^a-zA-Z]/)) { | |
this.Push.apply(this,this.InternalMath(text,0)); | |
this.i = i; | |
} | |
} | |
}, | |
Cr: function (name) { | |
this.Push(STACKITEM.cell().With({isCR: true, name: name})); | |
}, | |
CrLaTeX: function (name) { | |
var n; | |
if (this.string.charAt(this.i) === "[") { | |
n = this.GetBrackets(name,"").replace(/ /g,"").replace(/,/,"."); | |
if (n && !this.matchDimen(n)) { | |
TEX.Error(["BracketMustBeDimension", | |
"Bracket argument to %1 must be a dimension",name]); | |
} | |
} | |
this.Push(STACKITEM.cell().With({isCR: true, name: name, linebreak: true})); | |
var top = this.stack.Top(); | |
if (top.isa(STACKITEM.array)) { | |
if (n && top.arraydef.rowspacing) { | |
var rows = top.arraydef.rowspacing.split(/ /); | |
if (!top.rowspacing) {top.rowspacing = this.dimen2em(rows[0])} | |
while (rows.length < top.table.length) {rows.push(this.Em(top.rowspacing))} | |
rows[top.table.length-1] = this.Em(Math.max(0,top.rowspacing+this.dimen2em(n))); | |
top.arraydef.rowspacing = rows.join(' '); | |
} | |
} else { | |
if (n) {this.Push(MML.mspace().With({depth:n}))} | |
this.Push(MML.mspace().With({linebreak:MML.LINEBREAK.NEWLINE})); | |
} | |
}, | |
emPerInch: 7.2, | |
pxPerInch: 72, | |
matchDimen: function (dim) { | |
return dim.match(/^(-?(?:\.\d+|\d+(?:\.\d*)?))(px|pt|em|ex|mu|pc|in|mm|cm)$/); | |
}, | |
dimen2em: function (dim) { | |
var match = this.matchDimen(dim); | |
var m = parseFloat(match[1]||"1"), unit = match[2]; | |
if (unit === "em") {return m} | |
if (unit === "ex") {return m * .43} | |
if (unit === "pt") {return m / 10} // 10 pt to an em | |
if (unit === "pc") {return m * 1.2} // 12 pt to a pc | |
if (unit === "px") {return m * this.emPerInch / this.pxPerInch} | |
if (unit === "in") {return m * this.emPerInch} | |
if (unit === "cm") {return m * this.emPerInch / 2.54} // 2.54 cm to an inch | |
if (unit === "mm") {return m * this.emPerInch / 25.4} // 10 mm to a cm | |
if (unit === "mu") {return m / 18} | |
return 0; | |
}, | |
Em: function (m) { | |
if (Math.abs(m) < .0006) {return "0em"} | |
return m.toFixed(3).replace(/\.?0+$/,"") + "em"; | |
}, | |
HLine: function (name,style) { | |
if (style == null) {style = "solid"} | |
var top = this.stack.Top(); | |
if (!top.isa(STACKITEM.array) || top.data.length) | |
{TEX.Error(["Misplaced","Misplaced %1",name])} | |
if (top.table.length == 0) { | |
top.frame.push("top"); | |
} else { | |
var lines = (top.arraydef.rowlines ? top.arraydef.rowlines.split(/ /) : []); | |
while (lines.length < top.table.length) {lines.push("none")} | |
lines[top.table.length-1] = style; | |
top.arraydef.rowlines = lines.join(' '); | |
} | |
}, | |
HFill: function (name) { | |
var top = this.stack.Top(); | |
if (top.isa(STACKITEM.array)) top.hfill.push(top.data.length); | |
else TEX.Error(["UnsupportedHFill","Unsupported use of %1",name]); | |
}, | |
/************************************************************************/ | |
/* | |
* LaTeX environments | |
*/ | |
BeginEnd: function (name) { | |
var env = this.GetArgument(name), isEnd = false; | |
if (env.match(/^\\end\\/)) {isEnd = true; env = env.substr(5)} // special \end{} for \newenvironment environments | |
if (env.match(/\\/i)) {TEX.Error(["InvalidEnv","Invalid environment name '%1'",env])} | |
var cmd = this.envFindName(env); | |
if (!cmd) {TEX.Error(["UnknownEnv","Unknown environment '%1'",env])} | |
if (!isArray(cmd)) {cmd = [cmd]} | |
var end = (isArray(cmd[1]) ? cmd[1][0] : cmd[1]); | |
var mml = STACKITEM.begin().With({name: env, end: end, parse:this}); | |
if (name === "\\end") { | |
if (!isEnd && isArray(cmd[1]) && this[cmd[1][1]]) { | |
mml = this[cmd[1][1]].apply(this,[mml].concat(cmd.slice(2))); | |
} else { | |
mml = STACKITEM.end().With({name: env}); | |
} | |
} else { | |
if (++this.macroCount > TEX.config.MAXMACROS) { | |
TEX.Error(["MaxMacroSub2", | |
"MathJax maximum substitution count exceeded; " + | |
"is there a recursive latex environment?"]); | |
} | |
if (cmd[0] && this[cmd[0]]) {mml = this[cmd[0]].apply(this,[mml].concat(cmd.slice(2)))} | |
} | |
this.Push(mml); | |
}, | |
envFindName: function (name) {return TEXDEF.environment[name]}, | |
Equation: function (begin,row) {return row}, | |
ExtensionEnv: function (begin,file) {this.Extension(begin.name,file,"environment")}, | |
Array: function (begin,open,close,align,spacing,vspacing,style,raggedHeight) { | |
if (!align) {align = this.GetArgument("\\begin{"+begin.name+"}")} | |
var lines = ("c"+align).replace(/[^clr|:]/g,'').replace(/[^|:]([|:])+/g,'$1'); | |
align = align.replace(/[^clr]/g,'').split('').join(' '); | |
align = align.replace(/l/g,'left').replace(/r/g,'right').replace(/c/g,'center'); | |
var array = STACKITEM.array().With({ | |
arraydef: { | |
columnalign: align, | |
columnspacing: (spacing||"1em"), | |
rowspacing: (vspacing||"4pt") | |
} | |
}); | |
if (lines.match(/[|:]/)) { | |
if (lines.charAt(0).match(/[|:]/)) {array.frame.push("left"); array.frame.dashed = lines.charAt(0) === ":"} | |
if (lines.charAt(lines.length-1).match(/[|:]/)) {array.frame.push("right")} | |
lines = lines.substr(1,lines.length-2); | |
array.arraydef.columnlines = | |
lines.split('').join(' ').replace(/[^|: ]/g,'none').replace(/\|/g,'solid').replace(/:/g,'dashed'); | |
} | |
if (open) {array.open = this.convertDelimiter(open)} | |
if (close) {array.close = this.convertDelimiter(close)} | |
if (style === "D") {array.arraydef.displaystyle = true} | |
else if (style) {array.arraydef.displaystyle = false} | |
if (style === "S") {array.arraydef.scriptlevel = 1} // FIXME: should use mstyle? | |
if (raggedHeight) {array.arraydef.useHeight = false} | |
this.Push(begin); | |
return array; | |
}, | |
AlignedArray: function (begin) { | |
var align = this.GetBrackets("\\begin{"+begin.name+"}"); | |
return this.setArrayAlign(this.Array.apply(this,arguments),align); | |
}, | |
setArrayAlign: function (array,align) { | |
align = this.trimSpaces(align||""); | |
if (align === "t") {array.arraydef.align = "baseline 1"} | |
else if (align === "b") {array.arraydef.align = "baseline -1"} | |
else if (align === "c") {array.arraydef.align = "center"} | |
else if (align) {array.arraydef.align = align} // FIXME: should be an error? | |
return array; | |
}, | |
/************************************************************************/ | |
/* | |
* String handling routines | |
*/ | |
/* | |
* Convert delimiter to character | |
*/ | |
convertDelimiter: function (c) { | |
if (c) {c = TEXDEF.delimiter[c]} | |
if (c == null) {return null} | |
if (isArray(c)) {c = c[0]} | |
if (c.length === 4) {c = String.fromCharCode(parseInt(c,16))} | |
return c; | |
}, | |
/* | |
* Trim spaces from a string | |
*/ | |
trimSpaces: function (text) { | |
if (typeof(text) != 'string') {return text} | |
var TEXT = text.replace(/^\s+|\s+$/g,''); | |
if (TEXT.match(/\\$/) && text.match(/ $/)) TEXT += " "; | |
return TEXT; | |
}, | |
/* | |
* Check if the next character is a space | |
*/ | |
nextIsSpace: function () { | |
return this.string.charAt(this.i).match(/\s/); | |
}, | |
/* | |
* Get the next non-space character | |
*/ | |
GetNext: function () { | |
while (this.nextIsSpace()) {this.i++} | |
return this.string.charAt(this.i); | |
}, | |
/* | |
* Get and return a control-sequence name | |
*/ | |
GetCS: function () { | |
var CS = this.string.slice(this.i).match(/^([a-z]+|.) ?/i); | |
if (CS) {this.i += CS[1].length; return CS[1]} else {this.i++; return " "} | |
}, | |
/* | |
* Get and return a TeX argument (either a single character or control sequence, | |
* or the contents of the next set of braces). | |
*/ | |
GetArgument: function (name,noneOK) { | |
switch (this.GetNext()) { | |
case "": | |
if (!noneOK) {TEX.Error(["MissingArgFor","Missing argument for %1",name])} | |
return null; | |
case '}': | |
if (!noneOK) { | |
TEX.Error(["ExtraCloseMissingOpen", | |
"Extra close brace or missing open brace"]); | |
} | |
return null; | |
case '\\': | |
this.i++; return "\\"+this.GetCS(); | |
case '{': | |
var j = ++this.i, parens = 1; | |
while (this.i < this.string.length) { | |
switch (this.string.charAt(this.i++)) { | |
case '\\': this.i++; break; | |
case '{': parens++; break; | |
case '}': | |
if (--parens == 0) {return this.string.slice(j,this.i-1)} | |
break; | |
} | |
} | |
TEX.Error(["MissingCloseBrace","Missing close brace"]); | |
break; | |
} | |
return this.string.charAt(this.i++); | |
}, | |
/* | |
* Get an optional LaTeX argument in brackets | |
*/ | |
GetBrackets: function (name,def) { | |
if (this.GetNext() != '[') {return def}; | |
var j = ++this.i, parens = 0; | |
while (this.i < this.string.length) { | |
switch (this.string.charAt(this.i++)) { | |
case '{': parens++; break; | |
case '\\': this.i++; break; | |
case '}': | |
if (parens-- <= 0) { | |
TEX.Error(["ExtraCloseLooking", | |
"Extra close brace while looking for %1","']'"]); | |
} | |
break; | |
case ']': | |
if (parens == 0) {return this.string.slice(j,this.i-1)} | |
break; | |
} | |
} | |
TEX.Error(["MissingCloseBracket", | |
"Couldn't find closing ']' for argument to %1",name]); | |
}, | |
/* | |
* Get the name of a delimiter (check it in the delimiter list). | |
*/ | |
GetDelimiter: function (name,braceOK) { | |
while (this.nextIsSpace()) {this.i++} | |
var c = this.string.charAt(this.i); this.i++; | |
if (this.i <= this.string.length) { | |
if (c == "\\") {c += this.GetCS(name)} | |
else if (c === "{" && braceOK) {this.i--; c = this.GetArgument(name)} | |
if (TEXDEF.delimiter[c] != null) {return this.convertDelimiter(c)} | |
} | |
TEX.Error(["MissingOrUnrecognizedDelim", | |
"Missing or unrecognized delimiter for %1",name]); | |
}, | |
/* | |
* Get a dimension (including its units). | |
*/ | |
GetDimen: function (name) { | |
var dimen; | |
if (this.nextIsSpace()) {this.i++} | |
if (this.string.charAt(this.i) == '{') { | |
dimen = this.GetArgument(name); | |
if (dimen.match(/^\s*([-+]?([.,]\d+|\d+([.,]\d*)?))\s*(pt|em|ex|mu|px|mm|cm|in|pc)\s*$/)) | |
{return dimen.replace(/ /g,"").replace(/,/,".")} | |
} else { | |
dimen = this.string.slice(this.i); | |
var match = dimen.match(/^\s*(([-+]?([.,]\d+|\d+([.,]\d*)?))\s*(pt|em|ex|mu|px|mm|cm|in|pc)) ?/); | |
if (match) { | |
this.i += match[0].length; | |
return match[1].replace(/ /g,"").replace(/,/,"."); | |
} | |
} | |
TEX.Error(["MissingDimOrUnits", | |
"Missing dimension or its units for %1",name]); | |
}, | |
/* | |
* Get everything up to the given control sequence (token) | |
*/ | |
GetUpTo: function (name,token) { | |
while (this.nextIsSpace()) {this.i++} | |
var j = this.i, k, c, parens = 0; | |
while (this.i < this.string.length) { | |
k = this.i; c = this.string.charAt(this.i++); | |
switch (c) { | |
case '\\': c += this.GetCS(); break; | |
case '{': parens++; break; | |
case '}': | |
if (parens == 0) { | |
TEX.Error(["ExtraCloseLooking", | |
"Extra close brace while looking for %1",token]) | |
} | |
parens--; | |
break; | |
} | |
if (parens == 0 && c == token) {return this.string.slice(j,k)} | |
} | |
TEX.Error(["TokenNotFoundForCommand", | |
"Couldn't find %1 for %2",token,name]); | |
}, | |
/* | |
* Parse various substrings | |
*/ | |
ParseArg: function (name) {return TEX.Parse(this.GetArgument(name),this.stack.env).mml()}, | |
ParseUpTo: function (name,token) {return TEX.Parse(this.GetUpTo(name,token),this.stack.env).mml()}, | |
/* | |
* Break up a string into text and math blocks | |
*/ | |
InternalMath: function (text,level) { | |
var def = (this.stack.env.font ? {mathvariant: this.stack.env.font} : {}); | |
var mml = [], i = 0, k = 0, c, match = '', braces = 0; | |
if (text.match(/\\?[${}\\]|\\\(|\\(eq)?ref\s*\{/)) { | |
while (i < text.length) { | |
c = text.charAt(i++); | |
if (c === '$') { | |
if (match === '$' && braces === 0) { | |
mml.push(MML.TeXAtom(TEX.Parse(text.slice(k,i-1),{}).mml())); | |
match = ''; k = i; | |
} else if (match === '') { | |
if (k < i-1) mml.push(this.InternalText(text.slice(k,i-1),def)); | |
match = '$'; k = i; | |
} | |
} else if (c === '{' && match !== '') { | |
braces++; | |
} else if (c === '}') { | |
if (match === '}' && braces === 0) { | |
mml.push(MML.TeXAtom(TEX.Parse(text.slice(k,i),{}).mml().With(def))); | |
match = ''; k = i; | |
} else if (match !== '') { | |
if (braces) braces--; | |
} | |
} else if (c === '\\') { | |
if (match === '' && text.substr(i).match(/^(eq)?ref\s*\{/)) { | |
var len = RegExp["$&"].length; | |
if (k < i-1) mml.push(this.InternalText(text.slice(k,i-1),def)); | |
match = '}'; k = i-1; i += len; | |
} else { | |
c = text.charAt(i++); | |
if (c === '(' && match === '') { | |
if (k < i-2) mml.push(this.InternalText(text.slice(k,i-2),def)); | |
match = ')'; k = i; | |
} else if (c === ')' && match === ')' && braces === 0) { | |
mml.push(MML.TeXAtom(TEX.Parse(text.slice(k,i-2),{}).mml())); | |
match = ''; k = i; | |
} else if (c.match(/[${}\\]/) && match === '') { | |
i--; text = text.substr(0,i-1) + text.substr(i); // remove \ from \$, \{, \}, or \\ | |
} | |
} | |
} | |
} | |
if (match !== '') TEX.Error(["MathNotTerminated","Math not terminated in text box"]); | |
} | |
if (k < text.length) mml.push(this.InternalText(text.slice(k),def)); | |
if (level != null) { | |
mml = [MML.mstyle.apply(MML,mml).With({displaystyle:false,scriptlevel:level})]; | |
} else if (mml.length > 1) { | |
mml = [MML.mrow.apply(MML,mml)]; | |
} | |
return mml; | |
}, | |
InternalText: function (text,def) { | |
text = text.replace(/^\s+/,NBSP).replace(/\s+$/,NBSP); | |
return MML.mtext(MML.chars(text)).With(def); | |
}, | |
/* | |
* Replace macro paramters with their values | |
*/ | |
SubstituteArgs: function (args,string) { | |
var text = ''; var newstring = ''; var c; var i = 0; | |
while (i < string.length) { | |
c = string.charAt(i++); | |
if (c === "\\") {text += c + string.charAt(i++)} | |
else if (c === '#') { | |
c = string.charAt(i++); | |
if (c === '#') {text += c} else { | |
if (!c.match(/[1-9]/) || c > args.length) { | |
TEX.Error(["IllegalMacroParam", | |
"Illegal macro parameter reference"]); | |
} | |
newstring = this.AddArgs(this.AddArgs(newstring,text),args[c-1]); | |
text = ''; | |
} | |
} else {text += c} | |
} | |
return this.AddArgs(newstring,text); | |
}, | |
/* | |
* Make sure that macros are followed by a space if their names | |
* could accidentally be continued into the following text. | |
*/ | |
AddArgs: function (s1,s2) { | |
if (s2.match(/^[a-z]/i) && s1.match(/(^|[^\\])(\\\\)*\\[a-z]+$/i)) {s1 += ' '} | |
if (s1.length + s2.length > TEX.config.MAXBUFFER) { | |
TEX.Error(["MaxBufferSize", | |
"MathJax internal buffer size exceeded; is there a recursive macro call?"]); | |
} | |
return s1+s2; | |
} | |
}); | |
/************************************************************************/ | |
TEX.Augment({ | |
Stack: STACK, Parse: PARSE, Definitions: TEXDEF, Startup: STARTUP, | |
config: { | |
MAXMACROS: 10000, // maximum number of macro substitutions per equation | |
MAXBUFFER: 5*1024 // maximum size of TeX string to process | |
}, | |
sourceMenuTitle: /*_(MathMenu)*/ ["TeXCommands","TeX Commands"], | |
annotationEncoding: "application/x-tex", | |
prefilterHooks: MathJax.Callback.Hooks(true), // hooks to run before processing TeX | |
postfilterHooks: MathJax.Callback.Hooks(true), // hooks to run after processing TeX | |
// | |
// Check if AMSmath extension must be loaded and push | |
// it on the extensions array, if needed | |
// | |
Config: function () { | |
this.SUPER(arguments).Config.apply(this,arguments); | |
if (this.config.equationNumbers.autoNumber !== "none") { | |
if (!this.config.extensions) {this.config.extensions = []} | |
this.config.extensions.push("AMSmath.js"); | |
} | |
}, | |
// | |
// Convert TeX to ElementJax | |
// | |
Translate: function (script) { | |
var mml, isError = false, math = MathJax.HTML.getScript(script); | |
var display = (script.type.replace(/\n/g," ").match(/(;|\s|\n)mode\s*=\s*display(;|\s|\n|$)/) != null); | |
var data = {math:math, display:display, script:script}; | |
var callback = this.prefilterHooks.Execute(data); if (callback) return callback; | |
math = data.math; | |
try { | |
mml = TEX.Parse(math).mml(); | |
} catch(err) { | |
if (!err.texError) {throw err} | |
mml = this.formatError(err,math,display,script); | |
isError = true; | |
} | |
if (mml.isa(MML.mtable) && mml.displaystyle === "inherit") mml.displaystyle = display; // for tagged equations | |
if (mml.inferred) {mml = MML.apply(MathJax.ElementJax,mml.data)} else {mml = MML(mml)} | |
if (display) {mml.root.display = "block"} | |
if (isError) {mml.texError = true} | |
data.math = mml; | |
return this.postfilterHooks.Execute(data) || data.math; | |
}, | |
prefilterMath: function (math,displaystyle,script) { | |
return math; | |
}, | |
postfilterMath: function (math,displaystyle,script) { | |
this.combineRelations(math.root); | |
return math; | |
}, | |
formatError: function (err,math,display,script) { | |
var message = err.message.replace(/\n.*/,""); | |
HUB.signal.Post(["TeX Jax - parse error",message,math,display,script]); | |
return MML.Error(message); | |
}, | |
// | |
// Produce an error and stop processing this equation | |
// | |
Error: function (message) { | |
// | |
// Translate message if it is ["id","message",args] | |
// | |
if (isArray(message)) {message = _.apply(_,message)} | |
throw HUB.Insert(Error(message),{texError: true}); | |
}, | |
// | |
// Add a user-defined macro to the macro list | |
// | |
Macro: function (name,def,argn) { | |
TEXDEF.macros[name] = ['Macro'].concat([].slice.call(arguments,1)); | |
TEXDEF.macros[name].isUser = true; | |
}, | |
/* | |
* Create an mrow that has stretchy delimiters at either end, as needed | |
*/ | |
fenced: function (open,mml,close) { | |
var mrow = MML.mrow().With({open:open, close:close, texClass:MML.TEXCLASS.INNER}); | |
mrow.Append( | |
MML.mo(open).With({fence:true, stretchy:true, symmetric:true, texClass:MML.TEXCLASS.OPEN}), | |
mml, | |
MML.mo(close).With({fence:true, stretchy:true, symmetric:true, texClass:MML.TEXCLASS.CLOSE}) | |
); | |
return mrow; | |
}, | |
/* | |
* Create an mrow that has \mathchoice using \bigg and \big for the delimiters | |
*/ | |
fixedFence: function (open,mml,close) { | |
var mrow = MML.mrow().With({open:open, close:close, texClass:MML.TEXCLASS.ORD}); | |
if (open) {mrow.Append(this.mathPalette(open,"l"))} | |
if (mml.type === "mrow") {mrow.Append.apply(mrow,mml.data)} else {mrow.Append(mml)} | |
if (close) {mrow.Append(this.mathPalette(close,"r"))} | |
return mrow; | |
}, | |
mathPalette: function (fence,side) { | |
if (fence === '{' || fence === '}') {fence = "\\"+fence} | |
var D = '{\\bigg'+side+' '+fence+'}', T = '{\\big'+side+' '+fence+'}'; | |
return TEX.Parse('\\mathchoice'+D+T+T+T,{}).mml(); | |
}, | |
// | |
// Combine adjacent <mo> elements that are relations | |
// (since MathML treats the spacing very differently) | |
// | |
combineRelations: function (mml) { | |
var i, m, m1, m2; | |
for (i = 0, m = mml.data.length; i < m; i++) { | |
if (mml.data[i]) { | |
if (mml.isa(MML.mrow)) { | |
while (i+1 < m && (m1 = mml.data[i]) && (m2 = mml.data[i+1]) && | |
m1.isa(MML.mo) && m2.isa(MML.mo) && | |
m1.Get("texClass") === MML.TEXCLASS.REL && | |
m2.Get("texClass") === MML.TEXCLASS.REL) { | |
if (m1.variantForm == m2.variantForm && | |
m1.Get("mathvariant") == m2.Get("mathvariant") && m1.style == m2.style && | |
m1["class"] == m2["class"] && !m1.id && !m2.id) { | |
m1.Append.apply(m1,m2.data); | |
mml.data.splice(i+1,1); m--; | |
} else { | |
m1.rspace = m2.lspace = "0pt"; i++; | |
} | |
} | |
} | |
if (!mml.data[i].isToken) {this.combineRelations(mml.data[i])} | |
} | |
} | |
} | |
}); | |
// | |
// Add the default filters | |
// | |
TEX.prefilterHooks.Add(function (data) { | |
data.math = TEX.prefilterMath(data.math,data.display,data.script); | |
}); | |
TEX.postfilterHooks.Add(function (data) { | |
data.math = TEX.postfilterMath(data.math,data.display,data.script); | |
}); | |
TEX.loadComplete("jax.js"); | |
})(MathJax.InputJax.TeX,MathJax.Hub,MathJax.Ajax); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/output/SVG/config.js | |
* | |
* Initializes the SVG OutputJax (the main definition is in | |
* MathJax/jax/input/SVG/jax.js, which is loaded when needed). | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2011-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.OutputJax.SVG = MathJax.OutputJax({ | |
id: "SVG", | |
version: "2.7.1", | |
directory: MathJax.OutputJax.directory + "/SVG", | |
extensionDir: MathJax.OutputJax.extensionDir + "/SVG", | |
autoloadDir: MathJax.OutputJax.directory + "/SVG/autoload", | |
fontDir: MathJax.OutputJax.directory + "/SVG/fonts", // font name added later | |
config: { | |
scale: 100, minScaleAdjust: 50, // global math scaling factor, and minimum adjusted scale factor | |
font: "TeX", // currently the only font available | |
blacker: 1, // stroke-width to make fonts blacker | |
mtextFontInherit: false, // to make <mtext> be in page font rather than MathJax font | |
undefinedFamily: "STIXGeneral,'Arial Unicode MS',serif", // fonts to use for missing characters | |
addMMLclasses: false, // keep MathML structure and use CSS classes to mark elements | |
useFontCache: true, // use <use> elements to re-use font paths rather than repeat paths every time | |
useGlobalCache: true, // store fonts in a global <defs> for use in all equations, or one in each equation | |
EqnChunk: (MathJax.Hub.Browser.isMobile ? 10: 50), | |
// number of equations to process before showing them | |
EqnChunkFactor: 1.5, // chunk size is multiplied by this after each chunk | |
EqnChunkDelay: 100, // milliseconds to delay between chunks (to let browser | |
// respond to other events) | |
linebreaks: { | |
automatic: false, // when false, only process linebreak="newline", | |
// when true, insert line breaks automatically in long expressions. | |
width: "container" // maximum width of a line for automatic line breaks (e.g. "30em"). | |
// use "container" to compute size from containing element, | |
// use "nn% container" for a portion of the container, | |
// use "nn%" for a portion of the window size | |
}, | |
merrorStyle: { | |
fontSize:"90%", color:"#C00", background:"#FF8", | |
border: "1px solid #C00", padding:"3px" | |
}, | |
styles: { | |
".MathJax_SVG_Display": { | |
"text-align": "center", | |
margin: "1em 0em" | |
}, | |
// | |
// For mtextFontInherit version of \texttt{} | |
// | |
".MathJax_SVG .MJX-monospace": { | |
"font-family": "monospace" | |
}, | |
// | |
// For mtextFontInherit version of \textsf{} | |
// | |
".MathJax_SVG .MJX-sans-serif": { | |
"font-family": "sans-serif" | |
}, | |
// | |
// For tooltips | |
// | |
"#MathJax_SVG_Tooltip": { | |
"background-color": "InfoBackground", color: "InfoText", | |
border: "1px solid black", | |
"box-shadow": "2px 2px 5px #AAAAAA", // Opera 10.5 | |
"-webkit-box-shadow": "2px 2px 5px #AAAAAA", // Safari 3 and Chrome | |
"-moz-box-shadow": "2px 2px 5px #AAAAAA", // Forefox 3.5 | |
"-khtml-box-shadow": "2px 2px 5px #AAAAAA", // Konqueror | |
padding: "3px 4px", | |
"z-index": 401 | |
} | |
} | |
} | |
}); | |
if (!MathJax.Hub.config.delayJaxRegistration) {MathJax.OutputJax.SVG.Register("jax/mml")} | |
MathJax.OutputJax.SVG.loadComplete("config.js"); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/output/SVG/jax.js | |
* | |
* Implements the SVG OutputJax that displays mathematics using | |
* SVG (or VML in IE) to position the characters from math fonts | |
* in their proper locations. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2011-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
(function (AJAX,HUB,HTML,SVG) { | |
var MML; | |
var isArray = MathJax.Object.isArray; | |
var SVGNS = "http://www.w3.org/2000/svg"; | |
var XLINKNS = "http://www.w3.org/1999/xlink"; | |
// | |
// Get the URL of the page (for use with xlink:href) when there | |
// is a <base> element on the page. | |
// | |
var SVGURL = (document.getElementsByTagName("base").length === 0) ? "" : | |
String(document.location).replace(/#.*$/,""); | |
SVG.Augment({ | |
HFUZZ: 2, // adjustments for height and depth of final svg element | |
DFUZZ: 2, // to get baselines right (fragile). | |
config: { | |
styles: { | |
".MathJax_SVG": { | |
"display": "inline", | |
"font-style": "normal", | |
"font-weight": "normal", | |
"line-height": "normal", | |
"font-size": "100%", | |
"font-size-adjust":"none", | |
"text-indent": 0, | |
"text-align": "left", | |
"text-transform": "none", | |
"letter-spacing": "normal", | |
"word-spacing": "normal", | |
"word-wrap": "normal", | |
"white-space": "nowrap", | |
"float": "none", | |
"direction": "ltr", | |
"max-width": "none", "max-height": "none", | |
"min-width": 0, "min-height": 0, | |
border: 0, padding: 0, margin: 0 | |
}, | |
".MathJax_SVG_Display": { | |
position: "relative", | |
display: "block!important", | |
"text-indent": 0, | |
"max-width": "none", "max-height": "none", | |
"min-width": 0, "min-height": 0, | |
width: "100%" | |
}, | |
".MathJax_SVG *": { | |
transition: "none", | |
"-webkit-transition": "none", | |
"-moz-transition": "none", | |
"-ms-transition": "none", | |
"-o-transition": "none" | |
}, | |
".mjx-svg-href": { | |
fill: "blue", stroke: "blue" | |
}, | |
".MathJax_SVG_Processing": { | |
visibility: "hidden", position:"absolute", top:0, left:0, | |
width:0, height: 0, overflow:"hidden", display:"block!important" | |
}, | |
".MathJax_SVG_Processed": {display:"none!important"}, | |
".MathJax_SVG_ExBox": { | |
display:"block!important", overflow:"hidden", | |
width:"1px", height:"60ex", | |
"min-height": 0, "max-height":"none", | |
padding:0, border: 0, margin: 0 | |
}, | |
".MathJax_SVG_LineBox": {display: "table!important"}, | |
".MathJax_SVG_LineBox span": { | |
display: "table-cell!important", | |
width: "10000em!important", | |
"min-width":0, "max-width":"none", | |
padding:0, border:0, margin:0 | |
}, | |
"#MathJax_SVG_Tooltip": { | |
position: "absolute", left: 0, top: 0, | |
width: "auto", height: "auto", | |
display: "none" | |
} | |
} | |
}, | |
hideProcessedMath: true, // use display:none until all math is processed | |
fontNames: ["TeX","STIX","STIX-Web","Asana-Math", | |
"Gyre-Termes","Gyre-Pagella","Latin-Modern","Neo-Euler"], | |
Config: function () { | |
this.SUPER(arguments).Config.apply(this,arguments); | |
var settings = HUB.config.menuSettings, config = this.config, font = settings.font; | |
if (settings.scale) {config.scale = settings.scale} | |
if (font && font !== "Auto") { | |
font = font.replace(/(Local|Web|Image)$/i,""); | |
font = font.replace(/([a-z])([A-Z])/,"$1-$2"); | |
this.fontInUse = font; | |
} else { | |
this.fontInUse = config.font || "TeX"; | |
} | |
if (this.fontNames.indexOf(this.fontInUse) < 0) {this.fontInUse = "TeX"} | |
this.fontDir += "/" + this.fontInUse; | |
if (!this.require) {this.require = []} | |
this.require.push(this.fontDir+"/fontdata.js"); | |
this.require.push(MathJax.OutputJax.extensionDir+"/MathEvents.js"); | |
}, | |
Startup: function () { | |
// Set up event handling | |
EVENT = MathJax.Extension.MathEvents.Event; | |
TOUCH = MathJax.Extension.MathEvents.Touch; | |
HOVER = MathJax.Extension.MathEvents.Hover; | |
this.ContextMenu = EVENT.ContextMenu; | |
this.Mousedown = EVENT.AltContextMenu; | |
this.Mouseover = HOVER.Mouseover; | |
this.Mouseout = HOVER.Mouseout; | |
this.Mousemove = HOVER.Mousemove; | |
// Make hidden div for doing tests and storing global SVG <defs> | |
this.hiddenDiv = HTML.Element("div",{ | |
style:{visibility:"hidden", overflow:"hidden", position:"absolute", top:0, | |
height:"1px", width: "auto", padding:0, border:0, margin:0, | |
textAlign:"left", textIndent:0, textTransform:"none", | |
lineHeight:"normal", letterSpacing:"normal", wordSpacing:"normal"} | |
}); | |
if (!document.body.firstChild) {document.body.appendChild(this.hiddenDiv)} | |
else {document.body.insertBefore(this.hiddenDiv,document.body.firstChild)} | |
this.hiddenDiv = HTML.addElement(this.hiddenDiv,"div",{id:"MathJax_SVG_Hidden"}); | |
// Determine pixels-per-inch and em-size | |
var div = HTML.addElement(this.hiddenDiv,"div",{style:{width:"5in"}}); | |
this.pxPerInch = div.offsetWidth/5; this.hiddenDiv.removeChild(div); | |
// Used for measuring text sizes | |
this.textSVG = this.Element("svg"); | |
// Global defs for font glyphs | |
BBOX.GLYPH.defs = this.addElement(this.addElement(this.hiddenDiv.parentNode,"svg"), | |
"defs",{id:"MathJax_SVG_glyphs"}); | |
// Used in preTranslate to get scaling factors | |
this.ExSpan = HTML.Element("span", | |
{style:{position:"absolute","font-size-adjust":"none"}}, | |
[["span",{className:"MathJax_SVG_ExBox"}]] | |
); | |
// Used in preTranslate to get linebreak width | |
this.linebreakSpan = HTML.Element("span",{className:"MathJax_SVG_LineBox"},[["span"]]); | |
// Set up styles | |
return AJAX.Styles(this.config.styles,["InitializeSVG",this]); | |
}, | |
// | |
// Handle initialization that requires styles to be set up | |
// | |
InitializeSVG: function () { | |
// | |
// Get the default sizes (need styles in place to do this) | |
// | |
document.body.appendChild(this.ExSpan); | |
document.body.appendChild(this.linebreakSpan); | |
this.defaultEx = this.ExSpan.firstChild.offsetHeight/60; | |
this.defaultWidth = this.linebreakSpan.firstChild.offsetWidth; | |
document.body.removeChild(this.linebreakSpan); | |
document.body.removeChild(this.ExSpan); | |
}, | |
preTranslate: function (state) { | |
var scripts = state.jax[this.id], i, m = scripts.length, n, | |
script, prev, span, div, test, jax, ex, em, maxwidth, relwidth = false, cwidth, | |
linebreak = this.config.linebreaks.automatic, width = this.config.linebreaks.width; | |
if (linebreak) { | |
relwidth = (width.match(/^\s*(\d+(\.\d*)?%\s*)?container\s*$/) != null); | |
if (relwidth) {width = width.replace(/\s*container\s*/,"")} | |
else {maxwidth = this.defaultWidth} | |
if (width === "") {width = "100%"} | |
} else {maxwidth = 100000} // a big width, so no implicit line breaks | |
// | |
// Loop through the scripts | |
// | |
for (i = 0; i < m; i++) { | |
script = scripts[i]; if (!script.parentNode) continue; | |
// | |
// Remove any existing output | |
// | |
prev = script.previousSibling; | |
if (prev && String(prev.className).match(/^MathJax(_SVG)?(_Display)?( MathJax(_SVG)?_Process(ing|ed))?$/)) | |
{prev.parentNode.removeChild(prev)} | |
if (script.MathJax.preview) script.MathJax.preview.style.display = "none"; | |
// | |
// Add the span, and a div if in display mode, | |
// then set the role and mark it as being processed | |
// | |
jax = script.MathJax.elementJax; if (!jax) continue; | |
jax.SVG = { | |
display: (jax.root.Get("display") === "block"), | |
preview: (jax.SVG||{}).preview // in case typeset calls are interleaved | |
}; | |
span = div = HTML.Element("span",{ | |
style: {"font-size": this.config.scale+"%", display:"inline-block"}, | |
className:"MathJax_SVG", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id, | |
oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown, | |
onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove, | |
onclick:EVENT.Click, ondblclick:EVENT.DblClick, | |
// Added for keyboard accessible menu. | |
onkeydown: EVENT.Keydown, tabIndex: HUB.getTabOrder(jax) | |
}); | |
if (HUB.Browser.noContextMenu) { | |
span.ontouchstart = TOUCH.start; | |
span.ontouchend = TOUCH.end; | |
} | |
if (jax.SVG.display) { | |
div = HTML.Element("div",{className:"MathJax_SVG_Display"}); | |
div.appendChild(span); | |
} | |
div.className += " MathJax_SVG_Processing"; | |
script.parentNode.insertBefore(div,script); | |
// | |
// Add the test span for determining scales and linebreak widths | |
// | |
script.parentNode.insertBefore(this.ExSpan.cloneNode(true),script); | |
div.parentNode.insertBefore(this.linebreakSpan.cloneNode(true),div); | |
} | |
// | |
// Determine the scaling factors for each script | |
// (this only requires one reflow rather than a reflow for each equation) | |
// | |
var hidden = []; | |
for (i = 0; i < m; i++) { | |
script = scripts[i]; if (!script.parentNode) continue; | |
test = script.previousSibling; div = test.previousSibling; | |
jax = script.MathJax.elementJax; if (!jax) continue; | |
ex = test.firstChild.offsetHeight/60; | |
cwidth = Math.max(0,(div.previousSibling.firstChild.offsetWidth-2) / this.config.scale * 100); | |
if (ex === 0 || ex === "NaN") { | |
// can't read width, so move to hidden div for processing | |
hidden.push(div); | |
jax.SVG.isHidden = true; | |
ex = this.defaultEx; cwidth = this.defaultWidth; | |
} | |
if (relwidth) {maxwidth = cwidth} | |
jax.SVG.ex = ex; | |
jax.SVG.em = em = ex / SVG.TeX.x_height * 1000; // scale ex to x_height | |
jax.SVG.cwidth = cwidth/em * 1000; | |
jax.SVG.lineWidth = (linebreak ? this.length2em(width,1,maxwidth/em*1000) : SVG.BIGDIMEN); | |
} | |
for (i = 0, n = hidden.length; i < n; i++) { | |
this.hiddenDiv.appendChild(hidden[i]); | |
this.addElement(this.hiddenDiv,"br"); | |
} | |
// | |
// Remove the test spans used for determining scales and linebreak widths | |
// | |
for (i = 0; i < m; i++) { | |
script = scripts[i]; if (!script.parentNode) continue; | |
test = scripts[i].previousSibling; span = test.previousSibling; | |
jax = scripts[i].MathJax.elementJax; if (!jax) continue; | |
if (!jax.SVG.isHidden) {span = span.previousSibling} | |
span.parentNode.removeChild(span); | |
test.parentNode.removeChild(test); | |
if (script.MathJax.preview) script.MathJax.preview.style.display = ""; | |
} | |
// | |
// Set state variables used for displaying equations in chunks | |
// | |
state.SVGeqn = state.SVGlast = 0; state.SVGi = -1; | |
state.SVGchunk = this.config.EqnChunk; | |
state.SVGdelay = false; | |
}, | |
Translate: function (script,state) { | |
if (!script.parentNode) return; | |
// | |
// If we are supposed to do a chunk delay, do it | |
// | |
if (state.SVGdelay) { | |
state.SVGdelay = false; | |
HUB.RestartAfter(MathJax.Callback.Delay(this.config.EqnChunkDelay)); | |
} | |
// | |
// Get the data about the math | |
// | |
var jax = script.MathJax.elementJax, math = jax.root, div, span, | |
localCache = (SVG.config.useFontCache && !SVG.config.useGlobalCache); | |
if (jax.SVG.isHidden) { | |
span = document.getElementById(jax.inputID+"-Frame"); | |
div = (jax.SVG.display ? span.parentElement : span); | |
} else { | |
div = script.previousSibling; | |
span = (jax.SVG.display ? (div||{}).firstChild||div : div); | |
} | |
if (!div) return; | |
// | |
// Set the font metrics | |
// | |
this.em = MML.mbase.prototype.em = jax.SVG.em; this.ex = jax.SVG.ex; | |
this.linebreakWidth = jax.SVG.lineWidth; this.cwidth = jax.SVG.cwidth; | |
// | |
// Typeset the math | |
// | |
this.mathDiv = div; | |
span.appendChild(this.textSVG); | |
if (localCache) {SVG.resetGlyphs()} | |
this.initSVG(math,span); | |
math.setTeXclass(); | |
try {math.toSVG(span,div)} catch (err) { | |
if (err.restart) {while (span.firstChild) {span.removeChild(span.firstChild)}} | |
if (localCache) {BBOX.GLYPH.n--} | |
throw err; | |
} | |
span.removeChild(this.textSVG); | |
// | |
// Put it in place, and remove the processing marker | |
// | |
if (jax.SVG.isHidden) {script.parentNode.insertBefore(div,script)} | |
div.className = div.className.split(/ /)[0]; | |
// | |
// Check if we are hiding the math until more is processed | |
// | |
if (this.hideProcessedMath) { | |
// | |
// Hide the math and don't let its preview be removed | |
// | |
div.className += " MathJax_SVG_Processed"; | |
if (script.MathJax.preview) { | |
jax.SVG.preview = script.MathJax.preview; | |
delete script.MathJax.preview; | |
} | |
// | |
// Check if we should show this chunk of equations | |
// | |
state.SVGeqn += (state.i - state.SVGi); state.SVGi = state.i; | |
if (state.SVGeqn >= state.SVGlast + state.SVGchunk) { | |
this.postTranslate(state,true); | |
state.SVGchunk = Math.floor(state.SVGchunk*this.config.EqnChunkFactor); | |
state.SVGdelay = true; // delay if there are more scripts | |
} | |
} | |
}, | |
postTranslate: function (state,partial) { | |
var scripts = state.jax[this.id]; | |
if (!this.hideProcessedMath) return; | |
// | |
// Reveal this chunk of math | |
// | |
for (var i = state.SVGlast, m = state.SVGeqn; i < m; i++) { | |
var script = scripts[i]; | |
if (script && script.MathJax.elementJax) { | |
// | |
// Remove the processed marker | |
// | |
script.previousSibling.className = script.previousSibling.className.split(/ /)[0]; | |
var data = script.MathJax.elementJax.SVG; | |
// | |
// Remove the preview, if any | |
// | |
if (data.preview) { | |
data.preview.innerHTML = ""; | |
data.preview.style.display = "none"; | |
script.MathJax.preview = data.preview; | |
delete data.preview; | |
} | |
} | |
} | |
// | |
// Save our place so we know what is revealed | |
// | |
state.SVGlast = state.SVGeqn; | |
}, | |
resetGlyphs: function (reset) { | |
if (this.config.useFontCache) { | |
var GLYPH = BBOX.GLYPH; | |
if (this.config.useGlobalCache) { | |
GLYPH.defs = document.getElementById("MathJax_SVG_glyphs"); | |
GLYPH.defs.innerHTML = ""; | |
} else { | |
GLYPH.defs = this.Element("defs"); | |
GLYPH.n++; | |
} | |
GLYPH.glyphs = {}; | |
if (reset) {GLYPH.n = 0} | |
} | |
}, | |
// | |
// Return the containing HTML element rather than the SVG element, since | |
// most browsers can't position to an SVG element properly. | |
// | |
hashCheck: function (target) { | |
if (target && target.nodeName.toLowerCase() === "g") | |
{do {target = target.parentNode} while (target && target.firstChild.nodeName !== "svg")} | |
return target; | |
}, | |
getJaxFromMath: function (math) { | |
if (math.parentNode.className.match(/MathJax_SVG_Display/)) {math = math.parentNode} | |
do {math = math.nextSibling} while (math && math.nodeName.toLowerCase() !== "script"); | |
return HUB.getJaxFor(math); | |
}, | |
getHoverSpan: function (jax,math) { | |
math.style.position = "relative"; // make sure inline containers have position set | |
return math.firstChild; | |
}, | |
getHoverBBox: function (jax,span,math) { | |
var bbox = EVENT.getBBox(span.parentNode); | |
bbox.h += 2; bbox.d -= 2; // bbox seems to be a bit off, so compensate (FIXME) | |
return bbox; | |
}, | |
Zoom: function (jax,span,math,Mw,Mh) { | |
// | |
// Re-render at larger size | |
// | |
span.className = "MathJax_SVG"; | |
// | |
// get em size (taken from this.preTranslate) | |
// | |
var emex = span.appendChild(this.ExSpan.cloneNode(true)); | |
var ex = emex.firstChild.offsetHeight/60; | |
this.em = MML.mbase.prototype.em = ex / SVG.TeX.x_height * 1000; this.ex = ex; | |
this.linebreakWidth = jax.SVG.lineWidth; this.cwidth = jax.SVG.cwidth; | |
emex.parentNode.removeChild(emex); | |
span.appendChild(this.textSVG); | |
this.mathDIV = span; this.zoomScale = parseInt(HUB.config.menuSettings.zscale) / 100; | |
var tw = jax.root.data[0].SVGdata.tw; if (tw && tw < this.cwidth) this.cwidth = tw; | |
this.idPostfix = "-zoom"; jax.root.toSVG(span,span); this.idPostfix = ""; | |
this.zoomScale = 1; | |
span.removeChild(this.textSVG); | |
// | |
// Don't allow overlaps on any edge | |
// | |
var svg = span.getElementsByTagName("svg")[0].style; | |
svg.marginTop = svg.marginRight = svg.marginLeft = 0; | |
if (svg.marginBottom.charAt(0) === "-") | |
span.style.marginBottom = svg.marginBottom.substr(1); | |
if (this.operaZoomRefresh) | |
{setTimeout(function () {span.firstChild.style.border="1px solid transparent"},1)} | |
// | |
// WebKit bug (issue #749) | |
// | |
if (span.offsetWidth < span.firstChild.offsetWidth) { | |
span.style.minWidth = span.firstChild.offsetWidth + "px"; | |
math.style.minWidth = math.firstChild.offsetWidth + "px"; | |
} | |
// | |
// Get height and width of zoomed math and original math | |
// | |
span.style.position = math.style.position = "absolute"; | |
var zW = span.offsetWidth, zH = span.offsetHeight, | |
mH = math.offsetHeight, mW = math.offsetWidth; | |
span.style.position = math.style.position = ""; | |
// | |
return {Y:-EVENT.getBBox(span).h, mW:mW, mH:mH, zW:zW, zH:zH}; | |
}, | |
initSVG: function (math,span) {}, | |
Remove: function (jax) { | |
var span = document.getElementById(jax.inputID+"-Frame"); | |
if (span) { | |
if (jax.SVG.display) {span = span.parentNode} | |
span.parentNode.removeChild(span); | |
} | |
delete jax.SVG; | |
}, | |
Em: function (m) { | |
if (Math.abs(m) < .0006) return "0"; | |
return m.toFixed(3).replace(/\.?0+$/,"") + "em"; | |
}, | |
Ex: function (m) { | |
m = m / this.TeX.x_height; | |
if (Math.abs(m) < .0006) return "0"; | |
return m.toFixed(3).replace(/\.?0+$/,"") + "ex"; | |
}, | |
Percent: function (m) { | |
return (100*m).toFixed(1).replace(/\.?0+$/,"") + "%"; | |
}, | |
Fixed: function (m,n) { | |
if (Math.abs(m) < .0006) return "0"; | |
return m.toFixed(n||3).replace(/\.?0+$/,""); | |
}, | |
length2em: function (length,mu,size) { | |
if (typeof(length) !== "string") {length = length.toString()} | |
if (length === "") {return ""} | |
if (length === MML.SIZE.NORMAL) {return 1000} | |
if (length === MML.SIZE.BIG) {return 2000} | |
if (length === MML.SIZE.SMALL) {return 710} | |
if (length === "infinity") {return SVG.BIGDIMEN} | |
if (length.match(/mathspace$/)) {return 1000*SVG.MATHSPACE[length]} | |
var emFactor = (this.zoomScale || 1) / SVG.em; | |
var match = length.match(/^\s*([-+]?(?:\.\d+|\d+(?:\.\d*)?))?(pt|em|ex|mu|px|pc|in|mm|cm|%)?/); | |
var m = parseFloat(match[1]||"1") * 1000, unit = match[2]; | |
if (size == null) {size = 1000}; if (mu == null) {mu = 1} | |
if (unit === "em") {return m} | |
if (unit === "ex") {return m * SVG.TeX.x_height/1000} | |
if (unit === "%") {return m / 100 * size / 1000} | |
if (unit === "px") {return m * emFactor} | |
if (unit === "pt") {return m / 10} // 10 pt to an em | |
if (unit === "pc") {return m * 1.2} // 12 pt to a pc | |
if (unit === "in") {return m * this.pxPerInch * emFactor} | |
if (unit === "cm") {return m * this.pxPerInch * emFactor / 2.54} // 2.54 cm to an inch | |
if (unit === "mm") {return m * this.pxPerInch * emFactor / 25.4} // 10 mm to a cm | |
if (unit === "mu") {return m / 18 * mu} | |
return m*size / 1000; // relative to given size (or 1em as default) | |
}, | |
thickness2em: function (length,mu) { | |
var thick = SVG.TeX.rule_thickness; | |
if (length === MML.LINETHICKNESS.MEDIUM) {return thick} | |
if (length === MML.LINETHICKNESS.THIN) {return .67*thick} | |
if (length === MML.LINETHICKNESS.THICK) {return 1.67*thick} | |
return this.length2em(length,mu,thick); | |
}, | |
getPadding: function (styles) { | |
var padding = {top:0, right:0, bottom:0, left:0}, has = false; | |
for (var id in padding) {if (padding.hasOwnProperty(id)) { | |
var pad = styles["padding"+id.charAt(0).toUpperCase()+id.substr(1)]; | |
if (pad) {padding[id] = this.length2em(pad); has = true;} | |
}} | |
return (has ? padding : false); | |
}, | |
getBorders: function (styles) { | |
var border = {top:0, right:0, bottom:0, left:0}, has = false; | |
for (var id in border) {if (border.hasOwnProperty(id)) { | |
var ID = "border"+id.charAt(0).toUpperCase()+id.substr(1); | |
var style = styles[ID+"Style"]; | |
if (style && style !== "none") { | |
has = true; | |
border[id] = this.length2em(styles[ID+"Width"]); | |
border[id+"Style"] = styles[ID+"Style"]; | |
border[id+"Color"] = styles[ID+"Color"]; | |
if (border[id+"Color"] === "initial") {border[id+"Color"] = ""} | |
} else {delete border[id]} | |
}} | |
return (has ? border : false); | |
}, | |
Element: function (type,def) { | |
var obj = (typeof(type) === "string" ? document.createElementNS(SVGNS,type) : type); | |
obj.isMathJax = true; | |
if (def) {for (var id in def) {if (def.hasOwnProperty(id)) {obj.setAttribute(id,def[id].toString())}}} | |
return obj; | |
}, | |
addElement: function (parent,type,def) {return parent.appendChild(this.Element(type,def))}, | |
TextNode: HTML.TextNode, | |
addText: HTML.addText, | |
ucMatch: HTML.ucMatch, | |
HandleVariant: function (variant,scale,text) { | |
var svg = BBOX.G(); | |
var n, N, c, font, VARIANT, i, m, id, M, RANGES; | |
if (!variant) {variant = this.FONTDATA.VARIANT[MML.VARIANT.NORMAL]} | |
if (variant.forceFamily) { | |
text = BBOX.TEXT(scale,text,variant.font); | |
if (variant.h != null) {text.h = variant.h}; if (variant.d != null) {text.d = variant.d} | |
svg.Add(text); text = ""; | |
} | |
VARIANT = variant; | |
for (i = 0, m = text.length; i < m; i++) { | |
variant = VARIANT; | |
n = text.charCodeAt(i); c = text.charAt(i); | |
if (n >= 0xD800 && n < 0xDBFF) { | |
i++; n = (((n-0xD800)<<10)+(text.charCodeAt(i)-0xDC00))+0x10000; | |
if (this.FONTDATA.RemapPlane1) { | |
var nv = this.FONTDATA.RemapPlane1(n,variant); | |
n = nv.n; variant = nv.variant; | |
} | |
} else { | |
RANGES = this.FONTDATA.RANGES; | |
for (id = 0, M = RANGES.length; id < M; id++) { | |
if (RANGES[id].name === "alpha" && variant.noLowerCase) continue; | |
N = variant["offset"+RANGES[id].offset]; | |
if (N && n >= RANGES[id].low && n <= RANGES[id].high) { | |
if (RANGES[id].remap && RANGES[id].remap[n]) { | |
n = N + RANGES[id].remap[n]; | |
} else { | |
n = n - RANGES[id].low + N; | |
if (RANGES[id].add) {n += RANGES[id].add} | |
} | |
if (variant["variant"+RANGES[id].offset]) | |
{variant = this.FONTDATA.VARIANT[variant["variant"+RANGES[id].offset]]} | |
break; | |
} | |
} | |
} | |
if (variant.remap && variant.remap[n]) { | |
n = variant.remap[n]; | |
if (variant.remap.variant) {variant = this.FONTDATA.VARIANT[variant.remap.variant]} | |
} else if (this.FONTDATA.REMAP[n] && !variant.noRemap) { | |
n = this.FONTDATA.REMAP[n]; | |
} | |
if (isArray(n)) {variant = this.FONTDATA.VARIANT[n[1]]; n = n[0]} | |
if (typeof(n) === "string") { | |
text = n+text.substr(i+1); | |
m = text.length; i = -1; | |
continue; | |
} | |
font = this.lookupChar(variant,n); c = font[n]; | |
if (c) { | |
if ((c[5] && c[5].space) || (c[5] === "" && c[0]+c[1] === 0)) {svg.w += c[2]} else { | |
c = [scale,font.id+"-"+n.toString(16).toUpperCase()].concat(c); | |
svg.Add(BBOX.GLYPH.apply(BBOX,c),svg.w,0); | |
} | |
} else if (this.FONTDATA.DELIMITERS[n]) { | |
c = this.createDelimiter(n,0,1,font); | |
svg.Add(c,svg.w,(this.FONTDATA.DELIMITERS[n].dir === "V" ? c.d: 0)); | |
} else { | |
if (n <= 0xFFFF) {c = String.fromCharCode(n)} else { | |
N = n - 0x10000; | |
c = String.fromCharCode((N>>10)+0xD800) | |
+ String.fromCharCode((N&0x3FF)+0xDC00); | |
} | |
var box = BBOX.TEXT(scale*100/SVG.config.scale,c,{ | |
"font-family":variant.defaultFamily||SVG.config.undefinedFamily, | |
"font-style":(variant.italic?"italic":""), | |
"font-weight":(variant.bold?"bold":"") | |
}) | |
if (variant.h != null) {box.h = variant.h}; if (variant.d != null) {box.d = variant.d} | |
c = BBOX.G(); c.Add(box); svg.Add(c,svg.w,0); | |
HUB.signal.Post(["SVG Jax - unknown char",n,variant]); | |
} | |
} | |
if (text.length == 1 && font.skew && font.skew[n]) {svg.skew = font.skew[n]*1000} | |
if (svg.element.childNodes.length === 1 && !svg.element.firstChild.getAttribute("x")) { | |
svg.element = svg.element.firstChild; | |
svg.removeable = false; svg.scale = scale; | |
} | |
return svg; | |
}, | |
lookupChar: function (variant,n) { | |
var i, m; | |
if (!variant.FONTS) { | |
var FONTS = this.FONTDATA.FONTS; | |
var fonts = (variant.fonts || this.FONTDATA.VARIANT.normal.fonts); | |
if (!(fonts instanceof Array)) {fonts = [fonts]} | |
if (variant.fonts != fonts) {variant.fonts = fonts} | |
variant.FONTS = []; | |
for (i = 0, m = fonts.length; i < m; i++) { | |
if (FONTS[fonts[i]]) {variant.FONTS.push(FONTS[fonts[i]])} | |
} | |
} | |
for (i = 0, m = variant.FONTS.length; i < m; i++) { | |
var font = variant.FONTS[i]; | |
if (typeof(font) === "string") {delete variant.FONTS; this.loadFont(font)} | |
if (font[n]) {return font} else {this.findBlock(font,n)} | |
} | |
return {id:"unknown"}; | |
}, | |
findBlock: function (font,c) { | |
if (font.Ranges) { | |
// FIXME: do binary search? | |
for (var i = 0, m = font.Ranges.length; i < m; i++) { | |
if (c < font.Ranges[i][0]) return; | |
if (c <= font.Ranges[i][1]) { | |
var file = font.Ranges[i][2]; | |
for (var j = font.Ranges.length-1; j >= 0; j--) | |
{if (font.Ranges[j][2] == file) {font.Ranges.splice(j,1)}} | |
this.loadFont(font.directory+"/"+file+".js"); | |
} | |
} | |
} | |
}, | |
loadFont: function (file) { | |
HUB.RestartAfter(AJAX.Require(this.fontDir+"/"+file)); | |
}, | |
createDelimiter: function (code,HW,scale,font) { | |
if (!scale) {scale = 1}; | |
var svg = BBOX.G(); | |
if (!code) { | |
svg.Clean(); delete svg.element; | |
svg.w = svg.r = this.TeX.nulldelimiterspace * scale; | |
return svg; | |
} | |
if (!(HW instanceof Array)) {HW = [HW,HW]} | |
var hw = HW[1]; HW = HW[0]; | |
var delim = {alias: code}; | |
while (delim.alias) { | |
code = delim.alias; delim = this.FONTDATA.DELIMITERS[code]; | |
if (!delim) {delim = {HW: [0,this.FONTDATA.VARIANT[MML.VARIANT.NORMAL]]}} | |
} | |
if (delim.load) {HUB.RestartAfter(AJAX.Require(this.fontDir+"/fontdata-"+delim.load+".js"))} | |
for (var i = 0, m = delim.HW.length; i < m; i++) { | |
if (delim.HW[i][0]*scale >= HW-10-SVG.config.blacker || (i == m-1 && !delim.stretch)) { | |
if (delim.HW[i][2]) {scale *= delim.HW[i][2]} | |
if (delim.HW[i][3]) {code = delim.HW[i][3]} | |
return this.createChar(scale,[code,delim.HW[i][1]],font).With({stretched: true}); | |
} | |
} | |
if (delim.stretch) {this["extendDelimiter"+delim.dir](svg,hw,delim.stretch,scale,font)} | |
return svg; | |
}, | |
createChar: function (scale,data,font) { | |
var text = "", variant = {fonts: [data[1]], noRemap:true}; | |
if (font && font === MML.VARIANT.BOLD) {variant.fonts = [data[1]+"-bold",data[1]]} | |
if (typeof(data[1]) !== "string") {variant = data[1]} | |
if (data[0] instanceof Array) { | |
for (var i = 0, m = data[0].length; i < m; i++) {text += String.fromCharCode(data[0][i])} | |
} else {text = String.fromCharCode(data[0])} | |
if (data[4]) {scale = scale*data[4]} | |
var svg = this.HandleVariant(variant,scale,text); | |
if (data[2]) {svg.x = data[2]*1000} | |
if (data[3]) {svg.y = data[3]*1000} | |
if (data[5]) {svg.h += data[5]*1000} | |
if (data[6]) {svg.d += data[6]*1000} | |
return svg; | |
}, | |
extendDelimiterV: function (svg,H,delim,scale,font) { | |
var top = this.createChar(scale,(delim.top||delim.ext),font); | |
var bot = this.createChar(scale,(delim.bot||delim.ext),font); | |
var h = top.h + top.d + bot.h + bot.d; | |
var y = -top.h; svg.Add(top,0,y); y -= top.d; | |
if (delim.mid) {var mid = this.createChar(scale,delim.mid,font); h += mid.h + mid.d} | |
if (delim.min && H < h*delim.min) {H = h*delim.min} | |
if (H > h) { | |
var ext = this.createChar(scale,delim.ext,font); | |
var k = (delim.mid ? 2 : 1), eH = (H-h) / k, s = (eH+100) / (ext.h+ext.d); | |
while (k-- > 0) { | |
var g = SVG.Element("g",{transform:"translate("+ext.y+","+(y-s*ext.h+50+ext.y)+") scale(1,"+s+")"}); | |
g.appendChild(ext.element.cloneNode(false)); svg.element.appendChild(g); y -= eH; | |
if (delim.mid && k) {svg.Add(mid,0,y-mid.h); y -= (mid.h+mid.d)} | |
} | |
} else if (delim.mid) { | |
y += (h - H)/2; svg.Add(mid,0,y-mid.h); y += -(mid.h + mid.d) + (h - H)/2; | |
} else { | |
y += (h - H); | |
} | |
svg.Add(bot,0,y-bot.h); svg.Clean(); | |
svg.scale = scale; | |
svg.isMultiChar = true; | |
}, | |
extendDelimiterH: function (svg,W,delim,scale,font) { | |
var left = this.createChar(scale,(delim.left||delim.rep),font); | |
var right = this.createChar(scale,(delim.right||delim.rep),font); | |
svg.Add(left,-left.l,0); | |
var w = (left.r - left.l) + (right.r - right.l), x = left.r - left.l; | |
if (delim.mid) {var mid = this.createChar(scale,delim.mid,font); w += mid.w} | |
if (delim.min && W < w*delim.min) {W = w*delim.min} | |
if (W > w) { | |
var rep = this.createChar(scale,delim.rep,font), fuzz = delim.fuzz || 0; | |
var k = (delim.mid ? 2 : 1), rW = (W-w) / k, s = (rW+fuzz) / (rep.r-rep.l); | |
while (k-- > 0) { | |
var g = SVG.Element("g",{transform:"translate("+(x-fuzz/2-s*rep.l+rep.x)+","+rep.y+") scale("+s+",1)"}); | |
g.appendChild(rep.element.cloneNode(false)); svg.element.appendChild(g); x += rW; | |
if (delim.mid && k) {svg.Add(mid,x,0); x += mid.w} | |
} | |
} else if (delim.mid) { | |
x -= (w - W)/2; svg.Add(mid,x,0); x += mid.w - (w - W)/2; | |
} else { | |
x -= (w - W); | |
} | |
svg.Add(right,x-right.l,0); svg.Clean(); | |
svg.scale = scale; | |
svg.isMultiChar = true; | |
}, | |
MATHSPACE: { | |
veryverythinmathspace: 1/18, | |
verythinmathspace: 2/18, | |
thinmathspace: 3/18, | |
mediummathspace: 4/18, | |
thickmathspace: 5/18, | |
verythickmathspace: 6/18, | |
veryverythickmathspace: 7/18, | |
negativeveryverythinmathspace: -1/18, | |
negativeverythinmathspace: -2/18, | |
negativethinmathspace: -3/18, | |
negativemediummathspace: -4/18, | |
negativethickmathspace: -5/18, | |
negativeverythickmathspace: -6/18, | |
negativeveryverythickmathspace: -7/18 | |
}, | |
// | |
// Units are em/1000 so quad is 1em | |
// | |
TeX: { | |
x_height: 430.554, | |
quad: 1000, | |
num1: 676.508, | |
num2: 393.732, | |
num3: 443.73, | |
denom1: 685.951, | |
denom2: 344.841, | |
sup1: 412.892, | |
sup2: 362.892, | |
sup3: 288.888, | |
sub1: 150, | |
sub2: 247.217, | |
sup_drop: 386.108, | |
sub_drop: 50, | |
delim1: 2390, | |
delim2: 1000, | |
axis_height: 250, | |
rule_thickness: 60, | |
big_op_spacing1: 111.111, | |
big_op_spacing2: 166.666, | |
big_op_spacing3: 200, | |
big_op_spacing4: 600, | |
big_op_spacing5: 100, | |
scriptspace: 100, | |
nulldelimiterspace: 120, | |
delimiterfactor: 901, | |
delimitershortfall: 300, | |
min_rule_thickness: 1.25, // in pixels | |
min_root_space: 1.5 // in pixels | |
}, | |
BIGDIMEN: 10000000, | |
NBSP: "\u00A0" | |
}); | |
var BBOX = SVG.BBOX = MathJax.Object.Subclass({ | |
type: "g", removeable: true, | |
Init: function (def) { | |
this.h = this.d = -SVG.BIGDIMEN; this.H = this.D = 0; | |
this.w = this.r = 0; this.l = SVG.BIGDIMEN; | |
this.x = this.y = 0; this.scale = 1; this.n = 0; | |
if (this.type) {this.element = SVG.Element(this.type,def)} | |
}, | |
With: function (def) {return HUB.Insert(this,def)}, | |
Add: function (svg,dx,dy,forcew,infront) { | |
if (dx) {svg.x += dx}; if (dy) {svg.y += dy}; | |
if (svg.element) { | |
if (svg.removeable && svg.element.childNodes.length === 1 && svg.n === 1) { | |
var child = svg.element.firstChild, nodeName = child.nodeName.toLowerCase(); | |
if (nodeName === "use" || nodeName === "rect") { | |
svg.element = child; svg.scale = svg.childScale; | |
var x = svg.childX, y = svg.childY; | |
svg.x += x; svg.y += y; | |
svg.h -= y; svg.d += y; svg.H -= y; svg.D +=y; | |
svg.w -= x; svg.r -= x; svg.l += x; | |
svg.removeable = false; | |
child.setAttribute("x",Math.floor(svg.x/svg.scale)); | |
child.setAttribute("y",Math.floor(svg.y/svg.scale)); | |
} | |
} | |
if (Math.abs(svg.x) < 1 && Math.abs(svg.y) < 1) { | |
svg.remove = svg.removeable; | |
} else { | |
nodeName = svg.element.nodeName.toLowerCase(); | |
if (nodeName === "g") { | |
if (!svg.element.firstChild) {svg.remove = svg.removeable} | |
else {svg.element.setAttribute("transform","translate("+Math.floor(svg.x)+","+Math.floor(svg.y)+")")} | |
} else if (nodeName === "line" || nodeName === "polygon" || | |
nodeName === "path" || nodeName === "a") { | |
var transform = svg.element.getAttribute("transform") || ""; | |
if (transform) transform = " "+transform; | |
transform = "translate("+Math.floor(svg.x)+","+Math.floor(svg.y)+")"+transform; | |
svg.element.setAttribute("transform",transform); | |
} else { | |
svg.element.setAttribute("x",Math.floor(svg.x/svg.scale)); | |
svg.element.setAttribute("y",Math.floor(svg.y/svg.scale)); | |
} | |
} | |
if (svg.remove) { | |
this.n += svg.n; | |
while (svg.element.firstChild) { | |
if (infront && this.element.firstChild) { | |
this.element.insertBefore(svg.element.firstChild,this.element.firstChild); | |
} else { | |
this.element.appendChild(svg.element.firstChild); | |
} | |
} | |
} else { | |
if (infront) {this.element.insertBefore(svg.element,this.element.firstChild)} | |
else {this.element.appendChild(svg.element)} | |
} | |
delete svg.element; | |
} | |
if (svg.hasIndent) {this.hasIndent = svg.hasIndent} | |
if (svg.tw != null) {this.tw = svg.tw} | |
if (svg.d - svg.y > this.d) {this.d = svg.d - svg.y; if (this.d > this.D) {this.D = this.d}} | |
if (svg.y + svg.h > this.h) {this.h = svg.y + svg.h; if (this.h > this.H) {this.H = this.h}} | |
if (svg.D - svg.y > this.D) {this.D = svg.D - svg.y} | |
if (svg.y + svg.H > this.H) {this.H = svg.y + svg.H} | |
if (svg.x + svg.l < this.l) {this.l = svg.x + svg.l} | |
if (svg.x + svg.r > this.r) {this.r = svg.x + svg.r} | |
if (forcew || svg.x + svg.w + (svg.X||0) > this.w) {this.w = svg.x + svg.w + (svg.X||0)} | |
this.childScale = svg.scale; this.childX = svg.x; this.childY = svg.y; this.n++; | |
return svg; | |
}, | |
Align: function (svg,align,dx,dy,shift) { | |
dx = ({left: dx, center: (this.w - svg.w)/2, right: this.w - svg.w - dx})[align] || 0; | |
var w = this.w; this.Add(svg,dx+(shift||0),dy); this.w = w; | |
}, | |
Clean: function () { | |
if (this.h === -SVG.BIGDIMEN) {this.h = this.d = this.l = 0} | |
return this; | |
} | |
}); | |
BBOX.ROW = BBOX.Subclass({ | |
Init: function () { | |
this.SUPER(arguments).Init.call(this); | |
this.svg = []; this.sh = this.sd = 0; | |
}, | |
Check: function (data) { | |
var svg = data.toSVG(); this.svg.push(svg); | |
if (data.SVGcanStretch("Vertical")) {svg.mml = data} | |
if (svg.h > this.sh) {this.sh = svg.h} | |
if (svg.d > this.sd) {this.sd = svg.d} | |
}, | |
Stretch: function () { | |
for (var i = 0, m = this.svg.length; i < m; i++) | |
{ | |
var svg = this.svg[i], mml = svg.mml; | |
if (mml) { | |
if (mml.forceStretch || mml.SVGdata.h !== this.sh || mml.SVGdata.d !== this.sd) { | |
svg = mml.SVGstretchV(this.sh,this.sd); | |
} | |
mml.SVGdata.HW = this.sh; mml.SVGdata.D = this.sd; | |
} | |
if (svg.ic) {this.ic = svg.ic} else {delete this.ic} | |
this.Add(svg,this.w,0,true); | |
} | |
delete this.svg; | |
} | |
}); | |
BBOX.RECT = BBOX.Subclass({ | |
type: "rect", removeable: false, | |
Init: function (h,d,w,def) { | |
if (def == null) {def = {stroke:"none"}} | |
def.width = Math.floor(w); def.height = Math.floor(h+d); | |
this.SUPER(arguments).Init.call(this,def); | |
this.w = this.r = w; this.h = this.H = h+d; this.d = this.D = this.l = 0; this.y = -d; | |
} | |
}); | |
BBOX.FRAME = BBOX.Subclass({ | |
type: "rect", removeable: false, | |
Init: function (h,d,w,t,dash,color,def) { | |
if (def == null) {def = {}}; def.fill = "none"; | |
def["stroke-width"] = SVG.Fixed(t,2); | |
def.width = Math.floor(w-t); def.height = Math.floor(h+d-t); | |
def.transform = "translate("+Math.floor(t/2)+","+Math.floor(-d+t/2)+")"; | |
if (dash === "dashed") | |
{def["stroke-dasharray"] = [Math.floor(6*SVG.em),Math.floor(6*SVG.em)].join(" ")} | |
this.SUPER(arguments).Init.call(this,def); | |
this.w = this.r = w; this.h = this.H = h; | |
this.d = this.D = d; this.l = 0; | |
} | |
}); | |
BBOX.HLINE = BBOX.Subclass({ | |
type: "line", removeable: false, | |
Init: function (w,t,dash,color,def) { | |
if (def == null) {def = {"stroke-linecap":"square"}} | |
if (color && color !== "") {def.stroke = color} | |
def["stroke-width"] = SVG.Fixed(t,2); | |
def.x1 = def.y1 = def.y2 = Math.floor(t/2); def.x2 = Math.floor(w-t/2); | |
if (dash === "dashed") { | |
var n = Math.floor(Math.max(0,w-t)/(6*t)), m = Math.floor(Math.max(0,w-t)/(2*n+1)); | |
def["stroke-dasharray"] = m+" "+m; | |
} | |
if (dash === "dotted") { | |
def["stroke-dasharray"] = [1,Math.max(150,Math.floor(2*t))].join(" "); | |
def["stroke-linecap"] = "round"; | |
} | |
this.SUPER(arguments).Init.call(this,def); | |
this.w = this.r = w; this.l = 0; this.h = this.H = t; this.d = this.D = 0; | |
} | |
}); | |
BBOX.VLINE = BBOX.Subclass({ | |
type: "line", removeable: false, | |
Init: function (h,t,dash,color,def) { | |
if (def == null) {def = {"stroke-linecap":"square"}} | |
if (color && color !== "") {def.stroke = color} | |
def["stroke-width"] = SVG.Fixed(t,2); | |
def.x1 = def.x2 = def.y1 = Math.floor(t/2); def.y2 = Math.floor(h-t/2); | |
if (dash === "dashed") { | |
var n = Math.floor(Math.max(0,h-t)/(6*t)), m = Math.floor(Math.max(0,h-t)/(2*n+1)); | |
def["stroke-dasharray"] = m+" "+m; | |
} | |
if (dash === "dotted") { | |
def["stroke-dasharray"] = [1,Math.max(150,Math.floor(2*t))].join(" "); | |
def["stroke-linecap"] = "round"; | |
} | |
this.SUPER(arguments).Init.call(this,def); | |
this.w = this.r = t; this.l = 0; this.h = this.H = h; this.d = this.D = 0; | |
} | |
}); | |
BBOX.TEXT = BBOX.Subclass({ | |
type: "text", removeable: false, | |
Init: function (scale,text,def) { | |
if (!def) {def = {}}; def.stroke = "none"; | |
if (def["font-style"] === "") delete def["font-style"]; | |
if (def["font-weight"] === "") delete def["font-weight"]; | |
this.SUPER(arguments).Init.call(this,def); | |
SVG.addText(this.element,text); | |
SVG.textSVG.appendChild(this.element); | |
var bbox = this.element.getBBox(); | |
SVG.textSVG.removeChild(this.element); | |
scale *= 1000/SVG.em; | |
this.element.setAttribute("transform","scale("+SVG.Fixed(scale)+") matrix(1 0 0 -1 0 0)"); | |
this.w = this.r = bbox.width*scale; this.l = 0; | |
this.h = this.H = -bbox.y*scale; | |
this.d = this.D = (bbox.height + bbox.y)*scale; | |
} | |
}); | |
BBOX.G = BBOX; | |
BBOX.NULL = BBOX.Subclass({ | |
Init: function () { | |
this.SUPER(arguments).Init.apply(this,arguments); | |
this.Clean(); | |
} | |
}); | |
BBOX.GLYPH = BBOX.Subclass({ | |
type: "path", removeable: false, | |
Init: function (scale,id,h,d,w,l,r,p) { | |
var def, t = SVG.config.blacker, GLYPH = BBOX.GLYPH; | |
var cache = SVG.config.useFontCache; | |
var transform = (scale === 1 ? null : "scale("+SVG.Fixed(scale)+")"); | |
if (cache && !SVG.config.useGlobalCache) {id = "E"+GLYPH.n+"-"+id} | |
if (!cache || !GLYPH.glyphs[id]) { | |
def = {"stroke-width":t}; | |
if (cache) {def.id = id} else if (transform) {def.transform = transform} | |
def.d = (p ? "M"+p+"Z" : ""); | |
this.SUPER(arguments).Init.call(this,def); | |
if (cache) {GLYPH.defs.appendChild(this.element); GLYPH.glyphs[id] = true;} | |
} | |
if (cache) { | |
def = {}; if (transform) {def.transform = transform} | |
this.element = SVG.Element("use",def); | |
this.element.setAttributeNS(XLINKNS,"href",SVGURL+"#"+id); | |
} | |
this.h = (h+t) * scale; this.d = (d+t) * scale; this.w = (w+t/2) *scale; | |
this.l = (l+t/2) * scale; this.r = (r+t/2) * scale; | |
this.H = Math.max(0,this.h); this.D = Math.max(0,this.d); | |
this.x = this.y = 0; this.scale = scale; | |
} | |
},{ | |
glyphs: {}, // which glpyhs have been used | |
defs: null, // the SVG <defs> element where glyphs are stored | |
n: 0 // the ID for local <defs> for self-contained SVG elements | |
}); | |
HUB.Register.StartupHook("mml Jax Ready",function () { | |
MML = MathJax.ElementJax.mml; | |
MML.mbase.Augment({ | |
SVG: BBOX, | |
toSVG: function () { | |
this.SVGgetStyles(); | |
var variant = this.SVGgetVariant(); | |
var svg = this.SVG(); this.SVGgetScale(svg); | |
this.SVGhandleSpace(svg); | |
for (var i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i]) { | |
var child = svg.Add(this.data[i].toSVG(variant,svg.scale),svg.w,0,true); | |
if (child.skew) {svg.skew = child.skew} | |
} | |
} | |
svg.Clean(); var text = this.data.join(""); | |
if (svg.skew && text.length !== 1) {delete svg.skew} | |
if (svg.r > svg.w && text.length === 1 && !variant.noIC) | |
{svg.ic = svg.r - svg.w; svg.w = svg.r} | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGchildSVG: function (i) { | |
return (this.data[i] ? this.data[i].toSVG() : BBOX()); | |
}, | |
SVGdataStretched: function (i,HW,D) { | |
this.SVGdata = {HW:HW, D:D}; | |
if (!this.data[i]) {return BBOX()} | |
if (D != null) {return this.data[i].SVGstretchV(HW,D)} | |
if (HW != null) {return this.data[i].SVGstretchH(HW)} | |
return this.data[i].toSVG(); | |
}, | |
SVGsaveData: function (svg) { | |
if (!this.SVGdata) {this.SVGdata = {}} | |
this.SVGdata.w = svg.w, this.SVGdata.x = svg.x; | |
this.SVGdata.h = svg.h, this.SVGdata.d = svg.d; | |
if (svg.y) {this.SVGdata.h += svg.y; this.SVGdata.d -= svg.y} | |
if (svg.X != null) {this.SVGdata.X = svg.X} | |
if (svg.tw != null) {this.SVGdata.tw = svg.tw} | |
if (svg.skew) {this.SVGdata.skew = svg.skew} | |
if (svg.ic) {this.SVGdata.ic = svg.ic} | |
if (this["class"]) {svg.removeable = false; SVG.Element(svg.element,{"class":this["class"]})} | |
// FIXME: if an element is split by linebreaking, the ID will be the same on both parts | |
// FIXME: if an element has an id, its zoomed copy will have the same ID | |
if (this.id) {svg.removeable = false; SVG.Element(svg.element,{"id":this.id})} | |
if (this.href) {this.SVGaddHref(svg)} | |
if (SVG.config.addMMLclasses) { | |
this.SVGaddClass(svg.element,"mjx-svg-"+this.type); | |
svg.removeable = false; | |
} | |
var style = this.style; | |
if (style && svg.element) { | |
svg.element.style.cssText = style; | |
if (svg.element.style.fontSize) {svg.element.style.fontSize = ""} // handled by scale | |
svg.element.style.border = svg.element.style.padding = ""; | |
if (svg.removeable) {svg.removeable = (svg.element.style.cssText === "")} | |
} | |
this.SVGaddAttributes(svg); | |
}, | |
SVGaddClass: function (node,name) { | |
var classes = node.getAttribute("class"); | |
node.setAttribute("class",(classes ? classes+" " : "")+name); | |
}, | |
SVGaddAttributes: function (svg) { | |
// | |
// Copy RDFa, aria, and other tags from the MathML to the HTML-CSS | |
// output spans Don't copy those in the MML.nocopyAttributes list, | |
// the ignoreMMLattributes configuration list, or anything tha | |
// already exists as a property of the span (e.g., no "onlick", etc.) | |
// If a name in the ignoreMMLattributes object is set to false, then | |
// the attribute WILL be copied. | |
// | |
if (this.attrNames) { | |
var copy = this.attrNames, skip = MML.nocopyAttributes, ignore = HUB.config.ignoreMMLattributes; | |
var defaults = (this.type === "mstyle" ? MML.math.prototype.defaults : this.defaults); | |
for (var i = 0, m = copy.length; i < m; i++) { | |
var id = copy[i]; | |
if (ignore[id] == false || (!skip[id] && !ignore[id] && | |
defaults[id] == null && typeof(svg.element[id]) === "undefined")) { | |
svg.element.setAttribute(id,this.attr[id]); | |
svg.removeable = false; | |
} | |
} | |
} | |
}, | |
SVGaddHref: function (svg) { | |
var a = SVG.Element("a",{"class":"mjx-svg-href"}); | |
a.setAttributeNS(XLINKNS,"href",this.href); | |
a.onclick = this.SVGlink; | |
SVG.addElement(a,"rect",{width:svg.w, height:svg.h+svg.d, y:-svg.d, | |
fill:"none", stroke:"none", "pointer-events":"all"}); | |
if (svg.type === "svg") { | |
// for svg element, put <a> inside the main <g> element | |
var g = svg.element.firstChild; | |
while (g.firstChild) {a.appendChild(g.firstChild)} | |
g.appendChild(a); | |
} else { | |
a.appendChild(svg.element); svg.element = a; | |
} | |
svg.removeable = false; | |
}, | |
// | |
// WebKit currently scrolls to the BOTTOM of an svg element if it contains the | |
// target of the link, so implement link by hand, to the containing span element. | |
// | |
SVGlink: function () { | |
var href = this.href.animVal; | |
if (href.charAt(0) === "#") { | |
var target = SVG.hashCheck(document.getElementById(href.substr(1))); | |
if (target && target.scrollIntoView) | |
{setTimeout(function () {target.parentNode.scrollIntoView(true)},1)} | |
} | |
document.location = href; | |
}, | |
SVGgetStyles: function () { | |
if (this.style) { | |
var span = HTML.Element("span"); | |
span.style.cssText = this.style; | |
this.styles = this.SVGprocessStyles(span.style); | |
} | |
}, | |
SVGprocessStyles: function (style) { | |
var styles = {border:SVG.getBorders(style), padding:SVG.getPadding(style)}; | |
if (!styles.border) {delete styles.border} | |
if (!styles.padding) {delete styles.padding} | |
if (style.fontSize) {styles.fontSize = style.fontSize} | |
if (style.color) {styles.color = style.color} | |
if (style.backgroundColor) {styles.background = style.backgroundColor} | |
if (style.fontStyle) {styles.fontStyle = style.fontStyle} | |
if (style.fontWeight) {styles.fontWeight = style.fontWeight} | |
if (style.fontFamily) {styles.fontFamily = style.fontFamily} | |
if (styles.fontWeight && styles.fontWeight.match(/^\d+$/)) | |
{styles.fontWeight = (parseInt(styles.fontWeight) > 600 ? "bold" : "normal")} | |
return styles; | |
}, | |
SVGhandleSpace: function (svg) { | |
if (this.useMMLspacing) { | |
if (this.type !== "mo") return; | |
var values = this.getValues("scriptlevel","lspace","rspace"); | |
if (values.scriptlevel <= 0 || this.hasValue("lspace") || this.hasValue("rspace")) { | |
var mu = this.SVGgetMu(svg); | |
values.lspace = Math.max(0,SVG.length2em(values.lspace,mu)); | |
values.rspace = Math.max(0,SVG.length2em(values.rspace,mu)); | |
var core = this, parent = this.Parent(); | |
while (parent && parent.isEmbellished() && parent.Core() === core) | |
{core = parent; parent = parent.Parent()} | |
if (values.lspace) {svg.x += values.lspace} | |
if (values.rspace) {svg.X = values.rspace} | |
} | |
} else { | |
var space = this.texSpacing(); | |
this.SVGgetScale(); | |
if (space !== "") {svg.x += SVG.length2em(space,this.scale)*this.mscale} | |
} | |
}, | |
SVGhandleColor: function (svg) { | |
var values = this.getValues("mathcolor","color"); | |
if (this.styles && this.styles.color && !values.color) {values.color = this.styles.color} | |
if (values.color && !this.mathcolor) {values.mathcolor = values.color} | |
if (values.mathcolor) { | |
SVG.Element(svg.element,{fill:values.mathcolor,stroke:values.mathcolor}) | |
svg.removeable = false; | |
} | |
var borders = (this.styles||{}).border, padding = (this.styles||{}).padding, | |
bleft = ((borders||{}).left||0), pleft = ((padding||{}).left||0), id; | |
values.background = (this.mathbackground || this.background || | |
(this.styles||{}).background || MML.COLOR.TRANSPARENT); | |
if (bleft + pleft) { | |
// | |
// Make a box and move the contents of svg to it, | |
// then add it back into svg, but offset by the left amount | |
// | |
var dup = BBOX(); for (id in svg) {if (svg.hasOwnProperty(id)) {dup[id] = svg[id]}} | |
dup.x = 0; dup.y = 0; | |
svg.element = SVG.Element("g"); svg.removeable = true; | |
svg.Add(dup,bleft+pleft,0); | |
} | |
// | |
// Adjust size by padding and dashed borders (left is taken care of above) | |
// | |
if (padding) {svg.w += padding.right||0; svg.h += padding.top||0; svg.d += padding.bottom||0} | |
if (borders) {svg.w += borders.right||0; svg.h += borders.top||0; svg.d += borders.bottom||0} | |
// | |
// Add background color | |
// | |
if (values.background !== MML.COLOR.TRANSPARENT) { | |
var nodeName = svg.element.nodeName.toLowerCase(); | |
if (nodeName !== "g" && nodeName !== "svg") { | |
var g = SVG.Element("g"); g.appendChild(svg.element); | |
svg.element = g; svg.removeable = true; | |
} | |
svg.Add(BBOX.RECT(svg.h,svg.d,svg.w,{fill:values.background,stroke:"none"}),0,0,false,true) | |
} | |
// | |
// Add borders | |
// | |
if (borders) { | |
var dd = 5; // fuzz factor to avoid anti-alias problems at edges | |
var sides = { | |
left: ["V",svg.h+svg.d,-dd,-svg.d], | |
right: ["V",svg.h+svg.d,svg.w-borders.right+dd,-svg.d], | |
top: ["H",svg.w,0,svg.h-borders.top+dd], | |
bottom:["H",svg.w,0,-svg.d-dd] | |
} | |
for (id in sides) {if (sides.hasOwnProperty(id)) { | |
if (borders[id]) { | |
var side = sides[id], box = BBOX[side[0]+"LINE"]; | |
svg.Add(box(side[1],borders[id],borders[id+"Style"],borders[id+"Color"]),side[2],side[3]); | |
} | |
}} | |
} | |
}, | |
SVGhandleVariant: function (variant,scale,text) { | |
return SVG.HandleVariant(variant,scale,text); | |
}, | |
SVGgetVariant: function () { | |
var values = this.getValues("mathvariant","fontfamily","fontweight","fontstyle"); | |
var variant = values.mathvariant; | |
if (this.variantForm) variant = "-"+SVG.fontInUse+"-variant"; | |
values.hasVariant = this.Get("mathvariant",true); // null if not explicitly specified | |
if (!values.hasVariant) { | |
values.family = values.fontfamily; | |
values.weight = values.fontweight; | |
values.style = values.fontstyle; | |
} | |
if (this.styles) { | |
if (!values.style && this.styles.fontStyle) {values.style = this.styles.fontStyle} | |
if (!values.weight && this.styles.fontWeight) {values.weight = this.styles.fontWeight} | |
if (!values.family && this.styles.fontFamily) {values.family = this.styles.fontFamily} | |
} | |
if (values.family && !values.hasVariant) { | |
if (!values.weight && values.mathvariant.match(/bold/)) {values.weight = "bold"} | |
if (!values.style && values.mathvariant.match(/italic/)) {values.style = "italic"} | |
variant = {forceFamily: true, font: {"font-family":values.family}}; | |
if (values.style) {variant.font["font-style"] = values.style} | |
if (values.weight) {variant.font["font-weight"] = values.weight} | |
return variant; | |
} | |
if (values.weight === "bold") { | |
variant = { | |
normal:MML.VARIANT.BOLD, italic:MML.VARIANT.BOLDITALIC, | |
fraktur:MML.VARIANT.BOLDFRAKTUR, script:MML.VARIANT.BOLDSCRIPT, | |
"sans-serif":MML.VARIANT.BOLDSANSSERIF, | |
"sans-serif-italic":MML.VARIANT.SANSSERIFBOLDITALIC | |
}[variant]||variant; | |
} else if (values.weight === "normal") { | |
variant = { | |
bold:MML.VARIANT.normal, "bold-italic":MML.VARIANT.ITALIC, | |
"bold-fraktur":MML.VARIANT.FRAKTUR, "bold-script":MML.VARIANT.SCRIPT, | |
"bold-sans-serif":MML.VARIANT.SANSSERIF, | |
"sans-serif-bold-italic":MML.VARIANT.SANSSERIFITALIC | |
}[variant]||variant; | |
} | |
if (values.style === "italic") { | |
variant = { | |
normal:MML.VARIANT.ITALIC, bold:MML.VARIANT.BOLDITALIC, | |
"sans-serif":MML.VARIANT.SANSSERIFITALIC, | |
"bold-sans-serif":MML.VARIANT.SANSSERIFBOLDITALIC | |
}[variant]||variant; | |
} else if (values.style === "normal") { | |
variant = { | |
italic:MML.VARIANT.NORMAL, "bold-italic":MML.VARIANT.BOLD, | |
"sans-serif-italic":MML.VARIANT.SANSSERIF, | |
"sans-serif-bold-italic":MML.VARIANT.BOLDSANSSERIF | |
}[variant]||variant; | |
} | |
if (!(variant in SVG.FONTDATA.VARIANT)) { | |
// If the mathvariant value is invalid or not supported by this | |
// font, fallback to normal. See issue 363. | |
variant = "normal"; | |
} | |
return SVG.FONTDATA.VARIANT[variant]; | |
}, | |
SVGgetScale: function (svg) { | |
var scale = 1; | |
if (this.mscale) { | |
scale = this.scale; | |
} else { | |
var values = this.getValues("scriptlevel","fontsize"); | |
values.mathsize = (this.isToken ? this : this.Parent()).Get("mathsize"); | |
if ((this.styles||{}).fontSize && !values.fontsize) {values.fontsize = this.styles.fontSize} | |
if (values.fontsize && !this.mathsize) {values.mathsize = values.fontsize} | |
if (values.scriptlevel !== 0) { | |
if (values.scriptlevel > 2) {values.scriptlevel = 2} | |
scale = Math.pow(this.Get("scriptsizemultiplier"),values.scriptlevel); | |
values.scriptminsize = SVG.length2em(this.Get("scriptminsize"))/1000; | |
if (scale < values.scriptminsize) {scale = values.scriptminsize} | |
} | |
this.scale = scale; this.mscale = SVG.length2em(values.mathsize)/1000; | |
} | |
if (svg) {svg.scale = scale; if (this.isToken) {svg.scale *= this.mscale}} | |
return scale * this.mscale; | |
}, | |
SVGgetMu: function (svg) { | |
var mu = 1, values = this.getValues("scriptlevel","scriptsizemultiplier"); | |
if (svg.scale && svg.scale !== 1) {mu = 1/svg.scale} | |
if (values.scriptlevel !== 0) { | |
if (values.scriptlevel > 2) {values.scriptlevel = 2} | |
mu = Math.sqrt(Math.pow(values.scriptsizemultiplier,values.scriptlevel)); | |
} | |
return mu; | |
}, | |
SVGnotEmpty: function (data) { | |
while (data) { | |
if ((data.type !== "mrow" && data.type !== "texatom") || | |
data.data.length > 1) {return true} | |
data = data.data[0]; | |
} | |
return false; | |
}, | |
SVGcanStretch: function (direction) { | |
var can = false; | |
if (this.isEmbellished()) { | |
var core = this.Core(); | |
if (core && core !== this) { | |
can = core.SVGcanStretch(direction); | |
if (can && core.forceStretch) {this.forceStretch = true} | |
} | |
} | |
return can; | |
}, | |
SVGstretchV: function (h,d) {return this.toSVG(h,d)}, | |
SVGstretchH: function (w) {return this.toSVG(w)}, | |
SVGlineBreaks: function () {return false} | |
},{ | |
SVGemptySVG: function () { | |
var svg = this.SVG(); | |
svg.Clean(); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGautoload: function () { | |
var file = SVG.autoloadDir+"/"+this.type+".js"; | |
HUB.RestartAfter(AJAX.Require(file)); | |
}, | |
SVGautoloadFile: function (name) { | |
var file = SVG.autoloadDir+"/"+name+".js"; | |
HUB.RestartAfter(AJAX.Require(file)); | |
} | |
}); | |
MML.chars.Augment({ | |
toSVG: function (variant,scale,remap,chars) { | |
var text = this.data.join("").replace(/[\u2061-\u2064]/g,""); // remove invisibles | |
if (remap) {text = remap(text,chars)} | |
return this.SVGhandleVariant(variant,scale,text); | |
} | |
}); | |
MML.entity.Augment({ | |
toSVG: function (variant,scale,remap,chars) { | |
var text = this.toString().replace(/[\u2061-\u2064]/g,""); // remove invisibles | |
if (remap) {text = remap(text,chars)} | |
return this.SVGhandleVariant(variant,scale,text); | |
} | |
}); | |
MML.mo.Augment({ | |
toSVG: function (HW,D) { | |
this.SVGgetStyles(); | |
var svg = this.svg = this.SVG(); | |
var scale = this.SVGgetScale(svg); | |
this.SVGhandleSpace(svg); | |
if (this.data.length == 0) {svg.Clean(); this.SVGsaveData(svg); return svg} | |
// | |
// Stretch the operator, if that is requested | |
// | |
if (D != null) {return this.SVGstretchV(HW,D)} | |
else if (HW != null) {return this.SVG.strechH(HW)} | |
// | |
// Get the variant, and check for operator size | |
// | |
var variant = this.SVGgetVariant(); | |
var values = this.getValues("largeop","displaystyle"); | |
if (values.largeop) | |
{variant = SVG.FONTDATA.VARIANT[values.displaystyle ? "-largeOp" : "-smallOp"]} | |
// | |
// Get character translation for superscript and accents | |
// | |
var parent = this.CoreParent(), | |
isScript = (parent && parent.isa(MML.msubsup) && this !== parent.data[0]), | |
mapchars = (isScript?this.remapChars:null); | |
if (this.data.join("").length === 1 && parent && parent.isa(MML.munderover) && | |
this.CoreText(parent.data[parent.base]).length === 1) { | |
var over = parent.data[parent.over], under = parent.data[parent.under]; | |
if (over && this === over.CoreMO() && parent.Get("accent")) {mapchars = SVG.FONTDATA.REMAPACCENT} | |
else if (under && this === under.CoreMO() && parent.Get("accentunder")) {mapchars = SVG.FONTDATA.REMAPACCENTUNDER} | |
} | |
// | |
// Primes must come from another font | |
// | |
if (isScript && this.data.join("").match(/['`"\u00B4\u2032-\u2037\u2057]/)) | |
{variant = SVG.FONTDATA.VARIANT["-"+SVG.fontInUse+"-variant"]} | |
// | |
// Typeset contents | |
// | |
for (var i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i]) { | |
var text = this.data[i].toSVG(variant,scale,this.remap,mapchars), x = svg.w; | |
if (x === 0 && -text.l > 10*text.w) {x += -text.l} // initial combining character doesn't combine | |
svg.Add(text,x,0,true); | |
if (text.skew) {svg.skew = text.skew} | |
} | |
} | |
svg.Clean(); | |
if (this.data.join("").length !== 1) {delete svg.skew} | |
// | |
// Handle large operator centering | |
// | |
if (values.largeop) { | |
svg.y = SVG.TeX.axis_height - (svg.h - svg.d)/2/scale; | |
if (svg.r > svg.w) {svg.ic = svg.r - svg.w; svg.w = svg.r} | |
} | |
// | |
// Finish up | |
// | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGcanStretch: function (direction) { | |
if (!this.Get("stretchy")) {return false} | |
var c = this.data.join(""); | |
if (c.length > 1) {return false} | |
var parent = this.CoreParent(); | |
if (parent && parent.isa(MML.munderover) && | |
this.CoreText(parent.data[parent.base]).length === 1) { | |
var over = parent.data[parent.over], under = parent.data[parent.under]; | |
if (over && this === over.CoreMO() && parent.Get("accent")) {c = SVG.FONTDATA.REMAPACCENT[c]||c} | |
else if (under && this === under.CoreMO() && parent.Get("accentunder")) {c = SVG.FONTDATA.REMAPACCENTUNDER[c]||c} | |
} | |
c = SVG.FONTDATA.DELIMITERS[c.charCodeAt(0)]; | |
var can = (c && c.dir == direction.substr(0,1)); | |
if (!can) {delete this.svg} | |
this.forceStretch = can && (this.Get("minsize",true) || this.Get("maxsize",true)); | |
return can; | |
}, | |
SVGstretchV: function (h,d) { | |
var svg = this.svg || this.toSVG(); | |
var values = this.getValues("symmetric","maxsize","minsize"); | |
var axis = SVG.TeX.axis_height*svg.scale, mu = this.SVGgetMu(svg), H; | |
if (values.symmetric) {H = 2*Math.max(h-axis,d+axis)} else {H = h + d} | |
values.maxsize = SVG.length2em(values.maxsize,mu,svg.h+svg.d); | |
values.minsize = SVG.length2em(values.minsize,mu,svg.h+svg.d); | |
H = Math.max(values.minsize,Math.min(values.maxsize,H)); | |
if (H != values.minsize) | |
{H = [Math.max(H*SVG.TeX.delimiterfactor/1000,H-SVG.TeX.delimitershortfall),H]} | |
svg = SVG.createDelimiter(this.data.join("").charCodeAt(0),H,svg.scale); | |
if (values.symmetric) {H = (svg.h + svg.d)/2 + axis} | |
else {H = (svg.h + svg.d) * h/(h + d)} | |
svg.y = H - svg.h; | |
this.SVGhandleSpace(svg); | |
this.SVGhandleColor(svg); | |
delete this.svg.element; | |
this.SVGsaveData(svg); | |
svg.stretched = true; | |
return svg; | |
}, | |
SVGstretchH: function (w) { | |
var svg = this.svg || this.toSVG(), mu = this.SVGgetMu(svg); | |
var values = this.getValues("maxsize","minsize","mathvariant","fontweight"); | |
// FIXME: should take style="font-weight:bold" into account as well | |
if ((values.fontweight === "bold" || parseInt(values.fontweight) >= 600) && | |
!this.Get("mathvariant",true)) {values.mathvariant = MML.VARIANT.BOLD} | |
values.maxsize = SVG.length2em(values.maxsize,mu,svg.w); | |
values.minsize = SVG.length2em(values.minsize,mu,svg.w); | |
w = Math.max(values.minsize,Math.min(values.maxsize,w)); | |
svg = SVG.createDelimiter(this.data.join("").charCodeAt(0),w,svg.scale,values.mathvariant); | |
this.SVGhandleSpace(svg); | |
this.SVGhandleColor(svg); | |
delete this.svg.element; | |
this.SVGsaveData(svg); | |
svg.stretched = true; | |
return svg; | |
} | |
}); | |
MML.mn.Augment({ | |
SVGremapMinus: function (text) {return text.replace(/^-/,"\u2212")}, | |
toSVG: function () { | |
this.SVGgetStyles(); | |
var variant = this.SVGgetVariant(); | |
var svg = this.SVG(); this.SVGgetScale(svg); | |
this.SVGhandleSpace(svg); | |
var remap = this.SVGremapMinus; | |
for (var i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i]) { | |
var child = svg.Add(this.data[i].toSVG(variant,svg.scale,remap),svg.w,0,true); | |
if (child.skew) {svg.skew = child.skew} | |
remap = null; | |
} | |
} | |
svg.Clean(); var text = this.data.join(""); | |
if (svg.skew && text.length !== 1) {delete svg.skew} | |
if (svg.r > svg.w && text.length === 1 && !variant.noIC) | |
{svg.ic = svg.r - svg.w; svg.w = svg.r} | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
}), | |
MML.mtext.Augment({ | |
toSVG: function () { | |
if (SVG.config.mtextFontInherit || this.Parent().type === "merror") { | |
this.SVGgetStyles(); | |
var svg = this.SVG(), scale = this.SVGgetScale(svg); | |
this.SVGhandleSpace(svg); | |
var variant = this.SVGgetVariant(), def = {direction:this.Get("dir")}; | |
if (variant.bold) {def["font-weight"] = "bold"} | |
if (variant.italic) {def["font-style"] = "italic"} | |
variant = this.Get("mathvariant"); | |
if (variant === "monospace") {def["class"] = "MJX-monospace"} | |
else if (variant.match(/sans-serif/)) {def["class"] = "MJX-sans-serif"} | |
svg.Add(BBOX.TEXT(scale*100/SVG.config.scale,this.data.join(""),def)); svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
} else { | |
return this.SUPER(arguments).toSVG.call(this); | |
} | |
} | |
}); | |
MML.merror.Augment({ | |
toSVG: function (HW,D) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(), scale = SVG.length2em(this.styles.fontSize||1)/1000; | |
this.SVGhandleSpace(svg); | |
var def = (scale !== 1 ? {transform:"scale("+SVG.Fixed(scale)+")"} : {}); | |
var bbox = BBOX(def); | |
bbox.Add(this.SVGchildSVG(0)); bbox.Clean(); | |
if (scale !== 1) { | |
bbox.removeable = false; | |
var adjust = ["w","h","d","l","r","D","H"]; | |
for (var i = 0, m = adjust.length; i < m; i++) {bbox[adjust[i]] *= scale} | |
} | |
svg.Add(bbox); svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGgetStyles: function () { | |
var span = HTML.Element("span",{style: SVG.config.merrorStyle}); | |
this.styles = this.SVGprocessStyles(span.style); | |
if (this.style) { | |
span.style.cssText = this.style; | |
HUB.Insert(this.styles,this.SVGprocessStyles(span.style)); | |
} | |
} | |
}); | |
MML.ms.Augment({toSVG: MML.mbase.SVGautoload}); | |
MML.mglyph.Augment({toSVG: MML.mbase.SVGautoload}); | |
MML.mspace.Augment({ | |
toSVG: function () { | |
this.SVGgetStyles(); | |
var values = this.getValues("height","depth","width"); | |
values.mathbackground = this.mathbackground; | |
if (this.background && !this.mathbackground) {values.mathbackground = this.background} | |
var svg = this.SVG(); this.SVGgetScale(svg); | |
var scale = this.mscale, mu = this.SVGgetMu(svg); | |
svg.h = SVG.length2em(values.height,mu) * scale; | |
svg.d = SVG.length2em(values.depth,mu) * scale; | |
svg.w = svg.r = SVG.length2em(values.width,mu) * scale; | |
if (svg.w < 0) {svg.x = svg.w; svg.w = svg.r = 0} | |
if (svg.h < -svg.d) {svg.d = -svg.h} | |
svg.l = 0; svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
MML.mphantom.Augment({ | |
toSVG: function (HW,D) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(); this.SVGgetScale(svg); | |
if (this.data[0] != null) { | |
this.SVGhandleSpace(svg); svg.Add(this.SVGdataStretched(0,HW,D)); svg.Clean(); | |
while (svg.element.firstChild) {svg.element.removeChild(svg.element.firstChild)} | |
} | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
if (svg.removeable && !svg.element.firstChild) {delete svg.element} | |
return svg; | |
} | |
}); | |
MML.mpadded.Augment({ | |
toSVG: function (HW,D) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(); | |
if (this.data[0] != null) { | |
this.SVGgetScale(svg); this.SVGhandleSpace(svg); | |
var pad = this.SVGdataStretched(0,HW,D), mu = this.SVGgetMu(svg); | |
var values = this.getValues("height","depth","width","lspace","voffset"), X = 0, Y = 0; | |
if (values.lspace) {X = this.SVGlength2em(pad,values.lspace,mu)} | |
if (values.voffset) {Y = this.SVGlength2em(pad,values.voffset,mu)} | |
var h = pad.h, d = pad.d, w = pad.w, y = pad.y; // these can change durring the Add() | |
svg.Add(pad,X,Y); svg.Clean(); | |
svg.h = h+y; svg.d = d-y; svg.w = w; svg.removeable = false; | |
if (values.height !== "") {svg.h = this.SVGlength2em(svg,values.height,mu,"h",0)} | |
if (values.depth !== "") {svg.d = this.SVGlength2em(svg,values.depth,mu,"d",0)} | |
if (values.width !== "") {svg.w = this.SVGlength2em(svg,values.width,mu,"w",0)} | |
if (svg.h > svg.H) {svg.H = svg.h}; if (svg.d > svg.D) {svg.D = svg.d} | |
} | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGlength2em: function (svg,length,mu,d,m) { | |
if (m == null) {m = -SVG.BIGDIMEN} | |
var match = String(length).match(/width|height|depth/); | |
var size = (match ? svg[match[0].charAt(0)] : (d ? svg[d] : 0)); | |
var v = SVG.length2em(length,mu,size/this.mscale)*this.mscale; | |
if (d && String(length).match(/^\s*[-+]/)) | |
{return Math.max(m,svg[d]+v)} else {return v} | |
} | |
}); | |
MML.mrow.Augment({ | |
SVG: BBOX.ROW, | |
toSVG: function (h,d) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(); | |
this.SVGhandleSpace(svg); | |
if (d != null) {svg.sh = h; svg.sd = d} | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{if (this.data[i]) {svg.Check(this.data[i])}} | |
svg.Stretch(); svg.Clean(); | |
if (this.data.length === 1 && this.data[0]) { | |
var data = this.data[0].SVGdata; | |
if (data.skew) {svg.skew = data.skew} | |
} | |
if (this.SVGlineBreaks(svg)) {svg = this.SVGmultiline(svg)} | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGlineBreaks: function (svg) { | |
if (!this.parent.linebreakContainer) {return false} | |
return (SVG.config.linebreaks.automatic && | |
svg.w > SVG.linebreakWidth) || this.hasNewline(); | |
}, | |
SVGmultiline: function (span) {MML.mbase.SVGautoloadFile("multiline")}, | |
SVGstretchH: function (w) { | |
var svg = this.SVG(); | |
this.SVGhandleSpace(svg); | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{svg.Add(this.SVGdataStretched(i,w),svg.w,0)} | |
svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
MML.mstyle.Augment({ | |
toSVG: function () { | |
this.SVGgetStyles(); | |
var svg = this.SVG(); | |
if (this.data[0] != null) { | |
this.SVGhandleSpace(svg); | |
var math = svg.Add(this.data[0].toSVG()); svg.Clean(); | |
if (math.ic) {svg.ic = math.ic} | |
this.SVGhandleColor(svg); | |
} | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGstretchH: function (w) { | |
return (this.data[0] != null ? this.data[0].SVGstretchH(w) : BBOX.NULL()); | |
}, | |
SVGstretchV: function (h,d) { | |
return (this.data[0] != null ? this.data[0].SVGstretchV(h,d) : BBOX.NULL()); | |
} | |
}); | |
MML.mfrac.Augment({ | |
toSVG: function () { | |
this.SVGgetStyles(); | |
var svg = this.SVG(), scale = this.SVGgetScale(svg); | |
var frac = BBOX(); frac.scale = svg.scale; this.SVGhandleSpace(frac); | |
var num = this.SVGchildSVG(0), den = this.SVGchildSVG(1); | |
var values = this.getValues("displaystyle","linethickness","numalign","denomalign","bevelled"); | |
var isDisplay = values.displaystyle; | |
var a = SVG.TeX.axis_height * scale; | |
if (values.bevelled) { | |
var delta = (isDisplay ? 400 : 150); | |
var H = Math.max(num.h+num.d,den.h+den.d)+2*delta; | |
var bevel = SVG.createDelimiter(0x2F,H); | |
frac.Add(num,0,(num.d-num.h)/2+a+delta); | |
frac.Add(bevel,num.w-delta/2,(bevel.d-bevel.h)/2+a); | |
frac.Add(den,num.w+bevel.w-delta,(den.d-den.h)/2+a-delta); | |
} else { | |
var W = Math.max(num.w,den.w); | |
var t = SVG.thickness2em(values.linethickness,this.scale)*this.mscale, p,q, u,v; | |
var mt = SVG.TeX.min_rule_thickness/SVG.em * 1000; | |
if (isDisplay) {u = SVG.TeX.num1; v = SVG.TeX.denom1} | |
else {u = (t === 0 ? SVG.TeX.num3 : SVG.TeX.num2); v = SVG.TeX.denom2} | |
u *= scale; v *= scale; | |
if (t === 0) {// \atop | |
p = Math.max((isDisplay ? 7 : 3) * SVG.TeX.rule_thickness, 2*mt); // force to at least 2 px | |
q = (u - num.d) - (den.h - v); | |
if (q < p) {u += (p - q)/2; v += (p - q)/2} | |
frac.w = W; t = 0; | |
} else {// \over | |
p = Math.max((isDisplay ? 2 : 0) * mt + t, t/2 + 1.5*mt); // force to be at least 1.5px | |
q = (u - num.d) - (a + t/2); if (q < p) {u += p - q} | |
q = (a - t/2) - (den.h - v); if (q < p) {v += p - q} | |
frac.Add(BBOX.RECT(t/2,t/2,W+2*t),0,a); | |
} | |
frac.Align(num,values.numalign,t,u); | |
frac.Align(den,values.denomalign,t,-v); | |
} | |
frac.Clean(); svg.Add(frac,0,0); svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGcanStretch: function (direction) {return false}, | |
SVGhandleSpace: function (svg) { | |
if (!this.texWithDelims && !this.useMMLspacing) { | |
// | |
// Add nulldelimiterspace around the fraction | |
// (TeXBook pg 150 and Appendix G rule 15e) | |
// | |
svg.x = svg.X = SVG.TeX.nulldelimiterspace * this.mscale; | |
} | |
this.SUPER(arguments).SVGhandleSpace.call(this,svg); | |
} | |
}); | |
MML.msqrt.Augment({ | |
toSVG: function () { | |
this.SVGgetStyles(); | |
var svg = this.SVG(), scale = this.SVGgetScale(svg); this.SVGhandleSpace(svg); | |
var base = this.SVGchildSVG(0), rule, surd; | |
var t = SVG.TeX.rule_thickness * scale, p,q, H, x = 0; | |
if (this.Get("displaystyle")) {p = SVG.TeX.x_height * scale} else {p = t} | |
q = Math.max(t + p/4,1000*SVG.TeX.min_root_space/SVG.em); | |
H = base.h + base.d + q + t; | |
surd = SVG.createDelimiter(0x221A,H,scale); | |
if (surd.h + surd.d > H) {q = ((surd.h+surd.d) - (H-t)) / 2} | |
rule = BBOX.RECT(t,0,base.w); | |
H = base.h + q + t; | |
x = this.SVGaddRoot(svg,surd,x,surd.h+surd.d-H,scale); | |
svg.Add(surd,x,H-surd.h); | |
svg.Add(rule,x+surd.w,H-rule.h); | |
svg.Add(base,x+surd.w,0); | |
svg.Clean(); | |
svg.h += t; svg.H += t; | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGaddRoot: function (svg,surd,x,d,scale) {return x} | |
}); | |
MML.mroot.Augment({ | |
toSVG: MML.msqrt.prototype.toSVG, | |
SVGaddRoot: function (svg,surd,x,d,scale) { | |
var dx = (surd.isMultiChar ? .55 : .65) * surd.w; | |
if (this.data[1]) { | |
var root = this.data[1].toSVG(); root.x = 0; | |
var h = this.SVGrootHeight(surd.h+surd.d,scale,root)-d; | |
var w = Math.min(root.w,root.r); // remove extra right-hand padding, if any | |
x = Math.max(w,dx); | |
svg.Add(root,x-w,h); | |
} else {dx = x} | |
return x - dx; | |
}, | |
SVGrootHeight: function (d,scale,root) { | |
return .45*(d-900*scale) + 600*scale + Math.max(0,root.d-75); | |
} | |
}); | |
MML.mfenced.Augment({ | |
SVG: BBOX.ROW, | |
toSVG: function () { | |
this.SVGgetStyles(); | |
var svg = this.SVG(); | |
this.SVGhandleSpace(svg); | |
if (this.data.open) {svg.Check(this.data.open)} | |
if (this.data[0] != null) {svg.Check(this.data[0])} | |
for (var i = 1, m = this.data.length; i < m; i++) { | |
if (this.data[i]) { | |
if (this.data["sep"+i]) {svg.Check(this.data["sep"+i])} | |
svg.Check(this.data[i]); | |
} | |
} | |
if (this.data.close) {svg.Check(this.data.close)} | |
svg.Stretch(); svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
MML.menclose.Augment({toSVG: MML.mbase.SVGautoload}); | |
MML.maction.Augment({toSVG: MML.mbase.SVGautoload}); | |
MML.semantics.Augment({ | |
toSVG: function () { | |
this.SVGgetStyles(); | |
var svg = this.SVG(); | |
if (this.data[0] != null) { | |
this.SVGhandleSpace(svg); | |
svg.Add(this.data[0].toSVG()); svg.Clean(); | |
} else {svg.Clean()} | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGstretchH: function (w) { | |
return (this.data[0] != null ? this.data[0].SVGstretchH(w) : BBOX.NULL()); | |
}, | |
SVGstretchV: function (h,d) { | |
return (this.data[0] != null ? this.data[0].SVGstretchV(h,d) : BBOX.NULL()); | |
} | |
}); | |
MML.munderover.Augment({ | |
toSVG: function (HW,D) { | |
this.SVGgetStyles(); | |
var values = this.getValues("displaystyle","accent","accentunder","align"); | |
var base = this.data[this.base]; | |
if (!values.displaystyle && base != null && | |
(base.movablelimits || base.CoreMO().Get("movablelimits"))) | |
{return MML.msubsup.prototype.toSVG.call(this)} | |
var svg = this.SVG(), scale = this.SVGgetScale(svg); this.SVGhandleSpace(svg); | |
var boxes = [], stretch = [], box, i, m, W = -SVG.BIGDIMEN, WW = W; | |
for (i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i] != null) { | |
if (i == this.base) { | |
boxes[i] = this.SVGdataStretched(i,HW,D); | |
stretch[i] = (D != null || HW == null) && this.data[i].SVGcanStretch("Horizontal"); | |
} else { | |
boxes[i] = this.data[i].toSVG(); boxes[i].x = 0; delete boxes[i].X; | |
stretch[i] = this.data[i].SVGcanStretch("Horizontal"); | |
} | |
if (boxes[i].w > WW) {WW = boxes[i].w} | |
if (!stretch[i] && WW > W) {W = WW} | |
} | |
} | |
if (D == null && HW != null) {W = HW} else if (W == -SVG.BIGDIMEN) {W = WW} | |
for (i = WW = 0, m = this.data.length; i < m; i++) {if (this.data[i]) { | |
if (stretch[i]) { | |
boxes[i] = this.data[i].SVGstretchH(W); | |
if (i !== this.base) {boxes[i].x = 0; delete boxes[i].X} | |
} | |
if (boxes[i].w > WW) {WW = boxes[i].w} | |
}} | |
var t = SVG.TeX.rule_thickness * this.mscale; | |
var x, y, z1, z2, z3, dw, k, delta = 0; | |
base = boxes[this.base] || {w:0, h:0, d:0, H:0, D:0, l:0, r:0, y:0, scale:scale}; | |
if (base.ic) {delta = 1.3*base.ic + .05} // adjust faked IC to be more in line with expeted results | |
for (i = 0, m = this.data.length; i < m; i++) { | |
if (this.data[i] != null) { | |
box = boxes[i]; | |
z3 = SVG.TeX.big_op_spacing5 * scale; | |
var accent = (i != this.base && values[this.ACCENTS[i]]); | |
if (accent && box.w <= 1) { | |
box.x = -box.l; | |
boxes[i] = BBOX.G().With({removeable: false}); | |
boxes[i].Add(box); boxes[i].Clean(); | |
boxes[i].w = -box.l; box = boxes[i]; | |
} | |
dw = {left:0, center:(WW-box.w)/2, right:WW-box.w}[values.align]; | |
x = dw; y = 0; | |
if (i == this.over) { | |
if (accent) { | |
k = t * scale; z3 = 0; | |
if (base.skew) { | |
x += base.skew; svg.skew = base.skew; | |
if (x+box.w > WW) {svg.skew += (WW-box.w-x)/2} | |
} | |
} else { | |
z1 = SVG.TeX.big_op_spacing1 * scale; | |
z2 = SVG.TeX.big_op_spacing3 * scale; | |
k = Math.max(z1,z2-Math.max(0,box.d)); | |
} | |
k = Math.max(k,1500/SVG.em); | |
x += delta/2; y = base.y + base.h + box.d + k; | |
box.h += z3; if (box.h > box.H) {box.H = box.h} | |
} else if (i == this.under) { | |
if (accent) { | |
k = 3*t * scale; z3 = 0; | |
} else { | |
z1 = SVG.TeX.big_op_spacing2 * scale; | |
z2 = SVG.TeX.big_op_spacing4 * scale; | |
k = Math.max(z1,z2-box.h); | |
} | |
k = Math.max(k,1500/SVG.em); | |
x -= delta/2; y = base.y -(base.d + box.h + k); | |
box.d += z3; if (box.d > box.D) {box.D = box.d} | |
} | |
svg.Add(box,x,y); | |
} | |
} | |
svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
MML.msubsup.Augment({ | |
toSVG: function (HW,D) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(), scale = this.SVGgetScale(svg); this.SVGhandleSpace(svg); | |
var mu = this.SVGgetMu(svg); | |
var base = svg.Add(this.SVGdataStretched(this.base,HW,D)); | |
var sscale = (this.data[this.sup] || this.data[this.sub] || this).SVGgetScale(); | |
var x_height = SVG.TeX.x_height * scale, s = SVG.TeX.scriptspace * scale; | |
var sup, sub; | |
if (this.SVGnotEmpty(this.data[this.sup])) { | |
sup = this.data[this.sup].toSVG(); | |
sup.w += s; sup.r = Math.max(sup.w,sup.r); | |
} | |
if (this.SVGnotEmpty(this.data[this.sub])) { | |
sub = this.data[this.sub].toSVG(); | |
sub.w += s; sub.r = Math.max(sub.w,sub.r); | |
} | |
var q = SVG.TeX.sup_drop * sscale, r = SVG.TeX.sub_drop * sscale; | |
var u = base.h+(base.y||0) - q, v = base.d-(base.y||0) + r, delta = 0, p; | |
if (base.ic) { | |
base.w -= base.ic; // remove IC (added by mo and mi) | |
delta = 1.3*base.ic+.05; // adjust faked IC to be more in line with expeted results | |
} | |
if (this.data[this.base] && | |
(this.data[this.base].type === "mi" || this.data[this.base].type === "mo")) { | |
if (this.data[this.base].data.join("").length === 1 && base.scale === 1 && | |
!base.stretched && !this.data[this.base].Get("largeop")) {u = v = 0} | |
} | |
var min = this.getValues("subscriptshift","superscriptshift"); | |
min.subscriptshift = (min.subscriptshift === "" ? 0 : SVG.length2em(min.subscriptshift,mu)); | |
min.superscriptshift = (min.superscriptshift === "" ? 0 : SVG.length2em(min.superscriptshift,mu)); | |
var x = base.w + base.x; | |
if (!sup) { | |
if (sub) { | |
v = Math.max(v,SVG.TeX.sub1*scale,sub.h-(4/5)*x_height,min.subscriptshift); | |
svg.Add(sub,x,-v); this.data[this.sub].SVGdata.dy = -v; | |
} | |
} else { | |
if (!sub) { | |
values = this.getValues("displaystyle","texprimestyle"); | |
p = SVG.TeX[(values.displaystyle ? "sup1" : (values.texprimestyle ? "sup3" : "sup2"))]; | |
u = Math.max(u,p*scale,sup.d+(1/4)*x_height,min.superscriptshift); | |
svg.Add(sup,x+delta,u); | |
this.data[this.sup].SVGdata.dx = delta; | |
this.data[this.sup].SVGdata.dy = u; | |
} else { | |
v = Math.max(v,SVG.TeX.sub2*scale); | |
var t = SVG.TeX.rule_thickness * scale; | |
if ((u - sup.d) - (sub.h - v) < 3*t) { | |
v = 3*t - u + sup.d + sub.h; | |
q = (4/5)*x_height - (u - sup.d); | |
if (q > 0) {u += q; v -= q} | |
} | |
svg.Add(sup,x+delta,Math.max(u,min.superscriptshift)); | |
svg.Add(sub,x,-Math.max(v,min.subscriptshift)); | |
this.data[this.sup].SVGdata.dx = delta; | |
this.data[this.sup].SVGdata.dy = Math.max(u,min.superscriptshift); | |
this.data[this.sub].SVGdata.dy = -Math.max(v,min.subscriptshift); | |
} | |
} | |
svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
MML.mmultiscripts.Augment({toSVG: MML.mbase.SVGautoload}); | |
MML.mtable.Augment({toSVG: MML.mbase.SVGautoload}); | |
MML["annotation-xml"].Augment({toSVG: MML.mbase.SVGautoload}); | |
MML.math.Augment({ | |
SVG: BBOX.Subclass({type:"svg", removeable: false}), | |
toSVG: function (span,div) { | |
var CONFIG = SVG.config; | |
// | |
// All the data should be in an inferrerd row | |
// | |
if (this.data[0]) { | |
this.SVGgetStyles(); | |
MML.mbase.prototype.displayAlign = HUB.config.displayAlign; | |
MML.mbase.prototype.displayIndent = HUB.config.displayIndent; | |
if (String(HUB.config.displayIndent).match(/^0($|[a-z%])/i)) | |
MML.mbase.prototype.displayIndent = "0"; | |
// | |
// Put content in a <g> with defaults and matrix that flips y axis. | |
// Put that in an <svg> with xlink defined. | |
// | |
var box = BBOX.G(); box.Add(this.data[0].toSVG(),0,0,true); box.Clean(); | |
this.SVGhandleColor(box); | |
SVG.Element(box.element,{ | |
stroke:"currentColor", fill:"currentColor", "stroke-width":0, | |
transform: "matrix(1 0 0 -1 0 0)" | |
}); | |
box.removeable = false; | |
var svg = this.SVG(); | |
svg.element.setAttribute("xmlns:xlink",XLINKNS); | |
if (CONFIG.useFontCache && !CONFIG.useGlobalCache) | |
{svg.element.appendChild(BBOX.GLYPH.defs)} | |
svg.Add(box); svg.Clean(); | |
this.SVGsaveData(svg); | |
// | |
// If this element is not the top-level math element | |
// remove the transform and return the svg object | |
// (issue #614). | |
// | |
if (!span) { | |
svg.element = svg.element.firstChild; // remove <svg> element | |
svg.element.removeAttribute("transform"); | |
svg.removable = true; | |
return svg; | |
} | |
// | |
// Style the <svg> to get the right size and placement | |
// | |
var l = Math.max(-svg.l,0), r = Math.max(svg.r-svg.w,0); | |
var style = svg.element.style, px = SVG.TeX.x_height/SVG.ex; | |
var H = (Math.ceil(svg.H/px)+1)*px+SVG.HFUZZ, // round to pixels and add padding | |
D = (Math.ceil(svg.D/px)+1)*px+SVG.DFUZZ; | |
var w = l + svg.w + r; | |
svg.element.setAttribute("width",SVG.Ex(w)); | |
svg.element.setAttribute("height",SVG.Ex(H+D)); | |
style.verticalAlign = SVG.Ex(-D); | |
if (l) style.marginLeft = SVG.Ex(-l); | |
if (r) style.marginRight = SVG.Ex(-r); | |
svg.element.setAttribute("viewBox",SVG.Fixed(-l,1)+" "+SVG.Fixed(-H,1)+" "+ | |
SVG.Fixed(w,1)+" "+SVG.Fixed(H+D,1)); | |
// | |
// If there is extra height or depth, hide that | |
// | |
if (svg.H > svg.h) style.marginTop = SVG.Ex(svg.h-H); | |
if (svg.D > svg.d) { | |
style.marginBottom = SVG.Ex(svg.d-D); | |
style.verticalAlign = SVG.Ex(-svg.d); | |
} | |
// | |
// The approximate ex can cause full-width equations to be too wide, | |
// so if they are close to full width, make sure they aren't too big. | |
// | |
if (Math.abs(w-SVG.cwidth) < 10) | |
style.maxWidth = SVG.Fixed(SVG.cwidth*SVG.em/1000); | |
// | |
// Add it to the MathJax span | |
// | |
var alttext = this.Get("alttext"); | |
if (alttext && !svg.element.getAttribute("aria-label")) svg.element.setAttribute("aria-label",alttext); | |
if (!svg.element.getAttribute("role")) svg.element.setAttribute("role","img"); | |
svg.element.setAttribute("focusable","false"); | |
span.appendChild(svg.element); | |
svg.element = null; | |
// | |
// Handle indentalign and indentshift for single-line displays | |
// | |
if (!this.isMultiline && this.Get("display") === "block" && !svg.hasIndent) { | |
var values = this.getValues("indentalignfirst","indentshiftfirst","indentalign","indentshift"); | |
if (values.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) {values.indentalign = values.indentalignfirst} | |
if (values.indentalign === MML.INDENTALIGN.AUTO) {values.indentalign = this.displayAlign} | |
if (values.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) {values.indentshift = values.indentshiftfirst} | |
if (values.indentshift === "auto") {values.indentshift = "0"} | |
var shift = SVG.length2em(values.indentshift,1,SVG.cwidth); | |
if (this.displayIndent !== "0") { | |
var indent = SVG.length2em(this.displayIndent,1,SVG.cwidth); | |
shift += (values.indentalign === MML.INDENTALIGN.RIGHT ? -indent : indent); | |
} | |
div.style.textAlign = values.indentalign; | |
if (shift) { | |
HUB.Insert(style,({ | |
left: {marginLeft: SVG.Ex(shift)}, | |
right: {marginRight: SVG.Ex(-shift), marginLeft: SVG.Ex(Math.max(0,shift-w))}, | |
center: {marginLeft: SVG.Ex(shift), marginRight: SVG.Ex(-shift)} | |
})[values.indentalign]); | |
} | |
} | |
} | |
return span; | |
} | |
}); | |
MML.TeXAtom.Augment({ | |
toSVG: function (HW,D) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(); | |
this.SVGhandleSpace(svg); | |
if (this.data[0] != null) { | |
var box = this.SVGdataStretched(0,HW,D), y = 0; | |
if (this.texClass === MML.TEXCLASS.VCENTER) | |
{y = SVG.TeX.axis_height - (box.h+box.d)/2 + box.d} | |
svg.Add(box,0,y); | |
svg.ic = box.ic; svg.skew = box.skew; | |
} | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
// | |
// Make sure these don't generate output | |
// | |
MML.maligngroup.Augment({toSVG: MML.mbase.SVGemptySVG}); | |
MML.malignmark.Augment({toSVG: MML.mbase.SVGemptySVG}); | |
MML.mprescripts.Augment({toSVG: MML.mbase.SVGemptySVG}); | |
MML.none.Augment({toSVG: MML.mbase.SVGemptySVG}); | |
// | |
// Loading isn't complete until the element jax is modified, | |
// but can't call loadComplete within the callback for "mml Jax Ready" | |
// (it would call SVG's Require routine, asking for the mml jax again) | |
// so wait until after the mml jax has finished processing. | |
// | |
// We also need to wait for the onload handler to run, since the loadComplete | |
// will call Config and Startup, which need to modify the body. | |
// | |
HUB.Register.StartupHook("onLoad",function () { | |
setTimeout(MathJax.Callback(["loadComplete",SVG,"jax.js"]),0); | |
}); | |
}); | |
HUB.Browser.Select({ | |
Opera: function (browser) { | |
SVG.Augment({ | |
operaZoomRefresh: true // Opera needs a kick to redraw zoomed equations | |
}); | |
} | |
}); | |
HUB.Register.StartupHook("End Cookie", function () { | |
if (HUB.config.menuSettings.zoom !== "None") | |
{AJAX.Require("[MathJax]/extensions/MathZoom.js")} | |
}); | |
if (!document.createElementNS) { | |
// | |
// Try to handle SVG in IE8 and below, but fail | |
// (but don't crash on loading the file, so no delay for loadComplete) | |
// | |
if (!document.namespaces.svg) {document.namespaces.add("svg",SVGNS)} | |
SVG.Augment({ | |
Element: function (type,def) { | |
var obj = (typeof(type) === "string" ? document.createElement("svg:"+type) : type); | |
obj.isMathJax = true; | |
if (def) {for (var id in def) {if (def.hasOwnProperty(id)) {obj.setAttribute(id,def[id].toString())}}} | |
return obj; | |
} | |
}); | |
} | |
})(MathJax.Ajax, MathJax.Hub, MathJax.HTML, MathJax.OutputJax.SVG); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/output/SVG/autoload/mtable.js | |
* | |
* Implements the SVG output for <mtable> elements. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2011-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () { | |
var VERSION = "2.7.1"; | |
var MML = MathJax.ElementJax.mml, | |
SVG = MathJax.OutputJax.SVG, | |
BBOX = SVG.BBOX; | |
MML.mtable.Augment({ | |
toSVG: function (span) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(), scale = this.SVGgetScale(svg); | |
if (this.data.length === 0) {this.SVGsaveData(svg);return svg} | |
var values = this.getValues("columnalign","rowalign","columnspacing","rowspacing", | |
"columnwidth","equalcolumns","equalrows", | |
"columnlines","rowlines","frame","framespacing", | |
"align","useHeight","width","side","minlabelspacing"); | |
// Handle relative width as fixed width in relation to container | |
if (values.width.match(/%$/)) | |
{svg.width = values.width = SVG.Em((SVG.cwidth/1000)*(parseFloat(values.width)/100))} | |
var mu = this.SVGgetMu(svg); | |
var LABEL = -1; | |
var H = [], D = [], W = [], A = [], C = [], i, j, J = -1, | |
m, M, s, row, cell, mo, HD; | |
var LH = SVG.FONTDATA.lineH * scale * values.useHeight, | |
LD = SVG.FONTDATA.lineD * scale * values.useHeight; | |
// | |
// Create cells and measure columns and rows | |
// | |
for (i = 0, m = this.data.length; i < m; i++) { | |
row = this.data[i]; s = (row.type === "mlabeledtr" ? LABEL : 0); | |
A[i] = []; H[i] = LH; D[i] = LD; | |
for (j = s, M = row.data.length + s; j < M; j++) { | |
if (W[j] == null) { | |
if (j > J) {J = j} | |
C[j] = BBOX.G(); | |
W[j] = -SVG.BIGDIMEN; | |
} | |
cell = row.data[j-s]; | |
A[i][j] = cell.toSVG(); | |
// if (row.data[j-s].isMultiline) {A[i][j].style.width = "100%"} | |
if (cell.isEmbellished()) { | |
mo = cell.CoreMO(); | |
var min = mo.Get("minsize",true); | |
if (min) { | |
if (mo.SVGcanStretch("Vertical")) { | |
HD = mo.SVGdata.h + mo.SVGdata.d; | |
if (HD) { | |
min = SVG.length2em(min,mu,HD); | |
if (min*mo.SVGdata.h/HD > H[i]) {H[i] = min*mo.SVGdata.h/HD} | |
if (min*mo.SVGdata.d/HD > D[i]) {D[i] = min*mo.SVGdata.d/HD} | |
} | |
} else if (mo.SVGcanStretch("Horizontal")) { | |
min = SVG.length2em(min,mu,mo.SVGdata.w); | |
if (min > W[j]) {W[j] = min} | |
} | |
} | |
} | |
if (A[i][j].h > H[i]) {H[i] = A[i][j].h} | |
if (A[i][j].d > D[i]) {D[i] = A[i][j].d} | |
if (A[i][j].w > W[j]) {W[j] = A[i][j].w} | |
} | |
} | |
// | |
// Determine spacing and alignment | |
// | |
var SPLIT = MathJax.Hub.SplitList; | |
var CSPACE = SPLIT(values.columnspacing), | |
RSPACE = SPLIT(values.rowspacing), | |
CALIGN = SPLIT(values.columnalign), | |
RALIGN = SPLIT(values.rowalign), | |
CLINES = SPLIT(values.columnlines), | |
RLINES = SPLIT(values.rowlines), | |
CWIDTH = SPLIT(values.columnwidth), | |
RCALIGN = []; | |
for (i = 0, m = CSPACE.length; i < m; i++) {CSPACE[i] = SVG.length2em(CSPACE[i],mu)} | |
for (i = 0, m = RSPACE.length; i < m; i++) {RSPACE[i] = SVG.length2em(RSPACE[i],mu)} | |
while (CSPACE.length < J) {CSPACE.push(CSPACE[CSPACE.length-1])} | |
while (CALIGN.length <= J) {CALIGN.push(CALIGN[CALIGN.length-1])} | |
while (CLINES.length < J) {CLINES.push(CLINES[CLINES.length-1])} | |
while (CWIDTH.length <= J) {CWIDTH.push(CWIDTH[CWIDTH.length-1])} | |
while (RSPACE.length < A.length) {RSPACE.push(RSPACE[RSPACE.length-1])} | |
while (RALIGN.length <= A.length) {RALIGN.push(RALIGN[RALIGN.length-1])} | |
while (RLINES.length < A.length) {RLINES.push(RLINES[RLINES.length-1])} | |
if (C[LABEL]) { | |
CALIGN[LABEL] = (values.side.substr(0,1) === "l" ? "left" : "right"); | |
CSPACE[LABEL] = -W[LABEL]; | |
} | |
// | |
// Override row data | |
// | |
for (i = 0, m = A.length; i < m; i++) { | |
row = this.data[i]; RCALIGN[i] = []; | |
if (row.rowalign) {RALIGN[i] = row.rowalign} | |
if (row.columnalign) { | |
RCALIGN[i] = SPLIT(row.columnalign); | |
while (RCALIGN[i].length <= J) {RCALIGN[i].push(RCALIGN[i][RCALIGN[i].length-1])} | |
} | |
} | |
// | |
// Handle equal heights | |
// | |
if (values.equalrows) { | |
// FIXME: should really be based on row align (below is for baseline) | |
var Hm = Math.max.apply(Math,H), Dm = Math.max.apply(Math,D); | |
for (i = 0, m = A.length; i < m; i++) | |
{s = ((Hm + Dm) - (H[i] + D[i])) / 2; H[i] += s; D[i] += s} | |
} | |
// FIXME: do background colors for entire cell (include half the intercolumn space?) | |
// | |
// Determine array total height | |
// | |
HD = H[0] + D[A.length-1]; | |
for (i = 0, m = A.length-1; i < m; i++) | |
{HD += Math.max(0,D[i]+H[i+1]+RSPACE[i])} | |
// | |
// Determine frame and line sizes | |
// | |
var fx = 0, fy = 0, fW, fH = HD; | |
if (values.frame !== "none" || | |
(values.columnlines+values.rowlines).match(/solid|dashed/)) { | |
var frameSpacing = SPLIT(values.framespacing); | |
if (frameSpacing.length != 2) { | |
// invalid attribute value: use the default. | |
frameSpacing = SPLIT(this.defaults.framespacing); | |
} | |
fx = SVG.length2em(frameSpacing[0],mu); | |
fy = SVG.length2em(frameSpacing[1],mu); | |
fH = HD + 2*fy; // fW waits until svg.w is determined | |
} | |
// | |
// Compute alignment | |
// | |
var Y, fY, n = ""; | |
if (typeof(values.align) !== "string") {values.align = String(values.align)} | |
if (values.align.match(/(top|bottom|center|baseline|axis)( +(-?\d+))?/)) | |
{n = RegExp.$3||""; values.align = RegExp.$1} else {values.align = this.defaults.align} | |
if (n !== "") { | |
// | |
// Find the height of the given row | |
// | |
n = parseInt(n); | |
if (n < 0) {n = A.length + 1 + n} | |
if (n < 1) {n = 1} else if (n > A.length) {n = A.length} | |
Y = 0; fY = -(HD + fy) + H[0]; | |
for (i = 0, m = n-1; i < m; i++) { | |
// FIXME: Should handle values.align for final row | |
var dY = Math.max(0,D[i]+H[i+1]+RSPACE[i]); | |
Y += dY; fY += dY; | |
} | |
} else { | |
Y = ({ | |
top: -(H[0] + fy), | |
bottom: HD + fy - H[0], | |
center: HD/2 - H[0], | |
baseline: HD/2 - H[0], | |
axis: HD/2 + SVG.TeX.axis_height*scale - H[0] | |
})[values.align]; | |
fY = ({ | |
top: -(HD + 2*fy), | |
bottom: 0, | |
center: -(HD/2 + fy), | |
baseline: -(HD/2 + fy), | |
axis: SVG.TeX.axis_height*scale - HD/2 - fy | |
})[values.align]; | |
} | |
var WW, WP = 0, Wt = 0, Wp = 0, p = 0, f = 0, P = [], F = [], Wf = 1; | |
// | |
if (values.equalcolumns && values.width !== "auto") { | |
// | |
// Handle equalcolumns for percent-width and fixed-width tables | |
// | |
// Get total width minus column spacing | |
WW = SVG.length2em(values.width,mu); | |
for (i = 0, m = Math.min(J,CSPACE.length); i < m; i++) {WW -= CSPACE[i]} | |
// Determine individual column widths | |
WW /= J; | |
for (i = 0, m = Math.min(J+1,CWIDTH.length); i < m; i++) {W[i] = WW} | |
} else { | |
// | |
// Get column widths for fit and percentage columns | |
// | |
// Calculate the natural widths and percentage widths, | |
// while keeping track of the fit and percentage columns | |
for(i = 0, m = Math.min(J+1,CWIDTH.length); i < m; i++) { | |
if (CWIDTH[i] === "auto") {Wt += W[i]} | |
else if (CWIDTH[i] === "fit") {F[f] = i; f++; Wt += W[i]} | |
else if (CWIDTH[i].match(/%$/)) | |
{P[p] = i; p++; Wp += W[i]; WP += SVG.length2em(CWIDTH[i],mu,1)} | |
else {W[i] = SVG.length2em(CWIDTH[i],mu); Wt += W[i]} | |
} | |
// Get the full width (excluding inter-column spacing) | |
if (values.width === "auto") { | |
if (WP > .98) {Wf = Wp/(Wt+Wp); WW = Wt + Wp} else {WW = Wt / (1-WP)} | |
} else { | |
WW = SVG.length2em(values.width,mu); | |
for (i = 0, m = Math.min(J,CSPACE.length); i < m; i++) {WW -= CSPACE[i]} | |
} | |
// Determine the relative column widths | |
for (i = 0, m = P.length; i < m; i++) { | |
W[P[i]] = SVG.length2em(CWIDTH[P[i]],mu,WW*Wf); Wt += W[P[i]]; | |
} | |
// Stretch fit columns, if any, otherwise stretch (or shrink) everything | |
if (Math.abs(WW - Wt) > .01) { | |
if (f && WW > Wt) { | |
WW = (WW - Wt) / f; for (i = 0, m = F.length; i < m; i++) {W[F[i]] += WW} | |
} else {WW = WW/Wt; for (j = 0; j <= J; j++) {W[j] *= WW}} | |
} | |
// | |
// Handle equal columns | |
// | |
if (values.equalcolumns) { | |
var Wm = Math.max.apply(Math,W); | |
for (j = 0; j <= J; j++) {W[j] = Wm} | |
} | |
} | |
// | |
// Lay out array columns | |
// | |
var y = Y, dy, align; s = (C[LABEL] ? LABEL : 0); | |
for (j = s; j <= J; j++) { | |
C[j].w = W[j]; | |
for (i = 0, m = A.length; i < m; i++) { | |
if (A[i][j]) { | |
s = (this.data[i].type === "mlabeledtr" ? LABEL : 0); | |
cell = this.data[i].data[j-s]; | |
if (cell.SVGcanStretch("Horizontal")) { | |
A[i][j] = cell.SVGstretchH(W[j]); | |
} else if (cell.SVGcanStretch("Vertical")) { | |
mo = cell.CoreMO(); | |
var symmetric = mo.symmetric; mo.symmetric = false; | |
A[i][j] = cell.SVGstretchV(H[i],D[i]); | |
mo.symmetric = symmetric; | |
} | |
align = cell.rowalign||this.data[i].rowalign||RALIGN[i]; | |
dy = ({top: H[i] - A[i][j].h, | |
bottom: A[i][j].d - D[i], | |
center: ((H[i]-D[i]) - (A[i][j].h-A[i][j].d))/2, | |
baseline: 0, axis: 0})[align] || 0; // FIXME: handle axis better? | |
align = (cell.columnalign||RCALIGN[i][j]||CALIGN[j]) | |
C[j].Align(A[i][j],align,0,y+dy); | |
} | |
if (i < A.length-1) {y -= Math.max(0,D[i]+H[i+1]+RSPACE[i])} | |
} | |
y = Y; | |
} | |
// | |
// Place the columns and add column lines | |
// | |
var lw = 1.5*SVG.em; | |
var x = fx - lw/2; | |
for (j = 0; j <= J; j++) { | |
svg.Add(C[j],x,0); x += W[j] + CSPACE[j]; | |
if (CLINES[j] !== "none" && j < J && j !== LABEL) | |
{svg.Add(BBOX.VLINE(fH,lw,CLINES[j]),x-CSPACE[j]/2,fY)} | |
} | |
svg.w += fx; svg.d = -fY; svg.h = fH+fY; | |
fW = svg.w; | |
// | |
// Add frame | |
// | |
if (values.frame !== "none") { | |
svg.Add(BBOX.HLINE(fW,lw,values.frame),0,fY+fH-lw); | |
svg.Add(BBOX.HLINE(fW,lw,values.frame),0,fY); | |
svg.Add(BBOX.VLINE(fH,lw,values.frame),0,fY); | |
svg.Add(BBOX.VLINE(fH,lw,values.frame),fW-lw,fY); | |
} | |
// | |
// Add row lines | |
// | |
y = Y - lw/2; | |
for (i = 0, m = A.length-1; i < m; i++) { | |
dy = Math.max(0,D[i]+H[i+1]+RSPACE[i]); | |
if (RLINES[i] !== MML.LINES.NONE && RLINES[i] !== "") | |
{svg.Add(BBOX.HLINE(fW,lw,RLINES[i]),0,y-D[i]-(dy-D[i]-H[i+1])/2)} | |
y -= dy; | |
} | |
// | |
// Finish the table | |
// | |
svg.Clean(); | |
this.SVGhandleSpace(svg); | |
this.SVGhandleColor(svg); | |
// | |
// Place the labels, if any | |
// | |
if (C[LABEL]) { | |
svg.tw = Math.max(svg.w,svg.r) - Math.min(0,svg.l); | |
var indent = this.getValues("indentalignfirst","indentshiftfirst","indentalign","indentshift"); | |
if (indent.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) {indent.indentalign = indent.indentalignfirst} | |
if (indent.indentalign === MML.INDENTALIGN.AUTO) {indent.indentalign = this.displayAlign} | |
if (indent.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) {indent.indentshift = indent.indentshiftfirst} | |
if (indent.indentshift === "auto" || indent.indentshift === "") {indent.indentshift = "0"} | |
var shift = SVG.length2em(indent.indentshift,mu,SVG.cwidth); | |
var labelspace = SVG.length2em(values.minlabelspacing,mu,SVG.cwidth); | |
var labelW = labelspace + C[LABEL].w, labelshift = 0, tw = svg.w; | |
var dIndent = SVG.length2em(this.displayIndent,mu,SVG.cwidth); | |
s = (CALIGN[LABEL] === MML.INDENTALIGN.RIGHT ? -1 : 1); | |
if (indent.indentalign === MML.INDENTALIGN.CENTER) { | |
var dx = (SVG.cwidth-tw)/2; shift += dIndent; | |
if (labelW + s*labelshift > dx + s*shift) { | |
indent.indentalign = CALIGN[LABEL]; | |
shift = s*(labelW + s*labelshift); tw += labelW + Math.max(0,shift); | |
} | |
} else if (CALIGN[LABEL] === indent.indentalign) { | |
if (dIndent < 0) {labelshift = s*dIndent; dIndent = 0} | |
shift += s*dIndent; if (labelW > s*shift) shift = s*labelW; shift += labelshift; | |
tw += s*shift; | |
} else { | |
shift -= s*dIndent; | |
if (tw - s*shift + labelW > SVG.cwidth) { | |
shift = s*(tw + labelW - SVG.cwidth); | |
if (s*shift > 0) {tw = SVG.cwidth + s*shift; shift = 0} | |
} | |
} | |
var eqn = svg; svg = this.SVG(); | |
svg.hasIndent = true; | |
svg.w = svg.r = Math.max(tw,SVG.cwidth); | |
svg.Align(C[LABEL],CALIGN[LABEL],0,0,labelshift); | |
svg.Align(eqn,indent.indentalign,0,0,shift); | |
svg.tw = tw; | |
} | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGhandleSpace: function (svg) { | |
if (!this.hasFrame && !svg.width) {svg.x = svg.X = 167} | |
this.SUPER(arguments).SVGhandleSpace.call(this,svg); | |
} | |
}); | |
MML.mtd.Augment({ | |
toSVG: function (HW,D) { | |
var svg = this.svg = this.SVG(); | |
if (this.data[0]) { | |
svg.Add(this.SVGdataStretched(0,HW,D)); | |
svg.Clean(); | |
} | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
MathJax.Hub.Startup.signal.Post("SVG mtable Ready"); | |
MathJax.Ajax.loadComplete(SVG.autoloadDir+"/mtable.js"); | |
}); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/output/SVG/autoload/mglyph.js | |
* | |
* Implements the SVG output for <mglyph> elements. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2011-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () { | |
var VERSION = "2.7.1"; | |
var MML = MathJax.ElementJax.mml, | |
SVG = MathJax.OutputJax.SVG, | |
BBOX = SVG.BBOX, | |
LOCALE = MathJax.Localization; | |
var XLINKNS = "http://www.w3.org/1999/xlink"; | |
BBOX.MGLYPH = BBOX.Subclass({ | |
type: "image", removeable: false, | |
Init: function (img,w,h,align,mu,def) { | |
if (def == null) {def = {}} | |
var W = img.width*1000/SVG.em, H = img.height*1000/SVG.em; | |
var WW = W, HH = H, y = 0; | |
if (w !== "") {W = SVG.length2em(w,mu,WW); H = (WW ? W/WW * HH : 0)} | |
if (h !== "") {H = SVG.length2em(h,mu,HH); if (w === "") {W = (HH ? H/HH * WW : 0)}} | |
if (align !== "" && align.match(/\d/)) {y = SVG.length2em(align,mu); def.y = -y} | |
def.height = Math.floor(H); def.width = Math.floor(W); | |
def.transform = "translate(0,"+H+") matrix(1 0 0 -1 0 0)"; | |
def.preserveAspectRatio = "none"; | |
this.SUPER(arguments).Init.call(this,def); | |
this.element.setAttributeNS(XLINKNS,"href",img.SRC); | |
this.w = this.r = W; this.h = this.H = H + y; | |
this.d = this.D = -y; this.l = 0; | |
} | |
}); | |
MML.mglyph.Augment({ | |
toSVG: function (variant,scale) { | |
this.SVGgetStyles(); var svg = this.SVG(), img, err; | |
this.SVGhandleSpace(svg); | |
var values = this.getValues("src","width","height","valign","alt"); | |
if (values.src === "") { | |
values = this.getValues("index","fontfamily"); | |
if (values.index) { | |
if (!scale) {scale = this.SVGgetScale()} | |
var def = {}; if (values.fontfamily) {def["font-family"] = values.fontfamily} | |
svg.Add(BBOX.TEXT(scale,String.fromCharCode(values.index),def)); | |
} | |
} else { | |
if (!this.img) {this.img = MML.mglyph.GLYPH[values.src]} | |
if (!this.img) { | |
this.img = MML.mglyph.GLYPH[values.src] = {img: new Image(), status: "pending"}; | |
img = this.img.img; | |
img.onload = MathJax.Callback(["SVGimgLoaded",this]); | |
img.onerror = MathJax.Callback(["SVGimgError",this]); | |
img.src = img.SRC = values.src; | |
MathJax.Hub.RestartAfter(img.onload); | |
} | |
if (this.img.status !== "OK") { | |
err = MML.Error( | |
LOCALE._(["MathML","BadMglyph"],"Bad mglyph: %1",values.src), | |
{mathsize:"75%"}); | |
this.Append(err); svg = err.toSVG(); this.data.pop(); | |
} else { | |
var mu = this.SVGgetMu(svg); | |
svg.Add(BBOX.MGLYPH(this.img.img,values.width,values.height,values.valign,mu, | |
{alt:values.alt, title:values.alt})); | |
} | |
} | |
svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGimgLoaded: function (event,status) { | |
if (typeof(event) === "string") {status = event} | |
this.img.status = (status || "OK") | |
}, | |
SVGimgError: function () {this.img.img.onload("error")} | |
},{ | |
GLYPH: {} // global list of all loaded glyphs | |
}); | |
MathJax.Hub.Startup.signal.Post("SVG mglyph Ready"); | |
MathJax.Ajax.loadComplete(SVG.autoloadDir+"/mglyph.js"); | |
}); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/output/SVG/autoload/mmultiscripts.js | |
* | |
* Implements the SVG output for <mmultiscripts> elements. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2011-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () { | |
var VERSION = "2.7.1"; | |
var MML = MathJax.ElementJax.mml, | |
SVG = MathJax.OutputJax.SVG; | |
MML.mmultiscripts.Augment({ | |
toSVG: function (HW,D) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(), scale = this.SVGgetScale(svg); this.SVGhandleSpace(svg); | |
var base = (this.data[this.base] ? this.SVGdataStretched(this.base,HW,D) : SVG.BBOX.G().Clean()); | |
var x_height = SVG.TeX.x_height * scale, | |
s = SVG.TeX.scriptspace * scale * .75; // FIXME: .75 can be removed when IC is right? | |
var BOX = this.SVGgetScripts(s); | |
var sub = BOX[0], sup = BOX[1], presub = BOX[2], presup = BOX[3]; | |
var sscale = (this.data[1]||this).SVGgetScale(); | |
var q = SVG.TeX.sup_drop * sscale, r = SVG.TeX.sub_drop * sscale; | |
var u = base.h - q, v = base.d + r, delta = 0, p; | |
if (base.ic) {delta = base.ic} | |
if (this.data[this.base] && | |
(this.data[this.base].type === "mi" || this.data[this.base].type === "mo")) { | |
if (this.data[this.base].data.join("").length === 1 && base.scale === 1 && | |
!base.stretched && !this.data[this.base].Get("largeop")) {u = v = 0} | |
} | |
var min = this.getValues("subscriptshift","superscriptshift"), mu = this.SVGgetMu(svg); | |
min.subscriptshift = (min.subscriptshift === "" ? 0 : SVG.length2em(min.subscriptshift,mu)); | |
min.superscriptshift = (min.superscriptshift === "" ? 0 : SVG.length2em(min.superscriptshift,mu)); | |
var dx = 0; | |
if (presub) {dx = presub.w+delta} else if (presup) {dx = presup.w-delta} | |
svg.Add(base,Math.max(0,dx),0); | |
if (!sup && !presup) { | |
v = Math.max(v,SVG.TeX.sub1*scale,min.subscriptshift); | |
if (sub) {v = Math.max(v,sub.h-(4/5)*x_height)} | |
if (presub) {v = Math.max(v,presub.h-(4/5)*x_height)} | |
if (sub) {svg.Add(sub,dx+base.w+s-delta,-v)} | |
if (presub) {svg.Add(presub,0,-v)} | |
} else { | |
if (!sub && !presub) { | |
var values = this.getValues("displaystyle","texprimestyle"); | |
p = SVG.TeX[(values.displaystyle ? "sup1" : (values.texprimestyle ? "sup3" : "sup2"))]; | |
u = Math.max(u,p*scale,min.superscriptshift); | |
if (sup) {u = Math.max(u,sup.d+(1/4)*x_height)} | |
if (presup) {u = Math.max(u,presup.d+(1/4)*x_height)} | |
if (sup) {svg.Add(sup,dx+base.w+s,u)} | |
if (presup) {svg.Add(presup,0,u)} | |
} else { | |
v = Math.max(v,SVG.TeX.sub2*scale); | |
var t = SVG.TeX.rule_thickness * scale; | |
var h = (sub||presub).h, d = (sup||presup).d; | |
if (presub) {h = Math.max(h,presub.h)} | |
if (presup) {d = Math.max(d,presup.d)} | |
if ((u - d) - (h - v) < 3*t) { | |
v = 3*t - u + d + h; q = (4/5)*x_height - (u - d); | |
if (q > 0) {u += q; v -= q} | |
} | |
u = Math.max(u,min.superscriptshift); v = Math.max(v,min.subscriptshift); | |
if (sup) {svg.Add(sup,dx+base.w+s,u)} | |
if (presup) {svg.Add(presup,dx+delta-presup.w,u)} | |
if (sub) {svg.Add(sub,dx+base.w+s-delta,-v)} | |
if (presub) {svg.Add(presub,dx-presub.w,-v)} | |
} | |
} | |
svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
var data = this.SVGdata; | |
data.dx = dx; data.s = s; data.u = u, data.v = v; data.delta = delta; | |
return svg; | |
}, | |
SVGgetScripts: function (s) { | |
var sup, sub, BOX = []; | |
var i = 1, m = this.data.length, W = 0; | |
for (var k = 0; k < 4; k += 2) { | |
while (i < m && (this.data[i]||{}).type !== "mprescripts") { | |
var box = [null,null,null,null]; | |
for (var j = k; j < k+2; j++) { | |
if (this.data[i] && this.data[i].type !== "none" && this.data[i].type !== "mprescripts") { | |
if (!BOX[j]) {BOX[j] = SVG.BBOX.G()} | |
box[j] = this.data[i].toSVG(); | |
} | |
if ((this.data[i]||{}).type !== "mprescripts") i++; | |
} | |
var isPre = (k === 2); | |
if (isPre) W += Math.max((box[k]||{w:0}).w,(box[k+1]||{w:0}).w); | |
if (box[k]) BOX[k].Add(box[k].With({x:W-(isPre?box[k].w:0)})); | |
if (box[k+1]) BOX[k+1].Add(box[k+1].With({x:W-(isPre?box[k+1].w:0)})); | |
sub = BOX[k]||{w:0}; sup = BOX[k+1]||{w:0}; | |
sub.w = sup.w = W = Math.max(sub.w,sup.w); | |
} | |
i++; W = 0; | |
} | |
for (j = 0; j < 4; j++) {if (BOX[j]) {BOX[j].w += s; BOX[j].Clean()}} | |
return BOX; | |
} | |
}); | |
MathJax.Hub.Startup.signal.Post("SVG mmultiscripts Ready"); | |
MathJax.Ajax.loadComplete(SVG.autoloadDir+"/mmultiscripts.js"); | |
}); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/output/SVG/autoload/annotation-xml.js | |
* | |
* Implements the SVG output for <annotation-xml> elements. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2013-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () { | |
var VERSION = "2.7.1"; | |
var MML = MathJax.ElementJax.mml, | |
SVG = MathJax.OutputJax.SVG; | |
var BBOX = SVG.BBOX; | |
BBOX.FOREIGN = BBOX.Subclass({type: "foreignObject", removeable: false}); | |
MML["annotation-xml"].Augment({ | |
toSVG: function () { | |
var svg = this.SVG(); this.SVGhandleSpace(svg); | |
var encoding = this.Get("encoding"); | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{svg.Add(this.data[i].toSVG(encoding),svg.w,0)} | |
svg.Clean(); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
MML.xml.Augment({ | |
toSVG: function (encoding) { | |
// | |
// Get size of xml content | |
// | |
var span = SVG.textSVG.parentNode; | |
SVG.mathDiv.style.width = "auto"; // Firefox returns offsetWidth = 0 without this | |
span.insertBefore(this.div,SVG.textSVG); | |
var w = this.div.offsetWidth, h = this.div.offsetHeight; | |
var strut = MathJax.HTML.addElement(this.div,"span",{ | |
style:{display:"inline-block", overflow:"hidden", height:h+"px", | |
width:"1px", marginRight:"-1px"} | |
}); | |
var d = this.div.offsetHeight - h; h -= d; | |
this.div.removeChild(strut); | |
span.removeChild(this.div); SVG.mathDiv.style.width = ""; | |
// | |
// Create foreignObject element for the content | |
// | |
var scale = 1000/SVG.em; | |
var svg = BBOX.FOREIGN({ | |
y:(-h)+"px", width:w+"px", height:(h+d)+"px", | |
transform:"scale("+scale+") matrix(1 0 0 -1 0 0)" | |
}); | |
// | |
// Add the children to the foreignObject | |
// | |
for (var i = 0, m = this.data.length; i < m; i++) | |
{svg.element.appendChild(this.data[i].cloneNode(true))} | |
// | |
// Set the scale and finish up | |
// | |
svg.w = w*scale; svg.h = h*scale; svg.d = d*scale; | |
svg.r = svg.w; svg.l = 0; | |
svg.Clean(); | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
MathJax.Hub.Startup.signal.Post("SVG annotation-xml Ready"); | |
MathJax.Ajax.loadComplete(SVG.autoloadDir+"/annotation-xml.js"); | |
}); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/output/SVG/autoload/maction.js | |
* | |
* Implements the SVG output for <maction> elements. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2011-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () { | |
var VERSION = "2.7.1"; | |
var MML = MathJax.ElementJax.mml, | |
SVG = MathJax.OutputJax["SVG"]; | |
var currentTip, hover, clear; | |
// | |
// Add configuration for tooltips | |
// | |
var CONFIG = SVG.config.tooltip = MathJax.Hub.Insert({ | |
delayPost: 600, delayClear: 600, | |
offsetX: 10, offsetY: 5 | |
},SVG.config.tooltip||{}); | |
MML.maction.Augment({ | |
SVGtooltip: MathJax.HTML.addElement(document.body,"div",{id:"MathJax_SVG_Tooltip"}), | |
toSVG: function (HW,D) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(); | |
var selected = this.selected(); | |
if (selected.type == "null") {this.SVGsaveData(svg);return svg;} | |
svg.Add(this.SVGdataStretched(this.Get("selection")-1,HW,D)); | |
svg.removeable = false; | |
this.SVGhandleHitBox(svg); | |
this.SVGhandleSpace(svg); | |
this.SVGhandleColor(svg); | |
this.SVGsaveData(svg); | |
return svg; | |
}, | |
SVGhandleHitBox: function (svg) { | |
var frame = SVG.Element("rect", | |
{width:svg.w, height:svg.h+svg.d, y:-svg.d, fill:"none", "pointer-events":"all"}); | |
svg.element.insertBefore(frame,svg.element.firstChild); | |
var type = this.Get("actiontype"); | |
if (this.SVGaction[type]) | |
{this.SVGaction[type].call(this,svg,svg.element,this.Get("selection"))} | |
}, | |
SVGstretchH: MML.mbase.prototype.SVGstretchH, | |
SVGstretchV: MML.mbase.prototype.SVGstretchV, | |
// | |
// Implementations for the various actions | |
// | |
SVGaction: { | |
toggle: function (svg,frame,selection) { | |
this.selection = selection; | |
SVG.Element(frame,{cursor:"pointer"}); | |
frame.onclick = MathJax.Callback(["SVGclick",this]); | |
}, | |
statusline: function (svg,frame,selection) { | |
frame.onmouseover = MathJax.Callback(["SVGsetStatus",this]), | |
frame.onmouseout = MathJax.Callback(["SVGclearStatus",this]); | |
frame.onmouseover.autoReset = frame.onmouseout.autoReset = true; | |
}, | |
tooltip: function(svg,frame,selection) { | |
frame.onmouseover = MathJax.Callback(["SVGtooltipOver",this]), | |
frame.onmouseout = MathJax.Callback(["SVGtooltipOut",this]); | |
frame.onmouseover.autoReset = frame.onmouseout.autoReset = true; | |
} | |
}, | |
// | |
// Handle a click on the maction element | |
// (remove the original rendering and rerender) | |
// | |
SVGclick: function (event) { | |
this.selection++; | |
if (this.selection > this.data.length) {this.selection = 1} | |
var math = this; while (math.type !== "math") {math = math.inherit} | |
var jax = MathJax.Hub.getJaxFor(math.inputID); //, hover = !!jax.hover; | |
jax.Update(); | |
/* | |
* if (hover) { | |
* var span = document.getElementById(jax.inputID+"-Span"); | |
* MathJax.Extension.MathEvents.Hover.Hover(jax,span); | |
* } | |
*/ | |
return MathJax.Extension.MathEvents.Event.False(event); | |
}, | |
// | |
// Set/Clear the window status message | |
// | |
SVGsetStatus: function (event) { | |
// FIXME: Do something better with non-token elements | |
this.messageID = MathJax.Message.Set | |
((this.data[1] && this.data[1].isToken) ? | |
this.data[1].data.join("") : this.data[1].toString()); | |
}, | |
SVGclearStatus: function (event) { | |
if (this.messageID) {MathJax.Message.Clear(this.messageID,0)} | |
delete this.messageID; | |
}, | |
// | |
// Handle tooltips | |
// | |
SVGtooltipOver: function (event) { | |
if (!event) {event = window.event} | |
if (clear) {clearTimeout(clear); clear = null} | |
if (hover) {clearTimeout(hover)} | |
var x = event.pageX; var y = event.pageY; | |
if (x == null) { | |
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; | |
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; | |
} | |
var callback = MathJax.Callback(["SVGtooltipPost",this,x+CONFIG.offsetX,y+CONFIG.offsetY]) | |
hover = setTimeout(callback,CONFIG.delayPost); | |
}, | |
SVGtooltipOut: function (event) { | |
if (hover) {clearTimeout(hover); hover = null} | |
if (clear) {clearTimeout(clear)} | |
var callback = MathJax.Callback(["SVGtooltipClear",this,80]); | |
clear = setTimeout(callback,CONFIG.delayClear); | |
}, | |
SVGtooltipPost: function (x,y) { | |
hover = null; if (clear) {clearTimeout(clear); clear = null} | |
// | |
// Get the tip div and show it at the right location, then clear its contents | |
// | |
var tip = this.SVGtooltip; | |
tip.style.display = "block"; tip.style.opacity = ""; | |
if (this === currentTip) return; | |
tip.style.left = x+"px"; tip.style.top = y+"px"; | |
tip.innerHTML = ''; var span = MathJax.HTML.addElement(tip,"span"); | |
// | |
// Get the sizes from the jax (FIXME: should calculate again?) | |
// | |
var math = this; while (math.type !== "math") {math = math.inherit} | |
var jax = MathJax.Hub.getJaxFor(math.inputID); | |
this.em = MML.mbase.prototype.em = jax.SVG.em; this.ex = jax.SVG.ex; | |
this.linebreakWidth = jax.SVG.lineWidth; this.cwidth = jax.SVG.cwidth; | |
// | |
// Make a new math element and temporarily move the tooltip to it | |
// Display the math containing the tip, but check for errors | |
// Then put the tip back into the maction element | |
// | |
var mml = this.data[1]; | |
math = MML.math(mml); | |
try {math.toSVG(span,tip)} catch(err) { | |
this.SetData(1,mml); tip.style.display = "none"; | |
if (!err.restart) {throw err} | |
MathJax.Callback.After(["SVGtooltipPost",this,x,y],err.restart); | |
return; | |
} | |
this.SetData(1,mml); | |
currentTip = this; | |
}, | |
SVGtooltipClear: function (n) { | |
var tip = this.SVGtooltip; | |
if (n <= 0) { | |
tip.style.display = "none"; | |
tip.style.opacity = ""; | |
clear = null; | |
} else { | |
tip.style.opacity = n/100; | |
clear = setTimeout(MathJax.Callback(["SVGtooltipClear",this,n-20]),50); | |
} | |
} | |
}); | |
MathJax.Hub.Startup.signal.Post("SVG maction Ready"); | |
MathJax.Ajax.loadComplete(SVG.autoloadDir+"/maction.js"); | |
}); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/output/SVG/autoload/multiline.js | |
* | |
* Implements the SVG output for <mrow>'s that contain line breaks. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2011-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () { | |
var VERSION = "2.7.1"; | |
var MML = MathJax.ElementJax.mml, | |
SVG = MathJax.OutputJax.SVG, | |
BBOX = SVG.BBOX; | |
// | |
// Penalties for the various line breaks | |
// | |
var PENALTY = { | |
newline: 0, | |
nobreak: 1000000, | |
goodbreak: [-200], | |
badbreak: [+200], | |
auto: [0], | |
toobig: 800, | |
nestfactor: 400, | |
spacefactor: -100, | |
spaceoffset: 2, | |
spacelimit: 1, // spaces larger than this get a penalty boost | |
fence: 500, | |
close: 500 | |
}; | |
var ENDVALUES = {linebreakstyle: "after"}; | |
/**************************************************************************/ | |
MML.mrow.Augment({ | |
// | |
// Handle breaking an mrow into separate lines | |
// | |
SVGmultiline: function (svg) { | |
// | |
// Find the parent element and mark it as multiline | |
// | |
var parent = this; | |
while (parent.inferred || (parent.parent && parent.parent.type === "mrow" && | |
parent.isEmbellished())) {parent = parent.parent} | |
var isTop = ((parent.type === "math" && parent.Get("display") === "block") || | |
parent.type === "mtd"); | |
parent.isMultiline = true; | |
// | |
// Default values for the line-breaking parameters | |
// | |
var VALUES = this.getValues( | |
"linebreak","linebreakstyle","lineleading","linebreakmultchar", | |
"indentalign","indentshift", | |
"indentalignfirst","indentshiftfirst", | |
"indentalignlast","indentshiftlast" | |
); | |
if (VALUES.linebreakstyle === MML.LINEBREAKSTYLE.INFIXLINEBREAKSTYLE) | |
{VALUES.linebreakstyle = this.Get("infixlinebreakstyle")} | |
VALUES.lineleading = SVG.length2em(VALUES.lineleading,1,0.5); | |
// | |
// Start with a fresh SVG element | |
// and make it full width if we are breaking to a specific width | |
// in the top-level math element | |
// | |
svg = this.SVG(); | |
if (isTop && parent.type !== "mtd") { | |
if (SVG.linebreakWidth < SVG.BIGDIMEN) {svg.w = SVG.linebreakWidth} | |
else {svg.w = SVG.cwidth} | |
} | |
var state = { | |
n: 0, Y: 0, | |
scale: this.scale || 1, | |
isTop: isTop, | |
values: {}, | |
VALUES: VALUES | |
}, | |
align = this.SVGgetAlign(state,{}), | |
shift = this.SVGgetShift(state,{},align), | |
start = [], | |
end = { | |
index:[], penalty:PENALTY.nobreak, | |
w:0, W:shift, shift:shift, scanW:shift, | |
nest: 0 | |
}, | |
broken = false; | |
// | |
// Break the expression at its best line breaks | |
// | |
while (this.SVGbetterBreak(end,state) && | |
(end.scanW >= SVG.linebreakWidth || end.penalty === PENALTY.newline)) { | |
this.SVGaddLine(svg,start,end.index,state,end.values,broken); | |
start = end.index.slice(0); broken = true; | |
align = this.SVGgetAlign(state,end.values); | |
shift = this.SVGgetShift(state,end.values,align); | |
if (align === MML.INDENTALIGN.CENTER) {shift = 0} | |
end.W = end.shift = end.scanW = shift; end.penalty = PENALTY.nobreak; | |
} | |
state.isLast = true; | |
this.SVGaddLine(svg,start,[],state,ENDVALUES,broken); | |
this.SVGhandleSpace(svg); | |
this.SVGhandleColor(svg); | |
svg.isMultiline = true; | |
this.SVGsaveData(svg); | |
return svg; | |
} | |
}); | |
/**************************************************************************/ | |
MML.mbase.Augment({ | |
SVGlinebreakPenalty: PENALTY, | |
/****************************************************************/ | |
// | |
// Locate the next linebreak that is better than the current one | |
// | |
SVGbetterBreak: function (info,state) { | |
if (this.isToken) {return false} // FIXME: handle breaking of token elements | |
if (this.isEmbellished()) { | |
info.embellished = this; | |
return this.CoreMO().SVGbetterBreak(info,state); | |
} | |
if (this.linebreakContainer) {return false} | |
// | |
// Get the current breakpoint position and other data | |
// | |
var index = info.index.slice(0), i = info.index.shift(), | |
m = this.data.length, W, w, scanW, broken = (info.index.length > 0), better = false; | |
if (i == null) {i = -1}; if (!broken) {i++; info.W += info.w; info.w = 0} | |
scanW = info.scanW = info.W; info.nest++; | |
// | |
// Look through the line for breakpoints, | |
// (as long as we are not too far past the breaking width) | |
// | |
while (i < m && info.scanW < 1.33*SVG.linebreakWidth) { | |
if (this.data[i]) { | |
if (this.data[i].SVGbetterBreak(info,state)) { | |
better = true; index = [i].concat(info.index); W = info.W; w = info.w; | |
if (info.penalty === PENALTY.newline) { | |
info.index = index; | |
if (info.nest) {info.nest--} | |
return true; | |
} | |
} | |
scanW = (broken ? info.scanW : this.SVGaddWidth(i,info,scanW)); | |
} | |
info.index = []; i++; broken = false; | |
} | |
if (info.nest) {info.nest--} | |
info.index = index; | |
if (better) {info.W = W} | |
return better; | |
}, | |
SVGaddWidth: function (i,info,scanW) { | |
if (this.data[i]) { | |
var svg = this.data[i].SVGdata; | |
scanW += svg.w + svg.x; if (svg.X) {scanW += svg.X} | |
info.W = info.scanW = scanW; info.w = 0; | |
} | |
return scanW; | |
}, | |
/****************************************************************/ | |
// | |
// Create a new line and move the required elements into it | |
// Position it using proper alignment and indenting | |
// | |
SVGaddLine: function (svg,start,end,state,values,broken) { | |
// | |
// Create a box for the line, with empty BBox | |
// fill it with the proper elements, | |
// and clean up the bbox | |
// | |
var line = BBOX(); | |
state.first = broken; state.last = true; | |
this.SVGmoveLine(start,end,line,state,values); | |
line.Clean(); | |
// | |
// Get the alignment and shift values | |
// | |
var align = this.SVGgetAlign(state,values), | |
shift = this.SVGgetShift(state,values,align); | |
// | |
// Set the Y offset based on previous depth, leading, and current height | |
// | |
if (state.n > 0) { | |
var LHD = SVG.FONTDATA.baselineskip * state.scale; | |
var leading = (state.values.lineleading == null ? state.VALUES : state.values).lineleading * state.scale; | |
state.Y -= Math.max(LHD,state.d + line.h + leading); | |
} | |
// | |
// Place the new line | |
// | |
if (line.w + shift > svg.w) svg.w = line.w + shift; | |
svg.Align(line,align,0,state.Y,shift); | |
// | |
// Save the values needed for the future | |
// | |
state.d = line.d; state.values = values; state.n++; | |
}, | |
/****************************************************************/ | |
// | |
// Get alignment and shift values from the given data | |
// | |
SVGgetAlign: function (state,values) { | |
var cur = values, prev = state.values, def = state.VALUES, align; | |
if (state.n === 0) {align = cur.indentalignfirst || prev.indentalignfirst || def.indentalignfirst} | |
else if (state.isLast) {align = prev.indentalignlast || def.indentalignlast} | |
else {align = prev.indentalign || def.indentalign} | |
if (align === MML.INDENTALIGN.INDENTALIGN) {align = prev.indentalign || def.indentalign} | |
if (align === MML.INDENTALIGN.AUTO) {align = (state.isTop ? this.displayAlign : MML.INDENTALIGN.LEFT)} | |
return align; | |
}, | |
SVGgetShift: function (state,values,align) { | |
var cur = values, prev = state.values, def = state.VALUES, shift; | |
if (state.n === 0) {shift = cur.indentshiftfirst || prev.indentshiftfirst || def.indentshiftfirst} | |
else if (state.isLast) {shift = prev.indentshiftlast || def.indentshiftlast} | |
else {shift = prev.indentshift || def.indentshift} | |
if (shift === MML.INDENTSHIFT.INDENTSHIFT) {shift = prev.indentshift || def.indentshift} | |
if (shift === "auto" || shift === "") {shift = "0"} | |
shift = SVG.length2em(shift,1,SVG.cwidth); | |
if (state.isTop && this.displayIndent !== "0") { | |
var indent = SVG.length2em(this.displayIndent,1,SVG.cwidth); | |
shift += (align === MML.INDENTALIGN.RIGHT ? -indent: indent); | |
} | |
return shift; | |
}, | |
/****************************************************************/ | |
// | |
// Move the selected elements into the new line, | |
// moving whole items when possible, and parts of ones | |
// that are split by a line break. | |
// | |
SVGmoveLine: function (start,end,svg,state,values) { | |
var i = start[0], j = end[0]; | |
if (i == null) {i = -1}; if (j == null) {j = this.data.length-1} | |
if (i === j && start.length > 1) { | |
// | |
// If starting and ending in the same element move the subpiece to the new line | |
// | |
this.data[i].SVGmoveSlice(start.slice(1),end.slice(1),svg,state,values,"paddingLeft"); | |
} else { | |
// | |
// Otherwise, move the remainder of the initial item | |
// and any others up to the last one | |
// | |
var last = state.last; state.last = false; | |
while (i < j) { | |
if (this.data[i]) { | |
if (start.length <= 1) {this.data[i].SVGmove(svg,state,values)} | |
else {this.data[i].SVGmoveSlice(start.slice(1),[],svg,state,values,"paddingLeft")} | |
} | |
i++; state.first = false; start = []; | |
} | |
// | |
// If the last item is complete, move it, | |
// otherwise move the first part of it up to the split | |
// | |
state.last = last; | |
if (this.data[i]) { | |
if (end.length <= 1) {this.data[i].SVGmove(svg,state,values)} | |
else {this.data[i].SVGmoveSlice([],end.slice(1),svg,state,values,"paddingRight")} | |
} | |
} | |
}, | |
/****************************************************************/ | |
// | |
// Split an element and copy the selected items into the new part | |
// | |
SVGmoveSlice: function (start,end,svg,state,values,padding) { | |
// | |
// Create a new container for the slice of the element | |
// Move the selected portion into the slice | |
// | |
var slice = BBOX(); | |
this.SVGmoveLine(start,end,slice,state,values); | |
slice.Clean(); | |
if (this.href) {this.SVGaddHref(slice)} | |
this.SVGhandleColor(slice); | |
svg.Add(slice,svg.w,0,true); | |
return slice; | |
}, | |
/****************************************************************/ | |
// | |
// Move an element from its original position to its new location in | |
// a split element or the new line's position | |
// | |
SVGmove: function (line,state,values) { | |
// FIXME: handle linebreakstyle === "duplicate" | |
// FIXME: handle linebreakmultchar | |
if (!(state.first || state.last) || | |
(state.first && state.values.linebreakstyle === MML.LINEBREAKSTYLE.BEFORE) || | |
(state.last && values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER)) { | |
// | |
// Recreate output | |
// Remove padding (if first, remove at leftt, if last remove at right) | |
// Add to line | |
// | |
var svg = this.toSVG(this.SVGdata.HW,this.SVGdata.D); | |
if (state.first || state.nextIsFirst) {svg.x = 0} | |
if (state.last && svg.X) {svg.X = 0} | |
line.Add(svg,line.w,0,true); | |
} | |
if (state.first && svg && svg.w === 0) {state.nextIsFirst = true} | |
else {delete state.nextIsFirst} | |
} | |
}); | |
/**************************************************************************/ | |
MML.mfenced.Augment({ | |
SVGbetterBreak: function (info,state) { | |
// | |
// Get the current breakpoint position and other data | |
// | |
var index = info.index.slice(0), i = info.index.shift(), | |
m = this.data.length, W, w, scanW, broken = (info.index.length > 0), better = false; | |
if (i == null) {i = -1}; if (!broken) {i++; info.W += info.w; info.w = 0} | |
scanW = info.scanW = info.W; info.nest++; | |
// | |
// Create indices that include the delimiters and separators | |
// | |
if (!this.dataI) { | |
this.dataI = []; | |
if (this.data.open) {this.dataI.push("open")} | |
if (m) {this.dataI.push(0)} | |
for (var j = 1; j < m; j++) { | |
if (this.data["sep"+j]) {this.dataI.push("sep"+j)} | |
this.dataI.push(j); | |
} | |
if (this.data.close) {this.dataI.push("close")} | |
} | |
m = this.dataI.length; | |
// | |
// Look through the line for breakpoints, including the open, close, and separators | |
// (as long as we are not too far past the breaking width) | |
// | |
while (i < m && info.scanW < 1.33*SVG.linebreakWidth) { | |
var k = this.dataI[i]; | |
if (this.data[k]) { | |
if (this.data[k].SVGbetterBreak(info,state)) { | |
better = true; index = [i].concat(info.index); W = info.W; w = info.w; | |
if (info.penalty === PENALTY.newline) { | |
info.index = index; | |
if (info.nest) {info.nest--} | |
return true; | |
} | |
} | |
scanW = (broken ? info.scanW : this.SVGaddWidth(i,info,scanW)); | |
} | |
info.index = []; i++; broken = false; | |
} | |
if (info.nest) {info.nest--} | |
info.index = index; | |
if (better) {info.W = W; info.w = w} | |
return better; | |
}, | |
SVGmoveLine: function (start,end,svg,state,values) { | |
var i = start[0], j = end[0]; | |
if (i == null) {i = -1}; if (j == null) {j = this.dataI.length-1} | |
if (i === j && start.length > 1) { | |
// | |
// If starting and ending in the same element move the subpiece to the new line | |
// | |
this.data[this.dataI[i]].SVGmoveSlice(start.slice(1),end.slice(1),svg,state,values,"paddingLeft"); | |
} else { | |
// | |
// Otherwise, move the remainder of the initial item | |
// and any others (including open and separators) up to the last one | |
// | |
var last = state.last; state.last = false; var k = this.dataI[i]; | |
while (i < j) { | |
if (this.data[k]) { | |
if (start.length <= 1) {this.data[k].SVGmove(svg,state,values)} | |
else {this.data[k].SVGmoveSlice(start.slice(1),[],svg,state,values,"paddingLeft")} | |
} | |
i++; k = this.dataI[i]; state.first = false; start = []; | |
} | |
// | |
// If the last item is complete, move it | |
// | |
state.last = last; | |
if (this.data[k]) { | |
if (end.length <= 1) {this.data[k].SVGmove(svg,state,values)} | |
else {this.data[k].SVGmoveSlice([],end.slice(1),svg,state,values,"paddingRight")} | |
} | |
} | |
} | |
}); | |
/**************************************************************************/ | |
MML.msubsup.Augment({ | |
SVGbetterBreak: function (info,state) { | |
if (!this.data[this.base]) {return false} | |
// | |
// Get the current breakpoint position and other data | |
// | |
var index = info.index.slice(0), i = info.index.shift(), | |
W, w, scanW, broken = (info.index.length > 0), better = false; | |
if (!broken) {info.W += info.w; info.w = 0} | |
scanW = info.scanW = info.W; | |
// | |
// Record the width of the base and the super- and subscripts | |
// | |
if (i == null) {this.SVGdata.dw = this.SVGdata.w - this.data[this.base].SVGdata.w} | |
// | |
// Check if the base can be broken | |
// | |
if (this.data[this.base].SVGbetterBreak(info,state)) { | |
better = true; index = [this.base].concat(info.index); W = info.W; w = info.w; | |
if (info.penalty === PENALTY.newline) {better = broken = true} | |
} | |
// | |
// Add in the base if it is unbroken, and add the scripts | |
// | |
if (!broken) {this.SVGaddWidth(this.base,info,scanW)} | |
info.scanW += this.SVGdata.dw; info.W = info.scanW; | |
info.index = []; if (better) {info.W = W; info.w = w; info.index = index} | |
return better; | |
}, | |
SVGmoveLine: function (start,end,svg,state,values) { | |
// | |
// Move the proper part of the base | |
// | |
if (this.data[this.base]) { | |
if (start.length > 1) { | |
this.data[this.base].SVGmoveSlice(start.slice(1),end.slice(1),svg,state,values,"paddingLeft"); | |
} else { | |
if (end.length <= 1) {this.data[this.base].SVGmove(svg,state,values)} | |
else {this.data[this.base].SVGmoveSlice([],end.slice(1),svg,state,values,"paddingRight")} | |
} | |
} | |
// | |
// If this is the end, check for super and subscripts, and move those | |
// by moving the stack that contains them, and shifting by the amount of the | |
// base that has been removed. Remove the empty base box from the stack. | |
// | |
if (end.length === 0) { | |
var sup = this.data[this.sup], sub = this.data[this.sub], w = svg.w, data; | |
if (sup) {data = sup.SVGdata||{}; svg.Add(sup.toSVG(),w+(data.dx||0),data.dy)} | |
if (sub) {data = sub.SVGdata||{}; svg.Add(sub.toSVG(),w+(data.dx||0),data.dy)} | |
} | |
} | |
}); | |
/**************************************************************************/ | |
MML.mmultiscripts.Augment({ | |
SVGbetterBreak: function (info,state) { | |
if (!this.data[this.base]) {return false} | |
// | |
// Get the current breakpoint position and other data | |
// | |
var index = info.index.slice(0); info.index.shift(); | |
var W, w, scanW, broken = (info.index.length > 0), better = false; | |
if (!broken) {info.W += info.w; info.w = 0} | |
info.scanW = info.W; | |
// | |
// The width of the postscripts | |
// | |
var dw = this.SVGdata.w - this.data[this.base].SVGdata.w - this.SVGdata.dx; | |
// | |
// Add in the prescripts | |
// | |
info.scanW += this.SVGdata.dx; scanW = info.scanW; | |
// | |
// Check if the base can be broken (but don't break between prescripts and base) | |
// | |
if (this.data[this.base].SVGbetterBreak(info,state)) { | |
better = true; index = [this.base].concat(info.index); W = info.W; w = info.w; | |
if (info.penalty === PENALTY.newline) {better = broken = true} | |
} | |
// | |
// Add in the base if it is unbroken, and add the postscripts | |
// | |
if (!broken) {this.SVGaddWidth(this.base,info,scanW)} | |
info.scanW += dw; info.W = info.scanW; | |
info.index = []; if (better) {info.W = W; info.w = w; info.index = index} | |
return better; | |
}, | |
SVGmoveLine: function (start,end,svg,state,values) { | |
var dx, data = this.SVGdata; | |
// | |
// If this is the start, move the prescripts, if any. | |
// | |
if (start.length < 1) { | |
this.scriptBox = this.SVGgetScripts(this.SVGdata.s); | |
var presub = this.scriptBox[2], presup = this.scriptBox[3]; dx = svg.w + data.dx; | |
if (presup) {svg.Add(presup,dx+data.delta-presup.w,data.u)} | |
if (presub) {svg.Add(presub,dx-presub.w,-data.v)} | |
} | |
// | |
// Move the proper part of the base | |
// | |
if (this.data[this.base]) { | |
if (start.length > 1) { | |
this.data[this.base].SVGmoveSlice(start.slice(1),end.slice(1),svg,state,values,"paddingLeft"); | |
} else { | |
if (end.length <= 1) {this.data[this.base].SVGmove(svg,state,values)} | |
else {this.data[this.base].SVGmoveSlice([],end.slice(1),svg,state,values,"paddingRight")} | |
} | |
} | |
// | |
// If this is the end, move the postscripts, if any. | |
// | |
if (end.length === 0) { | |
var sub = this.scriptBox[0], sup = this.scriptBox[1]; dx = svg.w + data.s; | |
if (sup) {svg.Add(sup,dx,data.u)} | |
if (sub) {svg.Add(sub,dx-data.delta,-data.v)} | |
delete this.scriptBox; | |
} | |
} | |
}); | |
/**************************************************************************/ | |
MML.mo.Augment({ | |
// | |
// Override the method for checking line breaks to properly handle <mo> | |
// | |
SVGbetterBreak: function (info,state) { | |
if (info.values && info.values.last === this) {return false} | |
var values = this.getValues( | |
"linebreak","linebreakstyle","lineleading","linebreakmultchar", | |
"indentalign","indentshift", | |
"indentalignfirst","indentshiftfirst", | |
"indentalignlast","indentshiftlast", | |
"texClass", "fence" | |
); | |
if (values.linebreakstyle === MML.LINEBREAKSTYLE.INFIXLINEBREAKSTYLE) | |
{values.linebreakstyle = this.Get("infixlinebreakstyle")} | |
// | |
// Adjust nesting by TeX class (helps output that does not include | |
// mrows for nesting, but can leave these unbalanced. | |
// | |
if (values.texClass === MML.TEXCLASS.OPEN) {info.nest++} | |
if (values.texClass === MML.TEXCLASS.CLOSE && info.nest) {info.nest--} | |
// | |
// Get the default penalty for this location | |
// | |
var W = info.scanW, mo = info.embellished; delete info.embellished; | |
if (!mo || !mo.SVGdata) {mo = this} | |
var svg = mo.SVGdata, w = svg.w + svg.x; | |
if (values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER) {W += w; w = 0} | |
if (W - info.shift === 0 && values.linebreak !== MML.LINEBREAK.NEWLINE) | |
{return false} // don't break at zero width (FIXME?) | |
var offset = SVG.linebreakWidth - W; | |
// adjust offest for explicit first-line indent and align | |
if (state.n === 0 && (values.indentshiftfirst !== state.VALUES.indentshiftfirst || | |
values.indentalignfirst !== state.VALUES.indentalignfirst)) { | |
var align = this.SVGgetAlign(state,values), | |
shift = this.SVGgetShift(state,values,align); | |
offset += (info.shift - shift); | |
} | |
// | |
var penalty = Math.floor(offset / SVG.linebreakWidth * 1000); | |
if (penalty < 0) {penalty = PENALTY.toobig - 3*penalty} | |
if (values.fence) {penalty += PENALTY.fence} | |
if ((values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER && | |
values.texClass === MML.TEXCLASS.OPEN) || | |
values.texClass === MML.TEXCLASS.CLOSE) {penalty += PENALTY.close} | |
penalty += info.nest * PENALTY.nestfactor; | |
// | |
// Get the penalty for this type of break and | |
// use it to modify the default penalty | |
// | |
var linebreak = PENALTY[values.linebreak||MML.LINEBREAK.AUTO]; | |
if (!MathJax.Object.isArray(linebreak)) { | |
// for breaks past the width, don't modify penalty | |
if (offset >= 0) {penalty = linebreak * info.nest} | |
} else {penalty = Math.max(1,penalty + linebreak[0] * info.nest)} | |
// | |
// If the penalty is no better than the current one, return false | |
// Otherwise save the data for this breakpoint and return true | |
// | |
if (penalty >= info.penalty) {return false} | |
info.penalty = penalty; info.values = values; info.W = W; info.w = w; | |
values.lineleading = SVG.length2em(values.lineleading,1,state.VALUES.lineleading); | |
values.last = this; | |
return true; | |
} | |
}); | |
/**************************************************************************/ | |
MML.mspace.Augment({ | |
// | |
// Override the method for checking line breaks to properly handle <mspace> | |
// | |
SVGbetterBreak: function (info,state) { | |
if (info.values && info.values.last === this) {return false} | |
var values = this.getValues("linebreak"); | |
var linebreakValue = values.linebreak; | |
if (!linebreakValue || this.hasDimAttr()) { | |
// The MathML spec says that the linebreak attribute should be ignored | |
// if any dimensional attribute is set. | |
linebreakValue = MML.LINEBREAK.AUTO; | |
} | |
// | |
// Get the default penalty for this location | |
// | |
var W = info.scanW, svg = this.SVGdata, w = svg.w + svg.x; | |
if (W - info.shift === 0) {return false} // don't break at zero width (FIXME?) | |
var offset = SVG.linebreakWidth - W; | |
// | |
var penalty = Math.floor(offset / SVG.linebreakWidth * 1000); | |
if (penalty < 0) {penalty = PENALTY.toobig - 3*penalty} | |
penalty += info.nest * PENALTY.nestfactor; | |
// | |
// Get the penalty for this type of break and | |
// use it to modify the default penalty | |
// | |
var linebreak = PENALTY[linebreakValue]; | |
if (linebreakValue === MML.LINEBREAK.AUTO && w >= PENALTY.spacelimit*1000 && | |
!this.mathbackground && !this.backrgound) | |
{linebreak = [(w/1000+PENALTY.spaceoffset)*PENALTY.spacefactor]} | |
if (!MathJax.Object.isArray(linebreak)) { | |
// for breaks past the width, don't modify penalty | |
if (offset >= 0) {penalty = linebreak * info.nest} | |
} else {penalty = Math.max(1,penalty + linebreak[0] * info.nest)} | |
// | |
// If the penalty is no better than the current one, return false | |
// Otherwise save the data for this breakpoint and return true | |
// | |
if (penalty >= info.penalty) {return false} | |
info.penalty = penalty; info.values = values; info.W = W; info.w = w; | |
values.lineleading = state.VALUES.lineleading; | |
values.linebreakstyle = "before"; values.last = this; | |
return true; | |
} | |
}); | |
// | |
// Hook into the mathchoice extension | |
// | |
MathJax.Hub.Register.StartupHook("TeX mathchoice Ready",function () { | |
MML.TeXmathchoice.Augment({ | |
SVGbetterBreak: function (info,state) { | |
return this.Core().SVGbetterBreak(info,state); | |
}, | |
SVGmoveLine: function (start,end,svg,state,values) { | |
return this.Core().SVGmoveSlice(start,end,svg,state,values); | |
} | |
}); | |
}); | |
// | |
// Have maction process only the selected item | |
// | |
MML.maction.Augment({ | |
SVGbetterBreak: function (info,state) { | |
return this.Core().SVGbetterBreak(info,state); | |
}, | |
SVGmoveLine: function (start,end,svg,state,values) { | |
return this.Core().SVGmoveSlice(start,end,svg,state,values); | |
}, | |
}); | |
// | |
// Have semantics only do the first element | |
// (FIXME: do we need to do anything special about annotation-xml?) | |
// | |
MML.semantics.Augment({ | |
SVGbetterBreak: function (info,state) { | |
return (this.data[0] ? this.data[0].SVGbetterBreak(info,state) : false); | |
}, | |
SVGmoveLine: function (start,end,svg,state,values) { | |
return (this.data[0] ? this.data[0].SVGmoveSlice(start,end,svg,state,values) : null); | |
} | |
}); | |
/**************************************************************************/ | |
MathJax.Hub.Startup.signal.Post("SVG multiline Ready"); | |
MathJax.Ajax.loadComplete(SVG.autoloadDir+"/multiline.js"); | |
}); | |
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ | |
/* vim: set ts=2 et sw=2 tw=80: */ | |
/************************************************************* | |
* | |
* MathJax/jax/output/SVG/autoload/menclose.js | |
* | |
* Implements the SVG output for <menclose> elements. | |
* | |
* --------------------------------------------------------------------- | |
* | |
* Copyright (c) 2011-2017 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () { | |
var VERSION = "2.7.1"; | |
var MML = MathJax.ElementJax.mml, | |
SVG = MathJax.OutputJax.SVG, | |
BBOX = SVG.BBOX; | |
BBOX.ELLIPSE = BBOX.Subclass({ | |
type: "ellipse", removeable: false, | |
Init: function (h,d,w,t,color,def) { | |
if (def == null) {def = {}}; def.fill = "none"; | |
if (color) {def.stroke = color} | |
def["stroke-width"] = t.toFixed(2).replace(/\.?0+$/,""); | |
def.cx = Math.floor(w/2); def.cy = Math.floor((h+d)/2-d); | |
def.rx = Math.floor((w-t)/2); def.ry = Math.floor((h+d-t)/2); | |
this.SUPER(arguments).Init.call(this,def); | |
this.w = this.r = w; this.h = this.H = h; | |
this.d = this.D = d; this.l = 0; | |
} | |
}); | |
BBOX.DLINE = BBOX.Subclass({ | |
type: "line", removeable: false, | |
Init: function (h,d,w,t,color,updown,def) { | |
if (def == null) {def = {}}; def.fill = "none"; | |
if (color) {def.stroke = color} | |
def["stroke-width"] = t.toFixed(2).replace(/\.?0+$/,""); | |
if (updown == "up") { | |
def.x1 = Math.floor(t/2); def.y1 = Math.floor(t/2-d); | |
def.x2 = Math.floor(w-t/2); def.y2 = Math.floor(h-t/2); | |
} else { | |
def.x1 = Math.floor(t/2); def.y1 = Math.floor(h-t/2); | |
def.x2 = Math.floor(w-t/2); def.y2 = Math.floor(t/2-d); | |
} | |
this.SUPER(arguments).Init.call(this,def); | |
this.w = this.r = w; this.h = this.H = h; | |
this.d = this.D = d; this.l = 0; | |
} | |
}); | |
BBOX.FPOLY = BBOX.Subclass({ | |
type: "polygon", removeable: false, | |
Init: function (points,color,def) { | |
if (def == null) {def = {}} | |
if (color) {def.fill = color} | |
var P = [], mx = 100000000, my = mx, Mx = -mx, My = Mx; | |
for (var i = 0, m = points.length; i < m; i++) { | |
var x = points[i][0], y = points[i][1]; | |
if (x > Mx) {Mx = x}; if (x < mx) {mx = x} | |
if (y > My) {My = y}; if (y < my) {my = y} | |
P.push(Math.floor(x)+","+Math.floor(y)); | |
} | |
def.points = P.join(" "); | |
this.SUPER(arguments).Init.call(this,def); | |
this.w = this.r = Mx; this.h = this.H = My; | |
this.d = this.D = -my; this.l = -mx; | |
} | |
}); | |
BBOX.PPATH = BBOX.Subclass({ | |
type: "path", removeable: false, | |
Init: function (h,d,w,p,t,color,def) { | |
if (def == null) {def = {}}; def.fill = "none"; | |
if (color) {def.stroke = color} | |
def["stroke-width"] = t.toFixed(2).replace(/\.?0+$/,""); | |
def.d = p; | |
this.SUPER(arguments).Init.call(this,def); | |
this.w = this.r = w; this.h = this.H = h+d; | |
this.d = this.D = this.l = 0; this.y = -d; | |
} | |
}); | |
MML.menclose.Augment({ | |
toSVG: function (HW,DD) { | |
this.SVGgetStyles(); | |
var svg = this.SVG(), scale = this.SVGgetScale(svg); | |
this.SVGhandleSpace(svg); | |
var base = this.SVGdataStretched(0,HW,DD); | |
var values = this.getValues("notation","thickness","padding","mathcolor","color"); | |
if (values.color && !this.mathcolor) {values.mathcolor = values.color} | |
if (values.thickness == null) {values.thickness = ".075em"} | |
if (values.padding == null) {values.padding = ".2em"} | |
var mu = this.SVGgetMu(svg); | |
var p = SVG.length2em(values.padding,mu,1/SVG.em) * scale; // padding for enclosure | |
var t = SVG.length2em(values.thickness,mu,1/SVG.em); // thickness of lines | |
t = Math.max(1/SVG.em,t); // see issue #414 | |
var H = base.h+p+t, D = base.d+p+t, W = base.w+2*(p+t); | |
var dx = 0, w, h, i, m, borders = [false,false,false,false]; | |
// perform some reductio |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
配合AdBlock(或者类似拦截插件)拦截www.zhihu.com/equation请求较为合适(因为用脚本隐藏还是会进行网络请求。。。所以干脆不隐藏了)(修改自https://zhuanlan.zhihu.com/p/27551432,感谢@梨梨喵和@帅气可爱魔理沙,假装可以艾特到)