Add TeX style support

Test Plan:
`\blue\frac12 + \frac{2(y-z)}{2+\frac1{7+\frac31}} \div \orange{\arctan x^{2+\frac43}_{2}} * 2^{2^{2^2}}` looks reasonable, as does
`\blue\frac12 + \dfrac{2(y-z)}{2+\frac1{7+\frac31}} \div \orange{\arctan x^{2+\frac43}_{2}} * 2^{2^{2^2}}`.

Reviewers: emily

Reviewed By: emily

Differential Revision: http://phabricator.khanacademy.org/D3047
This commit is contained in:
Ben Alpert 2013-07-14 16:55:46 -07:00
parent ed82784cba
commit e472b0ba9d
4 changed files with 178 additions and 179 deletions

View File

@ -229,21 +229,27 @@ Parser.prototype.parseNucleus = function(pos) {
} else {
throw "Parse error: Expected group after '" + nucleus.text + "'";
}
} else if (nucleus.type === "\\dfrac") {
// If this is a dfrac, parse its two arguments and return
} else if (nucleus.type === "\\dfrac" || nucleus.type === "\\frac" ||
nucleus.type === "\\tfrac") {
// If this is a frac, parse its two arguments and return
var numer = this.parseGroup(nucleus.position);
if (numer) {
var denom = this.parseGroup(numer.position);
if (denom) {
return new ParseResult(
new ParseNode("dfrac",
{numer: numer.result, denom: denom.result}),
new ParseNode("frac", {
numer: numer.result,
denom: denom.result,
size: nucleus.type.slice(1)
}),
denom.position);
} else {
throw "Parse error: Expected denominator after '\\dfrac'";
throw "Parse error: Expected denominator after '" +
nucleus.type + "'";
}
} else {
throw "Parse error: Expected numerator after '\\dfrac'"
throw "Parse error: Expected numerator after '" + nucleus.type +
"'";
}
} else if (funcToType[nucleus.type]) {
// Otherwise if this is a no-argument function, find the type it

65
Style.js Normal file
View File

@ -0,0 +1,65 @@
function Style(id, size, cramped) {
this.id = id;
this.size = size;
this.cramped = cramped;
}
Style.prototype.sup = function() {
return styles[sup[this.id]];
};
Style.prototype.sub = function() {
return styles[sub[this.id]];
};
Style.prototype.fracNum = function() {
return styles[fracNum[this.id]];
};
Style.prototype.fracDen = function() {
return styles[fracDen[this.id]];
};
/**
* HTML class name, like "display cramped"
*/
Style.prototype.cls = function() {
return sizeNames[this.size] + (this.cramped ? " cramped" : " uncramped");
};
var D = 0;
var Dc = 1;
var T = 2;
var Tc = 3;
var S = 4;
var Sc = 5;
var SS = 6;
var SSc = 7;
var sizeNames = [
"displaystyle textstyle",
"textstyle",
"scriptstyle",
"scriptscriptstyle"
];
var styles = [
new Style(D, 0, false),
new Style(Dc, 0, true),
new Style(T, 1, false),
new Style(Tc, 1, true),
new Style(S, 2, false),
new Style(Sc, 2, true),
new Style(SS, 3, false),
new Style(SSc, 3, true)
];
var sup = [S, Sc, S, Sc, SS, SSc, SS, SSc];
var sub = [Sc, Sc, Sc, Sc, SSc, SSc, SSc, SSc];
var fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc];
var fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc];
module.exports = {
DISPLAY: styles[D],
TEXT: styles[T],
};

View File

