temml 0.10.19 → 0.10.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Temml-Asana.css +33 -0
- package/dist/Temml-Latin-Modern.css +33 -0
- package/dist/Temml-Libertinus.css +33 -0
- package/dist/Temml-Local.css +33 -0
- package/dist/Temml-STIX2.css +33 -0
- package/dist/temml.cjs +50 -65
- package/dist/temml.js +50 -65
- package/dist/temml.min.js +1 -1
- package/dist/temml.mjs +50 -65
- package/dist/temmlPostProcess.js +1 -1
- package/package.json +1 -1
- package/src/Parser.js +8 -3
- package/src/buildMathML.js +10 -8
- package/src/environments/array.js +6 -3
- package/src/environments/cd.js +2 -1
- package/src/functions/color.js +2 -9
- package/src/functions/delimsizing.js +2 -12
- package/src/functions/enclose.js +5 -24
- package/src/functions/font.js +1 -1
- package/src/functions/not.js +2 -2
- package/src/functions/op.js +10 -0
- package/src/functions/ordgroup.js +1 -1
- package/src/postProcess.js +1 -1
- package/src/svg.js +0 -110
    
        package/dist/temml.mjs
    CHANGED
    
    | @@ -2070,20 +2070,22 @@ const consolidateNumbers = expression => { | |
| 2070 2070 | 
             
             * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
         | 
| 2071 2071 | 
             
             * unless the array has length 1.  Always returns a single node.
         | 
| 2072 2072 | 
             
             */
         | 
| 2073 | 
            -
            const makeRow = function(body) {
         | 
| 2073 | 
            +
            const makeRow = function(body, semisimple = false) {
         | 
| 2074 2074 | 
             
              if (body.length === 1 && !(body[0] instanceof DocumentFragment)) {
         | 
| 2075 2075 | 
             
                return body[0];
         | 
| 2076 | 
            -
              } else {
         | 
| 2076 | 
            +
              } else if (!semisimple) {
         | 
| 2077 2077 | 
             
                // Suppress spacing on <mo> nodes at both ends of the row.
         | 
| 2078 2078 | 
             
                if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) {
         | 
| 2079 2079 | 
             
                  body[0].attributes.lspace = "0em";
         | 
| 2080 | 
            +
                  body[0].attributes.rspace = "0em";
         | 
| 2080 2081 | 
             
                }
         | 
| 2081 2082 | 
             
                const end = body.length - 1;
         | 
| 2082 2083 | 
             
                if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) {
         | 
| 2084 | 
            +
                  body[end].attributes.lspace = "0em";
         | 
| 2083 2085 | 
             
                  body[end].attributes.rspace = "0em";
         | 
| 2084 2086 | 
             
                }
         | 
| 2085 | 
            -
                return new mathMLTree.MathNode("mrow", body);
         | 
| 2086 2087 | 
             
              }
         | 
| 2088 | 
            +
              return new mathMLTree.MathNode("mrow", body);
         | 
| 2087 2089 | 
             
            };
         | 
