leqno and fleqn support (#1814)

* leqno support

* Add fleqn support

* Add tests

* Lint fix

* Add leqno and fleqn to website demo
This commit is contained in:
Erik Demaine
2019-01-29 04:40:17 -05:00
committed by ylemkimon
parent 58c36c6fdf
commit 7f778d1543
9 changed files with 82 additions and 12 deletions

4
cli.js
View File

@@ -26,6 +26,10 @@ const program = require("commander")
"Render math in display mode, which puts the math in display style " +
"(so \\int and \\sum are large, for example), and centers the math " +
"on the page on its own line.")
.option("--leqno",
"Render display math in leqno style (left-justified tags).")
.option("--fleqn",
"Render display math flush left.")
.option("-t, --no-throw-on-error",
"Render errors (in the color given by --error-color) instead of " +
"throwing a ParseError exception when encountering an error.")

View File

@@ -5,6 +5,8 @@ title: Options
You can provide an object of options as the last argument to [`katex.render` and `katex.renderToString`](api.md). Available options are:
- `displayMode`: `boolean`. If `true` the math will be rendered in display mode, which will put the math in display style (so `\int` and `\sum` are large, for example), and will center the math on the page on its own line. If `false` the math will be rendered in inline mode. (default: `false`)
- `leqno`: `boolean`. If `true`, display math has `\tag`s rendered on the left instead of the right, like `\usepackage[leqno]{amsmath}` in LaTeX.
- `fleqn`: `boolean`. If `true`, display math renders flush left, like `\documentclass[fleqn]` in LaTeX.
- `throwOnError`: `boolean`. If `true` (the default), KaTeX will throw a `ParseError` when it encounters an unsupported command or invalid LaTeX. If `false`, KaTeX will render unsupported commands as text, and render invalid LaTeX as its source code with hover text giving the error, in the color given by `errorColor`.
- `errorColor`: `string`. A color string given in the format `"#XXX"` or `"#XXXXXX"`. This option determines the color that unsupported commands and invalid LaTeX are rendered in when `throwOnError` is set to `false`. (default: `#cc0000`)
- `macros`: `object`. A collection of custom macros. Each macro is a property with a name like `\name` (written `"\\name"` in JavaScript) which maps to a string that describes the expansion of the macro, or a function that accepts an instance of `MacroExpander` as first argument and returns the expansion as a string. `MacroExpander` is an internal API and subject to non-backwards compatible changes. See [`src/macros.js`](https://github.com/KaTeX/KaTeX/blob/master/src/macros.js) for its usage. Single-character keys can also be included in which case the character will be redefined as the given macro (similar to TeX active characters). *This object will be modified* if the LaTeX code defines its own macros via `\gdef`, which enables consecutive calls to KaTeX to share state.

View File

@@ -18,6 +18,8 @@ export type StrictFunction =
export type SettingsOptions = {
displayMode?: boolean;
leqno?: boolean;
fleqn?: boolean;
throwOnError?: boolean;
errorColor?: string;
macros?: MacroMap;
@@ -40,6 +42,8 @@ export type SettingsOptions = {
*/
class Settings {
displayMode: boolean;
leqno: boolean;
fleqn: boolean;
throwOnError: boolean;
errorColor: string;
macros: MacroMap;
@@ -53,6 +57,8 @@ class Settings {
// allow null options
options = options || {};
this.displayMode = utils.deflt(options.displayMode, false);
this.leqno = utils.deflt(options.leqno, false);
this.fleqn = utils.deflt(options.fleqn, false);
this.throwOnError = utils.deflt(options.throwOnError, true);
this.errorColor = utils.deflt(options.errorColor, "#cc0000");
this.macros = options.macros || {};

View File

@@ -16,6 +16,20 @@ const optionsFromSettings = function(settings: Settings) {
});
};
const displayWrap = function(node: DomSpan, settings: Settings): DomSpan {
if (settings.displayMode) {
const classes = ["katex-display"];
if (settings.leqno) {
classes.push("leqno");
}
if (settings.fleqn) {
classes.push("fleqn");
}
node = buildCommon.makeSpan(classes, [node]);
}
return node;
};
export const buildTree = function(
tree: AnyParseNode[],
expression: string,
@@ -29,11 +43,7 @@ export const buildTree = function(
mathMLNode, htmlNode,
]);
if (settings.displayMode) {
return buildCommon.makeSpan(["katex-display"], [katexNode]);
} else {
return katexNode;
}
return displayWrap(katexNode, settings);
};
export const buildHTMLTree = function(
@@ -44,11 +54,7 @@ export const buildHTMLTree = function(
const options = optionsFromSettings(settings);
const htmlNode = buildHTML(tree, options);
const katexNode = buildCommon.makeSpan(["katex"], [htmlNode]);
if (settings.displayMode) {
return buildCommon.makeSpan(["katex-display"], [katexNode]);
} else {
return katexNode;
}
return displayWrap(katexNode, settings);
};
export default buildTree;

View File

@@ -588,3 +588,14 @@
}
}
}
// Left-justified tags (default is right-justified)
.katex-display.leqno > .katex > .katex-html > .tag {
left: 0;
right: auto;
}
// Flush-left display math
.katex-display.fleqn > .katex {
text-align: left;
}

