Spaces:
Sleeping
Sleeping
File size: 5,758 Bytes
9d1374f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | import defineFunction from "../defineFunction";
import {makeSpan, makeVList, wrapFragment} from "../buildCommon";
import {MathNode} from "../mathMLTree";
import {stretchyMathML, stretchySvg} from "../stretchy";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {ParseNode} from "../parseNode";
import type {MathDomNode} from "../mathMLTree";
// Helper function
const paddedNode = (group?: MathDomNode | null | undefined) => {
const node = new MathNode("mpadded", group ? [group] : []);
node.setAttribute("width", "+0.6em");
node.setAttribute("lspace", "0.3em");
return node;
};
// Stretchy arrows with an optional argument
defineFunction({
type: "xArrow",
names: [
"\\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",
// The next 3 functions are here only to support the {CD} environment.
"\\\\cdrightarrow", "\\\\cdleftarrow", "\\\\cdlongequal",
],
props: {
numArgs: 1,
numOptionalArgs: 1,
},
handler({parser, funcName}, args, optArgs) {
return {
type: "xArrow",
mode: parser.mode,
label: funcName,
body: args[0],
below: optArgs[0],
};
},
htmlBuilder(group: ParseNode<"xArrow">, options) {
const style = options.style;
// Build the argument groups in the appropriate style.
// Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
// Some groups can return document fragments. Handle those by wrapping
// them in a span.
let newOptions = options.havingStyle(style.sup());
const upperGroup = wrapFragment(
html.buildGroup(group.body, newOptions, options), options);
const arrowPrefix = group.label.slice(0, 2) === "\\x" ? "x" : "cd";
upperGroup.classes.push(arrowPrefix + "-arrow-pad");
let lowerGroup;
if (group.below) {
// Build the lower group
newOptions = options.havingStyle(style.sub());
lowerGroup = wrapFragment(
html.buildGroup(group.below, newOptions, options), options);
lowerGroup.classes.push(arrowPrefix + "-arrow-pad");
}
const arrowBody = stretchySvg(group, options);
// Re shift: Note that stretchySvg returned arrowBody.depth = 0.
// The point we want on the math axis is at 0.5 * arrowBody.height.
const arrowShift = -options.fontMetrics().axisHeight +
0.5 * arrowBody.height;
// 2 mu kern. Ref: amsmath.dtx: #7\if0#2\else\mkern#2mu\fi
let upperShift = -options.fontMetrics().axisHeight
- 0.5 * arrowBody.height - 0.111; // 0.111 em = 2 mu
if (upperGroup.depth > 0.25 || group.label === "\\xleftequilibrium") {
upperShift -= upperGroup.depth; // shift up if depth encroaches
}
// Generate the vlist
let vlist;
if (lowerGroup) {
const lowerShift = -options.fontMetrics().axisHeight
+ lowerGroup.height + 0.5 * arrowBody.height
+ 0.111;
vlist = makeVList({
positionType: "individualShift",
children: [
{type: "elem", elem: upperGroup, shift: upperShift},
{type: "elem", elem: arrowBody, shift: arrowShift},
{type: "elem", elem: lowerGroup, shift: lowerShift},
],
}, options);
} else {
vlist = makeVList({
positionType: "individualShift",
children: [
{type: "elem", elem: upperGroup, shift: upperShift},
{type: "elem", elem: arrowBody, shift: arrowShift},
],
}, options);
}
// TODO(ts): Replace this with passing "svg-align" into makeVList.
(vlist as any).children[0].children[0].children[1].classes.push("svg-align");
return makeSpan(["mrel", "x-arrow"], [vlist], options);
},
mathmlBuilder(group, options) {
const arrowNode = stretchyMathML(group.label);
arrowNode.setAttribute(
"minsize", group.label.charAt(0) === "x" ? "1.75em" : "3.0em"
);
let node;
if (group.body) {
const upperNode = paddedNode(mml.buildGroup(group.body, options));
if (group.below) {
const lowerNode = paddedNode(mml.buildGroup(group.below, options));
node = new MathNode(
"munderover", [arrowNode, lowerNode, upperNode]
);
} else {
node = new MathNode("mover", [arrowNode, upperNode]);
}
} else if (group.below) {
const lowerNode = paddedNode(mml.buildGroup(group.below, options));
node = new MathNode("munder", [arrowNode, lowerNode]);
} else {
// This should never happen.
// Parser.js throws an error if there is no argument.
node = paddedNode();
node = new MathNode("mover", [arrowNode, node]);
}
return node;
},
});
|