| 2088 2090 |  | 
| 2089 2091 | 
             
            const isRel = item => {
         | 
| @@ -2097,10 +2099,10 @@ const isRel = item => { | |
| 2097 2099 | 
             
             * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}.
         | 
| 2098 2100 | 
             
             * (2) Suppress spacing between two adjacent relations.
         | 
| 2099 2101 | 
             
             */
         | 
| 2100 | 
            -
            const buildExpression = function(expression, style,  | 
| 2101 | 
            -
              if (expression.length === 1) {
         | 
| 2102 | 
            +
            const buildExpression = function(expression, style, semisimple = false) {
         | 
| 2103 | 
            +
              if (!semisimple && expression.length === 1) {
         | 
| 2102 2104 | 
             
                const group = buildGroup$1(expression[0], style);
         | 
| 2103 | 
            -
                if ( | 
| 2105 | 
            +
                if (group instanceof MathNode && group.type === "mo") {
         | 
| 2104 2106 | 
             
                  // When TeX writers want to suppress spacing on an operator,
         | 
| 2105 2107 | 
             
                  // they often put the operator by itself inside braces.
         | 
| 2106 2108 | 
             
                  group.setAttribute("lspace", "0em");
         | 
| @@ -2130,8 +2132,8 @@ const buildExpression = function(expression, style, isOrdgroup) { | |
| 2130 2132 | 
             
             * Equivalent to buildExpression, but wraps the elements in an <mrow>
         | 
| 2131 2133 | 
             
             * if there's more than one.  Returns a single node instead of an array.
         | 
| 2132 2134 | 
             
             */
         | 
| 2133 | 
            -
            const buildExpressionRow = function(expression, style,  | 
| 2134 | 
            -
              return makeRow(buildExpression(expression, style,  | 
| 2135 | 
            +
            const buildExpressionRow = function(expression, style, semisimple = false) {
         | 
| 2136 | 
            +
              return makeRow(buildExpression(expression, style, semisimple), semisimple);
         | 
| 2135 2137 | 
             
            };
         | 
| 2136 2138 |  | 
| 2137 2139 | 
             
            /**
         | 
| @@ -2816,7 +2818,8 @@ function cdArrow(arrowChar, labels, parser) { | |
| 2816 2818 | 
             
                  const arrowGroup = {
         | 
| 2817 2819 | 
             
                    type: "ordgroup",
         | 
| 2818 2820 | 
             
                    mode: "math",
         | 
| 2819 | 
            -
                    body: [leftLabel, sizedArrow, rightLabel]
         | 
| 2821 | 
            +
                    body: [leftLabel, sizedArrow, rightLabel],
         | 
| 2822 | 
            +
                    semisimple: true
         | 
| 2820 2823 | 
             
                  };
         | 
| 2821 2824 | 
             
                  return parser.callFunction("\\\\cdparent", [arrowGroup], []);
         | 
| 2822 2825 | 
             
                }
         | 
| @@ -3242,7 +3245,7 @@ defineFunction({ | |
| 3242 3245 | 
             
                allowedInText: true,
         | 
| 3243 3246 | 
             
                argTypes: ["raw", "raw"]
         | 
| 3244 3247 | 
             
              },
         | 
| 3245 | 
            -
              handler({ parser, token }, args, optArgs) {
         | 
| 3248 | 
            +
              handler({ parser, breakOnTokenText, token }, args, optArgs) {
         | 
| 3246 3249 | 
             
                const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string;
         | 
| 3247 3250 | 
             
                let color = "";
         | 
| 3248 3251 | 
             
                if (model) {
         | 
| @@ -3252,15 +3255,8 @@ defineFunction({ | |
| 3252 3255 | 
             
                  color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token);
         | 
| 3253 3256 | 
             
                }
         | 
| 3254 3257 |  | 
| 3255 | 
            -
                // Set macro \current@color in current namespace to store the current
         | 
| 3256 | 
            -
                // color, mimicking the behavior of color.sty.
         | 
| 3257 | 
            -
                // This is currently used just to correctly color a \right
         | 
| 3258 | 
            -
                // that follows a \color command.
         | 
| 3259 | 
            -
                parser.gullet.macros.set("\\current@color", color);
         | 
| 3260 | 
            -
             | 
| 3261 3258 | 
             
                // Parse out the implicit body that should be colored.
         | 
| 3262 | 
            -
                 | 
| 3263 | 
            -
                const body = parser.parseExpression(true, "\\color");
         | 
| 3259 | 
            +
                const body = parser.parseExpression(true, breakOnTokenText);
         | 
| 3264 3260 |  | 
| 3265 3261 | 
             
                return {
         | 
| 3266 3262 | 
             
                  type: "color",
         | 
| @@ -3800,18 +3796,10 @@ defineFunction({ | |
| 3800 3796 | 
             
                argTypes: ["primitive"]
         | 
| 3801 3797 | 
             
              },
         | 
| 3802 3798 | 
             
              handler: (context, args) => {
         | 
| 3803 | 
            -
                // \left case below triggers parsing of \right in
         | 
| 3804 | 
            -
                //   `const right = parser.parseFunction();`
         | 
| 3805 | 
            -
                // uses this return value.
         | 
| 3806 | 
            -
                const color = context.parser.gullet.macros.get("\\current@color");
         | 
| 3807 | 
            -
                if (color && typeof color !== "string") {
         | 
| 3808 | 
            -
                  throw new ParseError("\\current@color set to non-string in \\right");
         | 
| 3809 | 
            -
                }
         | 
| 3810 3799 | 
             
                return {
         | 
| 3811 3800 | 
             
                  type: "leftright-right",
         | 
| 3812 3801 | 
             
                  mode: context.parser.mode,
         | 
| 3813 | 
            -
                  delim: checkDelimiter(args[0], context).text | 
| 3814 | 
            -
                  color // undefined if not set via \color
         | 
| 3802 | 
            +
                  delim: checkDelimiter(args[0], context).text
         | 
| 3815 3803 | 
             
                };
         | 
| 3816 3804 | 
             
              }
         | 
| 3817 3805 | 
             
            });
         | 
| @@ -3840,8 +3828,7 @@ defineFunction({ | |
| 3840 3828 | 
             
                  mode: parser.mode,
         | 
| 3841 3829 | 
             
                  body,
         | 
| 3842 3830 | 
             
                  left: delim.text,
         | 
| 3843 | 
            -
                  right: right.delim | 
| 3844 | 
            -
                  rightColor: right.color
         | 
| 3831 | 
            +
                  right: right.delim
         | 
| 3845 3832 | 
             
                };
         | 
| 3846 3833 | 
             
              },
         | 
| 3847 3834 | 
             
              mathmlBuilder: (group, style) => {
         | 
| @@ -3864,7 +3851,6 @@ defineFunction({ | |
| 3864 3851 | 
             
                if (group.right === "\u2216" || group.right.indexOf("arrow") > -1) {
         | 
| 3865 3852 | 
             
                  rightNode.setAttribute("stretchy", "true");
         | 
| 3866 3853 | 
             
                }
         | 
| 3867 | 
            -
                if (group.rightColor) { rightNode.style.color =  group.rightColor; }
         | 
| 3868 3854 | 
             
                inner.push(rightNode);
         | 
| 3869 3855 |  | 
| 3870 3856 | 
             
                return makeRow(inner);
         | 
| @@ -3938,20 +3924,12 @@ const mathmlBuilder$8 = (group, style) => { | |
| 3938 3924 | 
             
                  node.style.borderBottom = "0.065em solid";
         | 
| 3939 3925 | 
             
                  break
         | 
| 3940 3926 | 
             
                case "\\cancel":
         | 
| 3941 | 
            -
                   | 
| 3942 | 
            -
             | 
| 3943 | 
            -
             | 
| 3944 | 
            -
            rgba(0,0,0,1) 50%,
         | 
| 3945 | 
            -
            rgba(0,0,0,0) calc(50% + 0.06em),
         | 
| 3946 | 
            -
            rgba(0,0,0,0) 100%);`;
         | 
| 3927 | 
            +
                  // We can't use an inline background-gradient. It does not work client-side.
         | 
| 3928 | 
            +
                  // So set a class and put the rule in the external CSS file.
         | 
| 3929 | 
            +
                  node.classes.push("tml-cancel");
         | 
| 3947 3930 | 
             
                  break
         | 
| 3948 3931 | 
             
                case "\\bcancel":
         | 
| 3949 | 
            -
                  node. | 
| 3950 | 
            -
            rgba(0,0,0,0) 0%,
         | 
| 3951 | 
            -
            rgba(0,0,0,0) calc(50% - 0.06em),
         | 
| 3952 | 
            -
            rgba(0,0,0,1) 50%,
         | 
| 3953 | 
            -
            rgba(0,0,0,0) calc(50% + 0.06em),
         | 
| 3954 | 
            -
            rgba(0,0,0,0) 100%);`;
         | 
| 3932 | 
            +
                  node.classes.push("tml-bcancel");
         | 
| 3955 3933 | 
             
                  break
         | 
| 3956 3934 | 
             
                /*
         | 
| 3957 3935 | 
             
                case "\\longdiv":
         | 
| @@ -3998,18 +3976,7 @@ rgba(0,0,0,0) 100%);`; | |
| 3998 3976 | 
             
                  break
         | 
| 3999 3977 | 
             
                }
         | 
| 4000 3978 | 
             
                case "\\xcancel":
         | 
| 4001 | 
            -
                  node. | 
| 4002 | 
            -
            rgba(0,0,0,0) 0%,
         | 
| 4003 | 
            -
            rgba(0,0,0,0) calc(50% - 0.06em),
         | 
| 4004 | 
            -
            rgba(0,0,0,1) 50%,
         | 
| 4005 | 
            -
            rgba(0,0,0,0) calc(50% + 0.06em),
         | 
| 4006 | 
            -
            rgba(0,0,0,0) 100%),
         | 
| 4007 | 
            -
            linear-gradient(to top right,
         | 
| 4008 | 
            -
            rgba(0,0,0,0) 0%,
         | 
| 4009 | 
            -
            rgba(0,0,0,0) calc(50% - 0.06em),
         | 
| 4010 | 
            -
            rgba(0,0,0,1) 50%,
         | 
| 4011 | 
            -
            rgba(0,0,0,0) calc(50% + 0.06em),
         | 
| 4012 | 
            -
            rgba(0,0,0,0) 100%);`;
         | 
| 3979 | 
            +
                  node.classes.push("tml-xcancel");
         | 
| 4013 3980 | 
             
                  break
         | 
| 4014 3981 | 
             
              }
         | 
| 4015 3982 | 
             
              if (group.backgroundColor) {
         | 
| @@ -4207,7 +4174,7 @@ const getTag = (group, style, rowNum) => { | |
| 4207 4174 | 
             
              if (tagContents) {
         | 
| 4208 4175 | 
             
                // The author has written a \tag or a \notag in this row.
         | 
| 4209 4176 | 
             
                if (tagContents.body) {
         | 
| 4210 | 
            -
                  tag = buildExpressionRow(tagContents.body, style);
         | 
| 4177 | 
            +
                  tag = buildExpressionRow(tagContents.body, style, true);
         | 
| 4211 4178 | 
             
                  tag.classes = ["tml-tag"];
         | 
| 4212 4179 | 
             
                } else {
         | 
| 4213 4180 | 
             
                  // \notag. Return an empty span.
         | 
| @@ -4254,7 +4221,9 @@ function parseArray( | |
| 4254 4221 | 
             
                parser.gullet.macros.set("\\cr", "\\\\\\relax");
         | 
| 4255 4222 | 
             
              }
         | 
| 4256 4223 | 
             
              if (addEqnNum) {
         | 
| 4257 | 
            -
                parser.gullet.macros.set("\\tag", "\\ | 
| 4224 | 
            +
                parser.gullet.macros.set("\\tag", "\\@ifstar\\envtag@literal\\envtag@paren");
         | 
| 4225 | 
            +
                parser.gullet.macros.set("\\envtag@paren", "\\env@tag{{(\\text{#1})}}");
         | 
| 4226 | 
            +
                parser.gullet.macros.set("\\envtag@literal", "\\env@tag{\\text{#1}}");
         | 
| 4258 4227 | 
             
                parser.gullet.macros.set("\\notag", "\\env@notag");
         | 
| 4259 4228 | 
             
                parser.gullet.macros.set("\\nonumber", "\\env@notag");
         | 
| 4260 4229 | 
             
              }
         | 
| @@ -4295,7 +4264,8 @@ function parseArray( | |
| 4295 4264 | 
             
                cell = {
         | 
| 4296 4265 | 
             
                  type: "ordgroup",
         | 
| 4297 4266 | 
             
                  mode: parser.mode,
         | 
| 4298 | 
            -
                  body: cell
         | 
| 4267 | 
            +
                  body: cell,
         | 
| 4268 | 
            +
                  semisimple: true
         | 
| 4299 4269 | 
             
                };
         | 
| 4300 4270 | 
             
                row.push(cell);
         | 
| 4301 4271 | 
             
                const next = parser.fetch().text;
         | 
| @@ -5128,7 +5098,7 @@ const mathmlBuilder$6 = (group, style) => { | |
| 5128 5098 | 
             
              const mathGroup = buildGroup$1(group.body, newStyle);
         | 
| 5129 5099 |  | 
| 5130 5100 | 
             
              if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{}
         | 
| 5131 | 
            -
              if (font === "boldsymbol" && ["mo", "mpadded"].includes(mathGroup.type)) {
         | 
| 5101 | 
            +
              if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) {
         | 
| 5132 5102 | 
             
                mathGroup.style.fontWeight = "bold";
         | 
| 5133 5103 | 
             
                return mathGroup
         | 
| 5134 5104 | 
             
              }
         | 
| @@ -6485,10 +6455,10 @@ defineFunction({ | |
| 6485 6455 | 
             
              },
         | 
| 6486 6456 | 
             
              mathmlBuilder(group, style) {
         | 
| 6487 6457 | 
             
                if (group.isCharacterBox) {
         | 
| 6488 | 
            -
                  const inner = buildExpression(group.body, style);
         | 
| 6458 | 
            +
                  const inner = buildExpression(group.body, style, true);
         | 
| 6489 6459 | 
             
                  return inner[0]
         | 
| 6490 6460 | 
             
                } else {
         | 
| 6491 | 
            -
                  return buildExpressionRow(group.body, style | 
| 6461 | 
            +
                  return buildExpressionRow(group.body, style)
         | 
| 6492 6462 | 
             
                }
         | 
| 6493 6463 | 
             
              }
         | 
| 6494 6464 | 
             
            });
         | 
| @@ -6508,6 +6478,13 @@ const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"]; | |
| 6508 6478 | 
             
            // NOTE: Unlike most `builders`s, this one handles not only "op", but also
         | 
| 6509 6479 | 
             
            // "supsub" since some of them (like \int) can affect super/subscripting.
         | 
| 6510 6480 |  | 
| 6481 | 
            +
            const setSpacing = node => {
         | 
| 6482 | 
            +
              // The user wrote a \mathop{…} function. Change spacing from default to OP spacing.
         | 
| 6483 | 
            +
              // The most likely spacing for an OP is a thin space per TeXbook p170.
         | 
| 6484 | 
            +
              node.attributes.lspace = "0.1667em";
         | 
| 6485 | 
            +
              node.attributes.rspace = "0.1667em";
         | 
| 6486 | 
            +
            };
         | 
| 6487 | 
            +
             | 
| 6511 6488 | 
             
            const mathmlBuilder$2 = (group, style) => {
         | 
| 6512 6489 | 
             
              let node;
         | 
| 6513 6490 |  | 
| @@ -6519,9 +6496,11 @@ const mathmlBuilder$2 = (group, style) => { | |
| 6519 6496 | 
             
                } else {
         | 
| 6520 6497 | 
             
                  node.setAttribute("movablelimits", "false");
         | 
| 6521 6498 | 
             
                }
         | 
| 6499 | 
            +
                if (group.fromMathOp) { setSpacing(node); }
         | 
| 6522 6500 | 
             
              } else if (group.body) {
         | 
| 6523 6501 | 
             
                // This is an operator with children. Add them.
         | 
| 6524 6502 | 
             
                node = new MathNode("mo", buildExpression(group.body, style));
         | 
| 6503 | 
            +
                if (group.fromMathOp) { setSpacing(node); }
         | 
| 6525 6504 | 
             
              } else {
         | 
| 6526 6505 | 
             
                // This is a text operator. Add all of the characters from the operator's name.
         | 
| 6527 6506 | 
             
                node = new MathNode("mi", [new TextNode(group.name.slice(1))]);
         | 
| @@ -6641,6 +6620,7 @@ defineFunction({ | |
| 6641 6620 | 
             
                  limits: true,
         | 
| 6642 6621 | 
             
                  parentIsSupSub: false,
         | 
| 6643 6622 | 
             
                  symbol: isSymbol,
         | 
| 6623 | 
            +
                  fromMathOp: true,
         | 
| 6644 6624 | 
             
                  stack: false,
         | 
| 6645 6625 | 
             
                  name: isSymbol ? arr[0].text : null,
         | 
| 6646 6626 | 
             
                  body: isSymbol ? null : ordargument(body)
         | 
| @@ -6974,7 +6954,7 @@ defineMacro("\\operatorname", | |
| 6974 6954 | 
             
            defineFunctionBuilders({
         | 
| 6975 6955 | 
             
              type: "ordgroup",
         | 
| 6976 6956 | 
             
              mathmlBuilder(group, style) {
         | 
| 6977 | 
            -
                return buildExpressionRow(group.body, style,  | 
| 6957 | 
            +
                return buildExpressionRow(group.body, style, group.semisimple);
         | 
| 6978 6958 | 
             
              }
         | 
| 6979 6959 | 
             
            });
         | 
| 6980 6960 |  | 
| @@ -12003,6 +11983,8 @@ var unicodeSymbols = { | |
| 12003 11983 |  | 
| 12004 11984 | 
             
            /* eslint no-constant-condition:0 */
         | 
| 12005 11985 |  | 
| 11986 | 
            +
            const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
         | 
| 11987 | 
            +
             | 
| 12006 11988 | 
             
            /**
         | 
| 12007 11989 | 
             
             * This file contains the parser used to parse out a TeX expression from the
         | 
| 12008 11990 | 
             
             * input. Since TeX isn't context-free, standard parsers don't work particularly
         | 
| @@ -12772,8 +12754,7 @@ class Parser { | |
| 12772 12754 | 
             
                    body: expression,
         | 
| 12773 12755 | 
             
                    // A group formed by \begingroup...\endgroup is a semi-simple group
         | 
| 12774 12756 | 
             
                    // which doesn't affect spacing in math mode, i.e., is transparent.
         | 
| 12775 | 
            -
                    // https://tex.stackexchange.com/questions/1930/ | 
| 12776 | 
            -
                    // use-begingroup-instead-of-bgroup
         | 
| 12757 | 
            +
                    // https://tex.stackexchange.com/questions/1930/
         | 
| 12777 12758 | 
             
                    semisimple: text === "\\begingroup" || undefined
         | 
| 12778 12759 | 
             
                  };
         | 
| 12779 12760 | 
             
                } else {
         | 
| @@ -12887,7 +12868,11 @@ class Parser { | |
| 12887 12868 | 
             
                // Recognize base symbol
         | 
| 12888 12869 | 
             
                let symbol;
         | 
| 12889 12870 | 
             
                if (symbols[this.mode][text]) {
         | 
| 12890 | 
            -
                   | 
| 12871 | 
            +
                  let group = symbols[this.mode][text].group;
         | 
| 12872 | 
            +
                  if (group === "bin" && binLeftCancellers.includes(this.prevAtomType)) {
         | 
| 12873 | 
            +
                    // Change from a binary operator to a unary (prefix) operator
         | 
| 12874 | 
            +
                    group = "open";
         | 
| 12875 | 
            +
                  }
         | 
| 12891 12876 | 
             
                  const loc = SourceLocation.range(nucleus);
         | 
| 12892 12877 | 
             
                  let s;
         | 
| 12893 12878 | 
             
                  if (Object.prototype.hasOwnProperty.call(ATOMS, group )) {
         | 
| @@ -13157,7 +13142,7 @@ class Style { | |
| 13157 13142 | 
             
             * https://mit-license.org/
         | 
| 13158 13143 | 
             
             */
         | 
| 13159 13144 |  | 
| 13160 | 
            -
            const version = "0.10. | 
| 13145 | 
            +
            const version = "0.10.21";
         | 
| 13161 13146 |  | 
| 13162 13147 | 
             
            function postProcess(block) {
         | 
| 13163 13148 | 
             
              const labelMap = {};
         | 
    
        package/dist/temmlPostProcess.js
    CHANGED
    
    
    
        package/package.json
    CHANGED
    
    
    
        package/src/Parser.js
    CHANGED
    
    | @@ -15,6 +15,8 @@ import { isDelimiter } from "./functions/delimsizing" | |
| 15 15 | 
             
            import unicodeAccents from /*preval*/ "./unicodeAccents";
         | 
| 16 16 | 
             
            import unicodeSymbols from /*preval*/ "./unicodeSymbols";
         | 
| 17 17 |  | 
| 18 | 
            +
            const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
         | 
| 19 | 
            +
             | 
| 18 20 | 
             
            /**
         | 
| 19 21 | 
             
             * This file contains the parser used to parse out a TeX expression from the
         | 
| 20 22 | 
             
             * input. Since TeX isn't context-free, standard parsers don't work particularly
         | 
| @@ -784,8 +786,7 @@ export default class Parser { | |
| 784 786 | 
             
                    body: expression,
         | 
| 785 787 | 
             
                    // A group formed by \begingroup...\endgroup is a semi-simple group
         | 
| 786 788 | 
             
                    // which doesn't affect spacing in math mode, i.e., is transparent.
         | 
| 787 | 
            -
                    // https://tex.stackexchange.com/questions/1930/ | 
| 788 | 
            -
                    // use-begingroup-instead-of-bgroup
         | 
| 789 | 
            +
                    // https://tex.stackexchange.com/questions/1930/
         | 
| 789 790 | 
             
                    semisimple: text === "\\begingroup" || undefined
         | 
| 790 791 | 
             
                  };
         | 
| 791 792 | 
             
                } else {
         | 
| @@ -899,7 +900,11 @@ export default class Parser { | |
| 899 900 | 
             
                // Recognize base symbol
         | 
| 900 901 | 
             
                let symbol;
         | 
| 901 902 | 
             
                if (symbols[this.mode][text]) {
         | 
| 902 | 
            -
                   | 
| 903 | 
            +
                  let group = symbols[this.mode][text].group;
         | 
| 904 | 
            +
                  if (group === "bin" && binLeftCancellers.includes(this.prevAtomType)) {
         | 
| 905 | 
            +
                    // Change from a binary operator to a unary (prefix) operator
         | 
| 906 | 
            +
                    group = "open"
         | 
| 907 | 
            +
                  }
         | 
| 903 908 | 
             
                  const loc = SourceLocation.range(nucleus);
         | 
| 904 909 | 
             
                  let s;
         | 
| 905 910 | 
             
                  if (Object.prototype.hasOwnProperty.call(ATOMS, group )) {
         | 
    
        package/src/buildMathML.js
    CHANGED
    
    | @@ -135,20 +135,22 @@ const consolidateNumbers = expression => { | |
| 135 135 | 
             
             * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
         | 
| 136 136 | 
             
             * unless the array has length 1.  Always returns a single node.
         | 
| 137 137 | 
             
             */
         | 
| 138 | 
            -
            export const makeRow = function(body) {
         | 
| 138 | 
            +
            export const makeRow = function(body, semisimple = false) {
         | 
| 139 139 | 
             
              if (body.length === 1 && !(body[0] instanceof DocumentFragment)) {
         | 
| 140 140 | 
             
                return body[0];
         | 
| 141 | 
            -
              } else {
         | 
| 141 | 
            +
              } else if (!semisimple) {
         | 
| 142 142 | 
             
                // Suppress spacing on <mo> nodes at both ends of the row.
         | 
| 143 143 | 
             
                if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) {
         | 
| 144 144 | 
             
                  body[0].attributes.lspace = "0em"
         | 
| 145 | 
            +
                  body[0].attributes.rspace = "0em"
         | 
| 145 146 | 
             
                }
         | 
| 146 147 | 
             
                const end = body.length - 1
         | 
| 147 148 | 
             
                if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) {
         | 
| 149 | 
            +
                  body[end].attributes.lspace = "0em"
         | 
| 148 150 | 
             
                  body[end].attributes.rspace = "0em"
         | 
| 149 151 | 
             
                }
         | 
| 150 | 
            -
                return new mathMLTree.MathNode("mrow", body);
         | 
| 151 152 | 
             
              }
         | 
| 153 | 
            +
              return new mathMLTree.MathNode("mrow", body);
         | 
| 152 154 | 
             
            };
         | 
| 153 155 |  | 
| 154 156 | 
             
            const isRel = item => {
         | 
| @@ -162,10 +164,10 @@ const isRel = item => { | |
| 162 164 | 
             
             * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}.
         | 
| 163 165 | 
             
             * (2) Suppress spacing between two adjacent relations.
         | 
| 164 166 | 
             
             */
         | 
| 165 | 
            -
            export const buildExpression = function(expression, style,  | 
| 166 | 
            -
              if (expression.length === 1) {
         | 
| 167 | 
            +
            export const buildExpression = function(expression, style, semisimple = false) {
         | 
| 168 | 
            +
              if (!semisimple && expression.length === 1) {
         | 
| 167 169 | 
             
                const group = buildGroup(expression[0], style);
         | 
| 168 | 
            -
                if ( | 
| 170 | 
            +
                if (group instanceof MathNode && group.type === "mo") {
         | 
| 169 171 | 
             
                  // When TeX writers want to suppress spacing on an operator,
         | 
| 170 172 | 
             
                  // they often put the operator by itself inside braces.
         | 
| 171 173 | 
             
                  group.setAttribute("lspace", "0em");
         | 
| @@ -195,8 +197,8 @@ export const buildExpression = function(expression, style, isOrdgroup) { | |
| 195 197 | 
             
             * Equivalent to buildExpression, but wraps the elements in an <mrow>
         | 
| 196 198 | 
             
             * if there's more than one.  Returns a single node instead of an array.
         | 
| 197 199 | 
             
             */
         | 
| 198 | 
            -
            export const buildExpressionRow = function(expression, style,  | 
| 199 | 
            -
              return makeRow(buildExpression(expression, style,  | 
| 200 | 
            +
            export const buildExpressionRow = function(expression, style, semisimple = false) {
         | 
| 201 | 
            +
              return makeRow(buildExpression(expression, style, semisimple), semisimple);
         | 
| 200 202 | 
             
            };
         | 
| 201 203 |  | 
| 202 204 | 
             
            /**
         | 
| @@ -44,7 +44,7 @@ const getTag = (group, style, rowNum) => { | |
| 44 44 | 
             
              if (tagContents) {
         | 
| 45 45 | 
             
                // The author has written a \tag or a \notag in this row.
         | 
| 46 46 | 
             
                if (tagContents.body) {
         | 
| 47 | 
            -
                  tag = mml.buildExpressionRow(tagContents.body, style)
         | 
| 47 | 
            +
                  tag = mml.buildExpressionRow(tagContents.body, style, true)
         | 
| 48 48 | 
             
                  tag.classes = ["tml-tag"]
         | 
| 49 49 | 
             
                } else {
         | 
| 50 50 | 
             
                  // \notag. Return an empty span.
         | 
| @@ -91,7 +91,9 @@ function parseArray( | |
| 91 91 | 
             
                parser.gullet.macros.set("\\cr", "\\\\\\relax");
         | 
| 92 92 | 
             
              }
         | 
| 93 93 | 
             
              if (addEqnNum) {
         | 
| 94 | 
            -
                parser.gullet.macros.set("\\tag", "\\ | 
| 94 | 
            +
                parser.gullet.macros.set("\\tag", "\\@ifstar\\envtag@literal\\envtag@paren")
         | 
| 95 | 
            +
                parser.gullet.macros.set("\\envtag@paren", "\\env@tag{{(\\text{#1})}}");
         | 
| 96 | 
            +
                parser.gullet.macros.set("\\envtag@literal", "\\env@tag{\\text{#1}}")
         | 
| 95 97 | 
             
                parser.gullet.macros.set("\\notag", "\\env@notag");
         | 
| 96 98 | 
             
                parser.gullet.macros.set("\\nonumber", "\\env@notag")
         | 
| 97 99 | 
             
              }
         | 
| @@ -132,7 +134,8 @@ function parseArray( | |
| 132 134 | 
             
                cell = {
         | 
| 133 135 | 
             
                  type: "ordgroup",
         | 
| 134 136 | 
             
                  mode: parser.mode,
         | 
| 135 | 
            -
                  body: cell
         | 
| 137 | 
            +
                  body: cell,
         | 
| 138 | 
            +
                  semisimple: true
         | 
| 136 139 | 
             
                };
         | 
| 137 140 | 
             
                row.push(cell);
         | 
| 138 141 | 
             
                const next = parser.fetch().text;
         | 
    
        package/src/environments/cd.js
    CHANGED
    
    | @@ -49,7 +49,8 @@ function cdArrow(arrowChar, labels, parser) { | |
| 49 49 | 
             
                  const arrowGroup = {
         | 
| 50 50 | 
             
                    type: "ordgroup",
         | 
| 51 51 | 
             
                    mode: "math",
         | 
| 52 | 
            -
                    body: [leftLabel, sizedArrow, rightLabel]
         | 
| 52 | 
            +
                    body: [leftLabel, sizedArrow, rightLabel],
         | 
| 53 | 
            +
                    semisimple: true
         | 
| 53 54 | 
             
                  };
         | 
| 54 55 | 
             
                  return parser.callFunction("\\\\cdparent", [arrowGroup], []);
         | 
| 55 56 | 
             
                }
         | 
    
        package/src/functions/color.js
    CHANGED
    
    | @@ -201,7 +201,7 @@ defineFunction({ | |
| 201 201 | 
             
                allowedInText: true,
         | 
| 202 202 | 
             
                argTypes: ["raw", "raw"]
         | 
| 203 203 | 
             
              },
         | 
| 204 | 
            -
              handler({ parser, token }, args, optArgs) {
         | 
| 204 | 
            +
              handler({ parser, breakOnTokenText, token }, args, optArgs) {
         | 
| 205 205 | 
             
                const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string
         | 
| 206 206 | 
             
                let color = ""
         | 
| 207 207 | 
             
                if (model) {
         | 
| @@ -211,15 +211,8 @@ defineFunction({ | |
| 211 211 | 
             
                  color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token)
         | 
| 212 212 | 
             
                }
         | 
| 213 213 |  | 
| 214 | 
            -
                // Set macro \current@color in current namespace to store the current
         | 
| 215 | 
            -
                // color, mimicking the behavior of color.sty.
         | 
| 216 | 
            -
                // This is currently used just to correctly color a \right
         | 
| 217 | 
            -
                // that follows a \color command.
         | 
| 218 | 
            -
                parser.gullet.macros.set("\\current@color", color)
         | 
| 219 | 
            -
             | 
| 220 214 | 
             
                // Parse out the implicit body that should be colored.
         | 
| 221 | 
            -
                 | 
| 222 | 
            -
                const body = parser.parseExpression(true, "\\color")
         | 
| 215 | 
            +
                const body = parser.parseExpression(true, breakOnTokenText)
         | 
| 223 216 |  | 
| 224 217 | 
             
                return {
         | 
| 225 218 | 
             
                  type: "color",
         | 
| @@ -211,18 +211,10 @@ defineFunction({ | |
| 211 211 | 
             
                argTypes: ["primitive"]
         | 
| 212 212 | 
             
              },
         | 
| 213 213 | 
             
              handler: (context, args) => {
         | 
| 214 | 
            -
                // \left case below triggers parsing of \right in
         | 
| 215 | 
            -
                //   `const right = parser.parseFunction();`
         | 
| 216 | 
            -
                // uses this return value.
         | 
| 217 | 
            -
                const color = context.parser.gullet.macros.get("\\current@color");
         | 
| 218 | 
            -
                if (color && typeof color !== "string") {
         | 
| 219 | 
            -
                  throw new ParseError("\\current@color set to non-string in \\right");
         | 
| 220 | 
            -
                }
         | 
| 221 214 | 
             
                return {
         | 
| 222 215 | 
             
                  type: "leftright-right",
         | 
| 223 216 | 
             
                  mode: context.parser.mode,
         | 
| 224 | 
            -
                  delim: checkDelimiter(args[0], context).text | 
| 225 | 
            -
                  color // undefined if not set via \color
         | 
| 217 | 
            +
                  delim: checkDelimiter(args[0], context).text
         | 
| 226 218 | 
             
                };
         | 
| 227 219 | 
             
              }
         | 
| 228 220 | 
             
            });
         | 
| @@ -251,8 +243,7 @@ defineFunction({ | |
| 251 243 | 
             
                  mode: parser.mode,
         | 
| 252 244 | 
             
                  body,
         | 
| 253 245 | 
             
                  left: delim.text,
         | 
| 254 | 
            -
                  right: right.delim | 
| 255 | 
            -
                  rightColor: right.color
         | 
| 246 | 
            +
                  right: right.delim
         | 
| 256 247 | 
             
                };
         | 
| 257 248 | 
             
              },
         | 
| 258 249 | 
             
              mathmlBuilder: (group, style) => {
         | 
| @@ -275,7 +266,6 @@ defineFunction({ | |
| 275 266 | 
             
                if (group.right === "\u2216" || group.right.indexOf("arrow") > -1) {
         | 
| 276 267 | 
             
                  rightNode.setAttribute("stretchy", "true")
         | 
| 277 268 | 
             
                }
         | 
| 278 | 
            -
                if (group.rightColor) { rightNode.style.color =  group.rightColor }
         | 
| 279 269 | 
             
                inner.push(rightNode)
         | 
| 280 270 |  | 
| 281 271 | 
             
                return mml.makeRow(inner);
         | 
    
        package/src/functions/enclose.js
    CHANGED
    
    | @@ -34,20 +34,12 @@ const mathmlBuilder = (group, style) => { | |
| 34 34 | 
             
                  node.style.borderBottom = "0.065em solid"
         | 
| 35 35 | 
             
                  break
         | 
| 36 36 | 
             
                case "\\cancel":
         | 
| 37 | 
            -
                   | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
            rgba(0,0,0,1) 50%,
         | 
| 41 | 
            -
            rgba(0,0,0,0) calc(50% + 0.06em),
         | 
| 42 | 
            -
            rgba(0,0,0,0) 100%);`
         | 
| 37 | 
            +
                  // We can't use an inline background-gradient. It does not work client-side.
         | 
| 38 | 
            +
                  // So set a class and put the rule in the external CSS file.
         | 
| 39 | 
            +
                  node.classes.push("tml-cancel")
         | 
| 43 40 | 
             
                  break
         | 
| 44 41 | 
             
                case "\\bcancel":
         | 
| 45 | 
            -
                  node. | 
| 46 | 
            -
            rgba(0,0,0,0) 0%,
         | 
| 47 | 
            -
            rgba(0,0,0,0) calc(50% - 0.06em),
         | 
| 48 | 
            -
            rgba(0,0,0,1) 50%,
         | 
| 49 | 
            -
            rgba(0,0,0,0) calc(50% + 0.06em),
         | 
| 50 | 
            -
            rgba(0,0,0,0) 100%);`
         | 
| 42 | 
            +
                  node.classes.push("tml-bcancel")
         | 
| 51 43 | 
             
                  break
         | 
| 52 44 | 
             
                /*
         | 
| 53 45 | 
             
                case "\\longdiv":
         | 
| @@ -94,18 +86,7 @@ rgba(0,0,0,0) 100%);` | |
| 94 86 | 
             
                  break
         | 
| 95 87 | 
             
                }
         | 
| 96 88 | 
             
                case "\\xcancel":
         | 
| 97 | 
            -
                  node. | 
| 98 | 
            -
            rgba(0,0,0,0) 0%,
         | 
| 99 | 
            -
            rgba(0,0,0,0) calc(50% - 0.06em),
         | 
| 100 | 
            -
            rgba(0,0,0,1) 50%,
         | 
| 101 | 
            -
            rgba(0,0,0,0) calc(50% + 0.06em),
         | 
| 102 | 
            -
            rgba(0,0,0,0) 100%),
         | 
| 103 | 
            -
            linear-gradient(to top right,
         | 
| 104 | 
            -
            rgba(0,0,0,0) 0%,
         | 
| 105 | 
            -
            rgba(0,0,0,0) calc(50% - 0.06em),
         | 
| 106 | 
            -
            rgba(0,0,0,1) 50%,
         | 
| 107 | 
            -
            rgba(0,0,0,0) calc(50% + 0.06em),
         | 
| 108 | 
            -
            rgba(0,0,0,0) 100%);`
         | 
| 89 | 
            +
                  node.classes.push("tml-xcancel")
         | 
| 109 90 | 
             
                  break
         | 
| 110 91 | 
             
              }
         | 
| 111 92 | 
             
              if (group.backgroundColor) {
         | 
    
        package/src/functions/font.js
    CHANGED
    
    | @@ -8,7 +8,7 @@ const mathmlBuilder = (group, style) => { | |
| 8 8 | 
             
              const mathGroup = mml.buildGroup(group.body, newStyle)
         | 
| 9 9 |  | 
| 10 10 | 
             
              if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{}
         | 
| 11 | 
            -
              if (font === "boldsymbol" && ["mo", "mpadded"].includes(mathGroup.type)) {
         | 
| 11 | 
            +
              if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) {
         | 
| 12 12 | 
             
                mathGroup.style.fontWeight = "bold"
         | 
| 13 13 | 
             
                return mathGroup
         | 
| 14 14 | 
             
              }
         | 
    
        package/src/functions/not.js
    CHANGED
    
    | @@ -37,10 +37,10 @@ defineFunction({ | |
| 37 37 | 
             
              },
         | 
| 38 38 | 
             
              mathmlBuilder(group, style) {
         | 
| 39 39 | 
             
                if (group.isCharacterBox) {
         | 
| 40 | 
            -
                  const inner = mml.buildExpression(group.body, style);
         | 
| 40 | 
            +
                  const inner = mml.buildExpression(group.body, style, true);
         | 
| 41 41 | 
             
                  return inner[0]
         | 
| 42 42 | 
             
                } else {
         | 
| 43 | 
            -
                  return mml.buildExpressionRow(group.body, style | 
| 43 | 
            +
                  return mml.buildExpressionRow(group.body, style)
         | 
| 44 44 | 
             
                }
         | 
| 45 45 | 
             
              }
         | 
| 46 46 | 
             
            });
         | 
    
        package/src/functions/op.js
    CHANGED
    
    | @@ -17,6 +17,13 @@ export const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"] | |
| 17 17 | 
             
            // NOTE: Unlike most `builders`s, this one handles not only "op", but also
         | 
| 18 18 | 
             
            // "supsub" since some of them (like \int) can affect super/subscripting.
         | 
| 19 19 |  | 
| 20 | 
            +
            const setSpacing = node => {
         | 
| 21 | 
            +
              // The user wrote a \mathop{…} function. Change spacing from default to OP spacing.
         | 
| 22 | 
            +
              // The most likely spacing for an OP is a thin space per TeXbook p170.
         | 
| 23 | 
            +
              node.attributes.lspace = "0.1667em"
         | 
| 24 | 
            +
              node.attributes.rspace = "0.1667em"
         | 
| 25 | 
            +
            }
         | 
| 26 | 
            +
             | 
| 20 27 | 
             
            const mathmlBuilder = (group, style) => {
         | 
| 21 28 | 
             
              let node;
         | 
| 22 29 |  | 
| @@ -28,9 +35,11 @@ const mathmlBuilder = (group, style) => { | |
| 28 35 | 
             
                } else {
         | 
| 29 36 | 
             
                  node.setAttribute("movablelimits", "false")
         | 
| 30 37 | 
             
                }
         | 
| 38 | 
            +
                if (group.fromMathOp) { setSpacing(node) }
         | 
| 31 39 | 
             
              } else if (group.body) {
         | 
| 32 40 | 
             
                // This is an operator with children. Add them.
         | 
| 33 41 | 
             
                node = new mathMLTree.MathNode("mo", mml.buildExpression(group.body, style));
         | 
| 42 | 
            +
                if (group.fromMathOp) { setSpacing(node) }
         | 
| 34 43 | 
             
              } else {
         | 
| 35 44 | 
             
                // This is a text operator. Add all of the characters from the operator's name.
         | 
| 36 45 | 
             
                node = new mathMLTree.MathNode("mi", [new mathMLTree.TextNode(group.name.slice(1))]);
         | 
| @@ -150,6 +159,7 @@ defineFunction({ | |
| 150 159 | 
             
                  limits: true,
         | 
| 151 160 | 
             
                  parentIsSupSub: false,
         | 
| 152 161 | 
             
                  symbol: isSymbol,
         | 
| 162 | 
            +
                  fromMathOp: true,
         | 
| 153 163 | 
             
                  stack: false,
         | 
| 154 164 | 
             
                  name: isSymbol ? arr[0].text : null,
         | 
| 155 165 | 
             
                  body: isSymbol ? null : ordargument(body)
         |