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:
parent
ed82784cba
commit
e472b0ba9d
18
Parser.js
18
Parser.js
|
@ -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
65
Style.js
Normal 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],
|
||||
};
|
83
katex.js
83
katex.js
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
191
static/katex.css
191
static/katex.css
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user