You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2017/12/11 15:24:48 UTC
[1/3] openmeetings git commit: [OPENMEETINGS-1783] initial work on
Math support
Repository: openmeetings
Updated Branches:
refs/heads/4.0.x 0099e6299 -> c2e4f534d
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
index f2cc969..c1c43f8 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
@@ -101,6 +101,7 @@
<div wicket:message="title:77" class="ui-widget-header clickable om-icon big rect"></div>
<div wicket:message="title:78" class="ui-widget-header clickable om-icon big ellipse"></div>
<div wicket:message="title:79" class="ui-widget-header clickable om-icon big arrow"></div>
+ <div wicket:message="title:wb.tool.math.formula" class="ui-widget-header clickable om-icon big math"></div>
<div wicket:message="title:843" class="ui-widget-header clickable om-icon big settings"></div>
</div>
<div id="wb-tools-readonly" class="tools readonly ui-state-active vertical clear">
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/tool-math.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/tool-math.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/tool-math.js
new file mode 100644
index 0000000..11692b4
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/tool-math.js
@@ -0,0 +1,63 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+var TMath = function(wb, s) {
+ const math = ShapeBase();
+ math.obj = null;
+
+ math.mouseDown = function(o) {
+ const canvas = this
+ , pointer = canvas.getPointer(o.e)
+ , ao = canvas.getActiveObject();
+ if (!!ao && ao.omType === 'Math') {
+ math.obj = ao;
+ } else {
+ function tex2svg(tex, callback) {
+ const wrapper = $("<div>").html(tex);
+ MathJax.Hub.Queue(["Typeset", MathJax.Hub, wrapper[0]]);
+ MathJax.Hub.Queue(function() {
+ const mjOut = wrapper[0].getElementsByTagName("svg")[0];
+ $(mjOut).attr("xmlns", "http://www.w3.org/2000/svg");
+ callback(mjOut.outerHTML);
+ });
+ }
+ tex2svg('\\[f(a) = \\frac{1}{2\\pi i} \\oint\\frac{f(z)}{z-a}dz; \\sqrt{x} \\]', function(svg) {
+ fabric.loadSVGFromString(svg, function(objects, options) {
+ math.obj = fabric.util.groupSVGElements(objects, options);
+ //this have to be checked ....
+ canvas.add(math.obj.set({
+ scaleX: 10
+ , scaleY: 10
+ , left: pointer.x
+ , top: pointer.y
+ , omType: 'Math'
+ })).requestRenderAll();
+ math.objectCreated(math.obj, canvas);
+ });
+ });
+ }
+ };
+ math.activate = function() {
+ wb.eachCanvas(function(canvas) {
+ canvas.on('mouse:down', math.mouseDown);
+ canvas.selection = true;
+ canvas.forEachObject(function(o) {
+ if (o.omType === 'Math') {
+ o.selectable = true;
+ }
+ });
+ });
+ ToolUtil.disableAllProps(s);
+ };
+ math.deactivate = function() {
+ wb.eachCanvas(function(canvas) {
+ canvas.off('mouse:down', math.mouseDown);
+ canvas.selection = false;
+ canvas.forEachObject(function(o) {
+ if (o.omType === 'Math') {
+ o.selectable = false;
+ }
+ });
+ });
+ };
+
+ return math;
+};
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb-board.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb-board.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb-board.js
index 72c3aad..793995e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb-board.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/wb-board.js
@@ -4,7 +4,7 @@ var Wb = function() {
, area = $('.room.wb.area .wb-area .tabs.ui-tabs'), bar = area.find('.wb-tabbar')
, extraProps = ['uid', 'fileId', 'fileType', 'count', 'slide', 'omType', '_src'];
let a, t, z, s, mode, slide = 0, width = 0, height = 0
- , zoom = 1., zoomMode = 'fullFit', role = null;
+ , zoom = 1., zoomMode = 'pageWidth', role = null;
function getBtn(m) {
return !!t ? t.find(".om-icon." + (m || mode)) : null;
@@ -122,6 +122,7 @@ var Wb = function() {
initToolBtn('rect', _firstToolItem, Rect(wb, s));
initToolBtn('ellipse', _firstToolItem, Ellipse(wb, s));
initToolBtn('arrow', _firstToolItem, Arrow(wb, s));
+ initToolBtn('math', _firstToolItem, TMath(wb, s));
initCliparts();
t.find(".om-icon.settings").click(function() {
s.show();
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/webapp/css/images/math.png
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/webapp/css/images/math.png b/openmeetings-web/src/main/webapp/css/images/math.png
new file mode 100644
index 0000000..09ee605
Binary files /dev/null and b/openmeetings-web/src/main/webapp/css/images/math.png differ
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/webapp/css/wb.css
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/webapp/css/wb.css b/openmeetings-web/src/main/webapp/css/wb.css
index 9ef758d..1c011d5 100644
--- a/openmeetings-web/src/main/webapp/css/wb.css
+++ b/openmeetings-web/src/main/webapp/css/wb.css
@@ -63,14 +63,14 @@
}
.room.wb.area .tools.vertical {
width: 31px;
- height: 448px;
+ height: 479px;
}
.room.wb.area .tools.readonly.vertical {
width: 31px;
height: 42px;
}
.room.wb.area .tools.horisontal {
- width: 448px;
+ width: 479px;
height: 31px;
}
.room.wb.area .tools.readonly.horisontal {
@@ -126,6 +126,9 @@
.room.wb.area .tools .om-icon.big.undo {
background-image: url(images/arrow_undo.png);
}
+.room.wb.area .tools .om-icon.big.math {
+ background-image: url(images/math.png);
+}
.room.wb.area .om-icon.big.next {
background-image: url(images/next.png);
}
[2/3] openmeetings git commit: [OPENMEETINGS-1783] initial work on
Math support
Posted by so...@apache.org.
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax.js
new file mode 100644
index 0000000..a6cecc7
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax.js
@@ -0,0 +1,14946 @@
+// 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.2";
+MathJax.fileversion = "2.7.2";
+MathJax.cdnVersion = "2.7.2"; // 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 && typeof(args[0]) === 'function') {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.
+ //
+ "ar": {menuTitle: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629"},
+ "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: "portugu\u00EAs"},
+ "pt-br": {menuTitle: "portugu\u00EAs do Brasil"},
+ "ru": {menuTitle: "\u0440\u0443\u0441\u0441\u043A\u0438\u0439"},
+ "sco": {menuTitle: "Scots"},
+ "scn": {menuTitle: "sicilianu"},
+ "sk": {menuTitle: "sloven\u010Dina"},
+ "sl": {menuTitle: "sloven\u0161\u010Dina"},
+ "sv": {menuTitle: "svenska"},
+ "th": {menuTitle: "\u0E44\u0E17\u0E22"},
+ "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"},
+ "zh-hant": {menuTitle: "\u6C49\u8BED"}
+ },
+
+ //
+ // 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-proce
<TRUNCATED>
[3/3] openmeetings git commit: [OPENMEETINGS-1783] initial work on
Math support
Posted by so...@apache.org.
[OPENMEETINGS-1783] initial work on Math support
Project: http://git-wip-us.apache.org/repos/asf/openmeetings/repo
Commit: http://git-wip-us.apache.org/repos/asf/openmeetings/commit/c2e4f534
Tree: http://git-wip-us.apache.org/repos/asf/openmeetings/tree/c2e4f534
Diff: http://git-wip-us.apache.org/repos/asf/openmeetings/diff/c2e4f534
Branch: refs/heads/4.0.x
Commit: c2e4f534d718eb1af8bf0757dc2e88ceeb17580a
Parents: 0099e62
Author: Maxim Solodovnik <so...@gmail.com>
Authored: Mon Dec 11 22:24:37 2017 +0700
Committer: Maxim Solodovnik <so...@gmail.com>
Committed: Mon Dec 11 22:24:37 2017 +0700
----------------------------------------------------------------------
.../openmeetings/db/dto/room/Whiteboard.java | 2 +-
openmeetings-web/pom.xml | 3 +
.../web/app/Application.properties.xml | 1 +
.../web/app/Application_ar.properties.xml | 1 +
.../web/app/Application_bg.properties.xml | 1 +
.../web/app/Application_ca.properties.xml | 1 +
.../web/app/Application_cs.properties.xml | 1 +
.../web/app/Application_da.properties.xml | 1 +
.../web/app/Application_de.properties.xml | 1 +
.../web/app/Application_el.properties.xml | 1 +
.../web/app/Application_es.properties.xml | 1 +
.../web/app/Application_fa.properties.xml | 1 +
.../web/app/Application_fi.properties.xml | 1 +
.../web/app/Application_fr.properties.xml | 1 +
.../web/app/Application_gl.properties.xml | 1 +
.../web/app/Application_he.properties.xml | 1 +
.../web/app/Application_hu.properties.xml | 1 +
.../web/app/Application_id.properties.xml | 1 +
.../web/app/Application_it.properties.xml | 1 +
.../web/app/Application_ja.properties.xml | 1 +
.../web/app/Application_ko.properties.xml | 1 +
.../web/app/Application_nl.properties.xml | 1 +
.../web/app/Application_pl.properties.xml | 1 +
.../web/app/Application_pt.properties.xml | 1 +
.../web/app/Application_pt_BR.properties.xml | 1 +
.../web/app/Application_ru.properties.xml | 1 +
.../web/app/Application_sk.properties.xml | 1 +
.../web/app/Application_sv.properties.xml | 1 +
.../web/app/Application_th.properties.xml | 1 +
.../web/app/Application_tr.properties.xml | 1 +
.../web/app/Application_uk.properties.xml | 1 +
.../web/app/Application_zh_CN.properties.xml | 1 +
.../web/app/Application_zh_TW.properties.xml | 1 +
.../openmeetings/web/room/wb/MathJax-config.js | 13 +
.../apache/openmeetings/web/room/wb/MathJax.js | 14946 +++++++++++++++++
.../openmeetings/web/room/wb/WbPanel.html | 1 +
.../openmeetings/web/room/wb/tool-math.js | 63 +
.../apache/openmeetings/web/room/wb/wb-board.js | 3 +-
.../src/main/webapp/css/images/math.png | Bin 0 -> 1172 bytes
openmeetings-web/src/main/webapp/css/wb.css | 7 +-
40 files changed, 15065 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java
----------------------------------------------------------------------
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java
index 2785787..07fbfad 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dto/room/Whiteboard.java
@@ -58,7 +58,7 @@ public class Whiteboard implements Serializable {
private static final int DEFAULT_HEIGHT = 1080;
private long id;
private double zoom = 1.;
- private ZoomMode zoomMode = ZoomMode.fullFit;
+ private ZoomMode zoomMode = ZoomMode.pageWidth;
private int width = DEFAULT_WIDTH;
private int height = DEFAULT_HEIGHT;
private Map<String, String> roomItems = Collections.synchronizedMap(new LinkedHashMap<>());
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/pom.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/pom.xml b/openmeetings-web/pom.xml
index d079289..3a8e552 100644
--- a/openmeetings-web/pom.xml
+++ b/openmeetings-web/pom.xml
@@ -152,6 +152,8 @@
<jsSourceDir>../java/org/apache/openmeetings/web/room/wb</jsSourceDir>
<jsSourceFiles>
<jsSourceFile>fabric.js</jsSourceFile>
+ <jsSourceFile>MathJax-config.js</jsSourceFile>
+ <jsSourceFile>MathJax.js</jsSourceFile>
<jsSourceFile>wb-all.js</jsSourceFile>
<jsSourceFile>uuid.js</jsSourceFile>
<jsSourceFile>tool-util.js</jsSourceFile>
@@ -161,6 +163,7 @@
<jsSourceFile>tool-apointer.js</jsSourceFile>
<jsSourceFile>tool-shape-base.js</jsSourceFile>
<jsSourceFile>tool-text.js</jsSourceFile>
+ <jsSourceFile>tool-math.js</jsSourceFile>
<jsSourceFile>tool-paint.js</jsSourceFile>
<jsSourceFile>tool-shape.js</jsSourceFile>
<jsSourceFile>tool-line.js</jsSourceFile>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
index 150285f..15e6fa3 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ar.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ar.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ar.properties.xml
index ad42ce5..721658d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ar.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ar.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bg.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bg.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bg.properties.xml
index 6de747a..b233d99 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bg.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_bg.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ca.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ca.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ca.properties.xml
index 865d985..f42d64a 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ca.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ca.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_cs.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_cs.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_cs.properties.xml
index 8efb08c..74eaea3 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_cs.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_cs.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_da.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_da.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_da.properties.xml
index 5b1249e..2883503 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_da.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_da.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_de.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_de.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_de.properties.xml
index 52bf53f..0e98fb7 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_de.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_de.properties.xml
@@ -994,4 +994,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[hat den Raum verlassen.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_el.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_el.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_el.properties.xml
index 7c26697..bfc0ec5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_el.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_el.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_es.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_es.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_es.properties.xml
index b64a875..4067d20 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_es.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_es.properties.xml
@@ -987,4 +987,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[ha salido de la sala.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fa.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fa.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fa.properties.xml
index 4e5e87f..1b729fd 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fa.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fa.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fi.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fi.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fi.properties.xml
index 922fce0..8df49ae 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fi.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fi.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fr.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fr.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fr.properties.xml
index 0e5342e..1bd4b38 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fr.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_fr.properties.xml
@@ -981,4 +981,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[a quitté la salle.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_gl.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_gl.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_gl.properties.xml
index 8c875c7..876b7dd 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_gl.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_gl.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_he.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_he.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_he.properties.xml
index 150285f..15e6fa3 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_he.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_he.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hu.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hu.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hu.properties.xml
index 6d4b7c9..d240af1 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hu.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_hu.properties.xml
@@ -977,4 +977,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[kilépett a szobából.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_id.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_id.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_id.properties.xml
index b8d297d..d3277a7 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_id.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_id.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_it.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_it.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_it.properties.xml
index 8056024..ed227cc 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_it.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_it.properties.xml
@@ -990,4 +990,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[ha lasciato la stanza.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ja.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ja.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ja.properties.xml
index 36932a7..aa30d12 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ja.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ja.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[さんは会議室から退室しました]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ko.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ko.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ko.properties.xml
index 3c66e14..26fe9fc 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ko.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ko.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_nl.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_nl.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_nl.properties.xml
index e4a1d0b..2972494 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_nl.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_nl.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pl.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pl.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pl.properties.xml
index 078db85..73edd80 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pl.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pl.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt.properties.xml
index c11f767..47f1c1e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt_BR.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt_BR.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt_BR.properties.xml
index 492ec9c..13beec5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt_BR.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_pt_BR.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[saiu da sala.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ru.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ru.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ru.properties.xml
index 7d93bb4..1d7ba21 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ru.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_ru.properties.xml
@@ -990,4 +990,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Авто-очистка]]></entry>
<entry key="activities.msg.enter"><![CDATA[входит в комнату.]]></entry>
<entry key="activities.msg.exit"><![CDATA[покидает комнату.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Математическая формула]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sk.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sk.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sk.properties.xml
index ef5bf89..9f3c51e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sk.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sk.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sv.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sv.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sv.properties.xml
index e60d47f..f39c382 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sv.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_sv.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_th.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_th.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_th.properties.xml
index e5e4be0..3926592 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_th.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_th.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tr.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tr.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tr.properties.xml
index ea8065f..107d9af 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tr.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_tr.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_uk.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_uk.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_uk.properties.xml
index 7ea04fb..f57f266 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_uk.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_uk.properties.xml
@@ -990,4 +990,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[залишає кімнату.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_CN.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_CN.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_CN.properties.xml
index 2fe7c44..9d84306 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_CN.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_CN.properties.xml
@@ -979,4 +979,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[离开了会议室.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_TW.properties.xml
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_TW.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_TW.properties.xml
index 1c2adc4..68c54ad 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_TW.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application_zh_TW.properties.xml
@@ -989,4 +989,5 @@ see http://openmeetings.apache.org/LanguageEditor.html for Details
<entry key="activities.autoclean"><![CDATA[Auto-clean]]></entry>
<entry key="activities.msg.enter"><![CDATA[enter the room.]]></entry>
<entry key="activities.msg.exit"><![CDATA[has left the room.]]></entry>
+ <entry key="wb.tool.math.formula"><![CDATA[Math formula]]></entry>
</properties>
http://git-wip-us.apache.org/repos/asf/openmeetings/blob/c2e4f534/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax-config.js
----------------------------------------------------------------------
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax-config.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax-config.js
new file mode 100644
index 0000000..39d613d
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/MathJax-config.js
@@ -0,0 +1,13 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+window.MathJax = {
+ jax : [ "input/TeX", "output/SVG" ],
+ extensions : [ "tex2jax.js" ],
+ skipStartupTypeset : true,
+ showMathMenu : false,
+ showProcessingMessages : false,
+ messageStyle : "none",
+ SVG : {
+ useGlobalCache : false
+ },
+ TeX : {},
+};