Merge pull request #573 from kohler/kwork
Fix atom-spacing, add text-mode fonts, and miscellany
|
@ -99,7 +99,7 @@ Options.prototype.withPhantom = function() {
|
||||||
*/
|
*/
|
||||||
Options.prototype.withFont = function(font) {
|
Options.prototype.withFont = function(font) {
|
||||||
return this.extend({
|
return this.extend({
|
||||||
font: font,
|
font: font || this.font,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,8 @@ var mainitLetters = [
|
||||||
* classes to be attached to the node.
|
* classes to be attached to the node.
|
||||||
*
|
*
|
||||||
* TODO: make argument order closer to makeSpan
|
* TODO: make argument order closer to makeSpan
|
||||||
|
* TODO: add a separate argument for math class (e.g. `mop`, `mbin`), which
|
||||||
|
* should if present come first in `classes`.
|
||||||
*/
|
*/
|
||||||
var makeSymbol = function(value, fontFamily, mode, options, classes) {
|
var makeSymbol = function(value, fontFamily, mode, options, classes) {
|
||||||
// Replace the value with its replaced value from symbol.js
|
// Replace the value with its replaced value from symbol.js
|
||||||
|
@ -47,8 +49,12 @@ var makeSymbol = function(value, fontFamily, mode, options, classes) {
|
||||||
|
|
||||||
var symbolNode;
|
var symbolNode;
|
||||||
if (metrics) {
|
if (metrics) {
|
||||||
|
var italic = metrics.italic;
|
||||||
|
if (mode === "text") {
|
||||||
|
italic = 0;
|
||||||
|
}
|
||||||
symbolNode = new domTree.symbolNode(
|
symbolNode = new domTree.symbolNode(
|
||||||
value, metrics.height, metrics.depth, metrics.italic, metrics.skew,
|
value, metrics.height, metrics.depth, italic, metrics.skew,
|
||||||
classes);
|
classes);
|
||||||
} else {
|
} else {
|
||||||
// TODO(emily): Figure out a good way to only print this in development
|
// TODO(emily): Figure out a good way to only print this in development
|
||||||
|
@ -183,6 +189,8 @@ var sizeElementFromChildren = function(elem) {
|
||||||
*
|
*
|
||||||
* TODO: Ensure that `options` is always provided (currently some call sites
|
* TODO: Ensure that `options` is always provided (currently some call sites
|
||||||
* don't pass it).
|
* don't pass it).
|
||||||
|
* TODO: add a separate argument for math class (e.g. `mop`, `mbin`), which
|
||||||
|
* should if present come first in `classes`.
|
||||||
*/
|
*/
|
||||||
var makeSpan = function(classes, children, options) {
|
var makeSpan = function(classes, children, options) {
|
||||||
var span = new domTree.span(classes, children, options);
|
var span = new domTree.span(classes, children, options);
|
||||||
|
@ -422,6 +430,10 @@ var fontMap = {
|
||||||
variant: "normal",
|
variant: "normal",
|
||||||
fontName: "Main-Regular",
|
fontName: "Main-Regular",
|
||||||
},
|
},
|
||||||
|
"textit": {
|
||||||
|
variant: "italic",
|
||||||
|
fontName: "Main-Italic",
|
||||||
|
},
|
||||||
|
|
||||||
// "mathit" is missing because it requires the use of two fonts: Main-Italic
|
// "mathit" is missing because it requires the use of two fonts: Main-Italic
|
||||||
// and Math-Italic. This is handled by a special case in makeOrd which ends
|
// and Math-Italic. This is handled by a special case in makeOrd which ends
|
||||||
|
|
308
src/buildHTML.js
|
@ -21,27 +21,52 @@ var isSpace = function(node) {
|
||||||
return node instanceof domTree.span && node.classes[0] === "mspace";
|
return node instanceof domTree.span && node.classes[0] === "mspace";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Binary atoms (first class `mbin`) change into ordinary atoms (`mord`)
|
||||||
|
// depending on their surroundings. See TeXbook pg. 442-446, Rules 5 and 6,
|
||||||
|
// and the text before Rule 19.
|
||||||
|
|
||||||
|
var isBin = function(node) {
|
||||||
|
return node && node.classes[0] === "mbin";
|
||||||
|
};
|
||||||
|
|
||||||
|
var isBinLeftCanceller = function(node, isRealGroup) {
|
||||||
|
// TODO: This code assumes that a node's math class is the first element
|
||||||
|
// of its `classes` array. A later cleanup should ensure this, for
|
||||||
|
// instance by changing the signature of `makeSpan`.
|
||||||
|
if (node) {
|
||||||
|
return utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
|
||||||
|
node.classes[0]);
|
||||||
|
} else {
|
||||||
|
return isRealGroup;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var isBinRightCanceller = function(node, isRealGroup) {
|
||||||
|
if (node) {
|
||||||
|
return utils.contains(["mrel", "mclose", "mpunct"], node.classes[0]);
|
||||||
|
} else {
|
||||||
|
return isRealGroup;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take a list of nodes, build them in order, and return a list of the built
|
* Take a list of nodes, build them in order, and return a list of the built
|
||||||
* nodes. This function handles the `prev` node correctly, and passes the
|
* nodes. documentFragments are flattened into their contents, so the
|
||||||
* previous element from the list as the prev of the next element, ignoring
|
* returned list contains no fragments. `isRealGroup` is true if `expression`
|
||||||
* spaces. documentFragments are flattened into their contents, so the
|
* is a real group (no atoms will be added on either side), as opposed to
|
||||||
* returned list contains no fragments.
|
* a partial group (e.g. one created by \color).
|
||||||
*/
|
*/
|
||||||
var buildExpression = function(expression, options, prev) {
|
var buildExpression = function(expression, options, isRealGroup) {
|
||||||
// Parse expressions into `groups`.
|
// Parse expressions into `groups`.
|
||||||
var groups = [];
|
var groups = [];
|
||||||
for (var i = 0; i < expression.length; i++) {
|
for (var i = 0; i < expression.length; i++) {
|
||||||
var group = expression[i];
|
var group = expression[i];
|
||||||
var output = buildGroup(group, options, prev);
|
var output = buildGroup(group, options);
|
||||||
if (output instanceof domTree.documentFragment) {
|
if (output instanceof domTree.documentFragment) {
|
||||||
Array.prototype.push.apply(groups, output.children);
|
Array.prototype.push.apply(groups, output.children);
|
||||||
} else {
|
} else {
|
||||||
groups.push(output);
|
groups.push(output);
|
||||||
}
|
}
|
||||||
if (!isSpace(output)) {
|
|
||||||
prev = group;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// At this point `groups` consists entirely of `symbolNode`s and `span`s.
|
// At this point `groups` consists entirely of `symbolNode`s and `span`s.
|
||||||
|
|
||||||
|
@ -68,69 +93,32 @@ var buildExpression = function(expression, options, prev) {
|
||||||
Array.prototype.push.apply(groups, spaces);
|
Array.prototype.push.apply(groups, spaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Binary operators change to ordinary symbols in some contexts.
|
||||||
|
for (i = 0; i < groups.length; i++) {
|
||||||
|
if (isBin(groups[i])
|
||||||
|
&& (isBinLeftCanceller(groups[i - 1], isRealGroup)
|
||||||
|
|| isBinRightCanceller(groups[i + 1], isRealGroup))) {
|
||||||
|
groups[i].classes[0] = "mord";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return groups;
|
return groups;
|
||||||
};
|
};
|
||||||
|
|
||||||
// List of types used by getTypeOfGroup,
|
// Return math atom class (mclass) of a domTree.
|
||||||
// see https://github.com/Khan/KaTeX/wiki/Examining-TeX#group-types
|
var getTypeOfDomTree = function(node) {
|
||||||
var groupToType = {
|
if (node instanceof domTree.documentFragment) {
|
||||||
mathord: "mord",
|
if (node.children.length) {
|
||||||
textord: "mord",
|
return getTypeOfDomTree(
|
||||||
bin: "mbin",
|
node.children[node.children.length - 1]);
|
||||||
rel: "mrel",
|
|
||||||
text: "mord",
|
|
||||||
open: "mopen",
|
|
||||||
close: "mclose",
|
|
||||||
inner: "minner",
|
|
||||||
genfrac: "mord",
|
|
||||||
array: "mord",
|
|
||||||
spacing: "mord",
|
|
||||||
punct: "mpunct",
|
|
||||||
ordgroup: "mord",
|
|
||||||
op: "mop",
|
|
||||||
katex: "mord",
|
|
||||||
overline: "mord",
|
|
||||||
underline: "mord",
|
|
||||||
rule: "mord",
|
|
||||||
leftright: "minner",
|
|
||||||
sqrt: "mord",
|
|
||||||
accent: "mord",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the final math type of an expression, given its group type. This type is
|
|
||||||
* used to determine spacing between elements, and affects bin elements by
|
|
||||||
* causing them to change depending on what types are around them. This type
|
|
||||||
* must be attached to the outermost node of an element as a CSS class so that
|
|
||||||
* spacing with its surrounding elements works correctly.
|
|
||||||
*
|
|
||||||
* Some elements can be mapped one-to-one from group type to math type, and
|
|
||||||
* those are listed in the `groupToType` table.
|
|
||||||
*
|
|
||||||
* Others (usually elements that wrap around other elements) often have
|
|
||||||
* recursive definitions, and thus call `getTypeOfGroup` on their inner
|
|
||||||
* elements.
|
|
||||||
*/
|
|
||||||
var getTypeOfGroup = function(group) {
|
|
||||||
if (group == null) {
|
|
||||||
// Like when typesetting $^3$
|
|
||||||
return groupToType.mathord;
|
|
||||||
} else if (group.type === "supsub") {
|
|
||||||
return getTypeOfGroup(group.value.base);
|
|
||||||
} else if (group.type === "llap" || group.type === "rlap") {
|
|
||||||
return getTypeOfGroup(group.value);
|
|
||||||
} else if (group.type === "color" || group.type === "sizing"
|
|
||||||
|| group.type === "styling") {
|
|
||||||
// Return type of rightmost element of group.
|
|
||||||
var atoms = group.value.value;
|
|
||||||
return getTypeOfGroup(atoms[atoms.length - 1]);
|
|
||||||
} else if (group.type === "font") {
|
|
||||||
return getTypeOfGroup(group.value.body);
|
|
||||||
} else if (group.type === "delimsizing") {
|
|
||||||
return groupToType[group.value.delimType];
|
|
||||||
} else {
|
|
||||||
return groupToType[group.type];
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (utils.contains(["mord", "mop", "mbin", "mrel", "mopen", "mclose",
|
||||||
|
"mpunct", "minner"], node.classes[0])) {
|
||||||
|
return node.classes[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -201,12 +189,11 @@ var isCharacterBox = function(group) {
|
||||||
baseElem.type === "punct";
|
baseElem.type === "punct";
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeNullDelimiter = function(options) {
|
var makeNullDelimiter = function(options, classes) {
|
||||||
return makeSpan([
|
return makeSpan(classes.concat([
|
||||||
"sizing", "reset-" + options.size, "size5",
|
"sizing", "reset-" + options.size, "size5",
|
||||||
options.style.reset(), Style.TEXT.cls(),
|
options.style.reset(), Style.TEXT.cls(),
|
||||||
"nulldelimiter",
|
"nulldelimiter"]));
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -215,81 +202,70 @@ var makeNullDelimiter = function(options) {
|
||||||
*/
|
*/
|
||||||
var groupTypes = {};
|
var groupTypes = {};
|
||||||
|
|
||||||
groupTypes.mathord = function(group, options, prev) {
|
groupTypes.mathord = function(group, options) {
|
||||||
return buildCommon.makeOrd(group, options, "mathord");
|
return buildCommon.makeOrd(group, options, "mathord");
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.textord = function(group, options, prev) {
|
groupTypes.textord = function(group, options) {
|
||||||
return buildCommon.makeOrd(group, options, "textord");
|
return buildCommon.makeOrd(group, options, "textord");
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.bin = function(group, options, prev) {
|
groupTypes.bin = function(group, options) {
|
||||||
var className = "mbin";
|
|
||||||
// Pull out the most recent element. Do some special handling to find
|
|
||||||
// things at the end of a \color group. Note that we don't use the same
|
|
||||||
// logic for ordgroups (which count as ords).
|
|
||||||
var prevAtom = prev;
|
|
||||||
while (prevAtom && prevAtom.type === "color") {
|
|
||||||
var atoms = prevAtom.value.value;
|
|
||||||
prevAtom = atoms[atoms.length - 1];
|
|
||||||
}
|
|
||||||
// See TeXbook pg. 442-446, Rules 5 and 6, and the text before Rule 19.
|
|
||||||
// Here, we determine whether the bin should turn into an ord. We
|
|
||||||
// currently only apply Rule 5.
|
|
||||||
if (!prev || utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
|
|
||||||
getTypeOfGroup(prevAtom))) {
|
|
||||||
group.type = "textord";
|
|
||||||
className = "mord";
|
|
||||||
}
|
|
||||||
|
|
||||||
return buildCommon.mathsym(
|
return buildCommon.mathsym(
|
||||||
group.value, group.mode, options, [className]);
|
group.value, group.mode, options, ["mbin"]);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.rel = function(group, options, prev) {
|
groupTypes.rel = function(group, options) {
|
||||||
return buildCommon.mathsym(
|
return buildCommon.mathsym(
|
||||||
group.value, group.mode, options, ["mrel"]);
|
group.value, group.mode, options, ["mrel"]);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.open = function(group, options, prev) {
|
groupTypes.open = function(group, options) {
|
||||||
return buildCommon.mathsym(
|
return buildCommon.mathsym(
|
||||||
group.value, group.mode, options, ["mopen"]);
|
group.value, group.mode, options, ["mopen"]);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.close = function(group, options, prev) {
|
groupTypes.close = function(group, options) {
|
||||||
return buildCommon.mathsym(
|
return buildCommon.mathsym(
|
||||||
group.value, group.mode, options, ["mclose"]);
|
group.value, group.mode, options, ["mclose"]);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.inner = function(group, options, prev) {
|
groupTypes.inner = function(group, options) {
|
||||||
return buildCommon.mathsym(
|
return buildCommon.mathsym(
|
||||||
group.value, group.mode, options, ["minner"]);
|
group.value, group.mode, options, ["minner"]);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.punct = function(group, options, prev) {
|
groupTypes.punct = function(group, options) {
|
||||||
return buildCommon.mathsym(
|
return buildCommon.mathsym(
|
||||||
group.value, group.mode, options, ["mpunct"]);
|
group.value, group.mode, options, ["mpunct"]);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.ordgroup = function(group, options, prev) {
|
groupTypes.ordgroup = function(group, options) {
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mord", options.style.cls()],
|
["mord", options.style.cls()],
|
||||||
buildExpression(group.value, options.reset()),
|
buildExpression(group.value, options.reset(), true),
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.text = function(group, options, prev) {
|
groupTypes.text = function(group, options) {
|
||||||
return makeSpan(["mord", "text", options.style.cls()],
|
var newOptions = options.withFont(group.value.style);
|
||||||
buildExpression(group.value.body, options.reset()),
|
var inner = buildExpression(group.value.body, newOptions, true);
|
||||||
options);
|
for (var i = 0; i < inner.length - 1; i++) {
|
||||||
|
if (inner[i].tryCombine(inner[i + 1])) {
|
||||||
|
inner.splice(i + 1, 1);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return makeSpan(["mord", "text", newOptions.style.cls()],
|
||||||
|
inner, newOptions);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.color = function(group, options, prev) {
|
groupTypes.color = function(group, options) {
|
||||||
var elements = buildExpression(
|
var elements = buildExpression(
|
||||||
group.value.value,
|
group.value.value,
|
||||||
options.withColor(group.value.color),
|
options.withColor(group.value.color),
|
||||||
prev
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
// \color isn't supposed to affect the type of the elements it contains.
|
// \color isn't supposed to affect the type of the elements it contains.
|
||||||
|
@ -299,14 +275,14 @@ groupTypes.color = function(group, options, prev) {
|
||||||
return new buildCommon.makeFragment(elements);
|
return new buildCommon.makeFragment(elements);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.supsub = function(group, options, prev) {
|
groupTypes.supsub = function(group, options) {
|
||||||
// Superscript and subscripts are handled in the TeXbook on page
|
// Superscript and subscripts are handled in the TeXbook on page
|
||||||
// 445-446, rules 18(a-f).
|
// 445-446, rules 18(a-f).
|
||||||
|
|
||||||
// Here is where we defer to the inner group if it should handle
|
// Here is where we defer to the inner group if it should handle
|
||||||
// superscripts and subscripts itself.
|
// superscripts and subscripts itself.
|
||||||
if (shouldHandleSupSub(group.value.base, options)) {
|
if (shouldHandleSupSub(group.value.base, options)) {
|
||||||
return groupTypes[group.value.base.type](group, options, prev);
|
return groupTypes[group.value.base.type](group, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
var base = buildGroup(group.value.base, options.reset());
|
var base = buildGroup(group.value.base, options.reset());
|
||||||
|
@ -422,12 +398,13 @@ groupTypes.supsub = function(group, options, prev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We ensure to wrap the supsub vlist in a span.msupsub to reset text-align
|
// We ensure to wrap the supsub vlist in a span.msupsub to reset text-align
|
||||||
return makeSpan([getTypeOfGroup(group.value.base)],
|
var mclass = getTypeOfDomTree(base) || "mord";
|
||||||
|
return makeSpan([mclass],
|
||||||
[base, makeSpan(["msupsub"], [supsub])],
|
[base, makeSpan(["msupsub"], [supsub])],
|
||||||
options);
|
options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.genfrac = function(group, options, prev) {
|
groupTypes.genfrac = function(group, options) {
|
||||||
// Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
|
// Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
|
||||||
// Figure out what style this fraction should be in based on the
|
// Figure out what style this fraction should be in based on the
|
||||||
// function used
|
// function used
|
||||||
|
@ -546,18 +523,18 @@ groupTypes.genfrac = function(group, options, prev) {
|
||||||
var leftDelim;
|
var leftDelim;
|
||||||
var rightDelim;
|
var rightDelim;
|
||||||
if (group.value.leftDelim == null) {
|
if (group.value.leftDelim == null) {
|
||||||
leftDelim = makeNullDelimiter(options);
|
leftDelim = makeNullDelimiter(options, ["mopen"]);
|
||||||
} else {
|
} else {
|
||||||
leftDelim = delimiter.customSizedDelim(
|
leftDelim = delimiter.customSizedDelim(
|
||||||
group.value.leftDelim, delimSize, true,
|
group.value.leftDelim, delimSize, true,
|
||||||
options.withStyle(style), group.mode);
|
options.withStyle(style), group.mode, ["mopen"]);
|
||||||
}
|
}
|
||||||
if (group.value.rightDelim == null) {
|
if (group.value.rightDelim == null) {
|
||||||
rightDelim = makeNullDelimiter(options);
|
rightDelim = makeNullDelimiter(options, ["mclose"]);
|
||||||
} else {
|
} else {
|
||||||
rightDelim = delimiter.customSizedDelim(
|
rightDelim = delimiter.customSizedDelim(
|
||||||
group.value.rightDelim, delimSize, true,
|
group.value.rightDelim, delimSize, true,
|
||||||
options.withStyle(style), group.mode);
|
options.withStyle(style), group.mode, ["mclose"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
|
@ -566,7 +543,7 @@ groupTypes.genfrac = function(group, options, prev) {
|
||||||
options);
|
options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.array = function(group, options, prev) {
|
groupTypes.array = function(group, options) {
|
||||||
var r;
|
var r;
|
||||||
var c;
|
var c;
|
||||||
var nr = group.value.body.length;
|
var nr = group.value.body.length;
|
||||||
|
@ -730,42 +707,46 @@ groupTypes.array = function(group, options, prev) {
|
||||||
return makeSpan(["mord"], [body], options);
|
return makeSpan(["mord"], [body], options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.spacing = function(group, options, prev) {
|
groupTypes.spacing = function(group, options) {
|
||||||
if (group.value === "\\ " || group.value === "\\space" ||
|
if (group.value === "\\ " || group.value === "\\space" ||
|
||||||
group.value === " " || group.value === "~") {
|
group.value === " " || group.value === "~") {
|
||||||
// Spaces are generated by adding an actual space. Each of these
|
// Spaces are generated by adding an actual space. Each of these
|
||||||
// things has an entry in the symbols table, so these will be turned
|
// things has an entry in the symbols table, so these will be turned
|
||||||
// into appropriate outputs.
|
// into appropriate outputs.
|
||||||
return makeSpan(
|
if (group.mode === "text") {
|
||||||
["mspace"],
|
return buildCommon.makeOrd(group, options, "textord");
|
||||||
[buildCommon.mathsym(group.value, group.mode)]
|
} else {
|
||||||
);
|
return makeSpan(["mspace"],
|
||||||
|
[buildCommon.mathsym(group.value, group.mode, options)],
|
||||||
|
options);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Other kinds of spaces are of arbitrary width. We use CSS to
|
// Other kinds of spaces are of arbitrary width. We use CSS to
|
||||||
// generate these.
|
// generate these.
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["mspace",
|
["mspace",
|
||||||
buildCommon.spacingFunctions[group.value].className]);
|
buildCommon.spacingFunctions[group.value].className],
|
||||||
|
[], options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.llap = function(group, options, prev) {
|
groupTypes.llap = function(group, options) {
|
||||||
var inner = makeSpan(
|
var inner = makeSpan(
|
||||||
["inner"], [buildGroup(group.value.body, options.reset())]);
|
["inner"], [buildGroup(group.value.body, options.reset())]);
|
||||||
var fix = makeSpan(["fix"], []);
|
var fix = makeSpan(["fix"], []);
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["llap", options.style.cls()], [inner, fix], options);
|
["mord", "llap", options.style.cls()], [inner, fix], options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.rlap = function(group, options, prev) {
|
groupTypes.rlap = function(group, options) {
|
||||||
var inner = makeSpan(
|
var inner = makeSpan(
|
||||||
["inner"], [buildGroup(group.value.body, options.reset())]);
|
["inner"], [buildGroup(group.value.body, options.reset())]);
|
||||||
var fix = makeSpan(["fix"], []);
|
var fix = makeSpan(["fix"], []);
|
||||||
return makeSpan(
|
return makeSpan(
|
||||||
["rlap", options.style.cls()], [inner, fix], options);
|
["mord", "rlap", options.style.cls()], [inner, fix], options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.op = function(group, options, prev) {
|
groupTypes.op = function(group, options) {
|
||||||
// Operators are handled in the TeXbook pg. 443-444, rule 13(a).
|
// Operators are handled in the TeXbook pg. 443-444, rule 13(a).
|
||||||
var supGroup;
|
var supGroup;
|
||||||
var subGroup;
|
var subGroup;
|
||||||
|
@ -816,6 +797,11 @@ groupTypes.op = function(group, options, prev) {
|
||||||
|
|
||||||
// The slant of the symbol is just its italic correction.
|
// The slant of the symbol is just its italic correction.
|
||||||
slant = base.italic;
|
slant = base.italic;
|
||||||
|
} else if (group.value.value) {
|
||||||
|
// If this is a list, compose that list.
|
||||||
|
var inner = buildExpression(group.value.value, options, true);
|
||||||
|
|
||||||
|
base = makeSpan(["mop"], inner, options);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, this is a text operator. Build the text from the
|
// Otherwise, this is a text operator. Build the text from the
|
||||||
// operator's name.
|
// operator's name.
|
||||||
|
@ -930,7 +916,7 @@ groupTypes.op = function(group, options, prev) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.katex = function(group, options, prev) {
|
groupTypes.katex = function(group, options) {
|
||||||
// The KaTeX logo. The offsets for the K and a were chosen to look
|
// The KaTeX logo. The offsets for the K and a were chosen to look
|
||||||
// good, but the offsets for the T, E, and X were taken from the
|
// good, but the offsets for the T, E, and X were taken from the
|
||||||
// definition of \TeX in TeX (see TeXbook pg. 356)
|
// definition of \TeX in TeX (see TeXbook pg. 356)
|
||||||
|
@ -957,7 +943,7 @@ groupTypes.katex = function(group, options, prev) {
|
||||||
["mord", "katex-logo"], [k, a, t, e, x], options);
|
["mord", "katex-logo"], [k, a, t, e, x], options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.overline = function(group, options, prev) {
|
groupTypes.overline = function(group, options) {
|
||||||
// Overlines are handled in the TeXbook pg 443, Rule 9.
|
// Overlines are handled in the TeXbook pg 443, Rule 9.
|
||||||
var style = options.style;
|
var style = options.style;
|
||||||
|
|
||||||
|
@ -985,7 +971,7 @@ groupTypes.overline = function(group, options, prev) {
|
||||||
return makeSpan(["mord", "overline"], [vlist], options);
|
return makeSpan(["mord", "overline"], [vlist], options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.underline = function(group, options, prev) {
|
groupTypes.underline = function(group, options) {
|
||||||
// Underlines are handled in the TeXbook pg 443, Rule 10.
|
// Underlines are handled in the TeXbook pg 443, Rule 10.
|
||||||
var style = options.style;
|
var style = options.style;
|
||||||
|
|
||||||
|
@ -1011,7 +997,7 @@ groupTypes.underline = function(group, options, prev) {
|
||||||
return makeSpan(["mord", "underline"], [vlist], options);
|
return makeSpan(["mord", "underline"], [vlist], options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.sqrt = function(group, options, prev) {
|
groupTypes.sqrt = function(group, options) {
|
||||||
// Square roots are handled in the TeXbook pg. 443, Rule 11.
|
// Square roots are handled in the TeXbook pg. 443, Rule 11.
|
||||||
var style = options.style;
|
var style = options.style;
|
||||||
|
|
||||||
|
@ -1110,12 +1096,12 @@ groupTypes.sqrt = function(group, options, prev) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.sizing = function(group, options, prev) {
|
groupTypes.sizing = function(group, options) {
|
||||||
// Handle sizing operators like \Huge. Real TeX doesn't actually allow
|
// Handle sizing operators like \Huge. Real TeX doesn't actually allow
|
||||||
// these functions inside of math expressions, so we do some special
|
// these functions inside of math expressions, so we do some special
|
||||||
// handling.
|
// handling.
|
||||||
var inner = buildExpression(group.value.value,
|
var inner = buildExpression(group.value.value,
|
||||||
options.withSize(group.value.size), prev);
|
options.withSize(group.value.size), false);
|
||||||
|
|
||||||
// Compute the correct maxFontSize.
|
// Compute the correct maxFontSize.
|
||||||
var style = options.style;
|
var style = options.style;
|
||||||
|
@ -1141,7 +1127,7 @@ groupTypes.sizing = function(group, options, prev) {
|
||||||
return buildCommon.makeFragment(inner);
|
return buildCommon.makeFragment(inner);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.styling = function(group, options, prev) {
|
groupTypes.styling = function(group, options) {
|
||||||
// Style changes are handled in the TeXbook on pg. 442, Rule 3.
|
// Style changes are handled in the TeXbook on pg. 442, Rule 3.
|
||||||
|
|
||||||
// Figure out what style we're changing to.
|
// Figure out what style we're changing to.
|
||||||
|
@ -1157,7 +1143,7 @@ groupTypes.styling = function(group, options, prev) {
|
||||||
|
|
||||||
// Build the inner expression in the new style.
|
// Build the inner expression in the new style.
|
||||||
var inner = buildExpression(
|
var inner = buildExpression(
|
||||||
group.value.value, newOptions, prev);
|
group.value.value, newOptions, false);
|
||||||
|
|
||||||
// Add style-resetting classes to the inner list. Handle nested changes.
|
// Add style-resetting classes to the inner list. Handle nested changes.
|
||||||
for (var i = 0; i < inner.length; i++) {
|
for (var i = 0; i < inner.length; i++) {
|
||||||
|
@ -1174,31 +1160,29 @@ groupTypes.styling = function(group, options, prev) {
|
||||||
return new buildCommon.makeFragment(inner);
|
return new buildCommon.makeFragment(inner);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.font = function(group, options, prev) {
|
groupTypes.font = function(group, options) {
|
||||||
var font = group.value.font;
|
var font = group.value.font;
|
||||||
return buildGroup(group.value.body, options.withFont(font), prev);
|
return buildGroup(group.value.body, options.withFont(font));
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.delimsizing = function(group, options, prev) {
|
groupTypes.delimsizing = function(group, options) {
|
||||||
var delim = group.value.value;
|
var delim = group.value.value;
|
||||||
|
|
||||||
if (delim === ".") {
|
if (delim === ".") {
|
||||||
// Empty delimiters still count as elements, even though they don't
|
// Empty delimiters still count as elements, even though they don't
|
||||||
// show anything.
|
// show anything.
|
||||||
return makeSpan([groupToType[group.value.delimType]]);
|
return makeSpan([group.value.mclass]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use delimiter.sizedDelim to generate the delimiter.
|
// Use delimiter.sizedDelim to generate the delimiter.
|
||||||
return makeSpan(
|
return delimiter.sizedDelim(
|
||||||
[groupToType[group.value.delimType]],
|
delim, group.value.size, options, group.mode,
|
||||||
[delimiter.sizedDelim(
|
[group.value.mclass]);
|
||||||
delim, group.value.size, options, group.mode)],
|
|
||||||
options);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.leftright = function(group, options, prev) {
|
groupTypes.leftright = function(group, options) {
|
||||||
// Build the inner expression
|
// Build the inner expression
|
||||||
var inner = buildExpression(group.value.body, options.reset());
|
var inner = buildExpression(group.value.body, options.reset(), true);
|
||||||
|
|
||||||
var innerHeight = 0;
|
var innerHeight = 0;
|
||||||
var innerDepth = 0;
|
var innerDepth = 0;
|
||||||
|
@ -1220,13 +1204,13 @@ groupTypes.leftright = function(group, options, prev) {
|
||||||
var leftDelim;
|
var leftDelim;
|
||||||
if (group.value.left === ".") {
|
if (group.value.left === ".") {
|
||||||
// Empty delimiters in \left and \right make null delimiter spaces.
|
// Empty delimiters in \left and \right make null delimiter spaces.
|
||||||
leftDelim = makeNullDelimiter(options);
|
leftDelim = makeNullDelimiter(options, ["mopen"]);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, use leftRightDelim to generate the correct sized
|
// Otherwise, use leftRightDelim to generate the correct sized
|
||||||
// delimiter.
|
// delimiter.
|
||||||
leftDelim = delimiter.leftRightDelim(
|
leftDelim = delimiter.leftRightDelim(
|
||||||
group.value.left, innerHeight, innerDepth, options,
|
group.value.left, innerHeight, innerDepth, options,
|
||||||
group.mode);
|
group.mode, ["mopen"]);
|
||||||
}
|
}
|
||||||
// Add it to the beginning of the expression
|
// Add it to the beginning of the expression
|
||||||
inner.unshift(leftDelim);
|
inner.unshift(leftDelim);
|
||||||
|
@ -1234,11 +1218,11 @@ groupTypes.leftright = function(group, options, prev) {
|
||||||
var rightDelim;
|
var rightDelim;
|
||||||
// Same for the right delimiter
|
// Same for the right delimiter
|
||||||
if (group.value.right === ".") {
|
if (group.value.right === ".") {
|
||||||
rightDelim = makeNullDelimiter(options);
|
rightDelim = makeNullDelimiter(options, ["mclose"]);
|
||||||
} else {
|
} else {
|
||||||
rightDelim = delimiter.leftRightDelim(
|
rightDelim = delimiter.leftRightDelim(
|
||||||
group.value.right, innerHeight, innerDepth, options,
|
group.value.right, innerHeight, innerDepth, options,
|
||||||
group.mode);
|
group.mode, ["mclose"]);
|
||||||
}
|
}
|
||||||
// Add it to the end of the expression.
|
// Add it to the end of the expression.
|
||||||
inner.push(rightDelim);
|
inner.push(rightDelim);
|
||||||
|
@ -1247,7 +1231,7 @@ groupTypes.leftright = function(group, options, prev) {
|
||||||
["minner", style.cls()], inner, options);
|
["minner", style.cls()], inner, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.rule = function(group, options, prev) {
|
groupTypes.rule = function(group, options) {
|
||||||
// Make an empty span for the rule
|
// Make an empty span for the rule
|
||||||
var rule = makeSpan(["mord", "rule"], [], options);
|
var rule = makeSpan(["mord", "rule"], [], options);
|
||||||
var style = options.style;
|
var style = options.style;
|
||||||
|
@ -1290,7 +1274,7 @@ groupTypes.rule = function(group, options, prev) {
|
||||||
return rule;
|
return rule;
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.kern = function(group, options, prev) {
|
groupTypes.kern = function(group, options) {
|
||||||
// Make an empty span for the rule
|
// Make an empty span for the rule
|
||||||
var rule = makeSpan(["mord", "rule"], [], options);
|
var rule = makeSpan(["mord", "rule"], [], options);
|
||||||
var style = options.style;
|
var style = options.style;
|
||||||
|
@ -1310,7 +1294,7 @@ groupTypes.kern = function(group, options, prev) {
|
||||||
return rule;
|
return rule;
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.accent = function(group, options, prev) {
|
groupTypes.accent = function(group, options) {
|
||||||
// Accents are handled in the TeXbook pg. 443, rule 12.
|
// Accents are handled in the TeXbook pg. 443, rule 12.
|
||||||
var base = group.value.base;
|
var base = group.value.base;
|
||||||
var style = options.style;
|
var style = options.style;
|
||||||
|
@ -1337,7 +1321,7 @@ groupTypes.accent = function(group, options, prev) {
|
||||||
// Rerender the supsub group with its new base, and store that
|
// Rerender the supsub group with its new base, and store that
|
||||||
// result.
|
// result.
|
||||||
supsubGroup = buildGroup(
|
supsubGroup = buildGroup(
|
||||||
supsub, options.reset(), prev);
|
supsub, options.reset());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the base group
|
// Build the base group
|
||||||
|
@ -1419,11 +1403,11 @@ groupTypes.accent = function(group, options, prev) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.phantom = function(group, options, prev) {
|
groupTypes.phantom = function(group, options) {
|
||||||
var elements = buildExpression(
|
var elements = buildExpression(
|
||||||
group.value.value,
|
group.value.value,
|
||||||
options.withPhantom(),
|
options.withPhantom(),
|
||||||
prev
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
// \phantom isn't supposed to affect the elements it contains.
|
// \phantom isn't supposed to affect the elements it contains.
|
||||||
|
@ -1431,19 +1415,25 @@ groupTypes.phantom = function(group, options, prev) {
|
||||||
return new buildCommon.makeFragment(elements);
|
return new buildCommon.makeFragment(elements);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
groupTypes.mclass = function(group, options) {
|
||||||
|
var elements = buildExpression(group.value.value, options, true);
|
||||||
|
|
||||||
|
return makeSpan([group.value.mclass], elements, options);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* buildGroup is the function that takes a group and calls the correct groupType
|
* buildGroup is the function that takes a group and calls the correct groupType
|
||||||
* function for it. It also handles the interaction of size and style changes
|
* function for it. It also handles the interaction of size and style changes
|
||||||
* between parents and children.
|
* between parents and children.
|
||||||
*/
|
*/
|
||||||
var buildGroup = function(group, options, prev) {
|
var buildGroup = function(group, options) {
|
||||||
if (!group) {
|
if (!group) {
|
||||||
return makeSpan();
|
return makeSpan();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupTypes[group.type]) {
|
if (groupTypes[group.type]) {
|
||||||
// Call the groupTypes function
|
// Call the groupTypes function
|
||||||
var groupNode = groupTypes[group.type](group, options, prev);
|
var groupNode = groupTypes[group.type](group, options);
|
||||||
var multiplier;
|
var multiplier;
|
||||||
|
|
||||||
// If the style changed between the parent and the current group,
|
// If the style changed between the parent and the current group,
|
||||||
|
@ -1483,7 +1473,7 @@ var buildHTML = function(tree, options) {
|
||||||
tree = JSON.parse(JSON.stringify(tree));
|
tree = JSON.parse(JSON.stringify(tree));
|
||||||
|
|
||||||
// Build the expression contained in the tree
|
// Build the expression contained in the tree
|
||||||
var expression = buildExpression(tree, options);
|
var expression = buildExpression(tree, options, true);
|
||||||
var body = makeSpan(["base", options.style.cls()], expression, options);
|
var body = makeSpan(["base", options.style.cls()], expression, options);
|
||||||
|
|
||||||
// Add struts, which ensure that the top of the HTML element falls at the
|
// Add struts, which ensure that the top of the HTML element falls at the
|
||||||
|
|
|
@ -316,7 +316,7 @@ groupTypes.spacing = function(group) {
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.op = function(group) {
|
groupTypes.op = function(group, options) {
|
||||||
var node;
|
var node;
|
||||||
|
|
||||||
// TODO(emily): handle big operators using the `largeop` attribute
|
// TODO(emily): handle big operators using the `largeop` attribute
|
||||||
|
@ -325,6 +325,10 @@ groupTypes.op = function(group) {
|
||||||
// This is a symbol. Just add the symbol.
|
// This is a symbol. Just add the symbol.
|
||||||
node = new mathMLTree.MathNode(
|
node = new mathMLTree.MathNode(
|
||||||
"mo", [makeText(group.value.body, group.mode)]);
|
"mo", [makeText(group.value.body, group.mode)]);
|
||||||
|
} else if (group.value.value) {
|
||||||
|
// This is an operator with children. Add them.
|
||||||
|
node = new mathMLTree.MathNode(
|
||||||
|
"mo", buildExpression(group.value.value, options));
|
||||||
} else {
|
} else {
|
||||||
// This is a text operator. Add all of the characters from the
|
// This is a text operator. Add all of the characters from the
|
||||||
// operator's name.
|
// operator's name.
|
||||||
|
@ -358,10 +362,10 @@ groupTypes.delimsizing = function(group) {
|
||||||
|
|
||||||
var node = new mathMLTree.MathNode("mo", children);
|
var node = new mathMLTree.MathNode("mo", children);
|
||||||
|
|
||||||
if (group.value.delimType === "open" ||
|
if (group.value.mclass === "mopen" ||
|
||||||
group.value.delimType === "close") {
|
group.value.mclass === "mclose") {
|
||||||
// Only some of the delimsizing functions act as fences, and they
|
// Only some of the delimsizing functions act as fences, and they
|
||||||
// return "open" or "close" delimTypes.
|
// return "mopen" or "mclose" mclass.
|
||||||
node.setAttribute("fence", "true");
|
node.setAttribute("fence", "true");
|
||||||
} else {
|
} else {
|
||||||
// Explicitly disable fencing if it's not a fence, to override the
|
// Explicitly disable fencing if it's not a fence, to override the
|
||||||
|
@ -470,11 +474,16 @@ groupTypes.rlap = function(group, options) {
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.phantom = function(group, options, prev) {
|
groupTypes.phantom = function(group, options) {
|
||||||
var inner = buildExpression(group.value.value, options);
|
var inner = buildExpression(group.value.value, options);
|
||||||
return new mathMLTree.MathNode("mphantom", inner);
|
return new mathMLTree.MathNode("mphantom", inner);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
groupTypes.mclass = function(group, options) {
|
||||||
|
var inner = buildExpression(group.value.value, options);
|
||||||
|
return new mathMLTree.MathNode("mstyle", inner);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a list of nodes, builds them, and returns a list of the generated
|
* Takes a list of nodes, builds them, and returns a list of the generated
|
||||||
* MathML nodes. A little simpler than the HTML version because we don't do any
|
* MathML nodes. A little simpler than the HTML version because we don't do any
|
||||||
|
|
|
@ -56,9 +56,11 @@ var mathrmSize = function(value, size, mode, options) {
|
||||||
* Puts a delimiter span in a given style, and adds appropriate height, depth,
|
* Puts a delimiter span in a given style, and adds appropriate height, depth,
|
||||||
* and maxFontSizes.
|
* and maxFontSizes.
|
||||||
*/
|
*/
|
||||||
var styleWrap = function(delim, toStyle, options) {
|
var styleWrap = function(delim, toStyle, options, classes) {
|
||||||
|
classes = classes || [];
|
||||||
var span = makeSpan(
|
var span = makeSpan(
|
||||||
["style-wrap", options.style.reset(), toStyle.cls()], [delim], options);
|
classes.concat(["style-wrap", options.style.reset(), toStyle.cls()]),
|
||||||
|
[delim], options);
|
||||||
|
|
||||||
var multiplier = toStyle.sizeMultiplier / options.style.sizeMultiplier;
|
var multiplier = toStyle.sizeMultiplier / options.style.sizeMultiplier;
|
||||||
|
|
||||||
|
@ -74,10 +76,10 @@ var styleWrap = function(delim, toStyle, options) {
|
||||||
* font, but is restyled to either be in textstyle, scriptstyle, or
|
* font, but is restyled to either be in textstyle, scriptstyle, or
|
||||||
* scriptscriptstyle.
|
* scriptscriptstyle.
|
||||||
*/
|
*/
|
||||||
var makeSmallDelim = function(delim, style, center, options, mode) {
|
var makeSmallDelim = function(delim, style, center, options, mode, classes) {
|
||||||
var text = buildCommon.makeSymbol(delim, "Main-Regular", mode, options);
|
var text = buildCommon.makeSymbol(delim, "Main-Regular", mode, options);
|
||||||
|
|
||||||
var span = styleWrap(text, style, options);
|
var span = styleWrap(text, style, options, classes);
|
||||||
|
|
||||||
if (center) {
|
if (center) {
|
||||||
var shift =
|
var shift =
|
||||||
|
@ -96,13 +98,12 @@ var makeSmallDelim = function(delim, style, center, options, mode) {
|
||||||
* Makes a large delimiter. This is a delimiter that comes in the Size1, Size2,
|
* Makes a large delimiter. This is a delimiter that comes in the Size1, Size2,
|
||||||
* Size3, or Size4 fonts. It is always rendered in textstyle.
|
* Size3, or Size4 fonts. It is always rendered in textstyle.
|
||||||
*/
|
*/
|
||||||
var makeLargeDelim = function(delim, size, center, options, mode) {
|
var makeLargeDelim = function(delim, size, center, options, mode, classes) {
|
||||||
var inner = mathrmSize(delim, size, mode, options);
|
var inner = mathrmSize(delim, size, mode, options);
|
||||||
|
|
||||||
var span = styleWrap(
|
var span = styleWrap(
|
||||||
makeSpan(["delimsizing", "size" + size],
|
makeSpan(["delimsizing", "size" + size], [inner], options),
|
||||||
[inner], options),
|
Style.TEXT, options, classes);
|
||||||
Style.TEXT, options);
|
|
||||||
|
|
||||||
if (center) {
|
if (center) {
|
||||||
var shift = (1 - options.style.sizeMultiplier) *
|
var shift = (1 - options.style.sizeMultiplier) *
|
||||||
|
@ -142,7 +143,8 @@ var makeInner = function(symbol, font, mode) {
|
||||||
* Make a stacked delimiter out of a given delimiter, with the total height at
|
* Make a stacked delimiter out of a given delimiter, with the total height at
|
||||||
* least `heightTotal`. This routine is mentioned on page 442 of the TeXbook.
|
* least `heightTotal`. This routine is mentioned on page 442 of the TeXbook.
|
||||||
*/
|
*/
|
||||||
var makeStackedDelim = function(delim, heightTotal, center, options, mode) {
|
var makeStackedDelim = function(delim, heightTotal, center, options, mode,
|
||||||
|
classes) {
|
||||||
// There are four parts, the top, an optional middle, a repeated part, and a
|
// There are four parts, the top, an optional middle, a repeated part, and a
|
||||||
// bottom.
|
// bottom.
|
||||||
var top;
|
var top;
|
||||||
|
@ -320,7 +322,7 @@ var makeStackedDelim = function(delim, heightTotal, center, options, mode) {
|
||||||
|
|
||||||
return styleWrap(
|
return styleWrap(
|
||||||
makeSpan(["delimsizing", "mult"], [inner], options),
|
makeSpan(["delimsizing", "mult"], [inner], options),
|
||||||
Style.TEXT, options);
|
Style.TEXT, options, classes);
|
||||||
};
|
};
|
||||||
|
|
||||||
// There are three kinds of delimiters, delimiters that stack when they become
|
// There are three kinds of delimiters, delimiters that stack when they become
|
||||||
|
@ -354,7 +356,7 @@ var sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
|
||||||
/**
|
/**
|
||||||
* Used to create a delimiter of a specific size, where `size` is 1, 2, 3, or 4.
|
* Used to create a delimiter of a specific size, where `size` is 1, 2, 3, or 4.
|
||||||
*/
|
*/
|
||||||
var makeSizedDelim = function(delim, size, options, mode) {
|
var makeSizedDelim = function(delim, size, options, mode, classes) {
|
||||||
// < and > turn into \langle and \rangle in delimiters
|
// < and > turn into \langle and \rangle in delimiters
|
||||||
if (delim === "<" || delim === "\\lt") {
|
if (delim === "<" || delim === "\\lt") {
|
||||||
delim = "\\langle";
|
delim = "\\langle";
|
||||||
|
@ -365,10 +367,10 @@ var makeSizedDelim = function(delim, size, options, mode) {
|
||||||
// Sized delimiters are never centered.
|
// Sized delimiters are never centered.
|
||||||
if (utils.contains(stackLargeDelimiters, delim) ||
|
if (utils.contains(stackLargeDelimiters, delim) ||
|
||||||
utils.contains(stackNeverDelimiters, delim)) {
|
utils.contains(stackNeverDelimiters, delim)) {
|
||||||
return makeLargeDelim(delim, size, false, options, mode);
|
return makeLargeDelim(delim, size, false, options, mode, classes);
|
||||||
} else if (utils.contains(stackAlwaysDelimiters, delim)) {
|
} else if (utils.contains(stackAlwaysDelimiters, delim)) {
|
||||||
return makeStackedDelim(
|
return makeStackedDelim(
|
||||||
delim, sizeToMaxHeight[size], false, options, mode);
|
delim, sizeToMaxHeight[size], false, options, mode, classes);
|
||||||
} else {
|
} else {
|
||||||
throw new ParseError("Illegal delimiter: '" + delim + "'");
|
throw new ParseError("Illegal delimiter: '" + delim + "'");
|
||||||
}
|
}
|
||||||
|
@ -471,7 +473,8 @@ var traverseSequence = function(delim, height, sequence, options) {
|
||||||
* Make a delimiter of a given height+depth, with optional centering. Here, we
|
* Make a delimiter of a given height+depth, with optional centering. Here, we
|
||||||
* traverse the sequences, and create a delimiter that the sequence tells us to.
|
* traverse the sequences, and create a delimiter that the sequence tells us to.
|
||||||
*/
|
*/
|
||||||
var makeCustomSizedDelim = function(delim, height, center, options, mode) {
|
var makeCustomSizedDelim = function(delim, height, center, options, mode,
|
||||||
|
classes) {
|
||||||
if (delim === "<" || delim === "\\lt") {
|
if (delim === "<" || delim === "\\lt") {
|
||||||
delim = "\\langle";
|
delim = "\\langle";
|
||||||
} else if (delim === ">" || delim === "\\gt") {
|
} else if (delim === ">" || delim === "\\gt") {
|
||||||
|
@ -494,11 +497,13 @@ var makeCustomSizedDelim = function(delim, height, center, options, mode) {
|
||||||
// Depending on the sequence element we decided on, call the appropriate
|
// Depending on the sequence element we decided on, call the appropriate
|
||||||
// function.
|
// function.
|
||||||
if (delimType.type === "small") {
|
if (delimType.type === "small") {
|
||||||
return makeSmallDelim(delim, delimType.style, center, options, mode);
|
return makeSmallDelim(delim, delimType.style, center, options, mode,
|
||||||
|
classes);
|
||||||
} else if (delimType.type === "large") {
|
} else if (delimType.type === "large") {
|
||||||
return makeLargeDelim(delim, delimType.size, center, options, mode);
|
return makeLargeDelim(delim, delimType.size, center, options, mode,
|
||||||
|
classes);
|
||||||
} else if (delimType.type === "stack") {
|
} else if (delimType.type === "stack") {
|
||||||
return makeStackedDelim(delim, height, center, options, mode);
|
return makeStackedDelim(delim, height, center, options, mode, classes);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -506,7 +511,8 @@ var makeCustomSizedDelim = function(delim, height, center, options, mode) {
|
||||||
* Make a delimiter for use with `\left` and `\right`, given a height and depth
|
* Make a delimiter for use with `\left` and `\right`, given a height and depth
|
||||||
* of an expression that the delimiters surround.
|
* of an expression that the delimiters surround.
|
||||||
*/
|
*/
|
||||||
var makeLeftRightDelim = function(delim, height, depth, options, mode) {
|
var makeLeftRightDelim = function(delim, height, depth, options, mode,
|
||||||
|
classes) {
|
||||||
// We always center \left/\right delimiters, so the axis is always shifted
|
// We always center \left/\right delimiters, so the axis is always shifted
|
||||||
var axisHeight =
|
var axisHeight =
|
||||||
options.style.metrics.axisHeight * options.style.sizeMultiplier;
|
options.style.metrics.axisHeight * options.style.sizeMultiplier;
|
||||||
|
@ -533,7 +539,8 @@ var makeLeftRightDelim = function(delim, height, depth, options, mode) {
|
||||||
|
|
||||||
// Finally, we defer to `makeCustomSizedDelim` with our calculated total
|
// Finally, we defer to `makeCustomSizedDelim` with our calculated total
|
||||||
// height
|
// height
|
||||||
return makeCustomSizedDelim(delim, totalHeight, true, options, mode);
|
return makeCustomSizedDelim(delim, totalHeight, true, options, mode,
|
||||||
|
classes);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -57,6 +57,10 @@ span.prototype.setAttribute = function(attribute, value) {
|
||||||
this.attributes[attribute] = value;
|
this.attributes[attribute] = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
span.prototype.tryCombine = function(sibling) {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the span into an HTML node
|
* Convert the span into an HTML node
|
||||||
*/
|
*/
|
||||||
|
@ -220,6 +224,34 @@ function symbolNode(value, height, depth, italic, skew, classes, style) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
symbolNode.prototype.tryCombine = function(sibling) {
|
||||||
|
if (!sibling
|
||||||
|
|| !(sibling instanceof symbolNode)
|
||||||
|
|| this.italic > 0
|
||||||
|
|| createClass(this.classes) !== createClass(sibling.classes)
|
||||||
|
|| this.skew !== sibling.skew
|
||||||
|
|| this.maxFontSize !== sibling.maxFontSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (var style in this.style) {
|
||||||
|
if (this.style.hasOwnProperty(style)
|
||||||
|
&& this.style[style] !== sibling.style[style]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (style in sibling.style) {
|
||||||
|
if (sibling.style.hasOwnProperty(style)
|
||||||
|
&& this.style[style] !== sibling.style[style]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.value += sibling.value;
|
||||||
|
this.height = Math.max(this.height, sibling.height);
|
||||||
|
this.depth = Math.max(this.depth, sibling.depth);
|
||||||
|
this.italic = sibling.italic;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a text node or span from a symbol node. Note that a span is only
|
* Creates a text node or span from a symbol node. Note that a span is only
|
||||||
* created if it is needed.
|
* created if it is needed.
|
||||||
|
|
166
src/functions.js
|
@ -1,5 +1,7 @@
|
||||||
var utils = require("./utils");
|
var utils = require("./utils");
|
||||||
var ParseError = require("./ParseError");
|
var ParseError = require("./ParseError");
|
||||||
|
var parseData = require("./parseData");
|
||||||
|
var ParseNode = parseData.ParseNode;
|
||||||
|
|
||||||
/* This file contains a list of functions that we parse, identified by
|
/* This file contains a list of functions that we parse, identified by
|
||||||
* the calls to defineFunction.
|
* the calls to defineFunction.
|
||||||
|
@ -100,6 +102,16 @@ function defineFunction(names, props, handler) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since the corresponding buildHTML/buildMathML function expects a
|
||||||
|
// list of elements, we normalize for different kinds of arguments
|
||||||
|
var ordargument = function(arg) {
|
||||||
|
if (arg.type === "ordgroup") {
|
||||||
|
return arg.value;
|
||||||
|
} else {
|
||||||
|
return [arg];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// A normal square root
|
// A normal square root
|
||||||
defineFunction("\\sqrt", {
|
defineFunction("\\sqrt", {
|
||||||
numArgs: 1,
|
numArgs: 1,
|
||||||
|
@ -114,26 +126,27 @@ defineFunction("\\sqrt", {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Some non-mathy text
|
// Non-mathy text, possibly in a font
|
||||||
defineFunction("\\text", {
|
var textFunctionStyles = {
|
||||||
|
"\\text": undefined, "\\textrm": "mathrm", "\\textsf": "mathsf",
|
||||||
|
"\\texttt": "mathtt", "\\textnormal": "mathrm", "\\textbf": "mathbf",
|
||||||
|
"\\textit": "textit",
|
||||||
|
};
|
||||||
|
|
||||||
|
defineFunction([
|
||||||
|
"\\text", "\\textrm", "\\textsf", "\\texttt", "\\textnormal",
|
||||||
|
"\\textbf", "\\textit",
|
||||||
|
], {
|
||||||
numArgs: 1,
|
numArgs: 1,
|
||||||
argTypes: ["text"],
|
argTypes: ["text"],
|
||||||
greediness: 2,
|
greediness: 2,
|
||||||
|
allowedInText: true,
|
||||||
}, function(context, args) {
|
}, function(context, args) {
|
||||||
var body = args[0];
|
var body = args[0];
|
||||||
// Since the corresponding buildHTML/buildMathML function expects a
|
|
||||||
// list of elements, we normalize for different kinds of arguments
|
|
||||||
// TODO(emily): maybe this should be done somewhere else
|
|
||||||
var inner;
|
|
||||||
if (body.type === "ordgroup") {
|
|
||||||
inner = body.value;
|
|
||||||
} else {
|
|
||||||
inner = [body];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "text",
|
type: "text",
|
||||||
body: inner,
|
body: ordargument(body),
|
||||||
|
style: textFunctionStyles[context.funcName],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -146,18 +159,10 @@ defineFunction("\\color", {
|
||||||
}, function(context, args) {
|
}, function(context, args) {
|
||||||
var color = args[0];
|
var color = args[0];
|
||||||
var body = args[1];
|
var body = args[1];
|
||||||
// Normalize the different kinds of bodies (see \text above)
|
|
||||||
var inner;
|
|
||||||
if (body.type === "ordgroup") {
|
|
||||||
inner = body.value;
|
|
||||||
} else {
|
|
||||||
inner = [body];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "color",
|
type: "color",
|
||||||
color: color.value,
|
color: color.value,
|
||||||
value: inner,
|
value: ordargument(body),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -223,37 +228,73 @@ defineFunction("\\phantom", {
|
||||||
numArgs: 1,
|
numArgs: 1,
|
||||||
}, function(context, args) {
|
}, function(context, args) {
|
||||||
var body = args[0];
|
var body = args[0];
|
||||||
var inner;
|
|
||||||
if (body.type === "ordgroup") {
|
|
||||||
inner = body.value;
|
|
||||||
} else {
|
|
||||||
inner = [body];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "phantom",
|
type: "phantom",
|
||||||
value: inner,
|
value: ordargument(body),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Math class commands except \mathop
|
||||||
|
defineFunction([
|
||||||
|
"\\mathord", "\\mathbin", "\\mathrel", "\\mathopen",
|
||||||
|
"\\mathclose", "\\mathpunct", "\\mathinner",
|
||||||
|
], {
|
||||||
|
numArgs: 1,
|
||||||
|
}, function(context, args) {
|
||||||
|
var body = args[0];
|
||||||
|
return {
|
||||||
|
type: "mclass",
|
||||||
|
mclass: "m" + context.funcName.substr(5),
|
||||||
|
value: ordargument(body),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build a relation by placing one symbol on top of another
|
||||||
|
defineFunction("\\stackrel", {
|
||||||
|
numArgs: 2,
|
||||||
|
}, function(context, args) {
|
||||||
|
var top = args[0];
|
||||||
|
var bottom = args[1];
|
||||||
|
|
||||||
|
var bottomop = new ParseNode("op", {
|
||||||
|
type: "op",
|
||||||
|
limits: true,
|
||||||
|
alwaysHandleSupSub: true,
|
||||||
|
symbol: false,
|
||||||
|
value: ordargument(bottom),
|
||||||
|
}, bottom.mode);
|
||||||
|
|
||||||
|
var supsub = new ParseNode("supsub", {
|
||||||
|
base: bottomop,
|
||||||
|
sup: top,
|
||||||
|
sub: null,
|
||||||
|
}, top.mode);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "mclass",
|
||||||
|
mclass: "mrel",
|
||||||
|
value: [supsub],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Extra data needed for the delimiter handler down below
|
// Extra data needed for the delimiter handler down below
|
||||||
var delimiterSizes = {
|
var delimiterSizes = {
|
||||||
"\\bigl" : {type: "open", size: 1},
|
"\\bigl" : {mclass: "mopen", size: 1},
|
||||||
"\\Bigl" : {type: "open", size: 2},
|
"\\Bigl" : {mclass: "mopen", size: 2},
|
||||||
"\\biggl": {type: "open", size: 3},
|
"\\biggl": {mclass: "mopen", size: 3},
|
||||||
"\\Biggl": {type: "open", size: 4},
|
"\\Biggl": {mclass: "mopen", size: 4},
|
||||||
"\\bigr" : {type: "close", size: 1},
|
"\\bigr" : {mclass: "mclose", size: 1},
|
||||||
"\\Bigr" : {type: "close", size: 2},
|
"\\Bigr" : {mclass: "mclose", size: 2},
|
||||||
"\\biggr": {type: "close", size: 3},
|
"\\biggr": {mclass: "mclose", size: 3},
|
||||||
"\\Biggr": {type: "close", size: 4},
|
"\\Biggr": {mclass: "mclose", size: 4},
|
||||||
"\\bigm" : {type: "rel", size: 1},
|
"\\bigm" : {mclass: "mrel", size: 1},
|
||||||
"\\Bigm" : {type: "rel", size: 2},
|
"\\Bigm" : {mclass: "mrel", size: 2},
|
||||||
"\\biggm": {type: "rel", size: 3},
|
"\\biggm": {mclass: "mrel", size: 3},
|
||||||
"\\Biggm": {type: "rel", size: 4},
|
"\\Biggm": {mclass: "mrel", size: 4},
|
||||||
"\\big" : {type: "textord", size: 1},
|
"\\big" : {mclass: "mord", size: 1},
|
||||||
"\\Big" : {type: "textord", size: 2},
|
"\\Big" : {mclass: "mord", size: 2},
|
||||||
"\\bigg" : {type: "textord", size: 3},
|
"\\bigg" : {mclass: "mord", size: 3},
|
||||||
"\\Bigg" : {type: "textord", size: 4},
|
"\\Bigg" : {mclass: "mord", size: 4},
|
||||||
};
|
};
|
||||||
|
|
||||||
var delimiters = [
|
var delimiters = [
|
||||||
|
@ -298,17 +339,10 @@ defineFunction([
|
||||||
greediness: 3,
|
greediness: 3,
|
||||||
}, function(context, args) {
|
}, function(context, args) {
|
||||||
var body = args[0];
|
var body = args[0];
|
||||||
var atoms;
|
|
||||||
if (body.type === "ordgroup") {
|
|
||||||
atoms = body.value;
|
|
||||||
} else {
|
|
||||||
atoms = [body];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "color",
|
type: "color",
|
||||||
color: "katex-" + context.funcName.slice(1),
|
color: "katex-" + context.funcName.slice(1),
|
||||||
value: atoms,
|
value: ordargument(body),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -378,10 +412,24 @@ defineFunction([
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// \mathop class command
|
||||||
|
defineFunction("\\mathop", {
|
||||||
|
numArgs: 1,
|
||||||
|
}, function(context, args) {
|
||||||
|
var body = args[0];
|
||||||
|
return {
|
||||||
|
type: "op",
|
||||||
|
limits: false,
|
||||||
|
symbol: false,
|
||||||
|
value: ordargument(body),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// Fractions
|
// Fractions
|
||||||
defineFunction([
|
defineFunction([
|
||||||
"\\dfrac", "\\frac", "\\tfrac",
|
"\\dfrac", "\\frac", "\\tfrac",
|
||||||
"\\dbinom", "\\binom", "\\tbinom",
|
"\\dbinom", "\\binom", "\\tbinom",
|
||||||
|
"\\\\atopfrac", // can’t be entered directly
|
||||||
], {
|
], {
|
||||||
numArgs: 2,
|
numArgs: 2,
|
||||||
greediness: 2,
|
greediness: 2,
|
||||||
|
@ -399,6 +447,9 @@ defineFunction([
|
||||||
case "\\tfrac":
|
case "\\tfrac":
|
||||||
hasBarLine = true;
|
hasBarLine = true;
|
||||||
break;
|
break;
|
||||||
|
case "\\\\atopfrac":
|
||||||
|
hasBarLine = false;
|
||||||
|
break;
|
||||||
case "\\dbinom":
|
case "\\dbinom":
|
||||||
case "\\binom":
|
case "\\binom":
|
||||||
case "\\tbinom":
|
case "\\tbinom":
|
||||||
|
@ -472,7 +523,7 @@ defineFunction([
|
||||||
return {
|
return {
|
||||||
type: "delimsizing",
|
type: "delimsizing",
|
||||||
size: delimiterSizes[context.funcName].size,
|
size: delimiterSizes[context.funcName].size,
|
||||||
delimType: delimiterSizes[context.funcName].type,
|
mclass: delimiterSizes[context.funcName].mclass,
|
||||||
value: delim.value,
|
value: delim.value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -535,7 +586,7 @@ defineFunction([
|
||||||
});
|
});
|
||||||
|
|
||||||
// Infix generalized fractions
|
// Infix generalized fractions
|
||||||
defineFunction(["\\over", "\\choose"], {
|
defineFunction(["\\over", "\\choose", "\\atop"], {
|
||||||
numArgs: 0,
|
numArgs: 0,
|
||||||
infix: true,
|
infix: true,
|
||||||
}, function(context) {
|
}, function(context) {
|
||||||
|
@ -547,6 +598,9 @@ defineFunction(["\\over", "\\choose"], {
|
||||||
case "\\choose":
|
case "\\choose":
|
||||||
replaceWith = "\\binom";
|
replaceWith = "\\binom";
|
||||||
break;
|
break;
|
||||||
|
case "\\atop":
|
||||||
|
replaceWith = "\\\\atopfrac";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error("Unrecognized infix genfrac command");
|
throw new Error("Unrecognized infix genfrac command");
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,9 @@ defineSymbol(math, main, punct, "\u22c5", "\\cdotp");
|
||||||
|
|
||||||
// Misc Symbols
|
// Misc Symbols
|
||||||
defineSymbol(math, main, textord, "\u0023", "\\#");
|
defineSymbol(math, main, textord, "\u0023", "\\#");
|
||||||
|
defineSymbol(text, main, textord, "\u0023", "\\#");
|
||||||
defineSymbol(math, main, textord, "\u0026", "\\&");
|
defineSymbol(math, main, textord, "\u0026", "\\&");
|
||||||
|
defineSymbol(text, main, textord, "\u0026", "\\&");
|
||||||
defineSymbol(math, main, textord, "\u2135", "\\aleph");
|
defineSymbol(math, main, textord, "\u2135", "\\aleph");
|
||||||
defineSymbol(math, main, textord, "\u2200", "\\forall");
|
defineSymbol(math, main, textord, "\u2200", "\\forall");
|
||||||
defineSymbol(math, main, textord, "\u210f", "\\hbar");
|
defineSymbol(math, main, textord, "\u210f", "\\hbar");
|
||||||
|
@ -394,8 +396,11 @@ defineSymbol(math, ams, rel, "\u21be", "\\restriction");
|
||||||
|
|
||||||
defineSymbol(math, main, textord, "\u2018", "`");
|
defineSymbol(math, main, textord, "\u2018", "`");
|
||||||
defineSymbol(math, main, textord, "$", "\\$");
|
defineSymbol(math, main, textord, "$", "\\$");
|
||||||
|
defineSymbol(text, main, textord, "$", "\\$");
|
||||||
defineSymbol(math, main, textord, "%", "\\%");
|
defineSymbol(math, main, textord, "%", "\\%");
|
||||||
|
defineSymbol(text, main, textord, "%", "\\%");
|
||||||
defineSymbol(math, main, textord, "_", "\\_");
|
defineSymbol(math, main, textord, "_", "\\_");
|
||||||
|
defineSymbol(text, main, textord, "_", "\\_");
|
||||||
defineSymbol(math, main, textord, "\u2220", "\\angle");
|
defineSymbol(math, main, textord, "\u2220", "\\angle");
|
||||||
defineSymbol(math, main, textord, "\u221e", "\\infty");
|
defineSymbol(math, main, textord, "\u221e", "\\infty");
|
||||||
defineSymbol(math, main, textord, "\u2032", "\\prime");
|
defineSymbol(math, main, textord, "\u2032", "\\prime");
|
||||||
|
@ -534,7 +539,9 @@ defineSymbol(math, main, bin, "\u22c6", "\\star");
|
||||||
defineSymbol(math, main, bin, "\u25c3", "\\triangleleft");
|
defineSymbol(math, main, bin, "\u25c3", "\\triangleleft");
|
||||||
defineSymbol(math, main, bin, "\u25b9", "\\triangleright");
|
defineSymbol(math, main, bin, "\u25b9", "\\triangleright");
|
||||||
defineSymbol(math, main, open, "{", "\\{");
|
defineSymbol(math, main, open, "{", "\\{");
|
||||||
|
defineSymbol(text, main, textord, "{", "\\{");
|
||||||
defineSymbol(math, main, close, "}", "\\}");
|
defineSymbol(math, main, close, "}", "\\}");
|
||||||
|
defineSymbol(text, main, textord, "}", "\\}");
|
||||||
defineSymbol(math, main, open, "{", "\\lbrace");
|
defineSymbol(math, main, open, "{", "\\lbrace");
|
||||||
defineSymbol(math, main, close, "}", "\\rbrace");
|
defineSymbol(math, main, close, "}", "\\rbrace");
|
||||||
defineSymbol(math, main, open, "[", "\\lbrack");
|
defineSymbol(math, main, open, "[", "\\lbrack");
|
||||||
|
@ -652,3 +659,11 @@ for (i = 0x0410; i <= 0x044F; i++) {
|
||||||
ch = String.fromCharCode(i);
|
ch = String.fromCharCode(i);
|
||||||
defineSymbol(text, main, textord, ch, ch);
|
defineSymbol(text, main, textord, ch, ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unicode versions of existing characters
|
||||||
|
defineSymbol(text, main, textord, "\u2013", "–");
|
||||||
|
defineSymbol(text, main, textord, "\u2014", "—");
|
||||||
|
defineSymbol(text, main, textord, "\u2018", "‘");
|
||||||
|
defineSymbol(text, main, textord, "\u2019", "’");
|
||||||
|
defineSymbol(text, main, textord, "\u201c", "“");
|
||||||
|
defineSymbol(text, main, textord, "\u201d", "”");
|
||||||
|
|
|
@ -49,6 +49,14 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mathrm {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textit {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
.mathit {
|
.mathit {
|
||||||
font-family: KaTeX_Math;
|
font-family: KaTeX_Math;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
|
|
@ -606,6 +606,15 @@ describe("A frac parser", function() {
|
||||||
expect(tfracParse.value.numer).toBeDefined();
|
expect(tfracParse.value.numer).toBeDefined();
|
||||||
expect(tfracParse.value.denom).toBeDefined();
|
expect(tfracParse.value.denom).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should parse atop", function() {
|
||||||
|
var parse = getParsed("x \\atop y")[0];
|
||||||
|
|
||||||
|
expect(parse.type).toEqual("genfrac");
|
||||||
|
expect(parse.value.numer).toBeDefined();
|
||||||
|
expect(parse.value.denom).toBeDefined();
|
||||||
|
expect(parse.value.hasBarLine).toEqual(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("An over parser", function() {
|
describe("An over parser", function() {
|
||||||
|
@ -883,8 +892,8 @@ describe("A delimiter sizing parser", function() {
|
||||||
var leftParse = getParsed(normalDelim)[0];
|
var leftParse = getParsed(normalDelim)[0];
|
||||||
var rightParse = getParsed(bigDelim)[0];
|
var rightParse = getParsed(bigDelim)[0];
|
||||||
|
|
||||||
expect(leftParse.value.delimType).toEqual("open");
|
expect(leftParse.value.mclass).toEqual("mopen");
|
||||||
expect(rightParse.value.delimType).toEqual("close");
|
expect(rightParse.value.mclass).toEqual("mclose");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should parse the correct size delimiter", function() {
|
it("should parse the correct size delimiter", function() {
|
||||||
|
@ -1373,6 +1382,43 @@ describe("An HTML font tree-builder", function() {
|
||||||
expect(markup).toContain("<span class=\"mord mathfrak\">R</span>");
|
expect(markup).toContain("<span class=\"mord mathfrak\">R</span>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render \\text{R} with the correct font", function() {
|
||||||
|
var markup = katex.renderToString("\\text{R}");
|
||||||
|
expect(markup).toContain("<span class=\"mord mathrm\">R</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render \\textit{R} with the correct font", function() {
|
||||||
|
var markup = katex.renderToString("\\textit{R}");
|
||||||
|
expect(markup).toContain("<span class=\"mord textit\">R</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render \\text{\\textit{R}} with the correct font", function() {
|
||||||
|
var markup = katex.renderToString("\\text{\\textit{R}}");
|
||||||
|
expect(markup).toContain("<span class=\"mord textit\">R</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render \\text{R\\textit{S}T} with the correct fonts", function() {
|
||||||
|
var markup = katex.renderToString("\\text{R\\textit{S}T}");
|
||||||
|
expect(markup).toContain("<span class=\"mord mathrm\">R</span>");
|
||||||
|
expect(markup).toContain("<span class=\"mord textit\">S</span>");
|
||||||
|
expect(markup).toContain("<span class=\"mord mathrm\">T</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render \\textbf{R} with the correct font", function() {
|
||||||
|
var markup = katex.renderToString("\\textbf{R}");
|
||||||
|
expect(markup).toContain("<span class=\"mord mathbf\">R</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render \\textsf{R} with the correct font", function() {
|
||||||
|
var markup = katex.renderToString("\\textsf{R}");
|
||||||
|
expect(markup).toContain("<span class=\"mord mathsf\">R</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render \\texttt{R} with the correct font", function() {
|
||||||
|
var markup = katex.renderToString("\\texttt{R}");
|
||||||
|
expect(markup).toContain("<span class=\"mord mathtt\">R</span>");
|
||||||
|
});
|
||||||
|
|
||||||
it("should render a combination of font and color changes", function() {
|
it("should render a combination of font and color changes", function() {
|
||||||
var markup = katex.renderToString("\\color{blue}{\\mathbb R}");
|
var markup = katex.renderToString("\\color{blue}{\\mathbb R}");
|
||||||
var span = "<span class=\"mord mathbb\" style=\"color:blue;\">R</span>";
|
var span = "<span class=\"mord mathbb\" style=\"color:blue;\">R</span>";
|
||||||
|
|
BIN
test/screenshotter/images/BinCancellation-chrome.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
test/screenshotter/images/BinCancellation-firefox.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.3 KiB |
BIN
test/screenshotter/images/MathAtom-chrome.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
test/screenshotter/images/MathAtom-firefox.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
test/screenshotter/images/MathAtom2-chrome.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
test/screenshotter/images/MathAtom2-firefox.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
test/screenshotter/images/StackRel-chrome.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
test/screenshotter/images/StackRel-firefox.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
@ -26,6 +26,11 @@ Arrays: |
|
||||||
ArrayType: 1\begin{array}{c}2\\3\end{array}4
|
ArrayType: 1\begin{array}{c}2\\3\end{array}4
|
||||||
Baseline: a+b-c\cdot d/e
|
Baseline: a+b-c\cdot d/e
|
||||||
BasicTest: a
|
BasicTest: a
|
||||||
|
BinCancellation: |
|
||||||
|
\begin{array}{ccc}
|
||||||
|
+1 & 1+ & 1+1 & (,) \\
|
||||||
|
1++1 & 3\times) & 1+, & \left(,\right)
|
||||||
|
\end{array}
|
||||||
BinomTest: \dbinom{a}{b}\tbinom{a}{b}^{\binom{a}{b}+17}
|
BinomTest: \dbinom{a}{b}\tbinom{a}{b}^{\binom{a}{b}+17}
|
||||||
BoldSpacing: \mathbf{A}^2+\mathbf{B}_3*\mathscr{C}'
|
BoldSpacing: \mathbf{A}^2+\mathbf{B}_3*\mathscr{C}'
|
||||||
Cases: |
|
Cases: |
|
||||||
|
@ -57,7 +62,7 @@ DisplayStyle: |
|
||||||
{\displaystyle\sqrt{x}}{\sqrt{x}}
|
{\displaystyle\sqrt{x}}{\sqrt{x}}
|
||||||
{\displaystyle \frac12}{\frac12}{\displaystyle x^1_2}{x^1_2}
|
{\displaystyle \frac12}{\frac12}{\displaystyle x^1_2}{x^1_2}
|
||||||
Exponents: a^{a^a_a}_{a^a_a}
|
Exponents: a^{a^a_a}_{a^a_a}
|
||||||
FractionTest: \dfrac{a}{b}\frac{a}{b}\tfrac{a}{b}\;-\dfrac12\;1\tfrac12
|
FractionTest: \dfrac{a}{b}\frac{a}{b}\tfrac{a}{b}\;-\dfrac12\;1\tfrac12\;{1 \atop 2}
|
||||||
Functions: \sin\cos\tan\ln\log
|
Functions: \sin\cos\tan\ln\log
|
||||||
GreekLetters: \alpha\beta\gamma\omega
|
GreekLetters: \alpha\beta\gamma\omega
|
||||||
KaTeX: \KaTeX
|
KaTeX: \KaTeX
|
||||||
|
@ -74,6 +79,8 @@ LeftRightStyleSizing: |
|
||||||
LimitControls: |
|
LimitControls: |
|
||||||
\displaystyle\int\limits_2^3 3x^2\,dx + \sum\nolimits^n_{i=1}i +
|
\displaystyle\int\limits_2^3 3x^2\,dx + \sum\nolimits^n_{i=1}i +
|
||||||
\textstyle\int\limits_x^y z
|
\textstyle\int\limits_x^y z
|
||||||
|
MathAtom: a\mathrel{\mathop{=}\limits^{\blue ?}}b
|
||||||
|
MathAtom2: \mathop{\overline\mathrm{lim}}\limits_{x\to\infty}f(x)
|
||||||
MathDefaultFonts: Ax2k\breve{a}\omega\Omega\imath+\KaTeX
|
MathDefaultFonts: Ax2k\breve{a}\omega\Omega\imath+\KaTeX
|
||||||
MathBb: \mathbb{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
|
MathBb: \mathbb{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
|
||||||
MathBf: \mathbf{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
|
MathBf: \mathbf{Ax2k\breve{a}\omega\Omega\imath+\KaTeX}
|
||||||
|
@ -109,6 +116,7 @@ Sqrt: |
|
||||||
^{\sqrt{\sqrt{\sqrt{x}}}}}
|
^{\sqrt{\sqrt{\sqrt{x}}}}}
|
||||||
SqrtRoot: |
|
SqrtRoot: |
|
||||||
1+\sqrt[3]{2}+\sqrt[1923^234]{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^2}}}}}}}}}}}
|
1+\sqrt[3]{2}+\sqrt[1923^234]{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^{2^2}}}}}}}}}}}
|
||||||
|
StackRel: a \stackrel{?}{=} b \stackrel{\text{def}}{=} c
|
||||||
StyleSwitching: a\cdot b\scriptstyle a\cdot ba\textstyle\cdot ba\scriptstyle\cdot b
|
StyleSwitching: a\cdot b\scriptstyle a\cdot ba\textstyle\cdot ba\scriptstyle\cdot b
|
||||||
SupSubCharacterBox: a_2f_2{f}_2{aa}_2{af}_2\mathbf{y}_Ay_A
|
SupSubCharacterBox: a_2f_2{f}_2{aa}_2{af}_2\mathbf{y}_Ay_A
|
||||||
SupSubHorizSpacing: |
|
SupSubHorizSpacing: |
|
||||||
|
|