temml 0.10.2 → 0.10.4

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.mjs CHANGED
@@ -188,7 +188,7 @@ class Settings {
188
188
  this.leqno = utils.deflt(options.leqno, false); // boolean
189
189
  this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
190
190
  this.macros = options.macros || {};
191
- this.wrap = utils.deflt(options.wrap, "none"); // "none" | "tex" | "="
191
+ this.wrap = utils.deflt(options.wrap, "tex"); // "tex" | "="
192
192
  this.xml = utils.deflt(options.xml, false); // boolean
193
193
  this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
194
194
  this.strict = utils.deflt(options.strict, false); // boolean
@@ -1747,10 +1747,12 @@ for (let i = 0; i < 10; i++) {
1747
1747
  * Then the top level of a <math> element can be occupied by <mrow> elements, and the browser
1748
1748
  * will break after a <mrow> if the expression extends beyond the container limit.
1749
1749
  *
1750
- * We want the expression to render with soft line breaks after each top-level binary or
1750
+ * The default is for soft line breaks after each top-level binary or
1751
1751
  * relational operator, per TeXbook p. 173. So we gather the expression into <mrow>s so that
1752
1752
  * each <mrow> ends in a binary or relational operator.
1753
1753
  *
1754
+ * An option is for soft line breaks before an "=" sign. That changes the <mrow>s.
1755
+ *
1754
1756
  * Soft line breaks will not work in Chromium and Safari, only Firefox.
1755
1757
  *
1756
1758
  * Hopefully browsers will someday do their own linebreaking and we will be able to delete
@@ -1962,6 +1964,48 @@ const consolidateText = mrow => {
1962
1964
  return mtext
1963
1965
  };
1964
1966
 
1967
+ const numberRegEx$1 = /^[0-9]$/;
1968
+ const isCommaOrDot = node => {
1969
+ return (node.type === "atom" && node.text === ",") ||
1970
+ (node.type === "textord" && node.text === ".")
1971
+ };
1972
+ const consolidateNumbers = expression => {
1973
+ // Consolidate adjacent numbers. We want to return <mn>1,506.3</mn>,
1974
+ // not <mn>1</mn><mo>,</mo><mn>5</mn><mn>0</mn><mn>6</mn><mi>.</mi><mn>3</mn>
1975
+ if (expression.length < 2) { return }
1976
+ const nums = [];
1977
+ let inNum = false;
1978
+ // Find adjacent numerals
1979
+ for (let i = 0; i < expression.length; i++) {
1980
+ const node = expression[i];
1981
+ if (node.type === "textord" && numberRegEx$1.test(node.text)) {
1982
+ if (!inNum) { nums.push({ start: i }); }
1983
+ inNum = true;
1984
+ } else {
1985
+ if (inNum) { nums[nums.length - 1].end = i - 1; }
1986
+ inNum = false;
1987
+ }
1988
+ }
1989
+ if (inNum) { nums[nums.length - 1].end = expression.length - 1; }
1990
+
1991
+ // Determine if numeral groups are separated by a comma or dot.
1992
+ for (let i = nums.length - 1; i > 0; i--) {
1993
+ if (nums[i - 1].end === nums[i].start - 2 && isCommaOrDot(expression[nums[i].start - 1])) {
1994
+ // Merge the two groups.
1995
+ nums[i - 1].end = nums[i].end;
1996
+ nums.splice(i, 1);
1997
+ }
1998
+ }
1999
+
2000
+ // Consolidate the number nodes
2001
+ for (let i = nums.length - 1; i >= 0; i--) {
2002
+ for (let j = nums[i].start + 1; j <= nums[i].end; j++) {
2003
+ expression[nums[i].start].text += expression[j].text;
2004
+ }
2005
+ expression.splice(nums[i].start + 1, nums[i].end - nums[i].start);
2006
+ }
2007
+ };
2008
+
1965
2009
  /**
1966
2010
  * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
1967
2011
  * unless the array has length 1. Always returns a single node.
@@ -1997,6 +2041,8 @@ const buildExpression = function(expression, style, isOrdgroup) {
1997
2041
  return [group];
1998
2042
  }
1999
2043
 
2044
+ consolidateNumbers(expression);
2045
+
2000
2046
  const groups = [];
2001
2047
  for (let i = 0; i < expression.length; i++) {
2002
2048
  const group = buildGroup$1(expression[i], style);
@@ -2096,18 +2142,6 @@ function buildMathML(tree, texExpression, style, settings) {
2096
2142
  wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
2097
2143
  }
2098
2144
 
2099
- if (wrap !== "none" && wrapper.children.length > 1) {
2100
- const maths = [];
2101
- for (let i = 0; i < wrapper.children.length; i++) {
2102
- const math = new mathMLTree.MathNode("math", [wrapper.children[i]]);
2103
- if (settings.xml) {
2104
- math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
2105
- }
2106
- maths.push(math);
2107
- }
2108
- return mathMLTree.newDocumentFragment(maths)
2109
- }
2110
-
2111
2145
  const math = new mathMLTree.MathNode("math", [wrapper]);
2112
2146
 
2113
2147
  if (settings.xml) {
@@ -2115,6 +2149,9 @@ function buildMathML(tree, texExpression, style, settings) {
2115
2149
  }
2116
2150
  if (settings.displayMode) {
2117
2151
  math.setAttribute("display", "block");
2152
+ math.style.display = math.children.length === 1 && math.children[0].type === "mtable"
2153
+ ? "inline"
2154
+ : "inline-block";
2118
2155
  }
2119
2156
  return math;
2120
2157
  }
@@ -7648,7 +7685,7 @@ const smallCaps = Object.freeze({
7648
7685
  // "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in
7649
7686
  // src/symbols.js.
7650
7687
 
7651
- const numberRegEx$1 = /^\d(?:[\d,.]*\d)?$/; // Keep in sync with numberRegEx in Parser.js
7688
+ const numberRegEx = /^\d(?:[\d,.]*\d)?$/;
7652
7689
  const latinRegEx = /[A-Ba-z]/;
7653
7690
 
7654
7691
  const italicNumber = (text, variant, tag) => {
@@ -7702,7 +7739,7 @@ defineFunctionBuilders({
7702
7739
  const variant = getVariant(group, style) || "normal";
7703
7740
 
7704
7741
  let node;
7705
- if (numberRegEx$1.test(group.text)) {
7742
+ if (numberRegEx.test(group.text)) {
7706
7743
  const tag = group.mode === "text" ? "mtext" : "mn";
7707
7744
  if (variant === "italic" || variant === "bold-italic") {
7708
7745
  return italicNumber(text, variant, tag)
@@ -8015,8 +8052,7 @@ const combiningDiacriticalMarksEndRegex = new RegExp(`${combiningDiacriticalMark
8015
8052
  const tokenRegexString =
8016
8053
  `(${spaceRegexString}+)|` + // whitespace
8017
8054
  `${controlSpaceRegexString}|` + // whitespace
8018
- "(number" + // numbers (in non-strict mode)
8019
- "|[!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint
8055
+ "([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint
8020
8056
  `${combiningDiacriticalMarkString}*` + // ...plus accents
8021
8057
  "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair
8022
8058
  `${combiningDiacriticalMarkString}*` + // ...plus accents
@@ -8031,12 +8067,7 @@ class Lexer {
8031
8067
  // Separate accents from characters
8032
8068
  this.input = input;
8033
8069
  this.settings = settings;
8034
- this.tokenRegex = new RegExp(
8035
- // Strict Temml, like TeX, lexes one numeral at a time.
8036
- // Default Temml lexes contiguous numerals into a single <mn> element.
8037
- tokenRegexString.replace("number|", settings.strict ? "" : "\\d(?:[\\d,.]*\\d)?|"),
8038
- "g"
8039
- );
8070
+ this.tokenRegex = new RegExp(tokenRegexString, 'g');
8040
8071
  // Category codes. The lexer only supports comment characters (14) for now.
8041
8072
  // MacroExpander additionally distinguishes active (13).
8042
8073
  this.catcodes = {
@@ -11804,8 +11835,6 @@ var unicodeSymbols = {
11804
11835
 
11805
11836
  /* eslint no-constant-condition:0 */
11806
11837
 
11807
- const numberRegEx = /^\d(?:[\d,.]*\d)?$/; // Keep in sync with numberRegEx in symbolsOrd.js
11808
-
11809
11838
  /**
11810
11839
  * This file contains the parser used to parse out a TeX expression from the
11811
11840
  * input. Since TeX isn't context-free, standard parsers don't work particularly
@@ -12727,15 +12756,6 @@ class Parser {
12727
12756
  };
12728
12757
  }
12729
12758
  symbol = s;
12730
- } else if (!this.strict && numberRegEx.test(text)) {
12731
- // A number. Wrap in a <mn> if in math mode; <mtext> otherwise.
12732
- this.consume();
12733
- return {
12734
- type: "textord",
12735
- mode: this.mode,
12736
- loc: SourceLocation.range(nucleus),
12737
- text
12738
- }
12739
12759
  } else if (text.charCodeAt(0) >= 0x80) {
12740
12760
  // no symbol for e.g. ^
12741
12761
  if (this.settings.strict) {
@@ -12975,7 +12995,7 @@ class Style {
12975
12995
  * https://mit-license.org/
12976
12996
  */
12977
12997
 
12978
- const version = "0.10.2";
12998
+ const version = "0.10.4";
12979
12999
 
12980
13000
  function postProcess(block) {
12981
13001
  const labelMap = {};
@@ -14,7 +14,7 @@
14
14
  * https://mit-license.org/
15
15
  */
16
16
 
17
- const version = "0.10.2";
17
+ const version = "0.10.4";
18
18
 
19
19
  function postProcess(block) {
20
20
  const labelMap = {};
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "temml",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
+ "exports": {
7
+ ".": {
8
+ "require": "./dist/temml.cjs"
9
+ },
10
+ "./*": "./*"
11
+ },
6
12
  "homepage": "https://temml.org",
7
13
  "repository": {
8
14
  "type": "git",
@@ -24,7 +30,7 @@
24
30
  },
25
31
  "scripts": {
26
32
  "lint": "eslint temml.js src",
27
- "unit-test": "node -r esm ./test/unit-test.js",
33
+ "unit-test": "node ./test/unit-test.cjs",
28
34
  "visual-test": "node utils/buildTests.js",
29
35
  "test": "yarn lint && node utils/buildTests.js && yarn unit-test",
30
36
  "minify": "terser test/temml.js -o site/assets/temml.min.js -c -m && terser contrib/mhchem/mhchem.js -o site/assets/mhchem.min.js -c -m",
package/src/Lexer.js CHANGED
@@ -49,8 +49,7 @@ export const combiningDiacriticalMarksEndRegex = new RegExp(`${combiningDiacriti
49
49
  const tokenRegexString =
50
50
  `(${spaceRegexString}+)|` + // whitespace
51
51
  `${controlSpaceRegexString}|` + // whitespace
52
- "(number" + // numbers (in non-strict mode)
53
- "|[!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint
52
+ "([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint
54
53
  `${combiningDiacriticalMarkString}*` + // ...plus accents
55
54
  "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair
56
55
  `${combiningDiacriticalMarkString}*` + // ...plus accents
@@ -65,12 +64,7 @@ export default class Lexer {
65
64
  // Separate accents from characters
66
65
  this.input = input;
67
66
  this.settings = settings;
68
- this.tokenRegex = new RegExp(
69
- // Strict Temml, like TeX, lexes one numeral at a time.
70
- // Default Temml lexes contiguous numerals into a single <mn> element.
71
- tokenRegexString.replace("number|", settings.strict ? "" : "\\d(?:[\\d,.]*\\d)?|"),
72
- "g"
73
- );
67
+ this.tokenRegex = new RegExp(tokenRegexString, 'g');
74
68
  // Category codes. The lexer only supports comment characters (14) for now.
75
69
  // MacroExpander additionally distinguishes active (13).
76
70
  this.catcodes = {
package/src/Parser.js CHANGED
@@ -16,8 +16,6 @@ import { isDelimiter } from "./functions/delimsizing"
16
16
  import unicodeAccents from /*preval*/ "./unicodeAccents";
17
17
  import unicodeSymbols from /*preval*/ "./unicodeSymbols";
18
18
 
19
- const numberRegEx = /^\d(?:[\d,.]*\d)?$/ // Keep in sync with numberRegEx in symbolsOrd.js
20
-
21
19
  /**
22
20
  * This file contains the parser used to parse out a TeX expression from the
23
21
  * input. Since TeX isn't context-free, standard parsers don't work particularly
@@ -939,15 +937,6 @@ export default class Parser {
939
937
  };
940
938
  }
941
939
  symbol = s;
942
- } else if (!this.strict && numberRegEx.test(text)) {
943
- // A number. Wrap in a <mn> if in math mode; <mtext> otherwise.
944
- this.consume()
945
- return {
946
- type: "textord",
947
- mode: this.mode,
948
- loc: SourceLocation.range(nucleus),
949
- text
950
- }
951
940
  } else if (text.charCodeAt(0) >= 0x80) {
952
941
  // no symbol for e.g. ^
953
942
  if (this.settings.strict) {
package/src/Settings.js CHANGED
@@ -17,7 +17,7 @@ export default class Settings {
17
17
  this.leqno = utils.deflt(options.leqno, false); // boolean
18
18
  this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
19
19
  this.macros = options.macros || {};
20
- this.wrap = utils.deflt(options.wrap, "none") // "none" | "tex" | "="
20
+ this.wrap = utils.deflt(options.wrap, "tex") // "tex" | "="
21
21
  this.xml = utils.deflt(options.xml, false); // boolean
22
22
  this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
23
23
  this.strict = utils.deflt(options.strict, false); // boolean
@@ -75,6 +75,48 @@ export const consolidateText = mrow => {
75
75
  return mtext
76
76
  }
77
77
 
78
+ const numberRegEx = /^[0-9]$/
79
+ const isCommaOrDot = node => {
80
+ return (node.type === "atom" && node.text === ",") ||
81
+ (node.type === "textord" && node.text === ".")
82
+ }
83
+ const consolidateNumbers = expression => {
84
+ // Consolidate adjacent numbers. We want to return <mn>1,506.3</mn>,
85
+ // not <mn>1</mn><mo>,</mo><mn>5</mn><mn>0</mn><mn>6</mn><mi>.</mi><mn>3</mn>
86
+ if (expression.length < 2) { return }
87
+ const nums = [];
88
+ let inNum = false
89
+ // Find adjacent numerals
90
+ for (let i = 0; i < expression.length; i++) {
91
+ const node = expression[i];
92
+ if (node.type === "textord" && numberRegEx.test(node.text)) {
93
+ if (!inNum) { nums.push({ start: i }) }
94
+ inNum = true
95
+ } else {
96
+ if (inNum) { nums[nums.length - 1].end = i - 1 }
97
+ inNum = false
98
+ }
99
+ }
100
+ if (inNum) { nums[nums.length - 1].end = expression.length - 1 }
101
+
102
+ // Determine if numeral groups are separated by a comma or dot.
103
+ for (let i = nums.length - 1; i > 0; i--) {
104
+ if (nums[i - 1].end === nums[i].start - 2 && isCommaOrDot(expression[nums[i].start - 1])) {
105
+ // Merge the two groups.
106
+ nums[i - 1].end = nums[i].end
107
+ nums.splice(i, 1)
108
+ }
109
+ }
110
+
111
+ // Consolidate the number nodes
112
+ for (let i = nums.length - 1; i >= 0; i--) {
113
+ for (let j = nums[i].start + 1; j <= nums[i].end; j++) {
114
+ expression[nums[i].start].text += expression[j].text
115
+ }
116
+ expression.splice(nums[i].start + 1, nums[i].end - nums[i].start)
117
+ }
118
+ }
119
+
78
120
  /**
79
121
  * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
80
122
  * unless the array has length 1. Always returns a single node.
@@ -110,6 +152,8 @@ export const buildExpression = function(expression, style, isOrdgroup) {
110
152
  return [group];
111
153
  }
112
154
 
155
+ consolidateNumbers(expression)
156
+
113
157
  const groups = [];
114
158
  for (let i = 0; i < expression.length; i++) {
115
159
  const group = buildGroup(expression[i], style);
@@ -209,18 +253,6 @@ export default function buildMathML(tree, texExpression, style, settings) {
209
253
  wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
210
254
  }
211
255
 
212
- if (wrap !== "none" && wrapper.children.length > 1) {
213
- const maths = []
214
- for (let i = 0; i < wrapper.children.length; i++) {
215
- const math = new mathMLTree.MathNode("math", [wrapper.children[i]])
216
- if (settings.xml) {
217
- math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML")
218
- }
219
- maths.push(math)
220
- }
221
- return mathMLTree.newDocumentFragment(maths)
222
- }
223
-
224
256
  const math = new mathMLTree.MathNode("math", [wrapper])
225
257
 
226
258
  if (settings.xml) {
@@ -228,6 +260,9 @@ export default function buildMathML(tree, texExpression, style, settings) {
228
260
  }
229
261
  if (settings.displayMode) {
230
262
  math.setAttribute("display", "block");
263
+ math.style.display = math.children.length === 1 && math.children[0].type === "mtable"
264
+ ? "inline"
265
+ : "inline-block"
231
266
  }
232
267
  return math;
233
268
  }
@@ -7,7 +7,7 @@ import * as mml from "../buildMathML"
7
7
  // "mathord" and "textord" ParseNodes created in Parser.js from symbol Groups in
8
8
  // src/symbols.js.
9
9
 
10
- const numberRegEx = /^\d(?:[\d,.]*\d)?$/ // Keep in sync with numberRegEx in Parser.js
10
+ const numberRegEx = /^\d(?:[\d,.]*\d)?$/
11
11
  const latinRegEx = /[A-Ba-z]/
12
12
 
13
13
  const italicNumber = (text, variant, tag) => {
@@ -13,10 +13,12 @@ import mathMLTree from "./mathMLTree"
13
13
  * Then the top level of a <math> element can be occupied by <mrow> elements, and the browser
14
14
  * will break after a <mrow> if the expression extends beyond the container limit.
15
15
  *
16
- * We want the expression to render with soft line breaks after each top-level binary or
16
+ * The default is for soft line breaks after each top-level binary or
17
17
  * relational operator, per TeXbook p. 173. So we gather the expression into <mrow>s so that
18
18
  * each <mrow> ends in a binary or relational operator.
19
19
  *
20
+ * An option is for soft line breaks before an "=" sign. That changes the <mrow>s.
21
+ *
20
22
  * Soft line breaks will not work in Chromium and Safari, only Firefox.
21
23
  *
22
24
  * Hopefully browsers will someday do their own linebreaking and we will be able to delete
@@ -8,7 +8,7 @@
8
8
  * https://mit-license.org/
9
9
  */
10
10
 
11
- export const version = "0.10.2";
11
+ export const version = "0.10.4";
12
12
 
13
13
  export function postProcess(block) {
14
14
  const labelMap = {}