|
|
|
import defineFunction, {ordargument} from "../defineFunction"; |
|
import buildCommon from "../buildCommon"; |
|
import mathMLTree from "../mathMLTree"; |
|
import utils from "../utils"; |
|
import type {AnyParseNode} from "../parseNode"; |
|
|
|
import * as html from "../buildHTML"; |
|
import * as mml from "../buildMathML"; |
|
|
|
import type {ParseNode} from "../parseNode"; |
|
|
|
const makeSpan = buildCommon.makeSpan; |
|
|
|
function htmlBuilder(group: ParseNode<"mclass">, options) { |
|
const elements = html.buildExpression(group.body, options, true); |
|
return makeSpan([group.mclass], elements, options); |
|
} |
|
|
|
function mathmlBuilder(group: ParseNode<"mclass">, options) { |
|
let node: mathMLTree.MathNode; |
|
const inner = mml.buildExpression(group.body, options); |
|
|
|
if (group.mclass === "minner") { |
|
node = new mathMLTree.MathNode("mpadded", inner); |
|
} else if (group.mclass === "mord") { |
|
if (group.isCharacterBox) { |
|
node = inner[0]; |
|
node.type = "mi"; |
|
} else { |
|
node = new mathMLTree.MathNode("mi", inner); |
|
} |
|
} else { |
|
if (group.isCharacterBox) { |
|
node = inner[0]; |
|
node.type = "mo"; |
|
} else { |
|
node = new mathMLTree.MathNode("mo", inner); |
|
} |
|
|
|
|
|
|
|
if (group.mclass === "mbin") { |
|
node.attributes.lspace = "0.22em"; |
|
node.attributes.rspace = "0.22em"; |
|
} else if (group.mclass === "mpunct") { |
|
node.attributes.lspace = "0em"; |
|
node.attributes.rspace = "0.17em"; |
|
} else if (group.mclass === "mopen" || group.mclass === "mclose") { |
|
node.attributes.lspace = "0em"; |
|
node.attributes.rspace = "0em"; |
|
} else if (group.mclass === "minner") { |
|
node.attributes.lspace = "0.0556em"; |
|
node.attributes.width = "+0.1111em"; |
|
} |
|
|
|
|
|
} |
|
return node; |
|
} |
|
|
|
|
|
defineFunction({ |
|
type: "mclass", |
|
names: [ |
|
"\\mathord", "\\mathbin", "\\mathrel", "\\mathopen", |
|
"\\mathclose", "\\mathpunct", "\\mathinner", |
|
], |
|
props: { |
|
numArgs: 1, |
|
primitive: true, |
|
}, |
|
handler({parser, funcName}, args) { |
|
const body = args[0]; |
|
return { |
|
type: "mclass", |
|
mode: parser.mode, |
|
mclass: "m" + funcName.slice(5), |
|
body: ordargument(body), |
|
isCharacterBox: utils.isCharacterBox(body), |
|
}; |
|
}, |
|
htmlBuilder, |
|
mathmlBuilder, |
|
}); |
|
|
|
export const binrelClass = (arg: AnyParseNode): string => { |
|
|
|
|
|
|
|
|
|
const atom = (arg.type === "ordgroup" && arg.body.length ? arg.body[0] : arg); |
|
if (atom.type === "atom" && (atom.family === "bin" || atom.family === "rel")) { |
|
return "m" + atom.family; |
|
} else { |
|
return "mord"; |
|
} |
|
}; |
|
|
|
|
|
|
|
defineFunction({ |
|
type: "mclass", |
|
names: ["\\@binrel"], |
|
props: { |
|
numArgs: 2, |
|
}, |
|
handler({parser}, args) { |
|
return { |
|
type: "mclass", |
|
mode: parser.mode, |
|
mclass: binrelClass(args[0]), |
|
body: ordargument(args[1]), |
|
isCharacterBox: utils.isCharacterBox(args[1]), |
|
}; |
|
}, |
|
}); |
|
|
|
|
|
defineFunction({ |
|
type: "mclass", |
|
names: ["\\stackrel", "\\overset", "\\underset"], |
|
props: { |
|
numArgs: 2, |
|
}, |
|
handler({parser, funcName}, args) { |
|
const baseArg = args[1]; |
|
const shiftedArg = args[0]; |
|
|
|
let mclass; |
|
if (funcName !== "\\stackrel") { |
|
|
|
mclass = binrelClass(baseArg); |
|
} else { |
|
mclass = "mrel"; |
|
} |
|
|
|
const baseOp = { |
|
type: "op", |
|
mode: baseArg.mode, |
|
limits: true, |
|
alwaysHandleSupSub: true, |
|
parentIsSupSub: false, |
|
symbol: false, |
|
suppressBaseShift: funcName !== "\\stackrel", |
|
body: ordargument(baseArg), |
|
}; |
|
|
|
const supsub = { |
|
type: "supsub", |
|
mode: shiftedArg.mode, |
|
base: baseOp, |
|
sup: funcName === "\\underset" ? null : shiftedArg, |
|
sub: funcName === "\\underset" ? shiftedArg : null, |
|
}; |
|
|
|
return { |
|
type: "mclass", |
|
mode: parser.mode, |
|
mclass, |
|
body: [supsub], |
|
isCharacterBox: utils.isCharacterBox(supsub), |
|
}; |
|
}, |
|
htmlBuilder, |
|
mathmlBuilder, |
|
}); |
|
|
|
|