UNPKG

3.6 kBJavaScriptView Raw
1// @flow
2import defineFunction, {ordargument} from "../defineFunction";
3import buildCommon from "../buildCommon";
4import mathMLTree from "../mathMLTree";
5import type {AnyParseNode} from "../parseNode";
6
7import * as html from "../buildHTML";
8import * as mml from "../buildMathML";
9
10import type {ParseNode} from "../parseNode";
11
12const makeSpan = buildCommon.makeSpan;
13
14function htmlBuilder(group: ParseNode<"mclass">, options) {
15 const elements = html.buildExpression(group.body, options, true);
16 return makeSpan([group.mclass], elements, options);
17}
18
19function mathmlBuilder(group: ParseNode<"mclass">, options) {
20 const inner = mml.buildExpression(group.body, options);
21 return mathMLTree.newDocumentFragment(inner);
22}
23
24// Math class commands except \mathop
25defineFunction({
26 type: "mclass",
27 names: [
28 "\\mathord", "\\mathbin", "\\mathrel", "\\mathopen",
29 "\\mathclose", "\\mathpunct", "\\mathinner",
30 ],
31 props: {
32 numArgs: 1,
33 },
34 handler({parser, funcName}, args) {
35 const body = args[0];
36 return {
37 type: "mclass",
38 mode: parser.mode,
39 mclass: "m" + funcName.substr(5),
40 body: ordargument(body),
41 };
42 },
43 htmlBuilder,
44 mathmlBuilder,
45});
46
47export const binrelClass = (arg: AnyParseNode): string => {
48 // \binrel@ spacing varies with (bin|rel|ord) of the atom in the argument.
49 // (by rendering separately and with {}s before and after, and measuring
50 // the change in spacing). We'll do roughly the same by detecting the
51 // atom type directly.
52 const atom = (arg.type === "ordgroup" && arg.body.length ? arg.body[0] : arg);
53 if (atom.type === "atom" && (atom.family === "bin" || atom.family === "rel")) {
54 return "m" + atom.family;
55 } else {
56 return "mord";
57 }
58};
59
60// \@binrel{x}{y} renders like y but as mbin/mrel/mord if x is mbin/mrel/mord.
61// This is equivalent to \binrel@{x}\binrel@@{y} in AMSTeX.
62defineFunction({
63 type: "mclass",
64 names: ["\\@binrel"],
65 props: {
66 numArgs: 2,
67 },
68 handler({parser}, args) {
69 return {
70 type: "mclass",
71 mode: parser.mode,
72 mclass: binrelClass(args[0]),
73 body: [args[1]],
74 };
75 },
76});
77
78// Build a relation or stacked op by placing one symbol on top of another
79defineFunction({
80 type: "mclass",
81 names: ["\\stackrel", "\\overset", "\\underset"],
82 props: {
83 numArgs: 2,
84 },
85 handler({parser, funcName}, args) {
86 const baseArg = args[1];
87 const shiftedArg = args[0];
88
89 let mclass;
90 if (funcName !== "\\stackrel") {
91 // LaTeX applies \binrel spacing to \overset and \underset.
92 mclass = binrelClass(baseArg);
93 } else {
94 mclass = "mrel"; // for \stackrel
95 }
96
97 const baseOp = {
98 type: "op",
99 mode: baseArg.mode,
100 limits: true,
101 alwaysHandleSupSub: true,
102 parentIsSupSub: false,
103 symbol: false,
104 suppressBaseShift: funcName !== "\\stackrel",
105 body: ordargument(baseArg),
106 };
107
108 const supsub = {
109 type: "supsub",
110 mode: shiftedArg.mode,
111 base: baseOp,
112 sup: funcName === "\\underset" ? null : shiftedArg,
113 sub: funcName === "\\underset" ? shiftedArg : null,
114 };
115
116 return {
117 type: "mclass",
118 mode: parser.mode,
119 mclass,
120 body: [supsub],
121 };
122 },
123 htmlBuilder,
124 mathmlBuilder,
125});
126