@ -1,14 +1,15 @@
var parseTree = require("./parseTree");
var Style = require("./Style");
var parseTree = require("./parseTree");
var utils = require("./utils");
var buildExpression = function(expression) {
var buildExpression = function(style, expression) {
var groups = [];
for (var i = 0; i < expression.length; i++) {
var group = expression[i];
var prev = i > 0 ? expression[i-1] : null;
groups.push(buildGroup(group, prev));
groups.push(buildGroup(style, group, prev));
};
return groups;
};
@ -26,7 +27,7 @@ var makeSpan = function(className, children) {
return span;
};
var buildGroup = function(group, prev) {
var buildGroup = function(style, group, prev) {
if (group.type === "mathord") {
return makeSpan("mord", [mathit(group.value)]);
} else if (group.type === "textord") {
@ -41,30 +42,56 @@ var buildGroup = function(group, prev) {
} else if (group.type === "rel") {
return makeSpan("mrel", [textit(group.value)]);
} else if (group.type === "sup") {
var sup = makeSpan("msup", [buildGroup(group.value.sup)]);
return makeSpan("mord", [buildGroup(group.value.base), sup]);
var sup = makeSpan("msup " + style.cls(), [
makeSpan(style.sup().cls(), [
buildGroup(style.sup(), group.value.sup)
])
]);
return makeSpan("mord", [buildGroup(style, group.value.base), sup]);
} else if (group.type === "sub") {
var sub = makeSpan("msub", [buildGroup(group.value.sub)]);
return makeSpan("mord", [buildGroup(group.value.base), sub]);
var sub = makeSpan("msub " + style.cls(), [
makeSpan(style.sub().cls(), [
buildGroup(style.sub(), group.value.sub)
])
]);
return makeSpan("mord", [buildGroup(style, group.value.base), sub]);
} else if (group.type === "supsub") {
var sup = makeSpan("msup", [buildGroup(group.value.sup)]);
var sub = makeSpan("msub", [buildGroup(group.value.sub)]);
var sup = makeSpan("msup " + style.sup().cls(), [
buildGroup(style.sup(), group.value.sup)
]);
var sub = makeSpan("msub " + style.sub().cls(), [
buildGroup(style.sub(), group.value.sub)
]);
var supsub = makeSpan("msupsub", [sup, sub]);
var supsub = makeSpan("msupsub " + style.cls(), [sup, sub]);
return makeSpan("mord", [buildGroup(group.value.base), supsub]);
return makeSpan("mord", [buildGroup(style, group.value.base), supsub]);
} else if (group.type === "open") {
return makeSpan("mopen", [textit(group.value)]);
} else if (group.type === "close") {
return makeSpan("mclose", [textit(group.value)]);
} else if (group.type === "dfrac") {
var numer = makeSpan("mfracnum", [makeSpan("", [buildGroup(group.value.numer)])]);
var mid = makeSpan("mfracmid", [makeSpan()]);
var denom = makeSpan("mfracden", [buildGroup(group.value.denom)]);
} else if (group.type === "frac") {
var fstyle = style;
if (group.value.size === "dfrac") {
fstyle = Style.DISPLAY;
} else if (group.value.size === "tfrac") {
fstyle = Style.TEXT;
}
return makeSpan("minner mfrac", [numer, mid, denom]);
var nstyle = fstyle.fracNum();
var dstyle = fstyle.fracDen();
var numer = makeSpan("mfracnum " + nstyle.cls(), [
makeSpan("", [buildGroup(nstyle, group.value.numer)])
]);
var mid = makeSpan("mfracmid", [makeSpan()]);
var denom = makeSpan("mfracden " + dstyle.cls(), [
makeSpan("", [buildGroup(dstyle, group.value.denom)])
]);
return makeSpan("minner mfrac " + fstyle.cls(), [numer, mid, denom]);
} else if (group.type === "color") {
return makeSpan("mord " + group.value.color, [buildGroup(group.value.value)]);
return makeSpan("mord " + group.value.color, [buildGroup(style, group.value.value)]);
} else if (group.type === "spacing") {
if (group.value === "\\ " || group.value === "\\space") {
return makeSpan("mord mspace", [textit(group.value)]);
@ -80,15 +107,15 @@ var buildGroup = function(group, prev) {
return makeSpan("mord mspace " + spacingClassMap[group.value]);
}
} else if (group.type === "llap") {
var inner = makeSpan("", buildExpression(group.value));
return makeSpan("llap", [inner]);
var inner = makeSpan("", buildExpression(style, group.value));
return makeSpan("llap " + style.cls(), [inner]);
} else if (group.type === "rlap") {
var inner = makeSpan("", buildExpression(group.value));
return makeSpan("rlap", [inner]);
var inner = makeSpan("", buildExpression(style, group.value));
return makeSpan("rlap " + style.cls(), [inner]);
} else if (group.type === "punct") {
return makeSpan("mpunct", [textit(group.value)]);
} else if (group.type === "ordgroup") {
return makeSpan("mord", buildExpression(group.value));
return makeSpan("mord " + style.cls(), buildExpression(style, group.value));
} else if (group.type === "namedfn") {
return makeSpan("mop", [textit(group.value.slice(1))]);
} else {
@ -141,11 +168,13 @@ var process = function(toParse, baseElem) {
console.error(e);
return false;
}
var style = Style.TEXT;
var expression = buildExpression(style, tree);
var span = makeSpan(style.cls(), expression);
clearNode(baseElem);
var expression = buildExpression(tree);
for (var i = 0; i < expression.length; i++) {
baseElem.appendChild(expression[i]);
}
baseElem.appendChild(span);
return true;
};

View File

@ -16,7 +16,7 @@ big parens
.mathmathmath {
font: normal 1.21em katex_main;
line-height: 1.4;
line-height: 1.2;
}
.mathit {
@ -24,149 +24,47 @@ big parens
font-style: italic;
}
.mord + .mbin {
margin-left: 0.22222em;
}
.textstyle > .mbin + .minner { margin-left: 0.22222em; }
.textstyle > .mbin + .mop { margin-left: 0.22222em; }
.textstyle > .mbin + .mopen { margin-left: 0.22222em; }
.textstyle > .mbin + .mord { margin-left: 0.22222em; }
.textstyle > .mclose + .mbin { margin-left: 0.22222em; }
.textstyle > .mclose + .minner { margin-left: 0.16667em; }
.mclose + .mop { margin-left: 0.16667em; }
.textstyle > .mclose + .mrel { margin-left: 0.27778em; }
.textstyle > .minner + .mbin { margin-left: 0.22222em; }
.textstyle > .minner + .minner { margin-left: 0.16667em; }
.minner + .mop { margin-left: 0.16667em; }
.textstyle > .minner + .mopen { margin-left: 0.16667em; }
.textstyle > .minner + .mord { margin-left: 0.16667em; }
.textstyle > .minner + .mpunct { margin-left: 0.16667em; }
.textstyle > .minner + .mrel { margin-left: 0.27778em; }
.textstyle > .mop + .minner { margin-left: 0.16667em; }
.mop + .mop { margin-left: 0.16667em; }
.mop + .mord { margin-left: 0.16667em; }
.textstyle > .mop + .mrel { margin-left: 0.27778em; }
.textstyle > .mord + .mbin { margin-left: 0.22222em; }
.textstyle > .mord + .minner { margin-left: 0.16667em; }
.mord + .mop { margin-left: 0.16667em; }
.textstyle > .mord + .mrel { margin-left: 0.27778em; }
.textstyle > .mpunct + .mbin { margin-left: 0.16667em; }
.textstyle > .mpunct + .mclose { margin-left: 0.16667em; }
.textstyle > .mpunct + .minner { margin-left: 0.16667em; }
.textstyle > .mpunct + .mop { margin-left: 0.16667em; }
.textstyle > .mpunct + .mopen { margin-left: 0.16667em; }
.textstyle > .mpunct + .mord { margin-left: 0.16667em; }
.textstyle > .mpunct + .mpunct { margin-left: 0.16667em; }
.textstyle > .mpunct + .mrel { margin-left: 0.16667em; }
.textstyle > .mrel + .minner { margin-left: 0.27778em; }
.textstyle > .mrel + .mop { margin-left: 0.27778em; }
.textstyle > .mrel + .mopen { margin-left: 0.27778em; }
.textstyle > .mrel + .mord { margin-left: 0.27778em; }
.mbin + .mord {
margin-left: 0.22222em;
}
.mbin + .mopen {
margin-left: 0.22222em;
}
.mclose + .mbin {
margin-left: 0.22222em;
}
.mrel + .mord {
margin-left: 0.27778em;
}
.mord + .mrel {
margin-left: 0.27778em;
}
.mrel + .mopen {
margin-left: 0.27778em;
}
.mclose + .mrel {
margin-left: 0.27778em;
}
.mpunct + .mord {
margin-left: 0.16667em;
}
.mpunct + .mbin {
margin-left: 0.16667em;
}
.mpunct + .mrel {
margin-left: 0.16667em;
}
.mpunct + .mopen {
margin-left: 0.16667em;
}
.mpunct + .mclose {
margin-left: 0.16667em;
}
.mpunct + .mpunct {
margin-left: 0.16667em;
}
.minner + .mord {
margin-left: 0.16667em;
}
.minner + .mbin {
margin-left: 0.22222em;
}
.minner + .mrel {
margin-left: 0.27778em;
}
.minner + .mopen {
margin-left: 0.16667em;
}
.minner + .mpunct {
margin-left: 0.16667em;
}
.minner + .minner {
margin-left: 0.16667em;
}
.mord + .minner {
margin-left: 0.16667em;
}
.mbin + .minner {
margin-left: 0.22222em;
}
.mrel + .minner {
margin-left: 0.27778em;
}
.mclose + .minner {
margin-left: 0.16667em;
}
.mpunct + .minner {
margin-left: 0.16667em;
}
.mop + .mord {
margin-left: 0.16667em;
}
.mop + .mop {
margin-left: 0.16667em;
}
.mop + .mrel {
margin-left: 0.27778em;
}
.mop + .minner {
margin-left: 0.16667em;
}
.mord + .mop {
margin-left: 0.16667em;
}
.mbin + .mop {
margin-left: 0.22222em;
}
.mrel + .mop {
margin-left: 0.27778em;
}
.mclose + .mop {
margin-left: 0.16667em;
}
.mpunct + .mop {
margin-left: 0.16667em;
}
.minner + .mop {
margin-left: 0.16667em;
}
.textstyle > .scriptstyle { font-size: 0.66667em; }
.scriptstyle > .scriptscriptstyle { font-size: 0.75em; }
.msub {
vertical-align: bottom;
font-size: 70%;
position: relative;
top: 0.2em;
}
@ -174,7 +72,6 @@ big parens
.msup {
position: relative;
top: -0.5em;
font-size: 70%;
}
.msupsub {
@ -186,13 +83,15 @@ big parens
.msupsub > .msup, .msupsub > .msub {
display: table-row;
vertical-align: baseline;
line-height: 1em;
}
.mfrac {
display: inline-table;
vertical-align: 0.66em;
}
.mfrac { display: inline-table; }
/* TODO(alpert): Where do these numbers come from? */
.mfrac.textstyle.displaystyle { vertical-align: 0.58em; }
.mfrac.textstyle { vertical-align: 0.50em; }
.mfrac.scriptstyle { vertical-align: 0.50em; }
.mfrac.scriptscriptstyle { vertical-align: 0.6em; }
.mfracnum, .mfracmid, .mfracden {
display: table-row;