sample-dynamic-3.html 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Dynamic Preview of Textarea with MathJax Content</title>
  5. <!-- Copyright (c) 2012-2018 The MathJax Consortium -->
  6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  7. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  8. <style>
  9. .changed { color: red }
  10. </style>
  11. <script type="text/x-mathjax-config">
  12. MathJax.Hub.Config({
  13. TeX: {
  14. equationNumbers: {autoNumber: "AMS"},
  15. extensions: ["begingroup.js"],
  16. noErrors: {disabled: true}
  17. },
  18. showProcessingMessages: false,
  19. tex2jax: { inlineMath: [['$','$'],['\\(','\\)']] }
  20. });
  21. //MathJax.Hub.signal.Interest(function (message) {console.log(message,MathJax.Hub.cancelTypeset)});
  22. </script>
  23. <script type="text/javascript" src="../MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
  24. <script>
  25. var Preview = {
  26. typeset: null, // the typeset preview area (filled in by Init below)
  27. preview: null, // the untypeset preview (filled in by Init below)
  28. buffer: null, // the new preview to be typeset (filled in by Init below)
  29. par: [], // paragraph-specific data
  30. refs: [], // undefined references needing to be reprocessed
  31. updateNeeded: 0, // number of paragraphs needing update
  32. oldtext: '', // used to see if an update is needed
  33. pending: false, // true when a restart is in the MathJax queue
  34. highlight: true, // color setions that are changed
  35. noflicker: false, // reduce flicker
  36. flickertime: 1500, // processUpdateTime value used for noflicker
  37. classDelay: 400, // how long to leave changed paragraphs colored
  38. ctimeout: null, // timeout for changed style remover
  39. labelDelay: 1250, // how long to wait before reprocessing for label changes
  40. ltimeout: null, // timeout for changed labels
  41. keytimes: [], // tracks the times between keypresses
  42. keyrate: 100, // the average of the keytimes (default value)
  43. keyn: 0, // key index to replace next
  44. keysize: 10, // use this many keypresses
  45. //
  46. // Get the preview and buffer DIV's
  47. //
  48. Init: function () {
  49. this.typeset = document.getElementById("MathPreview");
  50. this.buffer = document.createElement("div");
  51. this.preview = document.createElement("div");
  52. this.krSpan = document.getElementById("keyrate");
  53. this.utSpan = document.getElementById("updatetime");
  54. for (var i = 0; i < this.keysize; i++) {this.keytimes[i] = this.keyrate}
  55. this.krSpan.innerHTML = this.keyrate;
  56. },
  57. //
  58. // This gets called when a key is pressed in the textarea.
  59. //
  60. Update: function (up) {
  61. if (up) {
  62. //
  63. // Determine the typing speed as a rolling average of the last few keystrokes
  64. //
  65. var time = new Date().getTime();
  66. if (this.lasttime) {
  67. var delta = time - this.lasttime;
  68. if (delta < Math.max(100,4*this.keyrate)) {
  69. this.keyrate = (this.keysize*this.keyrate+delta-this.keytimes[this.keyn])/this.keysize;
  70. this.keytimes[this.keyn++] = delta;
  71. if (this.keyn === this.keysize) {this.keyn = 0}
  72. }
  73. }
  74. this.lasttime = time;
  75. this.krSpan.innerHTML = Math.floor(this.keyrate+.5);
  76. }
  77. //
  78. // Get the new text and see if it has changed
  79. //
  80. var text = document.getElementById("MathInput").value;
  81. text = text.replace(/^\s+/,'').replace(/\s+$/,'').replace(/\r\n?/g,"\n");
  82. if (text !== this.oldtext) {
  83. //
  84. // Save the text and if we don't already have a pending Restart
  85. // clear the timeouts for updating the colors and other updates
  86. // then set the update time based on the key rate (so it can be
  87. // interrupted better), cancel the current typesetting, if any,
  88. // and start a new typesetting run after a short delay (to allow
  89. // the editor to process more keystrokes).
  90. //
  91. this.oldtext = text;
  92. if (!this.pending) {
  93. this.pending = true;
  94. if (this.ctimeout) {clearTimeout(this.ctimeout); this.ctimeout = null}
  95. if (this.ltimeout) {clearTimeout(this.ltimeout); this.ltimeout = null}
  96. MathJax.Hub.processUpdateTime = (this.noflicker ? this.flickertime : this.keyrate / 2);
  97. if (MathJax.Hub.Cancel) {MathJax.Hub.Cancel(); this.cancelled = true;}
  98. MathJax.Hub.Queue(
  99. ["Delay",MathJax.Callback,150], // allow a little time for additional typing
  100. ["Restart",this]
  101. );
  102. }
  103. }
  104. },
  105. //
  106. // To restart typesetting, touch up the text string
  107. // and save it in the buffer. Then check if an update is
  108. // needed, and if so, queue the update.
  109. //
  110. Restart: function () {
  111. this.pending = false;
  112. var text = this.oldtext.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  113. var text = text.replace(/\n\n+/g,"<p>");
  114. this.buffer.innerHTML = text;
  115. var update = this.CompareBuffers();
  116. if (update.needed) {
  117. MathJax.Hub.Queue(
  118. ["CopyChanges",this,update],
  119. ["PreTypeset",this,update],
  120. ["Typeset",this,update],
  121. ["PostTypeset",this,update]
  122. );
  123. }
  124. },
  125. //
  126. // Determine what paragraphs are changed in the buffer
  127. //
  128. CompareBuffers: function () {
  129. var b1 = this.buffer.childNodes,
  130. b2 = this.preview.childNodes,
  131. i, m1 = b1.length, m2 = b2.length, m = Math.min(m1,m2);
  132. //
  133. // Make sure all top-level elements are containers
  134. //
  135. for (i = 0; i < m1; i++) {
  136. var node = b1[i];
  137. if (typeof(node.innerHTML) === "undefined") {
  138. this.buffer.replaceChild(document.createElement("span"),node);
  139. b1[i].appendChild(node);
  140. }
  141. }
  142. //
  143. // Find first non-matching element, if any,
  144. // and the last non-matching element
  145. //
  146. for (i = 0; i < m; i++) {if (b1[i].innerHTML !== b2[i].innerHTML) break}
  147. if (i === m && m1 === m2) {return {needed: false}}
  148. while (m1 >= i && m2 >= i && m1 && m2) {if (b1[--m1].innerHTML !== b2[--m2].innerHTML) break}
  149. return {needed:true, start:i, end1:m1, end2:m2};
  150. },
  151. //
  152. // Copy the changed paragraphs from the buffer to the
  153. // preview and typeset areas, and keep track of data about
  154. // the paragraphs that were removed.
  155. //
  156. CopyChanges: function (update) {
  157. var i = update.start, m1 = update.end1, m2 = update.end2, j, par;
  158. var b1 = this.buffer.childNodes,
  159. b2 = this.typeset.childNodes;
  160. update.indices = []; update.nodes = []; update.replace = true;
  161. //
  162. // Remove differing elements from typeset copy
  163. // Copy any predeeding old replacements from an interrupted typeset
  164. // Add in the new (untypeset) elements
  165. // Copy any following old replacements from an interrupted typeset
  166. //
  167. this.recordOldData(this.par.splice(i,m2+1-i),i); var tail = b2[m2+1];
  168. if (this.replace && i > this.start) {
  169. j = update.start = this.start;
  170. this.copyPrevious(j,i,i,update);
  171. }
  172. while (m2 >= i && b2[i]) {this.typeset.removeChild(b2[i]); m2--}
  173. while (i <= m1 && b1[i]) {
  174. this.par.splice(i,0,{number:0, labels:[], defs:[], refs:[], replaced:true, update:true});
  175. var node = b1[i].cloneNode(true); update.nodes.push(node);
  176. update.indices.push(i++); this.updateNeeded++;
  177. if (tail) {this.typeset.insertBefore(node,tail)} else {this.typeset.appendChild(node)}
  178. if (this.highlight) {this.addChanged(node)}
  179. }
  180. if (this.replace && i <= this.end) {
  181. update.end = this.end;
  182. this.copyPrevious(i,this.end,update.start,update);
  183. } else {update.end = i-1};
  184. //
  185. // Swap buffers and set up the new buffer for the next change
  186. //
  187. this.preview = this.buffer; this.buffer = document.createElement("div");
  188. },
  189. //
  190. // Copy data for replaced paragraphs from an interrupted
  191. // typesetting run. Replace the paragraph by their originals
  192. // to avoid any problems with partially completed math.
  193. //
  194. copyPrevious: function (i,end,start,update) {
  195. while (i <= end) {
  196. par = this.par[i];
  197. if (par.replaced && par.update) {
  198. this.typeset.replaceChild(this.preview.childNodes[i].cloneNode(true),
  199. this.typeset.childNodes[i]);
  200. update.nodes.push(this.typeset.childNodes[i]);
  201. update.indices.push(i);
  202. }
  203. this.recordOldData([par],i,start); par.replaced = true; i++;
  204. }
  205. },
  206. //
  207. // Save the labels, defintions, and equation numbers
  208. // for the removed paragraphs, to compare (when typesetting
  209. // is done) to see if there have been changes that
  210. // would require additional paragraphs to be retypeset
  211. //
  212. recordOldData: function (par,p,q) {
  213. var AMS = MathJax.Extension["TeX/AMSmath"];
  214. var labels = [], defs = [], number = 0;
  215. for (var i = 0, m = par.length; i < m; i++) {
  216. if (!par.replace) {
  217. number += par[i].number;
  218. defs.push(par[i].defs.all);
  219. for (var j = 0, n = par[i].labels.length; j < n; j++) {
  220. delete AMS.labels[par[i].labels[j].split(/=/)[0]];
  221. labels.push(par[i].labels[j]);
  222. }
  223. }
  224. }
  225. labels = labels.join(''); defs = defs.join('');
  226. if (this.replace) {
  227. this.oldnumber += number;
  228. if (p < q) {
  229. this.oldlabels = labels + this.oldlabels;
  230. this.olddefs = defs + this.olddefs;
  231. } else {this.oldlabels += labels; this.olddefs += defs}
  232. } else {this.oldlabels = labels; this.olddefs = defs; this.oldnumber = number}
  233. },
  234. //
  235. // Save the data needed for the incremental typesetting
  236. // and reset the TeX begingroup stack
  237. //
  238. PreTypeset: function (update) {
  239. var TEX = MathJax.InputJax.TeX;
  240. //
  241. // Set the state variables
  242. //
  243. this.incremental = true; this.cancelled = false;
  244. this.i = this.j = 0; this.eqNum = 0;
  245. this.update = update.indices; this.replace = update.replace;
  246. if (this.replace) {this.start = update.start; this.end = update.end}
  247. //
  248. // Pop any left over \begingroups and push a new one
  249. // Reset the equation numbers (but not labels)
  250. //
  251. while (TEX.rootStack.top > 1) {TEX.rootStack.stack.pop(); TEX.rootStack.top--}
  252. TEX.rootStack.Push(TEX.nsStack.nsFrame());
  253. MathJax.Hub.cancelTypeset = false; // won't need to do this in the future
  254. this.updatetime = 0;
  255. },
  256. //
  257. // Typeset the given nodes
  258. //
  259. Typeset: function (update) {
  260. return MathJax.Hub.Typeset(update.nodes,{});
  261. },
  262. BeginMath: function () {
  263. //
  264. // Save the start time for this paragraph
  265. //
  266. this.time = new Date().getTime();
  267. },
  268. BeginInput: function () {
  269. //
  270. // Skip any paragraphs that aren't being updated, and
  271. // update the equation numbers and macro definitions
  272. // accordingly
  273. //
  274. var TEX = MathJax.InputJax.TeX, par;
  275. while (this.i < this.update[this.j]) {
  276. par = this.par[this.i++];
  277. this.eqNum += par.number;
  278. for (var i = 0, m = par.defs.length; i < m; i++) {
  279. TEX.rootStack.Def.apply(TEX.rootStack,par.defs[i]);
  280. }
  281. }
  282. TEX.resetEquationNumbers(this.eqNum,true);
  283. //
  284. // Store new macro and label definitions here
  285. //
  286. par = this.par[this.i];
  287. if (par) {
  288. if (!par.replaced) {par.olddefs = par.defs.all; par.oldlabels = par.labels.join('')}
  289. par.defs = []; par.defs.all = [];
  290. par.labels = [];
  291. }
  292. },
  293. TeXFilter: function (data) {
  294. //
  295. // Get any new labels for this paragraph
  296. //
  297. var AMS = MathJax.Extension["TeX/AMSmath"];
  298. var labels = this.par[this.i].labels;
  299. for (var id in AMS.eqlabels) {if (AMS.eqlabels.hasOwnProperty(id)) {
  300. labels.push(id+"="+AMS.eqlabels[id])
  301. }}
  302. },
  303. TeXDef: function (def) {
  304. //
  305. // Record the definition
  306. //
  307. var defs = this.par[this.i].defs;
  308. defs.push(def); defs.all.push(def[0]+"{"+def[1]+"}");
  309. },
  310. EndInput: function () {
  311. //
  312. // Record the undefined references, the new definitions, and the
  313. // equation number for this paragraph
  314. //
  315. var AMS = MathJax.Extension["TeX/AMSmath"];
  316. var par = this.par[this.i];
  317. if (par) {
  318. par.refs = AMS.refs; AMS.refs = [];
  319. par.defs.all = par.defs.all.join("");
  320. par.number = AMS.startNumber - this.eqNum;
  321. this.eqNum = AMS.startNumber;
  322. //
  323. // If this is an update to an existing paragraph, check if it
  324. // should force a refresh later
  325. //
  326. if (!par.replaced) {
  327. if (this.par.length !== this.update.length &&
  328. par.labels.join('') !== par.oldlabels) {this.refreshAll = true}
  329. if (this.update.length-this.j !== this.par.length-this.i &&
  330. par.defs.all !== par.olddefs) {this.refreshRest = true}
  331. delete par.olddefs; delete par.oldlabels;
  332. }
  333. }
  334. },
  335. EndMath: function () {
  336. //
  337. // Record the tie it took for this paragraph
  338. // and go on to the next one.
  339. //
  340. var par = this.par[this.i];
  341. if (par) {
  342. var time = new Date().getTime();
  343. par.time = time - this.time; this.time = time;
  344. this.updatetime += par.time;
  345. delete par.update; this.updateNeeded--;
  346. if (this.refreshRest || this.refreshAll) {MathJax.Hub.Cancel()}
  347. }
  348. this.j++; this.i++;
  349. },
  350. //
  351. // Finish an incremental typesetting pass by checking if there are any
  352. // undefined references that need updating, then checking if labels,
  353. // defs, or equation numbers have changed, which would require
  354. // updating additional paragraphs.
  355. //
  356. PostTypeset: function (update) {
  357. var incremental = this.incremental; this.incremental = false;
  358. if (MathJax.Hub.cancelTypeset && this.cancelled) return;
  359. //
  360. // Check if there are undefined references that might have been
  361. // defined in this update, and reprocess if so.
  362. //
  363. for (var i = 0, m = this.update.length; i < m; i++) {
  364. var par = this.par[this.update[i]];
  365. if (par.refs.length) {this.refs = this.refs.concat(par.refs); par.refs = []}
  366. }
  367. if (incremental && this.refs.length && !this.refreshAll && !this.refreshRest) {
  368. var queue = MathJax.Callback.Queue(
  369. ["Reprocess",MathJax.Hub,this.refs,{}],
  370. function () {if (!MathJax.Hub.cancelTypeset) {this.refs = []}}
  371. );
  372. return queue.Push(["PostTypeset",this,update]);
  373. }
  374. //
  375. // Set the timer for the color removal
  376. //
  377. if (this.highlight && !this.ctimeout)
  378. {this.ctimeout = setTimeout(this.Unmark,this.classDelay)}
  379. this.utSpan.innerHTML = this.updatetime;
  380. //
  381. //
  382. var labels = [], defs = [], number = 0;
  383. if (this.replace) {
  384. for (i = 0, m = this.update.length; i < m; i++) {
  385. var par = this.par[this.update[i]];
  386. if (par.replaced) {
  387. labels = labels.concat(par.labels.join(''));
  388. defs = defs.concat(par.defs.all);
  389. number += par.number;
  390. delete par.replaced;
  391. }
  392. }
  393. this.replace = false;
  394. this.loopCount = 0; // avoid any possibility of infinite loop
  395. // (shouldn't happen anyway, but I'm paranoid)
  396. //console.log("--------");
  397. //console.log(this.updateNeeded);
  398. //console.log(this.oldlabels,labels);
  399. //console.log(this.olddefs,defs);
  400. //console.log(this.oldnumber,number);
  401. }
  402. if (update.nodes.length !== this.preview.childNodes.length) {
  403. if (this.refreshAll || labels.join('') !== this.oldlabels) {
  404. this.MarkForUpdate(0); this.refreshAll = this.refreshRest = false; this.refs = [];
  405. } else if (this.refreshRest || defs.join('') !== this.olddefs) {
  406. this.MarkForUpdate(this.i); this.refreshRest = false;
  407. } else if (number !== this.oldnumber) {
  408. this.MarkForUpdate(this.i,true);
  409. }
  410. if (this.updateNeeded && this.loopCount++ < 10) {
  411. var delay = Math.min(this.labelDelay,3*this.keyRate);
  412. if (this.getTime() < 2*this.keyrate) {this.Refresh()}
  413. else {this.ltimeout = setTimeout(this.Refresh,delay)}
  414. }
  415. }
  416. },
  417. //
  418. // Mark the paragraphs from i onward for future updating
  419. //
  420. MarkForUpdate: function (i,numbered) {
  421. for (var m = this.par.length; i < m; i++) {
  422. if (!this.par[i].update && (!numbered || this.par[i].number)) {
  423. this.par[i].update = true; this.updateNeeded++;
  424. }
  425. }
  426. },
  427. //
  428. // Get the list of nodes needing update,
  429. // and their indices.
  430. //
  431. GetMarked: function () {
  432. var AMS = MathJax.Extension["TeX/AMSmath"];
  433. var nodes = [], indices = [], par = this.par;
  434. for (var i = 0, m = par.length; i < m; i++) {
  435. if (par[i].update) {
  436. var node = this.typeset.childNodes[i];
  437. nodes.push(node); indices.push(i);
  438. if (this.highlight) {this.addChanged(node)}
  439. for (var j = 0, n = par[i].labels.length; j < n; j++) {
  440. delete AMS.labels[par[i].labels[j].split(/=/)[0]];
  441. }
  442. }
  443. }
  444. return {nodes:nodes, indices:indices, replace:false};
  445. },
  446. //
  447. // Remove the colors from the updated paragraphs
  448. //
  449. Unmark: function () {
  450. Preview.ctimeout = null; var nodes = Preview.typeset.childNodes;
  451. for (var i = 0, m = nodes.length; i < m; i++) {Preview.removeChanged(nodes[i])}
  452. },
  453. //
  454. // Update the paragraphs that have pending updates
  455. //
  456. Refresh: function (method) {
  457. if (!method) {method = "Reprocess"}
  458. var update = Preview.GetMarked();
  459. Preview.ltimeout = null; this.replace = false;
  460. this.oldlabels = this.olddefs = ""; this.oldnumber = 0;
  461. if (update.nodes.length) {
  462. MathJax.Hub.Queue(
  463. ["PreTypeset",Preview,update],
  464. [method,MathJax.Hub,update.nodes,{}],
  465. ["PostTypeset",Preview,update]
  466. );
  467. }
  468. },
  469. //
  470. // Force a complete refresh of the preview
  471. //
  472. Redraw: function () {
  473. this.typeset.innerHTML = this.preview.innerHTML;
  474. this.MarkForUpdate(0); this.updateNeeded = this.par.length;
  475. this.Refresh("Typeset");
  476. },
  477. //
  478. // Find out how long updating the currently pending updates
  479. // would be likely to take.
  480. //
  481. getTime: function () {
  482. var time = 0, i = 0, m = this.par.length;
  483. while (i < m) {if (this.par[i].update) {time += this.par[i].time}; i++}
  484. return time;
  485. },
  486. //
  487. // Add the changed class to an element
  488. //
  489. addChanged: function (node) {
  490. if (!(node.className && node.className.toString().match(/(^| )changed( |$)/))) {
  491. if (node.className != "") {node.className += " changed"} else {node.className = "changed"}
  492. }
  493. },
  494. //
  495. // Remove the "changed" class from an element (leaving all other classes)
  496. //
  497. removeChanged: function (node) {
  498. if (node.className) {
  499. node.className = node.className.toString()
  500. .replace(/(^|\s+)changed(\s|$)/,"$2")
  501. .replace(/^\s+/,"");
  502. }
  503. }
  504. };
  505. //
  506. // Hook into the math signals
  507. //
  508. MathJax.Hub.Register.MessageHook("Begin Math",function () {
  509. if (Preview.incremental) {Preview.BeginMath()}
  510. });
  511. MathJax.Hub.Register.MessageHook("End Math",function () {
  512. if (Preview.incremental) {Preview.EndMath()}
  513. });
  514. MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
  515. MathJax.InputJax.TeX.postfilterHooks.Add(function (data) {
  516. if (Preview.incremental) {Preview.TeXFilter(data)}
  517. });
  518. });
  519. MathJax.Hub.Register.MessageHook("Begin Math Input",function () {
  520. if (Preview.incremental) {Preview.BeginInput()}
  521. });
  522. MathJax.Hub.Register.MessageHook("End Math Input",function () {
  523. if (Preview.incremental) {Preview.EndInput()}
  524. },5); // priority = 5 to make sure it is before AMS.eqlabels are removed.
  525. //
  526. // Hook into the definition routines to record
  527. // new definitions that occur.
  528. //
  529. MathJax.Hub.Register.StartupHook("TeX begingroup Ready",function () {
  530. var STACK = MathJax.InputJax.TeX.eqnStack;
  531. var DEF = STACK.Def;
  532. STACK.Def = function () {
  533. if (Preview.incremental) {Preview.TeXDef([].slice.call(arguments,0))}
  534. DEF.apply(this,arguments);
  535. }
  536. //
  537. // Temporary hack to fix typo in begingroup.js
  538. //
  539. MathJax.InputJax.TeX.rootStack.stack[0].environments =
  540. MathJax.InputJax.TeX.Definitions.environment;
  541. });
  542. //
  543. // Set up MathJax to allow canceling of typesetting, if it
  544. // doesn't already have that.
  545. //
  546. (function () {
  547. var HUB = MathJax.Hub;
  548. HUB.Queue(function () {
  549. ready = true;
  550. // HUB.processUpdateTime = 50; // reduce update time so that we can cancel easier
  551. HUB.Config({
  552. "HTML-CSS": {EqnChunk: 10, EqnChunkFactor: 1}, // reduce chunk for more frequent updates
  553. SVG: {EqnChunk: 10, EqnChunkFactor: 1}
  554. });
  555. });
  556. if (!HUB.Cancel) {
  557. HUB.cancelTypeset = false;
  558. var CANCELMESSAGE = "MathJax Canceled";
  559. HUB.Register.StartupHook("HTML-CSS Jax Config",function () {
  560. var HTMLCSS = MathJax.OutputJax["HTML-CSS"], TRANSLATE = HTMLCSS.Translate;
  561. HTMLCSS.Augment({
  562. Translate: function (script,state) {
  563. if (HUB.cancelTypeset || state.cancelled) {throw Error(CANCELMESSAGE)}
  564. return TRANSLATE.call(HTMLCSS,script,state);
  565. }
  566. });
  567. });
  568. HUB.Register.StartupHook("SVG Jax Config",function () {
  569. var SVG = MathJax.OutputJax["SVG"], TRANSLATE = SVG.Translate;
  570. SVG.Augment({
  571. Translate: function (script,state) {
  572. if (HUB.cancelTypeset || state.cancelled) {throw Error(CANCELMESSAGE)}
  573. return TRANSLATE.call(SVG,script,state);
  574. }
  575. });
  576. });
  577. HUB.Register.StartupHook("TeX Jax Config",function () {
  578. var TEX = MathJax.InputJax.TeX, TRANSLATE = TEX.Translate;
  579. TEX.Augment({
  580. Translate: function (script,state) {
  581. if (HUB.cancelTypeset || state.cancelled) {throw Error(CANCELMESSAGE)}
  582. return TRANSLATE.call(TEX,script,state);
  583. }
  584. });
  585. });
  586. var PROCESSERROR = HUB.processError;
  587. HUB.processError = function (error,state,type) {
  588. if (error.message !== CANCELMESSAGE) {return PROCESSERROR.call(HUB,error,state,type)}
  589. MathJax.Message.Clear(0,0);
  590. state.jaxIDs = []; state.jax = {}; state.scripts = [];
  591. state.i = state.j = 0;
  592. state.cancelled = true;
  593. return null;
  594. };
  595. HUB.Cancel = function () {this.cancelTypeset = true};
  596. }
  597. })();
  598. </script>
  599. </head>
  600. <body>
  601. Type text with embedded TeX in the box below:<br/>
  602. <textarea id="MathInput" cols="60" rows="10" onkeyup="Preview.Update(true)" onkeydown="Preview.Update()" style="margin-top:5px">
  603. a
  604. b
  605. </textarea>
  606. <br/><br/>
  607. <input type="checkbox" checked="true" onchange="Preview.highlight = this.checked"> Highlight changed sections
  608. &nbsp; &nbsp;
  609. <input type="checkbox" onchange="Preview.noflicker = this.checked"> Reduce flicker
  610. &nbsp; &nbsp;
  611. <input type="button" value="Refresh Preview" onclick="Preview.Redraw()" />
  612. &nbsp; &nbsp;
  613. <span id="keyrate" style="display:inline-block; width:2em;
  614. text-align:right; border: 1px solid black; padding:2px">
  615. 0</span>
  616. <span id="updatetime" style="display:inline-block; width:3em;
  617. text-align:right; border: 1px solid black; padding:2px">
  618. 0</span>
  619. <div id="MoreMath"></div>
  620. <div id="MathPreview" style="border:1px solid; padding: 3px; width:660px; margin-top:5px"></div>
  621. <div style="display:none">Force loading: $x$</div>
  622. <script>
  623. Preview.Init();
  624. MathJax.Hub.Queue(["Update",Preview]);
  625. </script>
  626. </body>
  627. </html>
  628. <!--
  629. | There must be some missing constraints. If $\alpha_n$ is allowed to be negative, we get the following counterexample. $\smash{\rlap{\phantom{\Bigg(}}}$
  630. |
  631. | Define
  632. | $$
  633. | u_{n+1}=(1-\alpha_n)u_n+\beta_n\tag{1}
  634. | $$
  635. | and
  636. | $$
  637. | A_n=\prod_{k=1}^{n-1}(1-\alpha_k)\tag{2}
  638. | $$
  639. | By induction, it can be verified that
  640. | $$
  641. | u_n=A_n\left(u_1+\sum_{k=1}^{n-1}\frac{\beta_k}{A_{k+1}}\right)\tag{3}
  642. | $$
  643. | For $j\ge1$, define
  644. | $$
  645. | n_j=\left\{\begin{array}{}
  646. | 2^{j(j-1)/2}&\text{when }j\text{ is odd}\\
  647. | 2^{j(j-1)/2+1}&\text{when }j\text{ is even}
  648. | \end{array}\right.\tag{4}
  649. | $$
  650. | and for $n\ge1$,
  651. | $$
  652. | \alpha_n=\left\{\begin{array}{}
  653. | \frac{1}{n+1}&\text{for }n_j\le n< n_{j+1}\text{ when }j\text{ is odd}\\
  654. | -\frac1n&\text{for }n_j\le n< n_{j+1}\text{ when }j\text{ is even}
  655. | \end{array}\right.\tag{5}
  656. | $$
  657. | Obviously, $\displaystyle\lim_{n\to\infty}\alpha_n=0$.
  658. |
  659. | Using telescoping products, it is not difficult to show that
  660. | $$
  661. | \frac{A_{n_{j+1}}}{A_{n_j}}=\left\{\begin{array}{}
  662. | \frac{n_j}{n_{j+1}}=2^{-j-1}&\text{when }j\text{ is odd}\\
  663. | \frac{n_{j+1}}{n_j}=2^{j-1}&\text{when }j\text{ is even}
  664. | \end{array}\right.\tag{6}
  665. | $$
  666. | Equation $(6)$ yields
  667. | $$
  668. | A_{n_j}=\left\{\begin{array}{}
  669. | 2^{-(j-1)/2}&\text{when }j\text{ is odd}\\
  670. | 2^{-(3j-2)/2}&\text{when }j\text{ is even}
  671. | \end{array}\right.\tag{7}
  672. | $$
  673. | Furthermore, using the standard formula for the partial harmonic series, when $j$ is odd,
  674. | $$
  675. | \begin{align}
  676. | \sum_{n=n_j}^{n_{j+1}-1}\alpha_n
  677. | &=\log\left(\frac{n_{j+1}}{n_j}\right)+O\left(\frac{1}{n_j}\right)\\
  678. | &=(j+1)\log(2)+O\left(2^{-j(j-1)/2}\right)\tag{8}
  679. | \end{align}
  680. | $$
  681. | and when $j$ is even,
  682. | $$
  683. | \begin{align}
  684. | \sum_{n=n_j}^{n_{j+1}-1}\alpha_n
  685. | &=-\log\left(\frac{n_{j+1}}{n_j}\right)+O\left(\frac{1}{n_j}\right)\\
  686. | &=-(j-1)\log(2)+O\left(2^{-j(j-1)/2}\right)\tag{9}
  687. | \end{align}
  688. | $$
  689. | Combining $(8)$ and $(9)$ yields
  690. | $$
  691. | \sum_{n=1}^{n_j-1}\alpha_n=\left\{\begin{array}{}
  692. | \frac{j-1}{2}\log(2)+O(1)&\text{when }j\text{ is odd}\\
  693. | \frac{3j-2}{2}\log(2)+O(1)&\text{when }j\text{ is even}
  694. | \end{array}\right.\tag{10}
  695. | $$
  696. | Equation $(10)$ says that $\displaystyle\sum_{n=1}^\infty\alpha_n=\infty$.
  697. |
  698. | Define
  699. | $$
  700. | \beta_n=\left\{\begin{array}{}
  701. | 2^{-j}&\text{when }n=n_j-1\text{ for }j\text{ even}\\
  702. | 0&\text{otherwise}
  703. | \end{array}\right.\tag{11}
  704. | $$
  705. | Summing the geometric series yields $\displaystyle\sum_{n=1}^\infty\beta_n=\frac13$.
  706. |
  707. | Using $(3)$, we get
  708. | $$
  709. | \begin{align}
  710. | u_{n_{j+1}}
  711. | &=A_{n_{j+1}}\left(u_1+\sum_{k=1}^{n_{j+1}-1}\frac{\beta_k}{A_{k+1}}\right)\\
  712. | &\ge\frac{A_{n_{j+1}}}{A_{n_j}}\beta_{n_j-1}\\
  713. | &=2^{j-1}\cdot2^{-j}\\
  714. | &=\frac12\tag{12}
  715. | \end{align}
  716. | $$
  717. | when $j$ is even. $(12)$ says that $\displaystyle\lim_{n\to\infty}u_n\not=0$.
  718. -->