View File

@@ -29,6 +29,16 @@ function init() {
options.displayMode = false;
}
// Use `leqno=1` (or `=t`/`=true`/`=y`/`=yes`) to put tags on left side.
if (query.leqno && query.leqno.match(/^(1|t|y)/)) {
options.leqno = true;
}
// Use `fleqn=1` (or `=t`/`=true`/`=y`/`=yes`) to put tags on left side.
if (query.fleqn && query.fleqn.match(/^(1|t|y)/)) {
options.fleqn = true;
}
// Use `strict=warn` for warning strict mode or `strict=error`
// (or `=1`/`=t`/`=true`/`=y`/`=yes`)
// to turn off displayMode (which is on by default).

View File

@@ -3174,6 +3174,29 @@ describe("\\tag support", function() {
});
});
describe("leqno and fleqn rendering options", () => {
const expr = r`\tag{hi}x+y`;
for (const opt of ["leqno", "fleqn"]) {
it(`should not add ${opt} class by default`, () => {
const settings = new Settings({displayMode: true});
const built = katex.__renderToDomTree(expr, settings);
expect(built.classes).not.toContain(opt);
});
it(`should not add ${opt} class when false`, () => {
const settings = new Settings({displayMode: true});
settings[opt] = false;
const built = katex.__renderToDomTree(expr, settings);
expect(built.classes).not.toContain(opt);
});
it(`should add ${opt} class when true`, () => {
const settings = new Settings({displayMode: true});
settings[opt] = true;
const built = katex.__renderToDomTree(expr, settings);
expect(built.classes).toContain(opt);
});
}
});
describe("\\@binrel automatic bin/rel/ord", () => {
it("should generate proper class", () => {
expect("L\\@binrel+xR").toParseLike("L\\mathbin xR");

View File

@@ -131,6 +131,14 @@
<td><label for="displayMode">displayMode</label></td>
<td><input type="checkbox" id="displayMode" checked/></td>
</tr>
<tr>
<td><label for="leqno">leqno</label></td>
<td><input type="checkbox" id="leqno"/></td>
</tr>
<tr>
<td><label for="fleqn">fleqn</label></td>
<td><input type="checkbox" id="fleqn"/></td>
</tr>
<tr>
<td><label for="throwOnError">throwOnError</label></td>
<td><input type="checkbox" id="throwOnError" checked/></td>

View File

@@ -36,8 +36,8 @@
demoInput.value = data.code;
}
var katexOptions = ["displayMode", "throwOnError", "errorColor", "strict",
"macros"].map(function(id) {
var katexOptions = ["displayMode", "leqno", "fleqn", "throwOnError",
"errorColor", "strict", "macros"].map(function(id) {
var el = document.getElementById(id);
if (el.type === "checkbox") {
if (typeof data[id] === "boolean") {