You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

441 lines
15 KiB

5 years ago
<!DOCTYPE html>
<html>
<head>
<title>Dynamic Preview of Textarea with MathJax Content</title>
<!-- Copyright (c) 2012-2018 The MathJax Consortium -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<style>
.changed { color: red }
</style>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
TeX: {
equationNumbers: {autoNumber: "AMS"},
extensions: ["begingroup.js"],
noErrors: {disabled: true}
},
showProcessingMessages: false,
tex2jax: { inlineMath: [['$','$'],['\\(','\\)']] }
});
//MathJax.Hub.signal.Interest(function (message) {console.log(message)});
</script>
<script type="text/javascript" src="../MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
var Preview = {
typeset: null, // the typeset preview area (filled in by Init below)
preview: null, // the untypeset preview (filled in by Init below)
buffer: null, // the new preview to be typeset (filled in by Init below)
data: [], // paragraph-specific data
oldtext: '', // used to see if an update is needed
pending: false, // true when a restart is in the MathJax queue
colorDelay: 400, // how long to leave changed paragraphs colored
ctimeout: null, // timeout for changed style remover
labelDelay: 1250, // how long to wait before reprocessing for label changes
ltimeout: null, // timeout for changed labels
keytimes: [], // tracks the times between keypresses
keyrate: 100, // the average of the keytimes (default value)
keyn: 0, // key index to replace next
keysize: 10, // use this many keypresses
//
// Get the preview and buffer DIV's
//
Init: function () {
this.typeset = document.getElementById("MathPreview");
this.buffer = document.createElement("div");
this.preview = document.createElement("div");
for (var i = 0; i < this.keysize; i++) {this.keytimes[i] = this.keyrate}
},
//
// This gets called when a key is pressed in the textarea.
//
Update: function (up) {
if (up) {
//
// Determine the typing speed as a rolling average of the last few keystrokes
//
var time = new Date().getTime();
if (this.lasttime) {
var delta = time - this.lasttime;
if (delta < 4*this.keyrate) {
this.keyrate = (this.keysize*this.keyrate+delta-this.keytimes[this.keyn])/this.keysize;
this.keytimes[this.keyn++] = delta;
if (this.keyn === this.keysize) {this.keyn = 0}
}
}
this.lasttime = time;
}
var text = document.getElementById("MathInput").value;
text = text.replace(/^\s+/,'').replace(/\s+$/,'').replace(/\r\n?/g,"\n");
if (text !== this.oldtext) {
this.oldtext = text;
if (!this.pending) {
this.pending = true;
MathJax.Hub.Queue(
// allow a little time for additional typing
["Delay",MathJax.Callback,Math.min(200,Math.floor(this.keyrate/2)+1)],
["Restart",this]
);
}
}
},
Restart: function (from) {
this.pending = false;
var text = this.oldtext.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
// var text = "<p>"+text.replace(/\n\n+/g,"</p><p>")+"</p>";
var text = text.replace(/\n\n+/g,"<p>");
this.buffer.innerHTML = text;
if (this.ctimeout) {clearTimeout(this.ctimeout); this.ctimeout = null}
if (this.ltimeout) {clearTimeout(this.ltimeout); this.ltimeout = null}
var update = this.CompareBuffers(from);
if (update.needed) {
MathJax.Hub.Queue(
["PreTypeset",this,update],
["Typeset",this,update],
["PostTypeset",this,update]
);
}
},
CompareBuffers: function (from) {
var b1 = this.buffer.childNodes,
b2 = this.preview.childNodes,
i, m1 = b1.length, m2 = b2.length;
//
// Make sure all top-level elements are containers
//
for (i = 0; i < m1; i++) {
var node = b1[i];
if (typeof(node.innerHTML) === "undefined") {
this.buffer.replaceChild(document.createElement("span"),node);
b1[i].appendChild(node);
}
}
//
// Determine the range of elements to update
//
if (from != null) {
//
// If from a starting point to the end, return the proper range
//
i = from; m1--; m2--;
} else {
//
// Find first non-matching element, if any,
// and the last non-matching element
//
m = Math.min(m1,m2);
for (i = 0; i < m; i++) {if (b1[i].innerHTML !== b2[i].innerHTML) break}
if (i === m && m1 === m2) {return {needed: false}}
while (m1 > i && m2 > i) {if (b1[--m1].innerHTML !== b2[--m2].innerHTML) break}
}
return {needed:true, start:i, end1:m1, end2:m2};
},
Typeset: function (update) {
return MathJax.Hub.Typeset(update.nodes,{});
},
PreTypeset: function (update) {
var TEX = MathJax.InputJax.TeX;
var i, m, n = 0, defs = [], m1 = update.end1, m2 = update.end2;
var b1 = this.buffer.childNodes,
b2 = this.typeset.childNodes;
//
// Determine the starting equation number
//
for (i = 0, m = update.start; i < m; i++) {
n += this.data[i].number;
defs = defs.concat(this.data[i].defs);
}
TEX.resetEquationNumbers(n,true);
//
// Pop any left over \begingroups and push a new one
// Then define any macros from previous paragraphs
//
while (TEX.rootStack.top > 1) {TEX.rootStack.stack.pop(); TEX.rootStack.top--}
TEX.rootStack.Push(TEX.nsStack.nsFrame());
for (i = 0, m = defs.length; i < m; i++) {TEX.rootStack.Def.apply(TEX.rootStack,defs[i])}
i = this.i = update.start; this.refs = [];
//
// Remove differing elements from typeset copy
// and add in the new (untypeset) elements.
//
this.recordOldData(this.data.splice(i,m2+1-i),n);
var tail = b2[m2+1]; update.nodes = [];
while (m2 >= i && b2[i]) {this.typeset.removeChild(b2[i]); m2--}
while (i <= m1 && b1[i]) {
this.data.splice(i,0,{number:0, labels:[], defs:[]});
var node = b1[i++].cloneNode(true); update.nodes.push(node);
if (tail) {this.typeset.insertBefore(node,tail)} else {this.typeset.appendChild(node)}
if (node.className && node.className != "")
{node.className += " changed"} else {node.className = "changed"}
}
//
// Swap buffers and set up the new buffer for the next change
//
this.preview = this.buffer; this.buffer = document.createElement("div");
this.incremental = true;
},
recordOldData: function (data,top) {
var AMS = MathJax.Extension["TeX/AMSmath"];
var labels = [], defs = [];
this.oldtop = this.newtop = top;
for (var i = 0, m = data.length; i < m; i++) {
this.oldtop += data[i].number;
defs.push(data[i].defs.all);
for (var j = 0, n = data[i].labels.length; j < n; j++) {
delete AMS.labels[data[i].labels[j].split(/=/)[0]];
labels.push(data[i].labels[j]);
}
}
this.oldlabels = labels.join(''); this.newlabels = [];
this.olddefs = defs.join(''); this.newdefs = [];
},
getTime: function (i) {
var time = 0;
for (var m = this.data.length; i < m; i++) {time += this.data[i].time}
return time;
},
PostTypeset: function (update) {
var time, delay, incremental = this.incremental; this.incremental = false;
if (incremental && this.refs.length) {
var refs = this.refs; this.refs = [];
var queue = MathJax.Callback.Queue(["Reprocess",MathJax.Hub,refs,{}]);
return queue.Push(["PostTypeset",this,update]);
}
this.ctimeout = setTimeout(this.Unmark,this.colorDelay);
if (update.nodes.length !== this.preview.childNodes.length) {
if (this.needsRefresh || this.newlabels && this.newlabels.join('') !== this.oldlabels) {
this.needsRefresh = true;
time = this.getTime(0); delay = Math.min(this.labelDelay,3*this.keyrate);
if (time < this.keyrate) {this.Refresh()}
else {this.ltimeout = setTimeout(this.Refresh,delay)}
} else {
if (this.newtop != this.oldtop || this.newdefs.join('') !== this.olddefs) {
if (this.needsRenumber == null) {this.needsRenumber = this.i}
else {this.needsRenumber = Math.min(this.needsRenumber,this.i)}
}
if (this.needsRenumber != null) {
time = this.getTime(this.needsRenumber);
delay = Math.min(this.labelDelay,3*this.keyRate);
if (time < this.keyrate) {this.Renumber()}
else {this.ltimeout = setTimeout(this.Renumber,delay)}
}
}
}
},
Unmark: function () {
Preview.ctimeout = null; var nodes = Preview.typeset.childNodes;
for (var i = 0, m = nodes.length; i < m; i++) {Preview.removeChanged(nodes[i])}
},
Refresh: function () {
Preview.pending = true; Preview.needsRefresh = false; delete Preview.needsRenumber;
MathJax.Hub.Queue(["Restart",Preview,0]);
},
Renumber: function () {
if (Preview.needsRenumber < Preview.preview.childNodes.length) {
var n = Preview.needsRenumber;
Preview.pending = true; delete Preview.needsRenumber;
MathJax.Hub.Queue(["Restart",Preview,n]);
}
},
//
// Remove the "changed" class from an element (leaving all other classes)
//
removeChanged: function (node) {
if (node.className) {
node.className = node.className.toString()
.replace(/(^|\s+)changed(\s|$)/,"$2")
.replace(/^\s+/,"");
}
}
};
MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
MathJax.InputJax.TeX.postfilterHooks.Add(function (data) {
if (Preview.incremental) {
var AMS = MathJax.Extension["TeX/AMSmath"];
var labels = Preview.data[Preview.i].labels;
for (var id in AMS.eqlabels) {if (AMS.eqlabels.hasOwnProperty(id)) {
labels.push(id+"="+AMS.eqlabels[id])
}}
Preview.newlabels = Preview.newlabels.concat(labels);
}
});
});
MathJax.Hub.Register.MessageHook("Begin Math Input",function () {
if (Preview.incremental) {Preview.eqDefs = []; Preview.eqDefs.all = []}
});
MathJax.Hub.Register.MessageHook("End Math Input",function () {
if (Preview.incremental) {
var AMS = MathJax.Extension["TeX/AMSmath"];
var data = Preview.data[Preview.i]||{};
Preview.refs = Preview.refs.concat(AMS.refs); AMS.refs = [];
Preview.eqDefs.all = Preview.eqDefs.all.join("");
Preview.newdefs.push(Preview.eqDefs.all);
data.defs = Preview.eqDefs;
data.number = AMS.startNumber - Preview.newtop;
Preview.newtop = AMS.startNumber;
}
},5); // priority = 5 to make sure it is before AMS runs.
MathJax.Hub.Register.MessageHook("Begin Math",function () {
if (Preview.incremental) {Preview.time = new Date().getTime()}
});
MathJax.Hub.Register.MessageHook("End Math",function () {
if (Preview.incremental) {
var time = new Date().getTime();
(Preview.data[Preview.i]||{}).time = time - Preview.time;
Preview.time = time;
Preview.i++;
}
});
MathJax.Hub.Register.StartupHook("TeX begingroup Ready",function () {
var STACK = MathJax.InputJax.TeX.eqnStack;
var DEF = STACK.Def;
STACK.Def = function () {
if (Preview.incremental) {
Preview.eqDefs.push([].slice.call(arguments,0));
Preview.eqDefs.all.push(arguments[0]+"{"+arguments[1]+"}");
}
DEF.apply(this,arguments);
}
//
// Temporary hack to fix typo in begingroup.js
//
MathJax.InputJax.TeX.rootStack.stack[0].environments =
MathJax.InputJax.TeX.Definitions.environment;
});
</script>
</head>
<body>
Type text with embedded TeX in the box below:<br/>
<textarea id="MathInput" cols="60" rows="10" onkeyup="Preview.Update(true)" onkeydown="Preview.Update()" style="margin-top:5px">
This is a test.
</textarea>
<br/><br/>
<div id="MoreMath"></div>
Preview is shown here:
<div id="MathPreview" style="border:1px solid; padding: 3px; width:50%; margin-top:5px"></div>
<div style="display:none">Force loading: $x$</div>
<script>
Preview.Init();
MathJax.Hub.Queue(["Update",Preview]);
</script>
</body>
</html>
<!--
| There must be some missing constraints. If $\alpha_n$ is allowed to be negative, we get the following counterexample. $\smash{\rlap{\phantom{\Bigg(}}}$
|
| Define
| $$
| u_{n+1}=(1-\alpha_n)u_n+\beta_n\tag{1}
| $$
| and
| $$
| A_n=\prod_{k=1}^{n-1}(1-\alpha_k)\tag{2}
| $$
| By induction, it can be verified that
| $$
| u_n=A_n\left(u_1+\sum_{k=1}^{n-1}\frac{\beta_k}{A_{k+1}}\right)\tag{3}
| $$
| For $j\ge1$, define
| $$
| n_j=\left\{\begin{array}{}
| 2^{j(j-1)/2}&\text{when }j\text{ is odd}\\
| 2^{j(j-1)/2+1}&\text{when }j\text{ is even}
| \end{array}\right.\tag{4}
| $$
| and for $n\ge1$,
| $$
| \alpha_n=\left\{\begin{array}{}
| \frac{1}{n+1}&\text{for }n_j\le n< n_{j+1}\text{ when }j\text{ is odd}\\
| -\frac1n&\text{for }n_j\le n< n_{j+1}\text{ when }j\text{ is even}
| \end{array}\right.\tag{5}
| $$
| Obviously, $\displaystyle\lim_{n\to\infty}\alpha_n=0$.
|
| Using telescoping products, it is not difficult to show that
| $$
| \frac{A_{n_{j+1}}}{A_{n_j}}=\left\{\begin{array}{}
| \frac{n_j}{n_{j+1}}=2^{-j-1}&\text{when }j\text{ is odd}\\
| \frac{n_{j+1}}{n_j}=2^{j-1}&\text{when }j\text{ is even}
| \end{array}\right.\tag{6}
| $$
| Equation $(6)$ yields
| $$
| A_{n_j}=\left\{\begin{array}{}
| 2^{-(j-1)/2}&\text{when }j\text{ is odd}\\
| 2^{-(3j-2)/2}&\text{when }j\text{ is even}
| \end{array}\right.\tag{7}
| $$
| Furthermore, using the standard formula for the partial harmonic series, when $j$ is odd,
| $$
| \begin{align}
| \sum_{n=n_j}^{n_{j+1}-1}\alpha_n
| &=\log\left(\frac{n_{j+1}}{n_j}\right)+O\left(\frac{1}{n_j}\right)\\
| &=(j+1)\log(2)+O\left(2^{-j(j-1)/2}\right)\tag{8}
| \end{align}
| $$
| and when $j$ is even,
| $$
| \begin{align}
| \sum_{n=n_j}^{n_{j+1}-1}\alpha_n
| &=-\log\left(\frac{n_{j+1}}{n_j}\right)+O\left(\frac{1}{n_j}\right)\\
| &=-(j-1)\log(2)+O\left(2^{-j(j-1)/2}\right)\tag{9}
| \end{align}
| $$
| Combining $(8)$ and $(9)$ yields
| $$
| \sum_{n=1}^{n_j-1}\alpha_n=\left\{\begin{array}{}
| \frac{j-1}{2}\log(2)+O(1)&\text{when }j\text{ is odd}\\
| \frac{3j-2}{2}\log(2)+O(1)&\text{when }j\text{ is even}
| \end{array}\right.\tag{10}
| $$
| Equation $(10)$ says that $\displaystyle\sum_{n=1}^\infty\alpha_n=\infty$.
|
| Define
| $$
| \beta_n=\left\{\begin{array}{}
| 2^{-j}&\text{when }n=n_j-1\text{ for }j\text{ even}\\
| 0&\text{otherwise}
| \end{array}\right.\tag{11}
| $$
| Summing the geometric series yields $\displaystyle\sum_{n=1}^\infty\beta_n=\frac13$.
|
| Using $(3)$, we get
| $$
| \begin{align}
| u_{n_{j+1}}
| &=A_{n_{j+1}}\left(u_1+\sum_{k=1}^{n_{j+1}-1}\frac{\beta_k}{A_{k+1}}\right)\\
| &\ge\frac{A_{n_{j+1}}}{A_{n_j}}\beta_{n_j-1}\\
| &=2^{j-1}\cdot2^{-j}\\
| &=\frac12\tag{12}
| \end{align}
| $$
| when $j$ is even. $(12)$ says that $\displaystyle\lim_{n\to\infty}u_n\not=0$.
-->