Add basic auto-render extension
Summary: Add an auto-render extension to render math on a page. It exposes a global function (maybe we should attach it to `katex`?) to render math in an element. It comes with a README on how to use it. Also, make `make build` build the minified file. Fixes #26 Test Plan: - Visit http://localhost:7936/contrib/auto-render/ - See that all of the math renders correctly - `make test` Reviewers: alpert, kevinb Reviewed By: kevinb Differential Revision: https://phabricator.khanacademy.org/D16620
This commit is contained in:
parent
99a81aca50
commit
cd9bca4a89
10
LICENSE.txt
10
LICENSE.txt
|
@ -1,6 +1,12 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Khan Academy
|
||||
Copyright (c) 2015 Khan Academy
|
||||
|
||||
This software also uses portions of the underscore.js project, which is
|
||||
MIT licensed with the following copyright:
|
||||
|
||||
Copyright (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative
|
||||
Reporters & Editors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -18,4 +24,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
|
|
31
Makefile
31
Makefile
|
@ -1,5 +1,13 @@
|
|||
.PHONY: build lint setup copy serve clean metrics test zip
|
||||
build: setup lint build/katex.min.js build/katex.min.css zip compress
|
||||
.PHONY: build lint setup copy serve clean metrics test zip contrib
|
||||
build: setup lint build/katex.min.js build/katex.min.css contrib zip compress
|
||||
|
||||
# Export these variables for use in contrib Makefiles
|
||||
export BUILDDIR = $(realpath build)
|
||||
export BROWSERIFY = $(realpath ./node_modules/.bin/browserify)
|
||||
export UGLIFYJS = $(realpath ./node_modules/.bin/uglifyjs) \
|
||||
--mangle \
|
||||
--beautify \
|
||||
ascii_only=true,beautify=false
|
||||
|
||||
setup:
|
||||
npm install
|
||||
|
@ -8,10 +16,10 @@ lint: katex.js $(wildcard src/*.js)
|
|||
./node_modules/.bin/jshint $^
|
||||
|
||||
build/katex.js: katex.js $(wildcard src/*.js)
|
||||
./node_modules/.bin/browserify $< --standalone katex > $@
|
||||
$(BROWSERIFY) $< --standalone katex > $@
|
||||
|
||||
build/katex.min.js: build/katex.js
|
||||
./node_modules/.bin/uglifyjs --mangle --beautify ascii_only=true,beautify=false < $< > $@
|
||||
$(UGLIFYJS) < $< > $@
|
||||
|
||||
build/katex.less.css: static/katex.less $(wildcard static/*.less)
|
||||
./node_modules/.bin/lessc $< $@
|
||||
|
@ -27,15 +35,27 @@ build/fonts:
|
|||
cp static/fonts/$$font* $@; \
|
||||
done
|
||||
|
||||
contrib: build/contrib
|
||||
|
||||
.PHONY: build/contrib
|
||||
build/contrib:
|
||||
mkdir -p build/contrib
|
||||
# Since everything in build/contrib is put in the built files, make sure
|
||||
# there's nothing in there we don't want.
|
||||
rm -rf build/contrib/*
|
||||
$(MAKE) -C contrib/auto-render
|
||||
|
||||
.PHONY: build/katex
|
||||
build/katex: build/katex.min.js build/katex.min.css build/fonts README.md
|
||||
build/katex: build/katex.min.js build/katex.min.css build/fonts README.md build/contrib
|
||||
mkdir -p build/katex
|
||||
rm -rf build/katex/*
|
||||
cp -r $^ build/katex
|
||||
|
||||
build/katex.tar.gz: build/katex
|
||||
cd build && tar czf katex.tar.gz katex/
|
||||
|
||||
build/katex.zip: build/katex
|
||||
rm -f $@
|
||||
cd build && zip -rq katex.zip katex/
|
||||
|
||||
zip: build/katex.tar.gz build/katex.zip
|
||||
|
@ -53,6 +73,7 @@ serve:
|
|||
|
||||
test:
|
||||
./node_modules/.bin/jasmine-node test/katex-spec.js
|
||||
./node_modules/.bin/jasmine-node contrib/auto-render/auto-render-spec.js
|
||||
|
||||
metrics:
|
||||
cd metrics && ./mapping.pl | ./extract_tfms.py | ./extract_ttfs.py | ./replace_line.py
|
||||
|
|
|
@ -49,6 +49,10 @@ For example:
|
|||
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element, { displayMode: true });
|
||||
```
|
||||
|
||||
#### Automatic rendering of math on a page
|
||||
|
||||
Math on the page can be automatically rendered using the auto-render extension. See [the Auto-render README](contrib/auto-render/README.md) for more information.
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
|
9
contrib/auto-render/Makefile
Normal file
9
contrib/auto-render/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
.PHONY: build
|
||||
|
||||
build: $(BUILDDIR)/contrib/auto-render.min.js
|
||||
|
||||
$(BUILDDIR)/contrib/auto-render.min.js: $(BUILDDIR)/auto-render.js
|
||||
$(UGLIFYJS) < $< > $@
|
||||
|
||||
$(BUILDDIR)/auto-render.js: auto-render.js
|
||||
$(BROWSERIFY) $< --standalone renderMathInElement > $@
|
63
contrib/auto-render/README.md
Normal file
63
contrib/auto-render/README.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
# Auto-render extension
|
||||
|
||||
This is an extension to automatically render all of the math inside of text. It
|
||||
searches all of the text nodes in a given element for the given delimiters, and
|
||||
renders the math in place.
|
||||
|
||||
### Usage
|
||||
|
||||
This extension isn't part of KaTeX proper, so the script should be separately
|
||||
included in the page:
|
||||
|
||||
```html
|
||||
<script src="/path/to/auto-render.min.js"></script>
|
||||
```
|
||||
|
||||
Then, call the exposed `renderMathInElement` function in a script tag
|
||||
before the close body tag:
|
||||
|
||||
```html
|
||||
<body>
|
||||
...
|
||||
<script>
|
||||
renderMathInElement(document.body);
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
See [index.html](index.html) for an example.
|
||||
|
||||
### API
|
||||
|
||||
This extension exposes a single function, `window.renderMathInElement`, with
|
||||
the following API:
|
||||
|
||||
```js
|
||||
function renderMathInElement(elem, options)
|
||||
```
|
||||
|
||||
`elem` is an HTML DOM element. The function will recursively search for text
|
||||
nodes inside this element and render the math in them.
|
||||
|
||||
`options` is an optional object argument with the following keys:
|
||||
|
||||
- `delimiters`: This is a list of delimiters to look for math. Each delimiter
|
||||
has three properties:
|
||||
|
||||
- `left`: A string which starts the math expression (i.e. the left delimiter).
|
||||
- `right`: A string which ends the math expression (i.e. the right delimiter).
|
||||
- `display`: A boolean of whether the math in the expression should be
|
||||
rendered in display mode or not.
|
||||
|
||||
The default value is:
|
||||
```js
|
||||
[
|
||||
{left: "$$", right: "$$", display: true},
|
||||
{left: "\\[", right: "\\]", display: true},
|
||||
{left: "\\(", right: "\\)", display: false}
|
||||
]
|
||||
```
|
||||
|
||||
- `ignoredTags`: This is a list of DOM node types to ignore when recursing
|
||||
through. The default value is
|
||||
`["script", "noscript", "style", "textarea", "pre", "code"]`.
|
217
contrib/auto-render/auto-render-spec.js
Normal file
217
contrib/auto-render/auto-render-spec.js
Normal file
|
@ -0,0 +1,217 @@
|
|||
var splitAtDelimiters = require("./splitAtDelimiters");
|
||||
|
||||
beforeEach(function() {
|
||||
jasmine.addMatchers({
|
||||
toSplitInto: function() {
|
||||
return {
|
||||
compare: function(actual, left, right, result) {
|
||||
var message = {
|
||||
pass: true,
|
||||
message: "'" + actual + "' split correctly"
|
||||
};
|
||||
|
||||
var startData = [{type: "text", data: actual}];
|
||||
|
||||
var split = splitAtDelimiters(startData, left, right, false);
|
||||
|
||||
if (split.length !== result.length) {
|
||||
message.pass = false;
|
||||
message.message = "Different number of splits: " +
|
||||
split.length + " vs. " + result.length + " (" +
|
||||
JSON.stringify(split) + " vs. " +
|
||||
JSON.stringify(result) + ")";
|
||||
return message;
|
||||
}
|
||||
|
||||
for (var i = 0; i < split.length; i++) {
|
||||
var real = split[i];
|
||||
var correct = result[i];
|
||||
|
||||
var good = true;
|
||||
|
||||
if (real.type !== correct.type) {
|
||||
good = false;
|
||||
diff = "type";
|
||||
} else if (real.data !== correct.data) {
|
||||
good = false;
|
||||
diff = "data";
|
||||
} else if (real.display !== correct.display) {
|
||||
good = false;
|
||||
diff = "display";
|
||||
}
|
||||
|
||||
if (!good) {
|
||||
message.pass = false;
|
||||
message.message = "Difference at split " +
|
||||
(i + 1) + ": " + JSON.stringify(real) +
|
||||
" vs. " + JSON.stringify(correct) +
|
||||
" (" + diff + " differs)";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("A delimiter splitter", function() {
|
||||
it("doesn't split when there are no delimiters", function() {
|
||||
expect("hello").toSplitInto("(", ")", [{type: "text", data: "hello"}]);
|
||||
});
|
||||
|
||||
it("doesn't create a math node when there's only a left delimiter", function() {
|
||||
expect("hello ( world").toSplitInto(
|
||||
"(", ")",
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "text", data: "( world"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("doesn't split when there's only a right delimiter", function() {
|
||||
expect("hello ) world").toSplitInto(
|
||||
"(", ")",
|
||||
[
|
||||
{type: "text", data: "hello ) world"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("splits when there are both delimiters", function() {
|
||||
expect("hello ( world ) boo").toSplitInto(
|
||||
"(", ")",
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world ", display: false},
|
||||
{type: "text", data: " boo"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("splits on multi-character delimiters", function() {
|
||||
expect("hello [[ world ]] boo").toSplitInto(
|
||||
"[[", "]]",
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world ", display: false},
|
||||
{type: "text", data: " boo"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("splits mutliple times", function() {
|
||||
expect("hello ( world ) boo ( more ) stuff").toSplitInto(
|
||||
"(", ")",
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world ", display: false},
|
||||
{type: "text", data: " boo "},
|
||||
{type: "math", data: " more ", display: false},
|
||||
{type: "text", data: " stuff"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("leaves the ending when there's only a left delimiter", function() {
|
||||
expect("hello ( world ) boo ( left").toSplitInto(
|
||||
"(", ")",
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world ", display: false},
|
||||
{type: "text", data: " boo "},
|
||||
{type: "text", data: "( left"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("doesn't split when close delimiters are in {}s", function() {
|
||||
expect("hello ( world { ) } ) boo").toSplitInto(
|
||||
"(", ")",
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world { ) } ", display: false},
|
||||
{type: "text", data: " boo"}
|
||||
]);
|
||||
|
||||
expect("hello ( world { { } ) } ) boo").toSplitInto(
|
||||
"(", ")",
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world { { } ) } ", display: false},
|
||||
{type: "text", data: " boo"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("doesn't split at escaped delimiters", function() {
|
||||
expect("hello ( world \\) ) boo").toSplitInto(
|
||||
"(", ")",
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world \\) ", display: false},
|
||||
{type: "text", data: " boo"}
|
||||
]);
|
||||
|
||||
/* TODO(emily): make this work maybe?
|
||||
expect("hello \\( ( world ) boo").toSplitInto(
|
||||
"(", ")",
|
||||
[
|
||||
{type: "text", data: "hello \\( "},
|
||||
{type: "math", data: " world ", display: false},
|
||||
{type: "text", data: " boo"}
|
||||
]);
|
||||
*/
|
||||
});
|
||||
|
||||
it("splits when the right and left delimiters are the same", function() {
|
||||
expect("hello $ world $ boo").toSplitInto(
|
||||
"$", "$",
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world ", display: false},
|
||||
{type: "text", data: " boo"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("remembers which delimiters are display-mode", function() {
|
||||
var startData = [{type: "text", data: "hello ( world ) boo"}];
|
||||
|
||||
expect(splitAtDelimiters(startData, "(", ")", true)).toEqual(
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world ", display: true},
|
||||
{type: "text", data: " boo"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("works with more than one start datum", function() {
|
||||
var startData = [
|
||||
{type: "text", data: "hello ( world ) boo"},
|
||||
{type: "math", data: "math", display: true},
|
||||
{type: "text", data: "hello ( world ) boo"}
|
||||
];
|
||||
|
||||
expect(splitAtDelimiters(startData, "(", ")", false)).toEqual(
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world ", display: false},
|
||||
{type: "text", data: " boo"},
|
||||
{type: "math", data: "math", display: true},
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world ", display: false},
|
||||
{type: "text", data: " boo"}
|
||||
]);
|
||||
});
|
||||
|
||||
it("doesn't do splitting inside of math nodes", function() {
|
||||
var startData = [
|
||||
{type: "text", data: "hello ( world ) boo"},
|
||||
{type: "math", data: "hello ( world ) boo", display: true}
|
||||
];
|
||||
|
||||
expect(splitAtDelimiters(startData, "(", ")", false)).toEqual(
|
||||
[
|
||||
{type: "text", data: "hello "},
|
||||
{type: "math", data: " world ", display: false},
|
||||
{type: "text", data: " boo"},
|
||||
{type: "math", data: "hello ( world ) boo", display: true}
|
||||
]);
|
||||
});
|
||||
});
|
95
contrib/auto-render/auto-render.js
Normal file
95
contrib/auto-render/auto-render.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
var splitAtDelimiters = require("./splitAtDelimiters");
|
||||
|
||||
var splitWithDelimiters = function(text, delimiters) {
|
||||
var data = [{type: "text", data: text}];
|
||||
for (var i = 0; i < delimiters.length; i++) {
|
||||
var delimiter = delimiters[i];
|
||||
data = splitAtDelimiters(
|
||||
data, delimiter.left, delimiter.right,
|
||||
delimiter.display || false);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
var renderMathInText = function(text, delimiters) {
|
||||
var data = splitWithDelimiters(text, delimiters);
|
||||
|
||||
var fragment = document.createDocumentFragment();
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].type === "text") {
|
||||
fragment.appendChild(document.createTextNode(data[i].data));
|
||||
} else {
|
||||
var span = document.createElement("span");
|
||||
var math = data[i].data;
|
||||
katex.render(math, span, {
|
||||
displayMode: data[i].display
|
||||
});
|
||||
fragment.appendChild(span);
|
||||
}
|
||||
}
|
||||
|
||||
return fragment;
|
||||
};
|
||||
|
||||
var renderElem = function(elem, delimiters, ignoredTags) {
|
||||
for (var i = 0; i < elem.childNodes.length; i++) {
|
||||
var childNode = elem.childNodes[i];
|
||||
if (childNode.nodeType === 3) {
|
||||
// Text node
|
||||
var frag = renderMathInText(childNode.textContent, delimiters);
|
||||
i += frag.childNodes.length - 1;
|
||||
elem.replaceChild(frag, childNode);
|
||||
} else if (childNode.nodeType === 1) {
|
||||
// Element node
|
||||
var shouldRender = ignoredTags.indexOf(
|
||||
childNode.nodeName.toLowerCase()) === -1;
|
||||
|
||||
if (shouldRender) {
|
||||
renderElem(childNode, delimiters, ignoredTags);
|
||||
}
|
||||
} else {
|
||||
// Something else, ignore
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var defaultOptions = {
|
||||
delimiters: [
|
||||
{left: "$$", right: "$$", display: true},
|
||||
{left: "\\[", right: "\\]", display: true},
|
||||
{left: "\\(", right: "\\)", display: false}
|
||||
// LaTeX uses this, but it ruins the display of normal `$` in text:
|
||||
// {left: "$", right: "$", display: false}
|
||||
],
|
||||
|
||||
ignoredTags: [
|
||||
"script", "noscript", "style", "textarea", "pre", "code"
|
||||
]
|
||||
};
|
||||
|
||||
var extend = function(obj) {
|
||||
// Adapted from underscore.js' `_.extend`. See LICENSE.txt for license.
|
||||
var source, prop;
|
||||
for (var i = 1, length = arguments.length; i < length; i++) {
|
||||
source = arguments[i];
|
||||
for (prop in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, prop)) {
|
||||
obj[prop] = source[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
var renderMathInElement = function(elem, options) {
|
||||
if (!elem) {
|
||||
throw new Error("No element provided to render");
|
||||
}
|
||||
|
||||
options = extend({}, defaultOptions, options);
|
||||
|
||||
renderElem(elem, options.delimiters, options.ignoredTags);
|
||||
};
|
||||
|
||||
module.exports = renderMathInElement;
|
47
contrib/auto-render/index.html
Normal file
47
contrib/auto-render/index.html
Normal file
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Auto-render test</title>
|
||||
<script src="/katex.js" type="text/javascript"></script>
|
||||
<link href="/katex.css" rel="stylesheet" type="text/css">
|
||||
<script src="./auto-render.js" type="text/javascript"></script>
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
font-size: 72px;
|
||||
}
|
||||
|
||||
#test > .blue {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="test">
|
||||
This is some text $math \frac12$ other text
|
||||
<span class="blue">
|
||||
Other node \[ displaymath \frac{1}{2} \] blah $$ \int_2^3 $$
|
||||
</span>
|
||||
and some <!-- comment --> more text \(and math\) blah. And $math with a
|
||||
\$ sign$.
|
||||
<pre>
|
||||
Stuff in a $pre tag$
|
||||
</pre>
|
||||
</div>
|
||||
<script>
|
||||
renderMathInElement(
|
||||
document.getElementById("test"),
|
||||
{
|
||||
delimiters: [
|
||||
{left: "$$", right: "$$", display: true},
|
||||
{left: "\\[", right: "\\]", display: true},
|
||||
{left: "$", right: "$", display: false},
|
||||
{left: "\\(", right: "\\)", display: false}
|
||||
]
|
||||
}
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
98
contrib/auto-render/splitAtDelimiters.js
Normal file
98
contrib/auto-render/splitAtDelimiters.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
var findEndOfMath = function(delimiter, text, startIndex) {
|
||||
// Adapted from
|
||||
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
||||
var index = startIndex;
|
||||
var braceLevel = 0;
|
||||
|
||||
var delimLength = delimiter.length;
|
||||
|
||||
while (index < text.length) {
|
||||
var character = text[index];
|
||||
|
||||
if (braceLevel <= 0 &&
|
||||
text.slice(index, index + delimLength) === delimiter) {
|
||||
return index;
|
||||
} else if (character === "\\") {
|
||||
index++;
|
||||
} else if (character === "{") {
|
||||
braceLevel++;
|
||||
} else if (character === "}") {
|
||||
braceLevel--;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
var splitAtDelimiters = function(startData, leftDelim, rightDelim, display) {
|
||||
var finalData = [];
|
||||
|
||||
for (var i = 0; i < startData.length; i++) {
|
||||
if (startData[i].type === "text") {
|
||||
var text = startData[i].data;
|
||||
|
||||
var lookingForLeft = true;
|
||||
var currIndex = 0;
|
||||
var nextIndex;
|
||||
|
||||
nextIndex = text.indexOf(leftDelim);
|
||||
if (nextIndex !== -1) {
|
||||
currIndex = nextIndex;
|
||||
finalData.push({
|
||||
type: "text",
|
||||
data: text.slice(0, currIndex)
|
||||
});
|
||||
lookingForLeft = false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (lookingForLeft) {
|
||||
nextIndex = text.indexOf(leftDelim, currIndex);
|
||||
if (nextIndex === -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
finalData.push({
|
||||
type: "text",
|
||||
data: text.slice(currIndex, nextIndex)
|
||||
});
|
||||
|
||||
currIndex = nextIndex;
|
||||
} else {
|
||||
nextIndex = findEndOfMath(
|
||||
rightDelim,
|
||||
text,
|
||||
currIndex + leftDelim.length);
|
||||
if (nextIndex === -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
finalData.push({
|
||||
type: "math",
|
||||
data: text.slice(
|
||||
currIndex + leftDelim.length,
|
||||
nextIndex),
|
||||
display: display
|
||||
});
|
||||
|
||||
currIndex = nextIndex + rightDelim.length;
|
||||
}
|
||||
|
||||
lookingForLeft = !lookingForLeft;
|
||||
}
|
||||
|
||||
finalData.push({
|
||||
type: "text",
|
||||
data: text.slice(currIndex)
|
||||
});
|
||||
} else {
|
||||
finalData.push(startData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return finalData;
|
||||
};
|
||||
|
||||
module.exports = splitAtDelimiters;
|
|
@ -17,7 +17,7 @@
|
|||
"browserify": "~2.29.1",
|
||||
"clean-css": "~2.2.15",
|
||||
"express": "~3.3.3",
|
||||
"jasmine-node": "git://github.com/mhevery/jasmine-node.git#Jasmine2.0",
|
||||
"jasmine-node": "2.0.0-beta4",
|
||||
"jshint": "^2.5.6",
|
||||
"less": "~1.7.5",
|
||||
"uglify-js": "~2.4.15"
|
||||
|
|
53
server.js
53
server.js
|
@ -9,20 +9,33 @@ var app = express();
|
|||
|
||||
app.use(express.logger());
|
||||
|
||||
app.get("/katex.js", function(req, res, next) {
|
||||
var b = browserify();
|
||||
b.add("./katex");
|
||||
var serveBrowserified = function(file, standaloneName) {
|
||||
return function(req, res, next) {
|
||||
var b = browserify();
|
||||
b.add(file);
|
||||
|
||||
var stream = b.bundle({standalone: "katex"});
|
||||
var options = {};
|
||||
if (standaloneName) {
|
||||
options.standalone = standaloneName;
|
||||
}
|
||||
|
||||
var body = "";
|
||||
stream.on("data", function(s) { body += s; });
|
||||
stream.on("error", function(e) { next(e); });
|
||||
stream.on("end", function() {
|
||||
res.setHeader("Content-Type", "text/javascript");
|
||||
res.send(body);
|
||||
});
|
||||
});
|
||||
var stream = b.bundle(options);
|
||||
|
||||
var body = "";
|
||||
stream.on("data", function(s) { body += s; });
|
||||
stream.on("error", function(e) { next(e); });
|
||||
stream.on("end", function() {
|
||||
res.setHeader("Content-Type", "text/javascript");
|
||||
res.send(body);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
app.get("/katex.js", serveBrowserified("./katex", "katex"));
|
||||
app.get("/test/katex-spec.js", serveBrowserified("./test/katex-spec"));
|
||||
app.get("/contrib/auto-render/auto-render.js",
|
||||
serveBrowserified("./contrib/auto-render/auto-render",
|
||||
"renderMathInElement"));
|
||||
|
||||
app.get("/katex.css", function(req, res, next) {
|
||||
fs.readFile("static/katex.less", {encoding: "utf8"}, function(err, data) {
|
||||
|
@ -48,24 +61,10 @@ app.get("/katex.css", function(req, res, next) {
|
|||
});
|
||||
});
|
||||
|
||||
app.get("/test/katex-spec.js", function(req, res, next) {
|
||||
var b = browserify();
|
||||
b.add("./test/katex-spec");
|
||||
|
||||
var stream = b.bundle({});
|
||||
|
||||
var body = "";
|
||||
stream.on("data", function(s) { body += s; });
|
||||
stream.on("error", function(e) { next(e); });
|
||||
stream.on("end", function() {
|
||||
res.setHeader("Content-Type", "text/javascript");
|
||||
res.send(body);
|
||||
});
|
||||
});
|
||||
|
||||
app.use(express.static(path.join(__dirname, "static")));
|
||||
app.use(express.static(path.join(__dirname, "build")));
|
||||
app.use("/test", express.static(path.join(__dirname, "test")));
|
||||
app.use("/contrib", express.static(path.join(__dirname, "contrib")));
|
||||
|
||||
app.use(function(err, req, res, next) {
|
||||
console.error(err.stack);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>KaTeX Test</title>
|
||||
<script src="katex.js" type="text/javascript"></script>
|
||||
<link href="katex.css" rel="stylesheet" type="text/css">
|
||||
|
|
Loading…
Reference in New Issue
Block a user