|
|
|
|
|
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, |
|
}); |
|
|