temml 0.13.4 → 0.14.0

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
@@ -68,9 +68,17 @@ ParseError.prototype.__proto__ = Error.prototype;
68
68
  */
69
69
 
70
70
  /**
71
- * Provide a default value if a setting is undefined
71
+ * Look up `key` on a user-supplied `options` object and return its value, or
72
+ * `defaultIfUndefined` if the setting is absent. Reads via
73
+ * `Object.prototype.hasOwnProperty.call` rather than plain property access,
74
+ * so a key that only exists because `Object.prototype` was polluted
75
+ * elsewhere is never mistaken for a setting the caller actually provided.
72
76
  */
73
- const deflt = function(setting, defaultIfUndefined) {
77
+ const deflt = function(options, key, defaultIfUndefined) {
78
+ if (!Object.prototype.hasOwnProperty.call(options, key)) {
79
+ return defaultIfUndefined;
80
+ }
81
+ const setting = options[key];
74
82
  return setting === undefined ? defaultIfUndefined : setting;
75
83
  };
76
84
 
@@ -188,6 +196,27 @@ const smalls = "acegıȷmnopqrsuvwxyzαγεηικμνοπρςστυχωϕ𝐚
188
196
  */
189
197
 
190
198
 
199
+ const FORBIDDEN_MACRO_KEYS = ["__proto__", "prototype", "constructor"];
200
+
201
+ // Sanitize a user-supplied macros object: strip its prototype (so later
202
+ // bracket-notation writes, e.g. from \gdef, can never reach Object.prototype)
203
+ // and delete any own key that could be used to pollute it. This mutates
204
+ // `macros` in place and returns the same reference, rather than a copy,
205
+ // because callers such as auto-render.js rely on that identity to share
206
+ // \gdef'd macros across multiple render calls that pass the same options
207
+ // object.
208
+ const sanitizeMacros = (macros) => {
209
+ if (macros == null || typeof macros !== "object") {
210
+ return Object.create(null);
211
+ }
212
+ for (const key of FORBIDDEN_MACRO_KEYS) {
213
+ if (Object.prototype.hasOwnProperty.call(macros, key)) {
214
+ delete macros[key];
215
+ }
216
+ }
217
+ return Object.setPrototypeOf(macros, null);
218
+ };
219
+
191
220
  /**
192
221
  * The main Settings object
193
222
  */
@@ -195,24 +224,29 @@ class Settings {
195
224
  constructor(options) {
196
225
  // allow null options
197
226
  options = options || {};
198
- this.displayMode = deflt(options.displayMode, false); // boolean
199
- this.annotate = deflt(options.annotate, false); // boolean
200
- this.leqno = deflt(options.leqno, false); // boolean
201
- this.throwOnError = deflt(options.throwOnError, false); // boolean
202
- this.errorColor = deflt(options.errorColor, "#b22222"); // string
203
- this.macros = options.macros || {};
204
- this.wrap = deflt(options.wrap, "none"); // "none" | "tex" | "="
205
- this.xml = deflt(options.xml, false); // boolean
206
- this.colorIsTextColor = deflt(options.colorIsTextColor, false); // boolean
207
- this.strict = deflt(options.strict, false); // boolean
208
- this.trust = deflt(options.trust, false); // trust context. See html.js.
209
- this.maxSize = (options.maxSize === undefined
210
- ? [Infinity, Infinity]
211
- : Array.isArray(options.maxSize)
227
+ this.displayMode = deflt(options, "displayMode", false); // boolean
228
+ this.annotate = deflt(options, "annotate", false); // boolean
229
+ this.leqno = deflt(options, "leqno", false); // boolean
230
+ this.throwOnError = deflt(options, "throwOnError", false); // boolean
231
+ this.errorColor = deflt(options, "errorColor", "#b22222"); // string
232
+ this.macros = sanitizeMacros(
233
+ Object.prototype.hasOwnProperty.call(options, "macros") ? options.macros : undefined
234
+ );
235
+ this.wrap = deflt(options, "wrap", "none"); // "none" | "tex" | "="
236
+ this.xml = deflt(options, "xml", false); // boolean
237
+ this.colorIsTextColor = deflt(options, "colorIsTextColor", false); // boolean
238
+ this.strict = deflt(options, "strict", false); // boolean
239
+ this.trust = deflt(options, "trust", false); // trust context. See html.js.
240
+ const maxSize = Object.prototype.hasOwnProperty.call(options, "maxSize")
212
241
  ? options.maxSize
242
+ : undefined;
243
+ this.maxSize = (maxSize === undefined
244
+ ? [Infinity, Infinity]
245
+ : Array.isArray(maxSize)
246
+ ? maxSize
213
247
  : [Infinity, Infinity]
214
248
  );
215
- this.maxExpand = Math.max(0, deflt(options.maxExpand, 1000)); // number
249
+ this.maxExpand = Math.max(0, deflt(options, "maxExpand", 1000)); // number
216
250
  this.wrapDelimiterPairs = true; // boolean
217
251
  }
218
252
 
@@ -912,22 +946,26 @@ const NON_ATOMS = {
912
946
  textord: 1
913
947
  };
914
948
 
915
- const symbols = {
916
- math: {},
917
- text: {}
918
- };
949
+ const symbols = Object.create(null);
950
+ symbols.math = Object.create(null);
951
+ symbols.text = Object.create(null);
919
952
 
920
- /** `acceptUnicodeChar = true` is only applicable if `replace` is set. */
921
953
  function defineSymbol(mode, group, replace, name, acceptUnicodeChar) {
922
- symbols[mode][name] = { group, replace };
954
+ if (mode !== "math" && mode !== "text") {
955
+ throw new TypeError(`Invalid mode: ${mode} called in defineSymbol`);
956
+ }
957
+
958
+ const entry = { group, replace };
959
+ symbols[mode][name] = entry;
923
960
 
961
+ /** `acceptUnicodeChar = true` is only applicable if `replace` is set. */
924
962
  if (acceptUnicodeChar && replace) {
925
- symbols[mode][replace] = symbols[mode][name];
963
+ symbols[mode][replace] = entry;
926
964
  }
927
965
  }
928
966
 
929
967
  // Some abbreviations for commonly used strings.
930
- // This helps minify the code, and also spotting typos using jshint.
968
+ // This helps minify the code, and also spotting typos via linter.
931
969
 
932
970
  // modes:
933
971
  const math = "math";
@@ -1765,9 +1803,11 @@ defineSymbol(math, op, "\u222b", "\\smallint");
1765
1803
  defineSymbol(text, inner, "\u2026", "\\textellipsis");
1766
1804
  defineSymbol(math, inner, "\u2026", "\\mathellipsis");
1767
1805
  defineSymbol(text, inner, "\u2026", "\\ldots", true);
1806
+ defineSymbol(math, inner, "\u2026", "\\dotso");
1807
+ defineSymbol(math, inner, "\u2026", "\\dotsc");
1768
1808
  defineSymbol(math, inner, "\u2026", "\\ldots", true);
1769
1809
  defineSymbol(math, inner, "\u22f0", "\\iddots", true);
1770
- defineSymbol(math, inner, "\u22ef", "\\@cdots", true);
1810
+ defineSymbol(math, inner, "\u22ef", "\\cdots", true);
1771
1811
  defineSymbol(math, inner, "\u22f1", "\\ddots", true);
1772
1812
  defineSymbol(math, textord, "\u22ee", "\\varvdots"); // \vdots is a macro
1773
1813
  defineSymbol(text, textord, "\u22ee", "\\varvdots");
@@ -3557,7 +3597,7 @@ const StyleLevel = {
3557
3597
  * `macros.js` exports this same dictionary again and makes it public.
3558
3598
  * `Parser.js` requires this dictionary via `macros.js`.
3559
3599
  */
3560
- const _macros = {};
3600
+ const _macros = Object.create(null);
3561
3601
 
3562
3602
  // This function might one day accept an additional argument and do more things.
3563
3603
  function defineMacro(name, body) {
@@ -3902,60 +3942,6 @@ defineMacro("\\dots", function(context) {
3902
3942
  return thedots;
3903
3943
  });
3904
3944
 
3905
- const spaceAfterDots = {
3906
- // \rightdelim@ checks for the following:
3907
- ")": true,
3908
- "]": true,
3909
- "\\rbrack": true,
3910
- "\\}": true,
3911
- "\\rbrace": true,
3912
- "\\rangle": true,
3913
- "\\rceil": true,
3914
- "\\rfloor": true,
3915
- "\\rgroup": true,
3916
- "\\rmoustache": true,
3917
- "\\right": true,
3918
- "\\bigr": true,
3919
- "\\biggr": true,
3920
- "\\Bigr": true,
3921
- "\\Biggr": true,
3922
- // \extra@ also tests for the following:
3923
- $: true,
3924
- // \extrap@ checks for the following:
3925
- ";": true,
3926
- ".": true,
3927
- ",": true
3928
- };
3929
-
3930
- defineMacro("\\dotso", function(context) {
3931
- const next = context.future().text;
3932
- if (next in spaceAfterDots) {
3933
- return "\\ldots\\,";
3934
- } else {
3935
- return "\\ldots";
3936
- }
3937
- });
3938
-
3939
- defineMacro("\\dotsc", function(context) {
3940
- const next = context.future().text;
3941
- // \dotsc uses \extra@ but not \extrap@, instead specially checking for
3942
- // ';' and '.', but doesn't check for ','.
3943
- if (next in spaceAfterDots && next !== ",") {
3944
- return "\\ldots\\,";
3945
- } else {
3946
- return "\\ldots";
3947
- }
3948
- });
3949
-
3950
- defineMacro("\\cdots", function(context) {
3951
- const next = context.future().text;
3952
- if (next in spaceAfterDots) {
3953
- return "\\@cdots\\,";
3954
- } else {
3955
- return "\\@cdots";
3956
- }
3957
- });
3958
-
3959
3945
  defineMacro("\\dotsb", "\\cdots");
3960
3946
  defineMacro("\\dotsm", "\\cdots");
3961
3947
  defineMacro("\\dotsi", "\\!\\cdots");
@@ -4085,7 +4071,7 @@ defineMacro("\\colon", `\\mathpunct{\\char"3a}`);
4085
4071
  //////////////////////////////////////////////////////////////////////
4086
4072
  // mathtools.sty
4087
4073
 
4088
- defineMacro("\\prescript", "\\pres@cript{_{#1}^{#2}}{}{#3}");
4074
+ defineMacro("\\prescript", "\\pres@cript{^{#1}_{#2}}{}{#3}");
4089
4075
 
4090
4076
  //\providecommand\ordinarycolon{:}
4091
4077
  defineMacro("\\ordinarycolon", `\\char"3a`);
@@ -11443,6 +11429,11 @@ const isArrow = str => {
11443
11429
  defineFunctionBuilders({
11444
11430
  type: "atom",
11445
11431
  mathmlBuilder(group, style) {
11432
+ if (group.family === "inner") {
11433
+ // Give \dots ORD spacing, not OP spacing.
11434
+ const tag = group.mode === "text" ? "mtext" : "mi";
11435
+ return new MathNode(tag, [makeText(group.text, group.mode)])
11436
+ }
11446
11437
  const node = new MathNode("mo", [makeText(group.text, group.mode)]);
11447
11438
  if (group.family === "punct") {
11448
11439
  node.setAttribute("separator", "true");
@@ -11998,7 +11989,7 @@ class Namespace {
11998
11989
  * of initial (global-level) mappings, which will constantly change
11999
11990
  * according to any global/top-level `set`s done.
12000
11991
  */
12001
- constructor(builtins = {}, globalMacros = {}) {
11992
+ constructor(builtins = Object.create(null), globalMacros = Object.create(null)) {
12002
11993
  this.current = globalMacros;
12003
11994
  this.builtins = builtins;
12004
11995
  this.undefStack = [];
@@ -12008,7 +11999,7 @@ class Namespace {
12008
11999
  * Start a new nested group, affecting future local `set`s.
12009
12000
  */
12010
12001
  beginGroup() {
12011
- this.undefStack.push({});
12002
+ this.undefStack.push(Object.create(null));
12012
12003
  }
12013
12004
 
12014
12005
  /**
@@ -14206,7 +14197,7 @@ class Style {
14206
14197
  * https://mit-license.org/
14207
14198
  */
14208
14199
 
14209
- const version = "0.13.4";
14200
+ const version = "0.14.0";
14210
14201
 
14211
14202
  function postProcess(block) {
14212
14203
  const labelMap = {};
@@ -14527,7 +14518,7 @@ const renderMathInElement = function(elem, options) {
14527
14518
 
14528
14519
  // Enable sharing of global macros defined via `\gdef` between different
14529
14520
  // math elements within a single call to `renderMathInElement`.
14530
- optionsCopy.macros = optionsCopy.macros || {};
14521
+ optionsCopy.macros = optionsCopy.macros || Object.create(null);
14531
14522
 
14532
14523
  renderElem(elem, optionsCopy);
14533
14524
  postProcess(elem);
@@ -14606,7 +14597,7 @@ const generateParseTree = function(expression, options) {
14606
14597
  */
14607
14598
  const definePreamble = function(expression, options) {
14608
14599
  const settings = new Settings(options);
14609
- settings.macros = {};
14600
+ settings.macros = Object.create(null);
14610
14601
  if (!(typeof expression === "string" || expression instanceof String)) {
14611
14602
  throw new TypeError("Temml can only parse string typed expression")
14612
14603
  }
@@ -11,7 +11,7 @@
11
11
  * https://mit-license.org/
12
12
  */
13
13
 
14
- const version = "0.13.4";
14
+ const version = "0.14.0";
15
15
 
16
16
  function postProcess(block) {
17
17
  const labelMap = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "temml",
3
- "version": "0.13.4",
3
+ "version": "0.14.0",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
6
  "engines": {
@@ -17,7 +17,7 @@
17
17
  "homepage": "https://temml.org",
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git://github.com/ronkok/Temml"
20
+ "url": "git+https://github.com/ronkok/Temml.git"
21
21
  },
22
22
  "packageManager": "yarn@3.3.1",
23
23
  "files": [
package/src/Namespace.js CHANGED
@@ -15,7 +15,7 @@ export default class Namespace {
15
15
  * of initial (global-level) mappings, which will constantly change
16
16
  * according to any global/top-level `set`s done.
17
17
  */
18
- constructor(builtins = {}, globalMacros = {}) {
18
+ constructor(builtins = Object.create(null), globalMacros = Object.create(null)) {
19
19
  this.current = globalMacros;
20
20
  this.builtins = builtins;
21
21
  this.undefStack = [];
@@ -25,7 +25,7 @@ export default class Namespace {
25
25
  * Start a new nested group, affecting future local `set`s.
26
26
  */
27
27
  beginGroup() {
28
- this.undefStack.push({});
28
+ this.undefStack.push(Object.create(null));
29
29
  }
30
30
 
31
31
  /**
package/src/Settings.js CHANGED
@@ -5,6 +5,27 @@
5
5
 
6
6
  import * as utils from "./utils";
7
7
 
8
+ const FORBIDDEN_MACRO_KEYS = ["__proto__", "prototype", "constructor"];
9
+
10
+ // Sanitize a user-supplied macros object: strip its prototype (so later
11
+ // bracket-notation writes, e.g. from \gdef, can never reach Object.prototype)
12
+ // and delete any own key that could be used to pollute it. This mutates
13
+ // `macros` in place and returns the same reference, rather than a copy,
14
+ // because callers such as auto-render.js rely on that identity to share
15
+ // \gdef'd macros across multiple render calls that pass the same options
16
+ // object.
17
+ const sanitizeMacros = (macros) => {
18
+ if (macros == null || typeof macros !== "object") {
19
+ return Object.create(null);
20
+ }
21
+ for (const key of FORBIDDEN_MACRO_KEYS) {
22
+ if (Object.prototype.hasOwnProperty.call(macros, key)) {
23
+ delete macros[key];
24
+ }
25
+ }
26
+ return Object.setPrototypeOf(macros, null);
27
+ };
28
+
8
29
  /**
9
30
  * The main Settings object
10
31
  */
@@ -12,24 +33,29 @@ export default class Settings {
12
33
  constructor(options) {
13
34
  // allow null options
14
35
  options = options || {};
15
- this.displayMode = utils.deflt(options.displayMode, false); // boolean
16
- this.annotate = utils.deflt(options.annotate, false) // boolean
17
- this.leqno = utils.deflt(options.leqno, false); // boolean
18
- this.throwOnError = utils.deflt(options.throwOnError, false); // boolean
19
- this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
20
- this.macros = options.macros || {};
21
- this.wrap = utils.deflt(options.wrap, "none") // "none" | "tex" | "="
22
- this.xml = utils.deflt(options.xml, false); // boolean
23
- this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // boolean
24
- this.strict = utils.deflt(options.strict, false); // boolean
25
- this.trust = utils.deflt(options.trust, false); // trust context. See html.js.
26
- this.maxSize = (options.maxSize === undefined
27
- ? [Infinity, Infinity]
28
- : Array.isArray(options.maxSize)
36
+ this.displayMode = utils.deflt(options, "displayMode", false); // boolean
37
+ this.annotate = utils.deflt(options, "annotate", false) // boolean
38
+ this.leqno = utils.deflt(options, "leqno", false); // boolean
39
+ this.throwOnError = utils.deflt(options, "throwOnError", false); // boolean
40
+ this.errorColor = utils.deflt(options, "errorColor", "#b22222"); // string
41
+ this.macros = sanitizeMacros(
42
+ Object.prototype.hasOwnProperty.call(options, "macros") ? options.macros : undefined
43
+ );
44
+ this.wrap = utils.deflt(options, "wrap", "none") // "none" | "tex" | "="
45
+ this.xml = utils.deflt(options, "xml", false); // boolean
46
+ this.colorIsTextColor = utils.deflt(options, "colorIsTextColor", false); // boolean
47
+ this.strict = utils.deflt(options, "strict", false); // boolean
48
+ this.trust = utils.deflt(options, "trust", false); // trust context. See html.js.
49
+ const maxSize = Object.prototype.hasOwnProperty.call(options, "maxSize")
29
50
  ? options.maxSize
51
+ : undefined;
52
+ this.maxSize = (maxSize === undefined
53
+ ? [Infinity, Infinity]
54
+ : Array.isArray(maxSize)
55
+ ? maxSize
30
56
  : [Infinity, Infinity]
31
57
  )
32
- this.maxExpand = Math.max(0, utils.deflt(options.maxExpand, 1000)); // number
58
+ this.maxExpand = Math.max(0, utils.deflt(options, "maxExpand", 1000)); // number
33
59
  this.wrapDelimiterPairs = true; // boolean
34
60
  }
35
61
 
@@ -256,7 +256,7 @@ export const renderMathInElement = function(elem, options) {
256
256
 
257
257
  // Enable sharing of global macros defined via `\gdef` between different
258
258
  // math elements within a single call to `renderMathInElement`.
259
- optionsCopy.macros = optionsCopy.macros || {};
259
+ optionsCopy.macros = optionsCopy.macros || Object.create(null);
260
260
 
261
261
  renderElem(elem, optionsCopy);
262
262
  postProcess(elem);
@@ -3,7 +3,7 @@
3
3
  * `macros.js` exports this same dictionary again and makes it public.
4
4
  * `Parser.js` requires this dictionary via `macros.js`.
5
5
  */
6
- export const _macros = {};
6
+ export const _macros = Object.create(null);
7
7
 
8
8
  // This function might one day accept an additional argument and do more things.
9
9
  export default function defineMacro(name, body) {
@@ -21,6 +21,11 @@ const isArrow = str => {
21
21
  defineFunctionBuilders({
22
22
  type: "atom",
23
23
  mathmlBuilder(group, style) {
24
+ if (group.family === "inner") {
25
+ // Give \dots ORD spacing, not OP spacing.
26
+ const tag = group.mode === "text" ? "mtext" : "mi"
27
+ return new mathMLTree.MathNode(tag, [mml.makeText(group.text, group.mode)])
28
+ }
24
29
  const node = new mathMLTree.MathNode("mo", [mml.makeText(group.text, group.mode)]);
25
30
  if (group.family === "punct") {
26
31
  node.setAttribute("separator", "true");
package/src/macros.js CHANGED
@@ -342,60 +342,6 @@ defineMacro("\\dots", function(context) {
342
342
  return thedots;
343
343
  });
344
344
 
345
- const spaceAfterDots = {
346
- // \rightdelim@ checks for the following:
347
- ")": true,
348
- "]": true,
349
- "\\rbrack": true,
350
- "\\}": true,
351
- "\\rbrace": true,
352
- "\\rangle": true,
353
- "\\rceil": true,
354
- "\\rfloor": true,
355
- "\\rgroup": true,
356
- "\\rmoustache": true,
357
- "\\right": true,
358
- "\\bigr": true,
359
- "\\biggr": true,
360
- "\\Bigr": true,
361
- "\\Biggr": true,
362
- // \extra@ also tests for the following:
363
- $: true,
364
- // \extrap@ checks for the following:
365
- ";": true,
366
- ".": true,
367
- ",": true
368
- };
369
-
370
- defineMacro("\\dotso", function(context) {
371
- const next = context.future().text;
372
- if (next in spaceAfterDots) {
373
- return "\\ldots\\,";
374
- } else {
375
- return "\\ldots";
376
- }
377
- });
378
-
379
- defineMacro("\\dotsc", function(context) {
380
- const next = context.future().text;
381
- // \dotsc uses \extra@ but not \extrap@, instead specially checking for
382
- // ';' and '.', but doesn't check for ','.
383
- if (next in spaceAfterDots && next !== ",") {
384
- return "\\ldots\\,";
385
- } else {
386
- return "\\ldots";
387
- }
388
- });
389
-
390
- defineMacro("\\cdots", function(context) {
391
- const next = context.future().text;
392
- if (next in spaceAfterDots) {
393
- return "\\@cdots\\,";
394
- } else {
395
- return "\\@cdots";
396
- }
397
- });
398
-
399
345
  defineMacro("\\dotsb", "\\cdots");
400
346
  defineMacro("\\dotsm", "\\cdots");
401
347
  defineMacro("\\dotsi", "\\!\\cdots");
@@ -525,7 +471,7 @@ defineMacro("\\colon", `\\mathpunct{\\char"3a}`)
525
471
  //////////////////////////////////////////////////////////////////////
526
472
  // mathtools.sty
527
473
 
528
- defineMacro("\\prescript", "\\pres@cript{_{#1}^{#2}}{}{#3}")
474
+ defineMacro("\\prescript", "\\pres@cript{^{#1}_{#2}}{}{#3}")
529
475
 
530
476
  //\providecommand\ordinarycolon{:}
531
477
  defineMacro("\\ordinarycolon", `\\char"3a`);
@@ -5,7 +5,7 @@
5
5
  * https://mit-license.org/
6
6
  */
7
7
 
8
- export const version = "0.13.4";
8
+ export const version = "0.14.0";
9
9
 
10
10
  export function postProcess(block) {
11
11
  const labelMap = {}
package/src/symbols.js CHANGED
@@ -33,23 +33,27 @@ export const NON_ATOMS = {
33
33
  textord: 1
34
34
  };
35
35
 
36
- const symbols = {
37
- math: {},
38
- text: {}
39
- };
36
+ const symbols = Object.create(null);
37
+ symbols.math = Object.create(null);
38
+ symbols.text = Object.create(null);
40
39
  export default symbols;
41
40
 
42
- /** `acceptUnicodeChar = true` is only applicable if `replace` is set. */
43
41
  export function defineSymbol(mode, group, replace, name, acceptUnicodeChar) {
44
- symbols[mode][name] = { group, replace };
42
+ if (mode !== "math" && mode !== "text") {
43
+ throw new TypeError(`Invalid mode: ${mode} called in defineSymbol`);
44
+ }
45
+
46
+ const entry = { group, replace };
47
+ symbols[mode][name] = entry;
45
48
 
49
+ /** `acceptUnicodeChar = true` is only applicable if `replace` is set. */
46
50
  if (acceptUnicodeChar && replace) {
47
- symbols[mode][replace] = symbols[mode][name];
51
+ symbols[mode][replace] = entry;
48
52
  }
49
53
  }
50
54
 
51
55
  // Some abbreviations for commonly used strings.
52
- // This helps minify the code, and also spotting typos using jshint.
56
+ // This helps minify the code, and also spotting typos via linter.
53
57
 
54
58
  // modes:
55
59
  const math = "math";
@@ -887,9 +891,11 @@ defineSymbol(math, op, "\u222b", "\\smallint");
887
891
  defineSymbol(text, inner, "\u2026", "\\textellipsis");
888
892
  defineSymbol(math, inner, "\u2026", "\\mathellipsis");
889
893
  defineSymbol(text, inner, "\u2026", "\\ldots", true);
894
+ defineSymbol(math, inner, "\u2026", "\\dotso");
895
+ defineSymbol(math, inner, "\u2026", "\\dotsc");
890
896
  defineSymbol(math, inner, "\u2026", "\\ldots", true);
891
897
  defineSymbol(math, inner, "\u22f0", "\\iddots", true);
892
- defineSymbol(math, inner, "\u22ef", "\\@cdots", true);
898
+ defineSymbol(math, inner, "\u22ef", "\\cdots", true);
893
899
  defineSymbol(math, inner, "\u22f1", "\\ddots", true);
894
900
  defineSymbol(math, textord, "\u22ee", "\\varvdots"); // \vdots is a macro
895
901
  defineSymbol(text, textord, "\u22ee", "\\varvdots");
package/src/utils.js CHANGED
@@ -5,9 +5,17 @@
5
5
  */
6
6
 
7
7
  /**
8
- * Provide a default value if a setting is undefined
8
+ * Look up `key` on a user-supplied `options` object and return its value, or
9
+ * `defaultIfUndefined` if the setting is absent. Reads via
10
+ * `Object.prototype.hasOwnProperty.call` rather than plain property access,
11
+ * so a key that only exists because `Object.prototype` was polluted
12
+ * elsewhere is never mistaken for a setting the caller actually provided.
9
13
  */
10
- export const deflt = function(setting, defaultIfUndefined) {
14
+ export const deflt = function(options, key, defaultIfUndefined) {
15
+ if (!Object.prototype.hasOwnProperty.call(options, key)) {
16
+ return defaultIfUndefined;
17
+ }
18
+ const setting = options[key];
11
19
  return setting === undefined ? defaultIfUndefined : setting;
12
20
  };
13
21
 
package/temml.js CHANGED
@@ -84,7 +84,7 @@ const generateParseTree = function(expression, options) {
84
84
  */
85
85
  const definePreamble = function(expression, options) {
86
86
  const settings = new Settings(options);
87
- settings.macros = {};
87
+ settings.macros = Object.create(null);
88
88
  if (!(typeof expression === "string" || expression instanceof String)) {
89
89
  throw new TypeError("Temml can only parse string typed expression")
90
90
  }