From 7df5b4bba846f20bd7e506adad8c82f8821a9474 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Fri, 12 Sep 2014 17:59:26 -0700 Subject: [PATCH] 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 --- domTree.js | 102 +++++++++++++++++++++++++++++++++++++++++---- katex.js | 8 +++- test/katex-spec.js | 12 ++++++ utils.js | 33 +++++++++++++++ 4 files changed, 146 insertions(+), 9 deletions(-) diff --git a/domTree.js b/domTree.js index 79a0ee22a..0fb5eb0b4 100644 --- a/domTree.js +++ b/domTree.js @@ -1,7 +1,10 @@ // 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 -// function. They are useful for both storing extra properties on the nodes, as -// well as providing a way to easily work with the DOM. +// extra data. They can then be transformed into real DOM nodes with the toNode +// function or HTML markup using toMarkup. They are useful for both storing +// 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) { classes = classes.slice(); @@ -23,7 +26,7 @@ function span(classes, children, height, depth, maxFontSize, style) { this.style = style || {}; } -span.prototype.toDOM = function() { +span.prototype.toNode = function() { var span = document.createElement("span"); span.className = createClass(this.classes); @@ -35,12 +38,44 @@ span.prototype.toDOM = function() { } for (var i = 0; i < this.children.length; i++) { - span.appendChild(this.children[i].toDOM()); + span.appendChild(this.children[i].toNode()); } return span; }; +span.prototype.toMarkup = function() { + var markup = " 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 += ""; + return markup; + } else { + return escaped; + } +}; + module.exports = { span: span, documentFragment: documentFragment, diff --git a/katex.js b/katex.js index 5f0a660f5..2de9e5662 100644 --- a/katex.js +++ b/katex.js @@ -8,12 +8,18 @@ var process = function(toParse, baseNode) { utils.clearNode(baseNode); var tree = parseTree(toParse); - var node = buildTree(tree).toDOM(); + var node = buildTree(tree).toNode(); baseNode.appendChild(node); }; +var renderToString = function(toParse) { + var tree = parseTree(toParse); + return buildTree(tree).toMarkup(); +}; + module.exports = { process: process, + renderToString: renderToString, ParseError: ParseError }; diff --git a/test/katex-spec.js b/test/katex-spec.js index 4caae2fe8..d03c34a11 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -1,3 +1,4 @@ +var katex = require("../katex"); var buildTree = require("../buildTree"); var parseTree = require("../parseTree"); var ParseError = require("../ParseError"); @@ -986,3 +987,14 @@ describe("A bin builder", function() { 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("": ">", + "<": "<", + "\"": """, + "'": "'" +}; + +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; if (typeof document !== "undefined") { @@ -40,6 +71,8 @@ function clearNode(node) { module.exports = { contains: contains, + escape: escape, + hyphenate: hyphenate, indexOf: indexOf, setTextContent: setTextContent, clearNode: clearNode