Files
KaTeX/src/functions.js
Ron Kok 2aee354ca2 Support Reaction Arrows (#1078)
* Support Reaction Arrows

This PR is written to supply reaction arrows for a future `mhchem` extension. `mhchem` uses seven reaction arrows. Four of them correspond to extensible arrows already available in KaTeX. This PR creates the other three.

These arrows will also be useful to chemists writing about reactions when `mhchem` is unavailable.

Three new extensible arrows are introduced: `\xrightleftarrows`, `\xrightequilibrium`, and `\xleftequilibrium`.

These names are not `mhchem`’s user-facing function names. In `mhchem`, users would call these arrows with syntax such as: `\ce{A<-->B}`, or `\ce{A<=>>B}`, or `\ce{A<<=>B}`. I’ve provided names that look more like the other extensible arrow names. That’s probably worth some discussion.

* generate screenshots for ReactionArrows

* Increase overlap in arrow shaft

To prevent a small gap when rendering in very large font sizes.

* Adjust upper text vert alignment. Add warning.

* Limit alignment adjustment to \xleftequilibrium

* generate screenshots for reaction arrows
2018-02-04 15:52:22 -05:00

301 lines
7.1 KiB
JavaScript

// @flow
/** Include this to ensure that all functions are defined. */
import ParseError from "./ParseError";
import ParseNode from "./ParseNode";
import {
default as _defineFunction,
ordargument,
_functions,
} from "./defineFunction";
import type {FunctionPropSpec, FunctionHandler} from "./defineFunction" ;
// WARNING: New functions should be added to src/functions and imported here.
const functions = _functions;
export default functions;
// Define a convenience function that mimcs the old semantics of defineFunction
// to support existing code so that we can migrate it a little bit at a time.
const defineFunction = function(
names: string[],
props: FunctionPropSpec,
handler: ?FunctionHandler, // null only if handled in parser
) {
_defineFunction({names, props, handler});
};
// TODO(kevinb): have functions return an object and call defineFunction with
// that object in this file instead of relying on side-effects.
import "./functions/sqrt";
import "./functions/color";
import "./functions/text";
import "./functions/enclose";
import "./functions/overline";
import "./functions/underline";
import "./functions/rule";
import "./functions/kern";
import "./functions/phantom";
// Math class commands except \mathop
defineFunction([
"\\mathord", "\\mathbin", "\\mathrel", "\\mathopen",
"\\mathclose", "\\mathpunct", "\\mathinner",
], {
numArgs: 1,
}, function(context, args) {
const 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) {
const top = args[0];
const bottom = args[1];
const bottomop = new ParseNode("op", {
type: "op",
limits: true,
alwaysHandleSupSub: true,
symbol: false,
value: ordargument(bottom),
}, bottom.mode);
const supsub = new ParseNode("supsub", {
base: bottomop,
sup: top,
sub: null,
}, top.mode);
return {
type: "mclass",
mclass: "mrel",
value: [supsub],
};
});
import "./functions/mod";
const singleCharIntegrals: {[string]: string} = {
"\u222b": "\\int",
"\u222c": "\\iint",
"\u222d": "\\iiint",
"\u222e": "\\oint",
};
// There are 2 flags for operators; whether they produce limits in
// displaystyle, and whether they are symbols and should grow in
// displaystyle. These four groups cover the four possible choices.
// No limits, not symbols
defineFunction([
"\\arcsin", "\\arccos", "\\arctan", "\\arctg", "\\arcctg",
"\\arg", "\\ch", "\\cos", "\\cosec", "\\cosh", "\\cot", "\\cotg",
"\\coth", "\\csc", "\\ctg", "\\cth", "\\deg", "\\dim", "\\exp",
"\\hom", "\\ker", "\\lg", "\\ln", "\\log", "\\sec", "\\sin",
"\\sinh", "\\sh", "\\tan", "\\tanh", "\\tg", "\\th",
], {
numArgs: 0,
}, function(context) {
return {
type: "op",
limits: false,
symbol: false,
body: context.funcName,
};
});
// Limits, not symbols
defineFunction([
"\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup",
], {
numArgs: 0,
}, function(context) {
return {
type: "op",
limits: true,
symbol: false,
body: context.funcName,
};
});
// No limits, symbols
defineFunction([
"\\int", "\\iint", "\\iiint", "\\oint", "\u222b", "\u222c",
"\u222d", "\u222e",
], {
numArgs: 0,
}, function(context) {
let fName = context.funcName;
if (fName.length === 1) {
fName = singleCharIntegrals[fName];
}
return {
type: "op",
limits: false,
symbol: true,
body: fName,
};
});
import "./functions/op";
import "./functions/operatorname";
import "./functions/genfrac";
import "./functions/lap";
import "./functions/smash";
import "./functions/delimsizing";
import "./functions/sizing";
import "./functions/styling";
import "./functions/font";
import "./functions/accent";
// Horizontal stretchy braces
defineFunction([
"\\overbrace", "\\underbrace",
], {
numArgs: 1,
}, function(context, args) {
const base = args[0];
return {
type: "horizBrace",
label: context.funcName,
isOver: /^\\over/.test(context.funcName),
base: base,
};
});
// Stretchy accents under the body
import "./functions/accentunder";
// Stretchy arrows with an optional argument
defineFunction([
"\\xleftarrow", "\\xrightarrow", "\\xLeftarrow", "\\xRightarrow",
"\\xleftrightarrow", "\\xLeftrightarrow", "\\xhookleftarrow",
"\\xhookrightarrow", "\\xmapsto", "\\xrightharpoondown",
"\\xrightharpoonup", "\\xleftharpoondown", "\\xleftharpoonup",
"\\xrightleftharpoons", "\\xleftrightharpoons", "\\xlongequal",
"\\xtwoheadrightarrow", "\\xtwoheadleftarrow", "\\xtofrom",
// The next 3 functions are here to support the mhchem extension.
// Direct use of these functions is discouraged and may break someday.
"\\xrightleftarrows", "\\xrightequilibrium",
"\\xleftequilibrium",
], {
numArgs: 1,
numOptionalArgs: 1,
}, function(context, args, optArgs) {
const below = optArgs[0];
const body = args[0];
return {
type: "xArrow", // x for extensible
label: context.funcName,
body: body,
below: below,
};
});
// Infix generalized fractions
defineFunction(["\\over", "\\choose", "\\atop"], {
numArgs: 0,
infix: true,
}, function(context) {
let replaceWith;
switch (context.funcName) {
case "\\over":
replaceWith = "\\frac";
break;
case "\\choose":
replaceWith = "\\binom";
break;
case "\\atop":
replaceWith = "\\\\atopfrac";
break;
default:
throw new Error("Unrecognized infix genfrac command");
}
return {
type: "infix",
replaceWith: replaceWith,
token: context.token,
};
});
// Row breaks for aligned data
defineFunction(["\\\\", "\\cr"], {
numArgs: 0,
numOptionalArgs: 1,
argTypes: ["size"],
}, function(context, args, optArgs) {
const size = optArgs[0];
return {
type: "cr",
size: size,
};
});
// Environment delimiters
defineFunction(["\\begin", "\\end"], {
numArgs: 1,
argTypes: ["text"],
}, function(context, args) {
const nameGroup = args[0];
if (nameGroup.type !== "ordgroup") {
throw new ParseError("Invalid environment name", nameGroup);
}
let name = "";
for (let i = 0; i < nameGroup.value.length; ++i) {
name += nameGroup.value[i].value;
}
return {
type: "environment",
name: name,
nameGroup: nameGroup,
};
});
// Box manipulation
defineFunction(["\\raisebox"], {
numArgs: 2,
argTypes: ["size", "text"],
allowedInText: true,
}, function(context, args) {
const amount = args[0];
const body = args[1];
return {
type: "raisebox",
dy: amount,
body: body,
value: ordargument(body),
};
});
import "./functions/verb";
// Hyperlinks
import "./functions/href";
// MathChoice
import "./functions/mathchoice";