import defineFunction, {ordargument} from "../defineFunction";
import buildCommon from "../buildCommon";
import {SymbolNode} from "../domTree";
import * as mathMLTree from "../mathMLTree";
import utils from "../utils";
import Style from "../Style";
import {assembleSupSub} from "./utils/assembleSupSub";
import {assertNodeType} from "../parseNode";
import {makeEm} from "../units";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
import type {ParseNode} from "../parseNode";
const noSuccessor = [
"\\smallint",
];
export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
let supGroup;
let subGroup;
let hasLimits = false;
let group: ParseNode<"op">;
if (grp.type === "supsub") {
supGroup = grp.sup;
subGroup = grp.sub;
group = assertNodeType(grp.base, "op");
hasLimits = true;
} else {
group = assertNodeType(grp, "op");
}
const style = options.style;
let large = false;
if (style.size === Style.DISPLAY.size &&
group.symbol &&
!utils.contains(noSuccessor, group.name)) {
large = true;
}
let base;
if (group.symbol) {
const fontName = large ? "Size2-Regular" : "Size1-Regular";
let stash = "";
if (group.name === "\\oiint" || group.name === "\\oiiint") {
stash = group.name.slice(1);
group.name = stash === "oiint" ? "\\iint" : "\\iiint";
}
base = buildCommon.makeSymbol(
group.name, fontName, "math", options,
["mop", "op-symbol", large ? "large-op" : "small-op"]);
if (stash.length > 0) {
const italic = base.italic;
const oval = buildCommon.staticSvg(stash + "Size"
+ (large ? "2" : "1"), options);
base = buildCommon.makeVList({
positionType: "individualShift",
children: [
{type: "elem", elem: base, shift: 0},
{type: "elem", elem: oval, shift: large ? 0.08 : 0},
],
}, options);
group.name = "\\" + stash;
base.classes.unshift("mop");
base.italic = italic;
}
} else if (group.body) {
const inner = html.buildExpression(group.body, options, true);
if (inner.length === 1 && inner[0] instanceof SymbolNode) {
base = inner[0];
base.classes[0] = "mop"; } else {
base = buildCommon.makeSpan(["mop"], inner, options);
}
} else {
const output = [];
for (let i = 1; i < group.name.length; i++) {
output.push(buildCommon.mathsym(group.name[i], group.mode, options));
}
base = buildCommon.makeSpan(["mop"], output, options);
}
let baseShift = 0;
let slant = 0;
if ((base instanceof SymbolNode
|| group.name === "\\oiint" || group.name === "\\oiiint")
&& !group.suppressBaseShift) {
baseShift = (base.height - base.depth) / 2 -
options.fontMetrics().axisHeight;
slant = base.italic;
}
if (hasLimits) {
return assembleSupSub(base, supGroup, subGroup, options,
style, slant, baseShift);
} else {
if (baseShift) {
base.style.position = "relative";
base.style.top = makeEm(baseShift);
}
return base;
}
};
const mathmlBuilder: MathMLBuilder<"op"> = (group, options) => {
let node;
if (group.symbol) {
node = new mathMLTree.MathNode(
"mo", [mml.makeText(group.name, group.mode)]);
if (utils.contains(noSuccessor, group.name)) {
node.setAttribute("largeop", "false");
}
} else if (group.body) {
node = new mathMLTree.MathNode(
"mo", mml.buildExpression(group.body, options));
} else {
node = new mathMLTree.MathNode(
"mi", [new mathMLTree.TextNode(group.name.slice(1))]);
const operator = new mathMLTree.MathNode("mo",
[mml.makeText("\u2061", "text")]);
if (group.parentIsSupSub) {
node = new mathMLTree.MathNode("mrow", [node, operator]);
} else {
node = mathMLTree.newDocumentFragment([node, operator]);
}
}
return node;
};
const singleCharBigOps: {[string]: string} = {
"\u220F": "\\prod",
"\u2210": "\\coprod",
"\u2211": "\\sum",
"\u22c0": "\\bigwedge",
"\u22c1": "\\bigvee",
"\u22c2": "\\bigcap",
"\u22c3": "\\bigcup",
"\u2a00": "\\bigodot",
"\u2a01": "\\bigoplus",
"\u2a02": "\\bigotimes",
"\u2a04": "\\biguplus",
"\u2a06": "\\bigsqcup",
};
defineFunction({
type: "op",
names: [
"\\coprod", "\\bigvee", "\\bigwedge", "\\biguplus", "\\bigcap",
"\\bigcup", "\\intop", "\\prod", "\\sum", "\\bigotimes",
"\\bigoplus", "\\bigodot", "\\bigsqcup", "\\smallint", "\u220F",
"\u2210", "\u2211", "\u22c0", "\u22c1", "\u22c2", "\u22c3", "\u2a00",
"\u2a01", "\u2a02", "\u2a04", "\u2a06",
],
props: {
numArgs: 0,
},
handler: ({parser, funcName}, args) => {
let fName = funcName;
if (fName.length === 1) {
fName = singleCharBigOps[fName];
}
return {
type: "op",
mode: parser.mode,
limits: true,
parentIsSupSub: false,
symbol: true,
name: fName,
};
},
htmlBuilder,
mathmlBuilder,
});
defineFunction({
type: "op",
names: ["\\mathop"],
props: {
numArgs: 1,
primitive: true,
},
handler: ({parser}, args) => {
const body = args[0];
return {
type: "op",
mode: parser.mode,
limits: false,
parentIsSupSub: false,
symbol: false,
body: ordargument(body),
};
},
htmlBuilder,
mathmlBuilder,
});
const singleCharIntegrals: {[string]: string} = {
"\u222b": "\\int",
"\u222c": "\\iint",
"\u222d": "\\iiint",
"\u222e": "\\oint",
"\u222f": "\\oiint",
"\u2230": "\\oiiint",
};
defineFunction({
type: "op",
names: [
"\\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",
],
props: {
numArgs: 0,
},
handler({parser, funcName}) {
return {
type: "op",
mode: parser.mode,
limits: false,
parentIsSupSub: false,
symbol: false,
name: funcName,
};
},
htmlBuilder,
mathmlBuilder,
});
defineFunction({
type: "op",
names: [
"\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup",
],
props: {
numArgs: 0,
},
handler({parser, funcName}) {
return {
type: "op",
mode: parser.mode,
limits: true,
parentIsSupSub: false,
symbol: false,
name: funcName,
};
},
htmlBuilder,
mathmlBuilder,
});
defineFunction({
type: "op",
names: [
"\\int", "\\iint", "\\iiint", "\\oint", "\\oiint", "\\oiiint",
"\u222b", "\u222c", "\u222d", "\u222e", "\u222f", "\u2230",
],
props: {
numArgs: 0,
},
handler({parser, funcName}) {
let fName = funcName;
if (fName.length === 1) {
fName = singleCharIntegrals[fName];
}
return {
type: "op",
mode: parser.mode,
limits: false,
parentIsSupSub: false,
symbol: true,
name: fName,
};
},
htmlBuilder,
mathmlBuilder,
});