Last active
July 19, 2016 12:39
-
-
Save pkra/1ae75a4bb86fbc3364c8d85675030223 to your computer and use it in GitHub Desktop.
mathjax pre-processing on node
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>MathJax even more basic pen</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
</head> | |
<body> | |
<h1>A very basic MathJax Pen</h1> | |
<p> | |
Just some basic configuration to build upon for tests, demos etc. Meant to be forked or based upon. $$LaTeX$$, $LaTeX$, | |
<!-- , `asciimath`, <math></math> --> | |
</p> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fs = require('fs'); | |
var jsdom = require('jsdom'); | |
var serializeDocument = require("jsdom").serializeDocument; | |
var tex2jax = require('./tex2jax.js').tex2jax; | |
// TODO abstract file loading | |
var markup = fs.readFileSync('test/index.html'); | |
// set up DOM | |
var doc = jsdom.jsdom(markup, { | |
FetchExternalResources: [], | |
ProcessExternalResources: false, | |
virtualConsole: jsdom.createVirtualConsole().sendTo(console) | |
}); | |
var window = doc.defaultView; | |
var document = window.document; | |
// import tex2jax into window | |
window.tex2jax = tex2jax; | |
// configure tex2jax | |
window.tex2jax.config.doc = document; | |
// do more, e.g., | |
// window.tex2jax.config.inlineMath = [['$','$'], ['\\(','\\)']]; | |
window.tex2jax.PreProcess(); | |
console.log("<!DOCTYPE html>\n" + window.document.querySelector('html').outerHTML); | |
// clean up | |
window.close(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* | |
* Copyright (c) 2009-2015 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. | |
*/ | |
var tex2jax = function() {}; | |
tex2jax.prototype.version = "2.6.0"; | |
tex2jax.prototype.config = { | |
doc: {}, | |
inlineMath: [ // The start/stop pairs for in-line math | |
// ['$','$'], // (comment out any you don't want, or add your own, but | |
['\\(', '\\)'] // be sure that you don't have an extra comma at the end) | |
], | |
displayMath: [ // The start/stop pairs for display math | |
['$$', '$$'], // (comment out any you don't want, or add your own, but | |
['\\[', '\\]'] // be sure that you don't have an extra comma at the end) | |
], | |
balanceBraces: true, // determines whether tex2jax requires braces to be | |
// balanced within math delimiters (allows for nested | |
// dollar signs). Set to false to get pre-v2.0 compatibility. | |
skipTags: ["script", "noscript", "style", "textarea", "pre", "code", "annotation", "annotation-xml"], | |
// The names of the tags whose contents will not be | |
// scanned for math delimiters | |
ignoreClass: "tex2jax_ignore", // the class name of elements whose contents should | |
// NOT be processed by tex2jax. Note that this | |
// is a regular expression, so be sure to quote any | |
// regexp special characters | |
processClass: "tex2jax_process", // the class name of elements whose contents SHOULD | |
// be processed when they appear inside ones that | |
// are ignored. Note that this is a regular expression, | |
// so be sure to quote any regexp special characters | |
processEscapes: false, // set to true to allow \$ to produce a dollar without | |
// starting in-line math mode | |
processEnvironments: true, // set to true to process \begin{xxx}...\end{xxx} outside | |
// of math mode, false to prevent that | |
processRefs: true, // set to true to process \ref{...} outside of math mode | |
preview: "TeX" // set to "none" to not insert MathJax_Preview spans | |
// or set to an array specifying an HTML snippet | |
// to use the same preview for every equation. | |
}; | |
tex2jax.prototype.PreProcess = function(element) { | |
if (typeof(element) === "string") { | |
element = this.config.doc.getElementById(element) | |
} | |
if (!element) { | |
element = this.config.doc.body | |
} | |
if (this.createPatterns()) { | |
this.scanElement(element, element.nextSibling) | |
} | |
}; | |
tex2jax.prototype.createPatterns = function() { | |
var starts = [], | |
parts = [], | |
i, m, config = this.config; | |
this.match = {}; | |
for (i = 0, m = config.inlineMath.length; i < m; i++) { | |
starts.push(this.patternQuote(config.inlineMath[i][0])); | |
this.match[config.inlineMath[i][0]] = { | |
mode: "", | |
end: config.inlineMath[i][1], | |
pattern: this.endPattern(config.inlineMath[i][1]) | |
}; | |
} | |
for (i = 0, m = config.displayMath.length; i < m; i++) { | |
starts.push(this.patternQuote(config.displayMath[i][0])); | |
this.match[config.displayMath[i][0]] = { | |
mode: "; mode=display", | |
end: config.displayMath[i][1], | |
pattern: this.endPattern(config.displayMath[i][1]) | |
}; | |
} | |
if (starts.length) { | |
parts.push(starts.sort(this.sortLength).join("|")) | |
} | |
if (config.processEnvironments) { | |
parts.push("\\\\begin\\{([^}]*)\\}") | |
} | |
if (config.processEscapes) { | |
parts.push("\\\\*\\\\\\\$") | |
} | |
if (config.processRefs) { | |
parts.push("\\\\(eq)?ref\\{[^}]*\\}") | |
} | |
this.start = new RegExp(parts.join("|"), "g"); | |
this.skipTags = new RegExp("^(" + config.skipTags.join("|") + ")$", "i"); | |
var ignore = []; | |
if (this.config.preRemoveClass) { | |
ignore.push(this.config.preRemoveClass) | |
}; | |
if (config.ignoreClass) { | |
ignore.push(config.ignoreClass) | |
} | |
this.ignoreClass = (ignore.length ? new RegExp("(^| )(" + ignore.join("|") + ")( |$)") : /^$/); | |
this.processClass = new RegExp("(^| )(" + config.processClass + ")( |$)"); | |
return (parts.length > 0); | |
}; | |
tex2jax.prototype.patternQuote = function(s) { | |
return s.replace(/([\^$(){}+*?\-|\[\]\:\\])/g, '\\$1') | |
}; | |
tex2jax.prototype.endPattern = function(end) { | |
return new RegExp(this.patternQuote(end) + "|\\\\.|[{}]", "g"); | |
}; | |
tex2jax.prototype.sortLength = function(a, b) { | |
if (a.length !== b.length) { | |
return b.length - a.length | |
} | |
return (a == b ? 0 : (a < b ? -1 : 1)); | |
}; | |
tex2jax.prototype.scanElement = function(element, stop, ignore) { | |
var cname, tname, ignoreChild, process; | |
while (element && element != stop) { | |
if (element.nodeName.toLowerCase() === '#text') { | |
if (!ignore) { | |
element = this.scanText(element) | |
} | |
} else { | |
cname = (typeof(element.className) === "undefined" ? "" : element.className); | |
tname = (typeof(element.tagName) === "undefined" ? "" : element.tagName); | |
if (typeof(cname) !== "string") { | |
cname = String(cname) | |
} // jsxgraph uses non-string class names! | |
process = this.processClass.exec(cname); | |
if (element.firstChild && !cname.match(/(^| )MathJax/) && | |
(process || !this.skipTags.exec(tname))) { | |
ignoreChild = (ignore || this.ignoreClass.exec(cname)) && !process; | |
this.scanElement(element.firstChild, stop, ignoreChild); | |
} | |
} | |
if (element) { | |
element = element.nextSibling | |
} | |
} | |
}; | |
tex2jax.prototype.scanText = function(element) { | |
if (element.nodeValue.replace(/\s+/, '') == '') { | |
return element | |
} | |
var match, prev; | |
this.search = { | |
start: true | |
}; | |
this.pattern = this.start; | |
while (element) { | |
this.pattern.lastIndex = 0; | |
while (element && element.nodeName.toLowerCase() === '#text' && | |
(match = this.pattern.exec(element.nodeValue))) { | |
if (this.search.start) { | |
element = this.startMatch(match, element) | |
} else { | |
element = this.endMatch(match, element) | |
} | |
} | |
if (this.search.matched) { | |
element = this.encloseMath(element) | |
} | |
if (element) { | |
do { | |
prev = element; | |
element = element.nextSibling | |
} | |
while (element && (element.nodeName.toLowerCase() === 'br' || | |
element.nodeName.toLowerCase() === '#comment')); | |
if (!element || element.nodeName !== '#text') { | |
return (this.search.close ? this.prevEndMatch() : prev) | |
} | |
} | |
} | |
return element; | |
}; | |
tex2jax.prototype.startMatch = function(match, element) { | |
var delim = this.match[match[0]]; | |
if (delim != null) { // a start delimiter | |
this.search = { | |
end: delim.end, | |
mode: delim.mode, | |
pcount: 0, | |
open: element, | |
olen: match[0].length, | |
opos: this.pattern.lastIndex - match[0].length | |
}; | |
this.switchPattern(delim.pattern); | |
} else if (match[0].substr(0, 6) === "\\begin") { // \begin{...} | |
this.search = { | |
end: "\\end{" + match[1] + "}", | |
mode: "; mode=display", | |
pcount: 0, | |
open: element, | |
olen: 0, | |
opos: this.pattern.lastIndex - match[0].length, | |
isBeginEnd: true | |
}; | |
this.switchPattern(this.endPattern(this.search.end)); | |
} else if (match[0].substr(0, 4) === "\\ref" || match[0].substr(0, 6) === "\\eqref") { | |
this.search = { | |
mode: "", | |
end: "", | |
open: element, | |
pcount: 0, | |
olen: 0, | |
opos: this.pattern.lastIndex - match[0].length | |
} | |
return this.endMatch([""], element); | |
} else { // escaped dollar signs | |
// put $ in a span so it doesn't get processed again | |
// split off backslashes so they don't get removed later | |
var slashes = match[0].substr(0, match[0].length - 1), | |
n, span; | |
if (slashes.length % 2 === 0) { | |
span = [slashes.replace(/\\\\/g, "\\")]; | |
n = 1 | |
} else { | |
span = [slashes.substr(1).replace(/\\\\/g, "\\"), "$"]; | |
n = 0 | |
} | |
span = MathJax.HTML.Element("span", null, span); | |
// var text = MathJax.HTML.TextNode(element.nodeValue.substr(0,match.index)); | |
element.nodeValue = element.nodeValue.substr(match.index + match[0].length - n); | |
element.parentNode.insertBefore(span, element); | |
// element.parentNode.insertBefore(text,span); | |
this.pattern.lastIndex = n; | |
} | |
return element; | |
}; | |
tex2jax.prototype.endMatch = function(match, element) { | |
var search = this.search; | |
if (match[0] == search.end) { | |
if (!search.close || search.pcount === 0) { | |
search.close = element; | |
search.cpos = this.pattern.lastIndex; | |
search.clen = (search.isBeginEnd ? 0 : match[0].length); | |
} | |
if (search.pcount === 0) { | |
search.matched = true; | |
element = this.encloseMath(element); | |
this.switchPattern(this.start); | |
} | |
} else if (match[0] === "{") { | |
search.pcount++ | |
} else if (match[0] === "}" && search.pcount) { | |
search.pcount-- | |
} | |
return element; | |
}; | |
tex2jax.prototype.prevEndMatch = function() { | |
this.search.matched = true; | |
var element = this.encloseMath(this.search.close); | |
this.switchPattern(this.start); | |
return element; | |
}; | |
tex2jax.prototype.switchPattern = function(pattern) { | |
pattern.lastIndex = this.pattern.lastIndex; | |
this.pattern = pattern; | |
this.search.start = (pattern === this.start); | |
}; | |
tex2jax.prototype.encloseMath = function(element) { | |
var search = this.search, | |
close = search.close, | |
CLOSE, math; | |
if (search.cpos === close.length) { | |
close = close.nextSibling | |
} else { | |
close = close.splitText(search.cpos) | |
} | |
// if (!close) {CLOSE = close = MathJax.HTML.addText(search.close.parentNode,"")} | |
search.close = close; | |
math = (search.opos ? search.open.splitText(search.opos) : search.open); | |
while (math.nextSibling && math.nextSibling !== close) { | |
if (math.nextSibling.nodeValue !== null) { | |
if (math.nextSibling.nodeName === "#comment") { | |
math.nodeValue += math.nextSibling.nodeValue.replace(/^\[CDATA\[((.|\n|\r)*)\]\]$/, "$1"); | |
} else { | |
math.nodeValue += math.nextSibling.nodeValue; | |
} | |
} else if (this.msieNewlineBug) { | |
math.nodeValue += (math.nextSibling.nodeName.toLowerCase() === "br" ? "\n" : " "); | |
} else { | |
math.nodeValue += " "; | |
} | |
math.parentNode.removeChild(math.nextSibling); | |
} | |
var TeX = math.nodeValue.substr(search.olen, math.nodeValue.length - search.olen - search.clen); | |
math.parentNode.removeChild(math); | |
if (this.config.preview !== "none") { | |
this.createPreview(search.mode, TeX) | |
} | |
math = this.createMathTag(search.mode, TeX); | |
this.search = {}; | |
this.pattern.lastIndex = 0; | |
if (CLOSE) { | |
CLOSE.parentNode.removeChild(CLOSE) | |
} | |
return math; | |
}; | |
tex2jax.prototype.insertNode = function(node) { | |
var search = this.search; | |
search.close.parentNode.insertBefore(node, search.close); | |
}; | |
tex2jax.prototype.createPreview = function(mode, tex) { | |
var previewClass = this.config.preRemoveClass; | |
var preview = this.config.preview; | |
if (preview === "none") return; | |
if ((this.search.close.previousSibling || {}).className === previewClass) return; | |
if (preview === "TeX") { | |
preview = [this.filterPreview(tex)] | |
} | |
if (preview) { | |
span = this.config.doc.createElement("span"); | |
span.setAttribute("class", previewClass); | |
span.innerHTML = preview; | |
this.insertNode(span); | |
} | |
}; | |
tex2jax.prototype.createMathTag = function(mode, tex) { | |
var script = this.config.doc.createElement("script"); | |
script.type = "math/tex" + mode; | |
script.text = tex; | |
this.insertNode(script); | |
return script; | |
}; | |
tex2jax.prototype.filterPreview = function(tex) { | |
return tex | |
}; | |
exports.tex2jax = new tex2jax(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment