pacer.style.marginRight = makeEm(slant); parts.unshift(spacer); } return buildCommon.makeSpan(["mop", "op-limits"], parts, options); }; ;// CONCATENATED MODULE: ./src/functions/op.js // Limits, symbols // Most operators have a large successor symbol, but these don't. const noSuccessor = ["\\smallint"]; // NOTE: Unlike most `htmlBuilder`s, this one handles not only "op", but also // "supsub" since some of them (like \int) can affect super/subscripting. const op_htmlBuilder = (grp, options) => { // Operators are handled in the TeXbook pg. 443-444, rule 13(a). let supGroup; let subGroup; let hasLimits = false; let group; if (grp.type === "supsub") { // If we have limits, supsub will pass us its group to handle. Pull // out the superscript and subscript and set the group to the op in // its base. 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 === src_Style.DISPLAY.size && group.symbol && !utils.contains(noSuccessor, group.name)) { // Most symbol operators get larger in displaystyle (rule 13) large = true; } let base; if (group.symbol) { // If this is a symbol, create the symbol. const fontName = large ? "Size2-Regular" : "Size1-Regular"; let stash = ""; if (group.name === "\\oiint" || group.name === "\\oiiint") { // No font glyphs yet, so use a glyph w/o the oval. // TODO: When font glyphs are available, delete this code. 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) { // We're in \oiint or \oiiint. Overlay the oval. // TODO: When font glyphs are available, delete this code. 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"); // $FlowFixMe base.italic = italic; } } else if (group.body) { // If this is a list, compose that list. const inner = buildExpression(group.body, options, true); if (inner.length === 1 && inner[0] instanceof SymbolNode) { base = inner[0]; base.classes[0] = "mop"; // replace old mclass } else { base = buildCommon.makeSpan(["mop"], inner, options); } } else { // Otherwise, this is a text operator. Build the text from the // operator's name. 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); } // If content of op is a single symbol, shift it vertically. let baseShift = 0; let slant = 0; if ((base instanceof SymbolNode || group.name === "\\oiint" || group.name === "\\oiiint") && !group.suppressBaseShift) { // We suppress the shift of the base of \overset and \underset. Otherwise, // shift the symbol so its center lies on the axis (rule 13). It // appears that our fonts have the centers of the symbols already // almost on the axis, so these numbers are very small. Note we // don't actually apply this here, but instead it is used either in // the vlist creation or separately when there are no limits. baseShift = (base.height - base.depth) / 2 - options.fontMetrics().axisHeight; // The slant of the symbol is just its italic correction. // $FlowFixMe 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 op_mathmlBuilder = (group, options) => { let node; if (group.symbol) { // This is a symbol. Just add the symbol. node = new MathNode("mo", [makeText(group.name, group.mode)]); if (utils.contains(noSuccessor, group.name)) { node.setAttribute("largeop", "false"); } } else if (group.body) { // This is an operator with children. Add them. node = new MathNode("mo", buildMathML_buildExpression(group.body, options)); } else { // This is a text operator. Add all of the characters from the // operator's name. node = new MathNode("mi", [new TextNode(group.name.slice(1))]); // Append an . // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4 const operator = new MathNode("mo", [makeText("\u2061", "text")]); if (group.parentIsSupSub) { node = new MathNode("mrow", [node, operator]); } else { node = newDocumentFragment([node, operator]); } } return node; }; const singleCharBigOps = { "\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: (_ref, args) => { let { parser, funcName } = _ref; 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: op_htmlBuilder, mathmlBuilder: op_mathmlBuilder }); // Note: calling defineFunction with a type that's already been defined only // works because the same htmlBuilder and mathmlBuilder are being used. defineFunction({ type: "op", names: ["\\mathop"], props: { numArgs: 1, primitive: true }, handler: (_ref2, args) => { let { parser } = _ref2; const body = args[0]; return { type: "op", mode: parser.mode, limits: false, parentIsSupSub: false, symbol: false, body: ordargument(body) }; }, htmlBuilder: op_htmlBuilder, mathmlBuilder: op_mathmlBuilder }); // There are 2 flags for operators; whether they produce limits in // displaystyle, and whether they are symbols and should grow in // displaystyle. These four groups cover the four possible choices. const singleCharIntegrals = { "\u222b": "\\int", "\u222c": "\\iint", "\u222d": "\\iiint", "\u222e": "\\oint", "\u222f": "\\oiint", "\u2230": "\\oiiint" }; // No limits, not symbols 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(_ref3) { let { parser, funcName } = _ref3; return { type: "op", mode: parser.mode, limits: false, parentIsSupSub: false, symbol: false, name: funcName }; }, htmlBuilder: op_htmlBuilder, mathmlBuilder: op_mathmlBuilder }); // Limits, not symbols defineFunction({ type: "op", names: ["\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup"], props: { numArgs: 0 }, handler(_ref4) { let { parser, funcName } = _ref4; return { type: "op", mode: parser.mode, limits: true, parentIsSupSub: false, symbol: false, name: funcName }; }, htmlBuilder: op_htmlBuilder, mathmlBuilder: op_mathmlBuilder }); // No limits, symbols defineFunction({ type: "op", names: ["\\int", "\\iint", "\\iiint", "\\oint", "\\oiint", "\\oiiint", "\u222b", "\u222c", "\u222d", "\u222e", "\u222f", "\u2230"], props: { numArgs: 0 }, handler(_ref5) { let { parser, funcName } = _ref5; 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: op_htmlBuilder, mathmlBuilder: op_mathmlBuilder }); ;// CONCATENATED MODULE: ./src/functions/operatorname.js // NOTE: Unlike most `htmlBuilder`s, this one handles not only // "operatorname", but also "supsub" since \operatorname* can // affect super/subscripting. const operatorname_htmlBuilder = (grp, options) => { // Operators are handled in the TeXbook pg. 443-444, rule 13(a). let supGroup; let subGroup; let hasLimits = false; let group; if (grp.type === "supsub") { // If we have limits, supsub will pass us its group to handle. Pull // out the superscript and subscript and set the group to the op in // its base. supGroup = grp.sup; subGroup = grp.sub; group = assertNodeType(grp.base, "operatorname"); hasLimits = true; } else { group = assertNodeType(grp, "operatorname"); } let base; if (group.body.length > 0) { const body = group.body.map(child => { // $FlowFixMe: Check if the node has a string `text` property. const childText = child.text; if (typeof childText === "string") { return { type: "textord", mode: child.mode, text: childText }; } else { return child; } }); // Consolidate function names into symbol characters. const expression = buildExpression(body, options.withFont("mathrm"), true); for (let i = 0; i < expression.length; i++) { const child = expression[i]; if (child instanceof SymbolNode) { // Per amsopn package, // change minus to hyphen and \ast to asterisk child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); } } base = buildCommon.makeSpan(["mop"], expression, options); } else { base = buildCommon.makeSpan(["mop"], [], options); } if (hasLimits) { return assembleSupSub(base, supGroup, subGroup, options, options.style, 0, 0); } else { return base; } }; const operatorname_mathmlBuilder = (group, options) => { // The steps taken here are similar to the html version. let expression = buildMathML_buildExpression(group.body, options.withFont("mathrm")); // Is expression a string or has it something like a fraction? let isAllString = true; // default for (let i = 0; i < expression.length; i++) { const node = expression[i]; if (node instanceof mathMLTree.SpaceNode) {// Do nothing } else if (node instanceof mathMLTree.MathNode) { switch (node.type) { case "mi": case "mn": case "ms": case "mspace": case "mtext": break; // Do nothing yet. case "mo": { const child = node.children[0]; if (node.children.length === 1 && child instanceof mathMLTree.TextNode) { child.text = child.text.replace(/\u2212/, "-").replace(/\u2217/, "*"); } else { isAllString = false; } break; } default: isAllString = false; } } else { isAllString = false; } } if (isAllString) { // Write a single TextNode instead of multiple nested tags. const word = expression.map(node => node.toText()).join(""); expression = [new mathMLTree.TextNode(word)]; } const identifier = new mathMLTree.MathNode("mi", expression); identifier.setAttribute("mathvariant", "normal"); // \u2061 is the same as ⁡ // ref: https://www.w3schools.com/charsets/ref_html_entities_a.asp const operator = new mathMLTree.MathNode("mo", [makeText("\u2061", "text")]); if (group.parentIsSupSub) { return new mathMLTree.MathNode("mrow", [identifier, operator]); } else { return mathMLTree.newDocumentFragment([identifier, operator]); } }; // \operatorname // amsopn.dtx: \mathop{#1\kern\z@\operator@font#3}\newmcodes@ defineFunction({ type: "operatorname", names: ["\\operatorname@", "\\operatornamewithlimits"], props: { numArgs: 1 }, handler: (_ref, args) => { let { parser, funcName } = _ref; const body = args[0]; return { type: "operatorname", mode: parser.mode, body: ordargument(body), alwaysHandleSupSub: funcName === "\\operatornamewithlimits", limits: false, parentIsSupSub: false }; }, htmlBuilder: operatorname_htmlBuilder, mathmlBuilder: operatorname_mathmlBuilder }); defineMacro("\\operatorname", "\\@ifstar\\operatornamewithlimits\\operatorname@"); ;// CONCATENATED MODULE: ./src/functions/ordgroup.js defineFunctionBuilders({ type: "ordgroup", htmlBuilder(group, options) { if (group.semisimple) { return buildCommon.makeFragment(buildExpression(group.body, options, false)); } return buildCommon.makeSpan(["mord"], buildExpression(group.body, options, true), options); }, mathmlBuilder(group, options) { return buildExpressionRow(group.body, options, true); } }); ;// CONCATENATED MODULE: ./src/functions/overline.js defineFunction({ type: "overline", names: ["\\overline"], props: { numArgs: 1 }, handler(_ref, args) { let { parser } = _ref; const body = args[0]; return { type: "overline", mode: parser.mode, body }; }, htmlBuilder(group, options) { // Overlines are handled in the TeXbook pg 443, Rule 9. // Build the inner group in the cramped style. const innerGroup = buildGroup(group.body, options.havingCrampedStyle()); // Create the line above the body const line = buildCommon.makeLineSpan("overline-line", options); // Generate the vlist, with the appropriate kerns const defaultRuleThickness = options.fontMetrics().defaultRuleThickness; const vlist = buildCommon.makeVList({ positionType: "firstBaseline", children: [{ type: "elem", elem: innerGroup }, { type: "kern", size: 3 * defaultRuleThickness }, { type: "elem", elem: line }, { type: "kern", size: defaultRuleThickness }] }, options); return buildCommon.makeSpan(["mord", "overline"], [vlist], options); }, mathmlBuilder(group, options) { const operator = new mathMLTree.MathNode("mo", [new mathMLTree.TextNode("\u203e")]); operator.setAttribute("stretchy", "true"); const node = new mathMLTree.MathNode("mover", [buildMathML_buildGroup(group.body, options), operator]); node.setAttribute("accent", "true"); return node; } }); ;// CONCATENATED MODULE: ./src/functions/phantom.js defineFunction({ type: "phantom", names: ["\\phantom"], props: { numArgs: 1, allowedInText: true }, handler: (_ref, args) => { let { parser } = _ref; const body = args[0]; return { type: "phantom", mode: parser.mode, body: ordargument(body) }; }, htmlBuilder: (group, options) => { const elements = buildExpression(group.body, options.withPhantom(), false); // \phantom isn't supposed to affect the elements it contains. // See "color" for more details. return buildCommon.makeFragment(elements); }, mathmlBuilder: (group, options) => { const inner = buildMathML_buildExpression(group.body, options); return new mathMLTree.MathNode("mphantom", inner); } }); defineFunction({ type: "hphantom", names: ["\\hphantom"], props: { numArgs: 1, allowedInText: true }, handler: (_ref2, args) => { let { parser } = _ref2; const body = args[0]; return { type: "hphantom", mode: parser.mode, body }; }, htmlBuilder: (group, options) => { let node = buildCommon.makeSpan([], [buildGroup(group.body, options.withPhantom())]); node.height = 0; node.depth = 0; if (node.children) { for (let i = 0; i < node.children.length; i++) { node.children[i].height = 0; node.children[i].depth = 0; } } // See smash for comment re: use of makeVList node = buildCommon.makeVList({ positionType: "firstBaseline", children: [{ type: "elem", elem: node }] }, options); // For spacing, TeX treats \smash as a math group (same spacing as ord). return buildCommon.makeSpan(["mord"], [node], options); }, mathmlBuilder: (group, options) => { const inner = buildMathML_buildExpression(ordargument(group.body), options); const phantom = new mathMLTree.MathNode("mphantom", inner); const node = new mathMLTree.MathNode("mpadded", [phantom]); node.setAttribute("height", "0px"); node.setAttribute("depth", "0px"); return node; } }); defineFunction({ type: "vphantom", names: ["\\vphantom"], props: { numArgs: 1, allowedInText: true }, handler: (_ref3, args) => { let { parser } = _ref3; const body = args[0]; return { type: "vphantom", mode: parser.mode, body }; }, htmlBuilder: (group, options) => { const inner = buildCommon.makeSpan(["inner"], [buildGroup(group.body, options.withPhantom())]); const fix = buildCommon.makeSpan(["fix"], []); return buildCommon.makeSpan(["mord", "rlap"], [inner, fix], options); }, mathmlBuilder: (group, options) => { const inner = buildMathML_buildExpression(ordargument(group.body), options); const phantom = new mathMLTree.MathNode("mphantom", inner); const node = new mathMLTree.MathNode("mpadded", [phantom]); node.setAttribute("width", "0px"); return node; } }); ;// CONCATENATED MODULE: ./src/functions/raisebox.js // Box manipulation defineFunction({ type: "raisebox", names: ["\\raisebox"], props: { numArgs: 2, argTypes: ["size", "hbox"], allowedInText: true }, handler(_ref, args) { let { parser } = _ref; const amount = assertNodeType(args[0], "size").value; const body = args[1]; return { type: "raisebox", mode: parser.mode, dy: amount, body }; }, htmlBuilder(group, options) { const body = buildGroup(group.body, options); const dy = calculateSize(group.dy, options); return buildCommon.makeVList({ positionType: "shift", positionData: -dy, children: [{ type: "elem", elem: body }] }, options); }, mathmlBuilder(group, options) { const node = new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)]); const dy = group.dy.number + group.dy.unit; node.setAttribute("voffset", dy); return node; } }); ;// CONCATENATED MODULE: ./src/functions/relax.js defineFunction({ type: "internal", names: ["\\relax"], props: { numArgs: 0, allowedInText: true, allowedInArgument: true }, handler(_ref) { let { parser } = _ref; return { type: "internal", mode: parser.mode }; } }); ;// CONCATENATED MODULE: ./src/functions/rule.js defineFunction({ type: "rule", names: ["\\rule"], props: { numArgs: 2, numOptionalArgs: 1, allowedInText: true, allowedInMath: true, argTypes: ["size", "size", "size"] }, handler(_ref, args, optArgs) { let { parser } = _ref; const shift = optArgs[0]; const width = assertNodeType(args[0], "size"); const height = assertNodeType(args[1], "size"); return { type: "rule", mode: parser.mode, shift: shift && assertNodeType(shift, "size").value, width: width.value, height: height.value }; }, htmlBuilder(group, options) { // Make an empty span for the rule const rule = buildCommon.makeSpan(["mord", "rule"], [], options); // Calculate the shift, width, and height of the rule, and account for units const width = calculateSize(group.width, options); const height = calculateSize(group.height, options); const shift = group.shift ? calculateSize(group.shift, options) : 0; // Style the rule to the right size rule.style.borderRightWidth = makeEm(width); rule.style.borderTopWidth = makeEm(height); rule.style.bottom = makeEm(shift); // Record the height and width rule.width = width; rule.height = height + shift; rule.depth = -shift; // Font size is the number large enough that the browser will // reserve at least `absHeight` space above the baseline. // The 1.125 factor was empirically determined rule.maxFontSize = height * 1.125 * options.sizeMultiplier; return rule; }, mathmlBuilder(group, options) { const width = calculateSize(group.width, options); const height = calculateSize(group.height, options); const shift = group.shift ? calculateSize(group.shift, options) : 0; const color = options.color && options.getColor() || "black"; const rule = new mathMLTree.MathNode("mspace"); rule.setAttribute("mathbackground", color); rule.setAttribute("width", makeEm(width)); rule.setAttribute("height", makeEm(height)); const wrapper = new mathMLTree.MathNode("mpadded", [rule]); if (shift >= 0) { wrapper.setAttribute("height", makeEm(shift)); } else { wrapper.setAttribute("height", makeEm(shift)); wrapper.setAttribute("depth", makeEm(-shift)); } wrapper.setAttribute("voffset", makeEm(shift)); return wrapper; } }); ;// CONCATENATED MODULE: ./src/functions/sizing.js function sizingGroup(value, options, baseOptions) { const inner = buildExpression(value, options, false); const multiplier = options.sizeMultiplier / baseOptions.sizeMultiplier; // Add size-resetting classes to the inner list and set maxFontSize // manually. Handle nested size changes. for (let i = 0; i < inner.length; i++) { const pos = inner[i].classes.indexOf("sizing"); if (pos < 0) { Array.prototype.push.apply(inner[i].classes, options.sizingClasses(baseOptions)); } else if (inner[i].classes[pos + 1] === "reset-size" + options.size) { // This is a nested size change: e.g., inner[i] is the "b" in // `\Huge a \small b`. Override the old size (the `reset-` class) // but not the new size. inner[i].classes[pos + 1] = "reset-size" + baseOptions.size; } inner[i].height *= multiplier; inner[i].depth *= multiplier; } return buildCommon.makeFragment(inner); } const sizeFuncs = ["\\tiny", "\\sixptsize", "\\scriptsize", "\\footnotesize", "\\small", "\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge"]; const sizing_htmlBuilder = (group, options) => { // Handle sizing operators like \Huge. Real TeX doesn't actually allow // these functions inside of math expressions, so we do some special // handling. const newOptions = options.havingSize(group.size); return sizingGroup(group.body, newOptions, options); }; defineFunction({ type: "sizing", names: sizeFuncs, props: { numArgs: 0, allowedInText: true }, handler: (_ref, args) => { let { breakOnTokenText, funcName, parser } = _ref; const body = parser.parseExpression(false, breakOnTokenText); return { type: "sizing", mode: parser.mode, // Figure out what size to use based on the list of functions above size: sizeFuncs.indexOf(funcName) + 1, body }; }, htmlBuilder: sizing_htmlBuilder, mathmlBuilder: (group, options) => { const newOptions = options.havingSize(group.size); const inner = buildMathML_buildExpression(group.body, newOptions); const node = new mathMLTree.MathNode("mstyle", inner); // TODO(emily): This doesn't produce the correct size for nested size // changes, because we don't keep state of what style we're currently // in, so we can't reset the size to normal before changing it. Now // that we're passing an options parameter we should be able to fix // this. node.setAttribute("mathsize", makeEm(newOptions.sizeMultiplier)); return node; } }); ;// CONCATENATED MODULE: ./src/functions/smash.js // smash, with optional [tb], as in AMS defineFunction({ type: "smash", names: ["\\smash"], props: { numArgs: 1, numOptionalArgs: 1, allowedInText: true }, handler: (_ref, args, optArgs) => { let { parser } = _ref; let smashHeight = false; let smashDepth = false; const tbArg = optArgs[0] && assertNodeType(optArgs[0], "ordgroup"); if (tbArg) { // Optional [tb] argument is engaged. // ref: amsmath: \renewcommand{\smash}[1][tb]{% // def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}% let letter = ""; for (let i = 0; i < tbArg.body.length; ++i) { const node = tbArg.body[i]; // $FlowFixMe: Not every node type has a `text` property. letter = node.text; if (letter === "t") { smashHeight = true; } else if (letter === "b") { smashDepth = true; } else { smashHeight = false; smashDepth = false; break; } } } else { smashHeight = true; smashDepth = true; } const body = args[0]; return { type: "smash", mode: parser.mode, body, smashHeight, smashDepth }; }, htmlBuilder: (group, options) => { const node = buildCommon.makeSpan([], [buildGroup(group.body, options)]); if (!group.smashHeight && !group.smashDepth) { return node; } if (group.smashHeight) { node.height = 0; // In order to influence makeVList, we have to reset the children. if (node.children) { for (let i = 0; i < node.children.length; i++) { node.children[i].height = 0; } } } if (group.smashDepth) { node.depth = 0; if (node.children) { for (let i = 0; i < node.children.length; i++) { node.children[i].depth = 0; } } } // At this point, we've reset the TeX-like height and depth values. // But the span still has an HTML line height. // makeVList applies "display: table-cell", which prevents the browser // from acting on that line height. So we'll call makeVList now. const smashedNode = buildCommon.makeVList({ positionType: "firstBaseline", children: [{ type: "elem", elem: node }] }, options); // For spacing, TeX treats \hphantom as a math group (same spacing as ord). return buildCommon.makeSpan(["mord"], [smashedNode], options); }, mathmlBuilder: (group, options) => { const node = new mathMLTree.MathNode("mpadded", [buildMathML_buildGroup(group.body, options)]); if (group.smashHeight) { node.setAttribute("height", "0px"); } if (group.smashDepth) { node.setAttribute("depth", "0px"); } return node; } }); ;// CONCATENATED MODULE: ./src/functions/sqrt.js defineFunction({ type: "sqrt", names: ["\\sqrt"], props: { numArgs: 1, numOptionalArgs: 1 }, handler(_ref, args, optArgs) { let { parser } = _ref; const index = optArgs[0]; const body = args[0]; return { type: "sqrt", mode: parser.mode, body, index }; }, htmlBuilder(group, options) { // Square roots are handled in the TeXbook pg. 443, Rule 11. // First, we do the same steps as in overline to build the inner group // and line let inner = buildGroup(group.body, options.havingCrampedStyle()); if (inner.height === 0) { // Render a small surd. inner.height = options.fontMetrics().xHeight; } // Some groups can return document fragments. Handle those by wrapping // them in a span. inner = buildCommon.wrapFragment(inner, options); // Calculate the minimum size for the \surd delimiter const metrics = options.fontMetrics(); const theta = metrics.defaultRuleThickness; let phi = theta; if (options.style.id < src_Style.TEXT.id) { phi = options.fontMetrics().xHeight; } // Calculate the clearance between the body and line let lineClearance = theta + phi / 4; const minDelimiterHeight = inner.height + inner.depth + lineClearance + theta; // Create a sqrt SVG of the required minimum size const { span: img, ruleWidth, advanceWidth } = delimiter.sqrtImage(minDelimiterHeight, options); const delimDepth = img.height - ruleWidth; // Adjust the clearance based on the delimiter size if (delimDepth > inner.height + inner.depth + lineClearance) { lineClearance = (lineClearance + delimDepth - inner.height - inner.depth) / 2; } // Shift the sqrt image const imgShift = img.height - inner.height - lineClearance - ruleWidth; inner.style.paddingLeft = makeEm(advanceWidth); // Overlay the image and the argument. const body = buildCommon.makeVList({ positionType: "firstBaseline", children: [{ type: "elem", elem: inner, wrapperClasses: ["svg-align"] }, { type: "kern", size: -(inner.height + imgShift) }, { type: "elem", elem: img }, { type: "kern", size: ruleWidth }] }, options); if (!group.index) { return buildCommon.makeSpan(["mord", "sqrt"], [body], options); } else { // Handle the optional root index // The index is always in scriptscript style const newOptions = options.havingStyle(src_Style.SCRIPTSCRIPT); const rootm = buildGroup(group.index, newOptions, options); // The amount the index is shifted by. This is taken from the TeX // source, in the definition of `\r@@t`. const toShift = 0.6 * (body.height - body.depth); // Build a VList with the superscript shifted up correctly const rootVList = buildCommon.makeVList({ positionType: "shift", positionData: -toShift, children: [{ type: "elem", elem: rootm }] }, options); // Add a class surrounding it so we can add on the appropriate // kerning const rootVListWrap = buildCommon.makeSpan(["root"], [rootVList]); return buildCommon.makeSpan(["mord", "sqrt"], [rootVListWrap, body], options); } }, mathmlBuilder(group, options) { const { body, index } = group; return index ? new mathMLTree.MathNode("mroot", [buildMathML_buildGroup(body, options), buildMathML_buildGroup(index, options)]) : new mathMLTree.MathNode("msqrt", [buildMathML_buildGroup(body, options)]); } }); ;// CONCATENATED MODULE: ./src/functions/styling.js const styling_styleMap = { "display": src_Style.DISPLAY, "text": src_Style.TEXT, "script": src_Style.SCRIPT, "scriptscript": src_Style.SCRIPTSCRIPT }; defineFunction({ type: "styling", names: ["\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"], props: { numArgs: 0, allowedInText: true, primitive: true }, handler(_ref, args) { let { breakOnTokenText, funcName, parser } = _ref; // parse out the implicit body const body = parser.parseExpression(true, breakOnTokenText); // TODO: Refactor to avoid duplicating styleMap in multiple places (e.g. // here and in buildHTML and de-dupe the enumeration of all the styles). // $FlowFixMe: The names above exactly match the styles. const style = funcName.slice(1, funcName.length - 5); return { type: "styling", mode: parser.mode, // Figure out what style to use by pulling out the style from // the function name style, body }; }, htmlBuilder(group, options) { // Style changes are handled in the TeXbook on pg. 442, Rule 3. const newStyle = styling_styleMap[group.style]; const newOptions = options.havingStyle(newStyle).withFont(''); return sizingGroup(group.body, newOptions, options); }, mathmlBuilder(group, options) { // Figure out what style we're changing to. const newStyle = styling_styleMap[group.style]; const newOptions = options.havingStyle(newStyle); const inner = buildMathML_buildExpression(group.body, newOptions); const node = new mathMLTree.MathNode("mstyle", inner); const styleAttributes = { "display": ["0", "true"], "text": ["0", "false"], "script": ["1", "false"], "scriptscript": ["2", "false"] }; const attr = styleAttributes[group.style]; node.setAttribute("scriptlevel", attr[0]); node.setAttribute("displaystyle", attr[1]); return node; } }); ;// CONCATENATED MODULE: ./src/functions/supsub.js /** * Sometimes, groups perform special rules when they have superscripts or * subscripts attached to them. This function lets the `supsub` group know that * Sometimes, groups perform special rules when they have superscripts or * its inner element should handle the superscripts and subscripts instead of * handling them itself. */ const htmlBuilderDelegate = function (group, options) { const base = group.base; if (!base) { return null; } else if (base.type === "op") { // Operators handle supsubs differently when they have limits // (e.g. `\displaystyle\sum_2^3`) const delegate = base.limits && (options.style.size === src_Style.DISPLAY.size || base.alwaysHandleSupSub); return delegate ? op_htmlBuilder : null; } else if (base.type === "operatorname") { const delegate = base.alwaysHandleSupSub && (options.style.size === src_Style.DISPLAY.size || base.limits); return delegate ? operatorname_htmlBuilder : null; } else if (base.type === "accent") { return utils.isCharacterBox(base.base) ? htmlBuilder : null; } else if (base.type === "horizBrace") { const isSup = !group.sub; return isSup === base.isOver ? horizBrace_htmlBuilder : null; } else { return null; } }; // Super scripts and subscripts, whose precise placement can depend on other // functions that precede them. defineFunctionBuilders({ type: "supsub", htmlBuilder(group, options) { // Superscript and subscripts are handled in the TeXbook on page // 445-446, rules 18(a-f). // Here is where we defer to the inner group if it should handle // superscripts and subscripts itself. const builderDelegate = htmlBuilderDelegate(group, options); if (builderDelegate) { return builderDelegate(group, options); } const { base: valueBase, sup: valueSup, sub: valueSub } = group; const base = buildGroup(valueBase, options); let supm; let subm; const metrics = options.fontMetrics(); // Rule 18a let supShift = 0; let subShift = 0; const isCharacterBox = valueBase && utils.isCharacterBox(valueBase); if (valueSup) { const newOptions = options.havingStyle(options.style.sup()); supm = buildGroup(valueSup, newOptions, options); if (!isCharacterBox) { supShift = base.height - newOptions.fontMetrics().supDrop * newOptions.sizeMultiplier / options.sizeMultiplier; } } if (valueSub) { const newOptions = options.havingStyle(options.style.sub()); subm = buildGroup(valueSub, newOptions, options); if (!isCharacterBox) { subShift = base.depth + newOptions.fontMetrics().subDrop * newOptions.sizeMultiplier / options.sizeMultiplier; } } // Rule 18c let minSupShift; if (options.style === src_Style.DISPLAY) { minSupShift = metrics.sup1; } else if (options.style.cramped) { minSupShift = metrics.sup3; } else { minSupShift = metrics.sup2; } // scriptspace is a font-size-independent size, so scale it // appropriately for use as the marginRight. const multiplier = options.sizeMultiplier; const marginRight = makeEm(0.5 / metrics.ptPerEm / multi "301", "message" => "ERROR - [wsPolizasAnalytics] Parametros incompletos para el Servicio Web, faltan el appOwner"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } if($anio == "") { $j_array = array('code' => "302", "message" => "ERROR - [wsPolizasAnalytics] Parametros incompletos para el Servicio Web, faltan el Anio de vigencia"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } if($mes == "") { $j_array = array('code' => "303", "message" => "ERROR - [wsPolizasAnalytics] Parametros incompletos para el Servicio Web, faltan el Mes de vigencia"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } if($organi_id_Analytics == "") { $j_array = array('code' => "304", "message" => "ERROR - [wsPolizasAnalytics] Parametros incompletos para el Servicio Web, faltan el Organization ID de Analytics"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } if($RFC == "") { $j_array = array('code' => "305", "message" => "ERROR - [wsPolizasAnalytics] Parametros incompletos para el Servicio Web, faltan el RFC"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } if($idWorkspace == "") { $j_array = array('code' => "306", "message" => "ERROR - [wsPolizasAnalytics] Parametros incompletos para el Servicio Web, faltan el ID del workspace"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } if($idView == "") { $j_array = array('code' => "307", "message" => "ERROR - [wsPolizasAnalytics] Parametros incompletos para el Servicio Web, faltan el Organization ID de la vista"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } $dirBase = realpath("../"); ### DEFINICIÓN DE CONSTANTES ################################################### $SendaPEM = "archs_pem/"; $SendaXML = "archs_xml/"; $SendaZIP = "archs_zip/"; #---------------------------------------------------------------- #== Llamado para generar el token para donatello #---------------------------------------------------------------- $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => 'https://donatello.aptuslegal.app/oauth/token/'.$organi_id_Analytics, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => array( 'Authorization: Token e68d5a079f937ea29ad2ec5a5b105b75491a0e0c' ), )); $response = curl_exec($curl); curl_close($curl); // Decodificamos la respuesta JSON $data = json_decode($response, true); // Accedemos al valor de 'access_token de donatello' $access_token = $data['access_token']; #---------------------------------------------------------------- #== Hacemos el llamado para generar la obtención de los datos en la operación Bulk y obtenemos el Job ID. #---------------------------------------------------------------- $curl = curl_init(); // $dia = obtenerDiasDelMes($mes); $anio = "2025"; $mes = "04"; $dia = "09"; if($dia == "Formato de mes inválido."){ $j_array = array('code' => "309", "message" => "ERROR - [wsPolizasAnalytics] El formato del parámetro del mes no es valido"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } curl_setopt_array($curl, array( CURLOPT_URL => 'https://analyticsapi.zoho.com/restapi/v2/bulk/workspaces/'.$idWorkspace.'/views/'.$idView.'/data?CONFIG=%7B%22criteria%22%3A%22%5C%22Fecha%20de%20transaccion%5C%22%20between%20%27'.$anio.'-'.$mes.'-09%27%20and%20%27'.$anio.'-'.$mes.'-'.$dia.'%27%22%2C%20%22responseFormat%22%3A%22json%22%7D', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => array( 'ZANALYTICS-ORGID: '.$organi_id_Analytics, 'Authorization: Zoho-oauthtoken '.$access_token, 'Content-Type: application/json' ), )); $response = curl_exec($curl); curl_close($curl); $responseData = json_decode($response, true); ob_start(); echo '
';
  print_r($responseData);
  echo '
