temml 0.10.2 → 0.10.4

Sign up to get free protection for your applications and to get access to all the features.
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 = {}