mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-09 04:58:40 +00:00
Add code for generating HTML
Test Plan: Ran unit tests. Looked at `\blue{\displaystyle \left(\dfrac{a^\sigma}{\sin \theta}\right\Updownarrow \intop_{1/2}^{z^z} \sum_{i=0}^\infty x \,dx}` in Chrome and saw the future in my eyes. Reviewers: emily Reviewed By: emily Subscribers: jessie Differential Revision: http://phabricator.khanacademy.org/D13154
This commit is contained in:
102
domTree.js
102
domTree.js
@@ -1,7 +1,10 @@
|
|||||||
// These objects store the data about the DOM nodes we create, as well as some
|
// These objects store the data about the DOM nodes we create, as well as some
|
||||||
// extra data. They can then be transformed into real DOM nodes with the toDOM
|
// extra data. They can then be transformed into real DOM nodes with the toNode
|
||||||
// function. They are useful for both storing extra properties on the nodes, as
|
// function or HTML markup using toMarkup. They are useful for both storing
|
||||||
// well as providing a way to easily work with the DOM.
|
// extra properties on the nodes, as well as providing a way to easily work
|
||||||
|
// with the DOM.
|
||||||
|
|
||||||
|
var utils = require("./utils");
|
||||||
|
|
||||||
var createClass = function(classes) {
|
var createClass = function(classes) {
|
||||||
classes = classes.slice();
|
classes = classes.slice();
|
||||||
@@ -23,7 +26,7 @@ function span(classes, children, height, depth, maxFontSize, style) {
|
|||||||
this.style = style || {};
|
this.style = style || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
span.prototype.toDOM = function() {
|
span.prototype.toNode = function() {
|
||||||
var span = document.createElement("span");
|
var span = document.createElement("span");
|
||||||
|
|
||||||
span.className = createClass(this.classes);
|
span.className = createClass(this.classes);
|
||||||
@@ -35,12 +38,44 @@ span.prototype.toDOM = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < this.children.length; i++) {
|
for (var i = 0; i < this.children.length; i++) {
|
||||||
span.appendChild(this.children[i].toDOM());
|
span.appendChild(this.children[i].toNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
return span;
|
return span;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
span.prototype.toMarkup = function() {
|
||||||
|
var markup = "<span";
|
||||||
|
|
||||||
|
if (this.classes.length) {
|
||||||
|
markup += " class=\"";
|
||||||
|
markup += utils.escape(createClass(this.classes));
|
||||||
|
markup += "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
var styles = "";
|
||||||
|
|
||||||
|
for (var style in this.style) {
|
||||||
|
if (this.style.hasOwnProperty(style)) {
|
||||||
|
styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (styles) {
|
||||||
|
markup += " style=\"" + utils.escape(styles) + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
markup += ">";
|
||||||
|
|
||||||
|
for (var i = 0; i < this.children.length; i++) {
|
||||||
|
markup += this.children[i].toMarkup();
|
||||||
|
}
|
||||||
|
|
||||||
|
markup += "</span>";
|
||||||
|
|
||||||
|
return markup;
|
||||||
|
};
|
||||||
|
|
||||||
function documentFragment(children, height, depth, maxFontSize) {
|
function documentFragment(children, height, depth, maxFontSize) {
|
||||||
this.children = children || [];
|
this.children = children || [];
|
||||||
this.height = height || 0;
|
this.height = height || 0;
|
||||||
@@ -48,16 +83,26 @@ function documentFragment(children, height, depth, maxFontSize) {
|
|||||||
this.maxFontSize = maxFontSize || 0;
|
this.maxFontSize = maxFontSize || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
documentFragment.prototype.toDOM = function() {
|
documentFragment.prototype.toNode = function() {
|
||||||
var frag = document.createDocumentFragment();
|
var frag = document.createDocumentFragment();
|
||||||
|
|
||||||
for (var i = 0; i < this.children.length; i++) {
|
for (var i = 0; i < this.children.length; i++) {
|
||||||
frag.appendChild(this.children[i].toDOM());
|
frag.appendChild(this.children[i].toNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
return frag;
|
return frag;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
documentFragment.prototype.toMarkup = function() {
|
||||||
|
var markup = "";
|
||||||
|
|
||||||
|
for (var i = 0; i < this.children.length; i++) {
|
||||||
|
markup += this.children[i].toMarkup();
|
||||||
|
}
|
||||||
|
|
||||||
|
return markup;
|
||||||
|
};
|
||||||
|
|
||||||
function symbolNode(value, height, depth, italic, classes, style) {
|
function symbolNode(value, height, depth, italic, classes, style) {
|
||||||
this.value = value || "";
|
this.value = value || "";
|
||||||
this.height = height || 0;
|
this.height = height || 0;
|
||||||
@@ -68,7 +113,7 @@ function symbolNode(value, height, depth, italic, classes, style) {
|
|||||||
this.maxFontSize = 0;
|
this.maxFontSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
symbolNode.prototype.toDOM = function() {
|
symbolNode.prototype.toNode = function() {
|
||||||
var node = document.createTextNode(this.value);
|
var node = document.createTextNode(this.value);
|
||||||
var span = null;
|
var span = null;
|
||||||
|
|
||||||
@@ -97,6 +142,47 @@ symbolNode.prototype.toDOM = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
symbolNode.prototype.toMarkup = function() {
|
||||||
|
// TODO(alpert): More duplication than I'd like from
|
||||||
|
// span.prototype.toMarkup and symbolNode.prototype.toNode...
|
||||||
|
var needsSpan = false;
|
||||||
|
|
||||||
|
var markup = "<span";
|
||||||
|
|
||||||
|
if (this.classes.length) {
|
||||||
|
needsSpan = true;
|
||||||
|
markup += " class=\"";
|
||||||
|
markup += utils.escape(createClass(this.classes));
|
||||||
|
markup += "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
var styles = "";
|
||||||
|
|
||||||
|
if (this.italic > 0) {
|
||||||
|
styles += "margin-right:" + this.italic + "em;";
|
||||||
|
}
|
||||||
|
for (var style in this.style) {
|
||||||
|
if (this.style.hasOwnProperty(style)) {
|
||||||
|
styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (styles) {
|
||||||
|
needsSpan = true;
|
||||||
|
markup += " style=\"" + utils.escape(styles) + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
var escaped = utils.escape(this.value);
|
||||||
|
if (needsSpan) {
|
||||||
|
markup += ">";
|
||||||
|
markup += escaped;
|
||||||
|
markup += "</span>";
|
||||||
|
return markup;
|
||||||
|
} else {
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
span: span,
|
span: span,
|
||||||
documentFragment: documentFragment,
|
documentFragment: documentFragment,
|
||||||
|
8
katex.js
8
katex.js
@@ -8,12 +8,18 @@ var process = function(toParse, baseNode) {
|
|||||||
utils.clearNode(baseNode);
|
utils.clearNode(baseNode);
|
||||||
|
|
||||||
var tree = parseTree(toParse);
|
var tree = parseTree(toParse);
|
||||||
var node = buildTree(tree).toDOM();
|
var node = buildTree(tree).toNode();
|
||||||
|
|
||||||
baseNode.appendChild(node);
|
baseNode.appendChild(node);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var renderToString = function(toParse) {
|
||||||
|
var tree = parseTree(toParse);
|
||||||
|
return buildTree(tree).toMarkup();
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
process: process,
|
process: process,
|
||||||
|
renderToString: renderToString,
|
||||||
ParseError: ParseError
|
ParseError: ParseError
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
var katex = require("../katex");
|
||||||
var buildTree = require("../buildTree");
|
var buildTree = require("../buildTree");
|
||||||
var parseTree = require("../parseTree");
|
var parseTree = require("../parseTree");
|
||||||
var ParseError = require("../ParseError");
|
var ParseError = require("../ParseError");
|
||||||
@@ -986,3 +987,14 @@ describe("A bin builder", function() {
|
|||||||
expect(getBuilt("\\blue{x+}+y")[1].classes).toContain("mord");
|
expect(getBuilt("\\blue{x+}+y")[1].classes).toContain("mord");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("A markup generator", function() {
|
||||||
|
it("marks trees up", function() {
|
||||||
|
// Just a few quick sanity checks here...
|
||||||
|
var markup = katex.renderToString("\\sigma^2");
|
||||||
|
expect(markup.indexOf("<span")).toBe(0);
|
||||||
|
expect(markup).toContain("\u03c3"); // sigma
|
||||||
|
expect(markup).toContain("margin-right");
|
||||||
|
expect(markup).not.toContain("marginRight");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
33
utils.js
33
utils.js
@@ -19,6 +19,37 @@ var contains = function(list, elem) {
|
|||||||
return indexOf(list, elem) !== -1;
|
return indexOf(list, elem) !== -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// hyphenate and escape adapted from Facebook's React under Apache 2 license
|
||||||
|
|
||||||
|
var uppercase = /([A-Z])/g;
|
||||||
|
var hyphenate = function(str) {
|
||||||
|
return str.replace(uppercase, "-$1").toLowerCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
var ESCAPE_LOOKUP = {
|
||||||
|
"&": "&",
|
||||||
|
">": ">",
|
||||||
|
"<": "<",
|
||||||
|
"\"": """,
|
||||||
|
"'": "'"
|
||||||
|
};
|
||||||
|
|
||||||
|
var ESCAPE_REGEX = /[&><"']/g;
|
||||||
|
|
||||||
|
function escaper(match) {
|
||||||
|
return ESCAPE_LOOKUP[match];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes text to prevent scripting attacks.
|
||||||
|
*
|
||||||
|
* @param {*} text Text value to escape.
|
||||||
|
* @return {string} An escaped string.
|
||||||
|
*/
|
||||||
|
function escape(text) {
|
||||||
|
return ('' + text).replace(ESCAPE_REGEX, escaper);
|
||||||
|
}
|
||||||
|
|
||||||
var setTextContent;
|
var setTextContent;
|
||||||
|
|
||||||
if (typeof document !== "undefined") {
|
if (typeof document !== "undefined") {
|
||||||
@@ -40,6 +71,8 @@ function clearNode(node) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
contains: contains,
|
contains: contains,
|
||||||
|
escape: escape,
|
||||||
|
hyphenate: hyphenate,
|
||||||
indexOf: indexOf,
|
indexOf: indexOf,
|
||||||
setTextContent: setTextContent,
|
setTextContent: setTextContent,
|
||||||
clearNode: clearNode
|
clearNode: clearNode
|
||||||
|
Reference in New Issue
Block a user