'; $debug_output = ob_get_clean(); // Acceder al 'jobId' $jobId = $responseData['data']['jobId']; #---------------------------------------------------------------- #== Obtenemos el status del JobId para saber si se puede descargar la respuesta #---------------------------------------------------------------- $jobStatus = "JOB IN PROGRESS"; while ($jobStatus === "JOB IN PROGRESS") { $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => 'https://analyticsapi.zoho.com/restapi/v2/bulk/workspaces/'.$idWorkspace.'/exportjobs/'.$jobId, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => array( 'ZANALYTICS-ORGID: '.$organi_id_Analytics, 'Authorization: Zoho-oauthtoken '.$access_token ), )); $response = curl_exec($curl); // Decodificar la respuesta JSON $responseData = json_decode($response, true); // Verificar el status del trabajo if (isset($responseData['data']['jobStatus'])) { $jobStatus = $responseData['data']['jobStatus']; } else { // Si no se encuentra jobStatus en la respuesta, salir del ciclo $j_array = array('code' => "311", "message" => "ERROR - [wsPolizasAnalytics] Error al esperar el Status de JobId de Analytics"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } // Esperar 5 segundos antes de volver a consultar sleep(5); } //Si se obtiene un status diferente al esperado lo interpreta como error if($jobStatus != "JOB COMPLETED"){ $j_array = array('code' => "312", "message" => "ERROR - [wsPolizasAnalytics] Error el Status del JobId inesperado"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } if (isset($responseData['data']['downloadUrl'])) { $downloadUrl = $responseData['data']['downloadUrl']; // echo "Download URL: " . $downloadUrl; } else { $j_array = array('code' => "313", "message" => "ERROR - [wsPolizasAnalytics] Error al obtener el downloadUrl de Analytics"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } #---------------------------------------------------------------- #== Llamado para la obtención de los datos en formato JSON de la vista de Analytics. #---------------------------------------------------------------- $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $downloadUrl, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_HTTPHEADER => array( 'ZANALYTICS-ORGID: '.$organi_id_Analytics, 'Authorization: Zoho-oauthtoken '.$access_token ), )); $response = curl_exec($curl); curl_close($curl); // Decodificar el JSON $responseData = json_decode($response, true); // Verificar si el campo 'data' existe if (isset($responseData['data'])) { #---------------------------------------------------------------- #== Creación de la variable de tipo DOM, aquí se conforma el XML a timbrar posteriormente. #---------------------------------------------------------------- $xml = new DOMdocument('1.0', 'UTF-8'); $root = $xml->createElement("Polizas"); $root = $xml->appendChild($root); #== Se crea e inserta el primer nodo donde se declaran los namespaces ====== cargaAttNodo( $root, array("xsi:schemaLocation"=>"www.sat.gob.mx/esquemas/ContabilidadE/1_1/PolizasPeriodo http://www.sat.gob.mx/esquemas/ContabilidadE/1_1/CatalogosParaEsqContE/CatalogosParaEsqContE.xsd", "xmlns:Polizas"=>"www.sat.gob.mx/esquemas/ContabilidadE/1_1/AuxiliarFolios", "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema") ); #== Rutina de integración de nodos ========================================= cargaAttNodo($root, array( "Version"=>"1.3", "RFC"=>$RFC, "Mes"=>$mes, "Anio"=>$anio ) ); $sumTotalDebe = 0; $sumTotalHaber = 0; $totalDebe = 0; $totalHaber = 0; $polizaControl = ""; $polizaNodo = null; // Variable para mantener la referencia al nodo Poliza $numeroTotalPolizas = 0; $array = array_reverse($responseData['data']); foreach ($array as $item) { // Asignar cada valor a variables $tipoPoliza = $item['Tipo Poliza']; $transaccion = $item['Transaccion']; $fechaTransaccion = str_replace('.', '-', $item['Fecha de transaccion']); $idCuenta = $item['ID de la cuenta']; $nombre = $item['Nombre']; $debe = $item['Debe']; $haber = $item['Haber']; $creditoDebitoTotal = $item['Credito-Debito - Total']; $debitoCreditoTotal = $item['Debito-Credito Total']; $concepto = $item['Concepto']; $numCta = $item['NumCta']; // Cuando detectas una nueva transacción (nueva póliza) if($transaccion != $polizaControl){ // Si ya hay una póliza en proceso, agregas el atributo MontoTotal a la póliza anterior if ($polizaNodo !== null) { $montoTotal = $totalDebe - $totalHaber; $polizaNodo->setAttribute("MontoTotal", number_format($montoTotal, 2, '.', '')); // Agregar MontoTotal } // Crear una nueva póliza $polizaControl = $transaccion; $Poliza = $xml->createElement("Poliza"); $polizaNodo = $root->appendChild($Poliza); cargaAttNodo($polizaNodo, array( "NumUnIdenPol" => $transaccion, "Fecha" => $fechaTransaccion, "Concepto" => $tipoPoliza )); // Reiniciar los totales para la nueva póliza $totalDebe = 0; $totalHaber = 0; } // Crear un nodo Transaccion y agregarlo a la póliza actual $transaccion = $xml->createElement("Transaccion"); $nodo = $Poliza->appendChild($transaccion); cargaAttNodo($nodo, array( "NumCta" => $numCta, "DesCta" => $nombre, "Concepto" => $concepto, "Debe" => $debe, "Haber" => $haber )); // Acumular los valores de Debe y Haber $totalDebe += (float)$debe; $totalHaber += (float)$haber; // Acumular los totales generales $sumTotalDebe += (float)$debe; $sumTotalHaber += (float)$haber; $numeroTotalPolizas += 1; } // Para la última póliza en el loop, también agregamos el MontoTotal if ($polizaNodo !== null) { $montoTotal = $totalDebe - $totalHaber; $polizaNodo->setAttribute("MontoTotal", number_format($montoTotal, 2, '.', '')); // Agregar MontoTotal al final } // Establecer el tipo de contenido a XML y devolver el contenido // header('Content-Type: application/xml; charset=utf-8'); // echo $xml->saveXML(); #=== Se guarda el archivo .XML del Catalogo de Cuentas ======================= $file_name_with_full_path = '/var/www/html/aptusContaElec/archs_xml/'.$RFC.$anio.$mes."PP.xml"; #=== Si existe el archivo XML se elimina para crear el nuevo ======================= if(!file_exists($file_name_with_full_path)){ unlink($file_name_with_full_path); } #=== Se guarda el archivo XML creado ======================= $polizaXML = $xml->saveXML(); $xml->formatOutput = true; $xml->save($file_name_with_full_path); unset($xml); #=== Se dan permisos de escritura al archivo .xml. ========================= chmod($file_name_with_full_path, 0777); #=== Se procede a crear el archivo ZIP del Catalogo de Cuentas ========================= $zip = new ZipArchive(); $nombreArchivoZip = '/var/www/html/aptusContaElec/archs_zip/'.$RFC.$anio.$mes."PP.zip"; $nameArchZip = $RFC.$anio.$mes."PP.zip"; #=== Si existe el archivo ZIP se elimina para crear el nuevo ======================= if(!file_exists($nombreArchivoZip)){ unlink($nombreArchivoZip); } if (!$zip->open($nombreArchivoZip, ZipArchive::CREATE | ZipArchive::OVERWRITE)) { exit("Error abriendo ZIP en $nombreArchivoZip"); } $nombre = basename($file_name_with_full_path); $zip->addFile($file_name_with_full_path, $nombre); $resultado = $zip->close(); if ($resultado) { } else { $j_array = array('code' => "318", "message" => "ERROR - [wsPolizasAnalytics] Error al crear el archivo ZIP"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } #---------------------------------------------------------------- # Se sube archivo ZIP a Workdrive #---------------------------------------------------------------- $woa_access_token = oauth($appOwner, 'ZWorkrdv', $woa_RefreshToken, $woa_ClientId, $woa_ClientSecret, $woa_RedirectUri, $woa_GrantType, $woa_AuthUrl); $urlWorkdrv = 'https://workdrive.zoho.com/api/v1/upload?parent_id='.$wdrv_parent_id.'&filename='.$nameArchZip.'&override-name-exist=true'; $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $urlWorkdrv, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => array('content'=> new CURLFILE($nombreArchivoZip)), CURLOPT_HTTPHEADER => array('Authorization: Zoho-oauthtoken '.$woa_access_token ), ) ); $response = curl_exec($curl); curl_close($curl); #=== FIN DEL PROCESO ======================= $j_array = array('code' => "200", 'message' => "Proceso de creacion de archivo de pólizas del periodo exitoso", 'xml_file_name' => $RFC.$anio.$mes."PP.xml", 'zip_file_name' => $RFC.$anio.$mes."PP.zip",'Total de Polizas' => $numeroTotalPolizas,'Total Debe' =>number_format($sumTotalDebe, 2, '.', ''),'Total Haber' => number_format($sumTotalHaber, 2, '.', '')); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } else { $j_array = array('code' => "315", "message" => "ERROR - [wsPolizasAnalytics] No se obtuvieron datos de la vista"); $Resultado = json_encode($j_array); echo $Resultado; return $Resultado; } ### FUNCIONES DEL MÓDULO ######################################################### # Función que integra los nodos al archivo .XML function cargaAttNodo(&$nodo, $attr){ global $xmldoc; foreach ($attr as $key => $val){ $val = preg_replace('/\s\s+/', ' ', $val); $val = trim($val); if (strlen($val)>0){ $val = utf8_encode(str_replace("|","/",$val)); $nodo->setAttribute($key,$val); } } } function obtenerDiasDelMes($mes) { // Validar que el parámetro sea una cadena con dos dígitos if (!preg_match('/^\d{2}$/', $mes)) { return "Formato de mes inválido."; } // Obtener el año actual $anio = date("Y"); // Usar la función `cal_days_in_month` para obtener la cantidad de días del mes $dias = cal_days_in_month(CAL_GREGORIAN, intval($mes), $anio); return $dias; }