@khanacademy/math-input 16.5.0 → 17.0.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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Khan Academy's new expression editor for the mobile web.",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "16.5.0",
6
+ "version": "17.0.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -21,7 +21,7 @@
21
21
  "scripts": {},
22
22
  "dependencies": {
23
23
  "@khanacademy/perseus-core": "1.4.1",
24
- "mathquill": "git+https://git@github.com/Khan/mathquill.git#48410e80d760bbd5105544d4a4ab459a28dc2cbc",
24
+ "mathquill": "git+https://git@github.com/Khan/mathquill.git#774bb82942c268bd56cd1024dc18b01ac4d90029",
25
25
  "performance-now": "^0.2.0"
26
26
  },
27
27
  "devDependencies": {
@@ -29,7 +29,7 @@
29
29
  "@khanacademy/wonder-blocks-color": "^3.0.0",
30
30
  "@khanacademy/wonder-blocks-core": "^6.3.1",
31
31
  "@khanacademy/wonder-blocks-i18n": "^2.0.2",
32
- "@khanacademy/wonder-blocks-popover": "^3.0.22",
32
+ "@khanacademy/wonder-blocks-popover": "^3.1.0",
33
33
  "@khanacademy/wonder-blocks-timing": "^4.0.2",
34
34
  "@khanacademy/wonder-stuff-core": "^1.5.1",
35
35
  "@phosphor-icons/core": "^2.0.2",
@@ -17,7 +17,7 @@ import {KeypadContext, StatefulKeypadContextProvider} from "../keypad-context";
17
17
 
18
18
  import type {KeypadConfiguration} from "../../types";
19
19
 
20
- const MQ = MathQuill.getInterface(2);
20
+ const MQ = MathQuill.getInterface(3);
21
21
 
22
22
  const defaultConfiguration: KeypadConfiguration = {
23
23
  keypadType: KeypadType.FRACTION,
@@ -176,11 +176,14 @@ describe("math input integration", () => {
176
176
 
177
177
  // MathQuill is problematic,
178
178
  // this is how to get the value of the input directly from MQ
179
- const mathquillInstance =
179
+ const mathquillInstance = MQ(
180
180
  // eslint-disable-next-line testing-library/no-node-access
181
- MQ(document.getElementsByClassName("mq-editable-field")[0]);
181
+ document.getElementsByClassName(
182
+ "mq-editable-field",
183
+ )[0] as HTMLElement,
184
+ );
182
185
 
183
- expect(mathquillInstance.latex()).toBe("8675309");
186
+ expect(mathquillInstance?.latex()).toBe("8675309");
184
187
  });
185
188
 
186
189
  it("can handle symbols", async () => {
@@ -202,11 +205,14 @@ describe("math input integration", () => {
202
205
 
203
206
  // MathQuill is problematic,
204
207
  // this is how to get the value of the input directly from MQ
205
- const mathquillInstance =
208
+ const mathquillInstance = MQ(
206
209
  // eslint-disable-next-line testing-library/no-node-access
207
- MQ(document.getElementsByClassName("mq-editable-field")[0]);
210
+ document.getElementsByClassName(
211
+ "mq-editable-field",
212
+ )[0] as HTMLElement,
213
+ );
208
214
 
209
- expect(mathquillInstance.latex()).toBe("42\\%");
215
+ expect(mathquillInstance?.latex()).toBe("42\\%");
210
216
  });
211
217
 
212
218
  it("handles fractions correctly in expression", async () => {
@@ -243,11 +249,14 @@ describe("math input integration", () => {
243
249
 
244
250
  // MathQuill is problematic,
245
251
  // this is how to get the value of the input directly from MQ
246
- const mathquillInstance =
252
+ const mathquillInstance = MQ(
247
253
  // eslint-disable-next-line testing-library/no-node-access
248
- MQ(document.getElementsByClassName("mq-editable-field")[0]);
254
+ document.getElementsByClassName(
255
+ "mq-editable-field",
256
+ )[0] as HTMLElement,
257
+ );
249
258
 
250
- expect(mathquillInstance.latex()).toBe("1\\frac{4}{2}");
259
+ expect(mathquillInstance?.latex()).toBe("1\\frac{4}{2}");
251
260
  });
252
261
 
253
262
  it("handles fractions correctly in fraction", async () => {
@@ -279,10 +288,13 @@ describe("math input integration", () => {
279
288
 
280
289
  // MathQuill is problematic,
281
290
  // this is how to get the value of the input directly from MQ
282
- const mathquillInstance =
291
+ const mathquillInstance = MQ(
283
292
  // eslint-disable-next-line testing-library/no-node-access
284
- MQ(document.getElementsByClassName("mq-editable-field")[0]);
293
+ document.getElementsByClassName(
294
+ "mq-editable-field",
295
+ )[0] as HTMLElement,
296
+ );
285
297
 
286
- expect(mathquillInstance.latex()).toBe("1\\frac{4}{2}");
298
+ expect(mathquillInstance?.latex()).toBe("1\\frac{4}{2}");
287
299
  });
288
300
  });
@@ -35,23 +35,23 @@ describe("MathQuill", () => {
35
35
  });
36
36
 
37
37
  it("should work after an expression", () => {
38
- mathField.setContent("35x^2");
38
+ mathField.setContent("35x^{2}");
39
39
  mathField.pressKey("FRAC_INCLUSIVE");
40
- expect(mathField.getContent()).toEqual("\\frac{35x^2}{ }");
40
+ expect(mathField.getContent()).toEqual("\\frac{35x^{2}}{ }");
41
41
  });
42
42
 
43
43
  it("should work before an expression", () => {
44
- mathField.setContent("35x^2");
44
+ mathField.setContent("35x^{2}");
45
45
  mathField.moveToStart();
46
46
  mathField.pressKey("FRAC_INCLUSIVE");
47
- expect(mathField.getContent()).toEqual("\\frac{ }{ }35x^2");
47
+ expect(mathField.getContent()).toEqual("\\frac{ }{ }35x^{2}");
48
48
  });
49
49
 
50
50
  it("should work with a selected expression", () => {
51
- mathField.setContent("35x^2");
51
+ mathField.setContent("35x^{2}");
52
52
  mathField.selectAll();
53
53
  mathField.pressKey("FRAC_INCLUSIVE");
54
- expect(mathField.getContent()).toEqual("\\frac{35x^2}{ }");
54
+ expect(mathField.getContent()).toEqual("\\frac{35x^{2}}{ }");
55
55
  });
56
56
  });
57
57
 
@@ -63,30 +63,30 @@ describe("MathQuill", () => {
63
63
  });
64
64
 
65
65
  it("should work after an expression", () => {
66
- mathField.setContent("35x^2");
66
+ mathField.setContent("35x^{2}");
67
67
  mathField.pressKey("RIGHT_PAREN");
68
- expect(mathField.getContent()).toEqual("\\left(35x^2\\right)");
68
+ expect(mathField.getContent()).toEqual("\\left(35x^{2}\\right)");
69
69
  });
70
70
 
71
71
  it("should work before an expression", () => {
72
- mathField.setContent("35x^2");
72
+ mathField.setContent("35x^{2}");
73
73
  mathField.moveToStart();
74
74
  mathField.pressKey("LEFT_PAREN");
75
- expect(mathField.getContent()).toEqual("\\left(35x^2\\right)");
75
+ expect(mathField.getContent()).toEqual("\\left(35x^{2}\\right)");
76
76
  });
77
77
 
78
78
  it.skip("should work on a selected expression", () => {
79
79
  mathField.setContent("35x + 5");
80
80
  mathField.selectAll();
81
81
  mathField.pressKey("LEFT_PAREN");
82
- expect(mathField.getContent()).toEqual("\\left(35x^2\\right)");
82
+ expect(mathField.getContent()).toEqual("\\left(35x^{2}\\right)");
83
83
  });
84
84
  });
85
85
 
86
86
  describe("Squared", () => {
87
87
  it("should prefix with empty parens after no content", () => {
88
88
  mathField.pressKey("EXP_2");
89
- expect(mathField.getContent()).toEqual("\\left(\\right)^2");
89
+ expect(mathField.getContent()).toEqual("\\left(\\right)^{2}");
90
90
 
91
91
  // Verify that the cursor is in parens.
92
92
  expect(isInsideEmptyParens(mathField.getCursor())).toBeTruthy();
@@ -95,27 +95,27 @@ describe("MathQuill", () => {
95
95
  it("should prefix with empty parens after an operator", () => {
96
96
  mathField.pressKey("DIVIDE");
97
97
  mathField.pressKey("EXP_2");
98
- expect(mathField.getContent()).toEqual("\\div\\left(\\right)^2");
98
+ expect(mathField.getContent()).toEqual("\\div\\left(\\right)^{2}");
99
99
  });
100
100
 
101
101
  it("should work after an expression", () => {
102
102
  mathField.setContent("35x");
103
103
  mathField.pressKey("EXP_2");
104
- expect(mathField.getContent()).toEqual("35x^2");
104
+ expect(mathField.getContent()).toEqual("35x^{2}");
105
105
  });
106
106
 
107
107
  it.skip("should work on a selected expression", () => {
108
108
  mathField.setContent("35x+5");
109
109
  mathField.selectAll();
110
110
  mathField.pressKey("EXP_2");
111
- expect(mathField.getContent()).toEqual("\\left(35x+5\\right)^2");
111
+ expect(mathField.getContent()).toEqual("\\left(35x+5\\right)^{2}");
112
112
  });
113
113
  });
114
114
 
115
115
  describe("Cubed", () => {
116
116
  it("should prefix with empty parens after no content", () => {
117
117
  mathField.pressKey("EXP_3");
118
- expect(mathField.getContent()).toEqual("\\left(\\right)^3");
118
+ expect(mathField.getContent()).toEqual("\\left(\\right)^{3}");
119
119
 
120
120
  // Verify that the cursor is in parens.
121
121
  expect(isInsideEmptyParens(mathField.getCursor())).toBeTruthy();
@@ -124,20 +124,20 @@ describe("MathQuill", () => {
124
124
  it("should prefix with empty parens after an operator", () => {
125
125
  mathField.pressKey("EQUAL");
126
126
  mathField.pressKey("EXP_3");
127
- expect(mathField.getContent()).toEqual("=\\left(\\right)^3");
127
+ expect(mathField.getContent()).toEqual("=\\left(\\right)^{3}");
128
128
  });
129
129
 
130
130
  it("should work after an expression", () => {
131
131
  mathField.setContent("35x");
132
132
  mathField.pressKey("EXP_3");
133
- expect(mathField.getContent()).toEqual("35x^3");
133
+ expect(mathField.getContent()).toEqual("35x^{3}");
134
134
  });
135
135
 
136
136
  it.skip("should work on a selected expression", () => {
137
137
  mathField.setContent("35x+5");
138
138
  mathField.selectAll();
139
139
  mathField.pressKey("EXP_3");
140
- expect(mathField.getContent()).toEqual("\\left(35x+5\\right)^3");
140
+ expect(mathField.getContent()).toEqual("\\left(35x+5\\right)^{3}");
141
141
  });
142
142
  });
143
143
 
@@ -150,7 +150,7 @@ describe("MathQuill", () => {
150
150
  // writing a unique character to verify cursor position.
151
151
  expect(isInsideEmptyParens(mathField.getCursor())).toBeFalsy();
152
152
  mathField.pressKey("PLUS");
153
- expect(mathField.getContent()).toEqual("\\left(\\right)^+");
153
+ expect(mathField.getContent()).toEqual("\\left(\\right)^{+}");
154
154
  });
155
155
 
156
156
  it("should prefix with empty parens after an operator", () => {
@@ -181,9 +181,9 @@ describe("MathQuill", () => {
181
181
  });
182
182
 
183
183
  it("should work after an expression", () => {
184
- mathField.setContent("35x^2");
184
+ mathField.setContent("35x^{2}");
185
185
  mathField.pressKey("SQRT");
186
- expect(mathField.getContent()).toEqual("35x^2\\sqrt{ }");
186
+ expect(mathField.getContent()).toEqual("35x^{2}\\sqrt{ }");
187
187
  });
188
188
 
189
189
  it("should work on a selected expression", () => {
@@ -201,9 +201,9 @@ describe("MathQuill", () => {
201
201
  });
202
202
 
203
203
  it("should work after an expression", () => {
204
- mathField.setContent("35x^2");
204
+ mathField.setContent("35x^{2}");
205
205
  mathField.pressKey("RADICAL");
206
- expect(mathField.getContent()).toEqual("35x^2\\sqrt[]{}");
206
+ expect(mathField.getContent()).toEqual("35x^{2}\\sqrt[]{}");
207
207
  });
208
208
 
209
209
  it.skip("should work on a selected expression", () => {
@@ -222,9 +222,11 @@ describe("MathQuill", () => {
222
222
  });
223
223
 
224
224
  it("should work after an expression", () => {
225
- mathField.setContent("35x^2");
225
+ mathField.setContent("35x^{2}");
226
226
  mathField.pressKey("LOG");
227
- expect(mathField.getContent()).toEqual("35x^2\\log\\left(\\right)");
227
+ expect(mathField.getContent()).toEqual(
228
+ "35x^{2}\\log\\left(\\right)",
229
+ );
228
230
  });
229
231
 
230
232
  it.skip("should work on a selected expression", () => {
@@ -242,10 +244,10 @@ describe("MathQuill", () => {
242
244
  });
243
245
 
244
246
  it("should work after an expression", () => {
245
- mathField.setContent("35x^2");
247
+ mathField.setContent("35x^{2}");
246
248
  mathField.pressKey("LOG_N");
247
249
  expect(mathField.getContent()).toEqual(
248
- "35x^2\\log_{ }\\left(\\right)",
250
+ "35x^{2}\\log_{ }\\left(\\right)",
249
251
  );
250
252
  });
251
253
 
@@ -269,15 +271,15 @@ describe("MathQuill", () => {
269
271
  });
270
272
 
271
273
  it("should convert a fraction when deleting the denominator", () => {
272
- mathField.setContent("\\frac{35x^2}{ }");
274
+ mathField.setContent("\\frac{35x^{2}}{ }");
273
275
  mathField.pressKey("LEFT");
274
276
  mathField.pressKey("BACKSPACE");
275
- expect(mathField.getContent()).toEqual("35x^2");
277
+ expect(mathField.getContent()).toEqual("35x^{2}");
276
278
  });
277
279
 
278
280
  // TODO(kevinb) math isn't selected
279
281
  it("should select a fraction when deleting from outside of it", () => {
280
- const expr = "\\frac{35x+5}{x^2}";
282
+ const expr = "\\frac{35x+5}{x^{2}}";
281
283
  mathField.setContent(expr);
282
284
  mathField.pressKey("BACKSPACE");
283
285
  expect(mathField.isSelected()).toBeTruthy();
@@ -358,9 +360,9 @@ describe("MathQuill", () => {
358
360
 
359
361
  // TODO(kevinb) fix this behavior so that we delete the exponent too
360
362
  it.skip("should not delete squared exponents", () => {
361
- mathField.setContent("35x^2");
363
+ mathField.setContent("35x^{2}");
362
364
  mathField.pressKey("BACKSPACE");
363
- expect(mathField.getContent()).toEqual("35x^2");
365
+ expect(mathField.getContent()).toEqual("35x^{2}");
364
366
  mathField.pressKey("BACKSPACE");
365
367
  expect(mathField.getContent()).toEqual("35x^{ }");
366
368
  });
@@ -368,7 +370,7 @@ describe("MathQuill", () => {
368
370
  it("should not delete non-square exponents", () => {
369
371
  mathField.setContent("35x^5");
370
372
  mathField.pressKey("BACKSPACE");
371
- expect(mathField.getContent()).toEqual("35x^5");
373
+ expect(mathField.getContent()).toEqual("35x^{5}");
372
374
  mathField.pressKey("BACKSPACE");
373
375
  expect(mathField.getContent()).toEqual("35x^{ }");
374
376
  });
@@ -592,7 +594,7 @@ describe("MathQuill", () => {
592
594
  const cursor = mathField.getCursor();
593
595
 
594
596
  expect(cursor[MQ.L].ctrlSeq).toEqual("5");
595
- expect(mathField.getContent()).toEqual("\\log_5\\left(\\right)");
597
+ expect(mathField.getContent()).toEqual("\\log_{5}\\left(\\right)");
596
598
  });
597
599
 
598
600
  it("should select full log when deleting from empty index (1)", () => {
@@ -1,5 +1,4 @@
1
1
  import MathWrapper from "../math-wrapper";
2
- import {mathQuillInstance} from "../mathquill-instance";
3
2
 
4
3
  export default class TestMathWrapper extends MathWrapper {
5
4
  getContent() {
@@ -15,7 +14,7 @@ export default class TestMathWrapper extends MathWrapper {
15
14
  }
16
15
 
17
16
  moveToStart() {
18
- this.mathField.moveToDirEnd(mathQuillInstance.L);
17
+ this.mathField.moveToLeftEnd();
19
18
  }
20
19
 
21
20
  isSelected() {
@@ -99,21 +99,6 @@ class MathInput extends React.Component<Props, State> {
99
99
  },
100
100
  });
101
101
 
102
- // NOTE(charlie): MathQuill binds this handler to manage its
103
- // drag-to-select behavior. For reasons that I can't explain, the event
104
- // itself gets triggered even if you tap slightly outside of the
105
- // bound container (maybe 5px outside of any boundary). As a result, the
106
- // cursor appears when tapping at those locations, even though the input
107
- // itself doesn't receive any touch start or mouse down event and, as
108
- // such, doesn't focus itself. This makes for a confusing UX, as the
109
- // cursor appears, but the keypad does not and the input otherwise
110
- // treats itself as unfocused. Thankfully, we don't need this behavior--
111
- // we manage all of the cursor interactions ourselves--so we can safely
112
- // unbind the handler.
113
- this.mathField.mathField.__controller.container.unbind(
114
- "mousedown.mathquill",
115
- );
116
-
117
102
  this.mathField.setContent(this.props.value);
118
103
 
119
104
  this._updateInputPadding();
@@ -542,7 +527,7 @@ class MathInput extends React.Component<Props, State> {
542
527
  // Pre-emptively check if the input has any child nodes; if not, the
543
528
  // input is empty, so we throw the cursor at the start.
544
529
  if (!this._root.hasChildNodes()) {
545
- cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
530
+ cursor.insAtLeftEnd(this.mathField.mathField.controller().root);
546
531
  return;
547
532
  }
548
533
 
@@ -595,9 +580,9 @@ class MathInput extends React.Component<Props, State> {
595
580
  // or left of all of the math, so we place the cursor at the end to
596
581
  // which it's closest.
597
582
  if (Math.abs(x - right) < Math.abs(x - left)) {
598
- cursor.insAtRightEnd(this.mathField.mathField.__controller.root);
583
+ cursor.insAtRightEnd(this.mathField.mathField.controller().root);
599
584
  } else {
600
- cursor.insAtLeftEnd(this.mathField.mathField.__controller.root);
585
+ cursor.insAtLeftEnd(this.mathField.mathField.controller().root);
601
586
  }
602
587
  // In that event, we need to update the cursor context ourselves.
603
588
  this.props.keypadElement &&
@@ -19,11 +19,7 @@ import $ from "jquery";
19
19
  import handleBackspace from "../key-handlers/handle-backspace";
20
20
  import keyTranslator from "../key-handlers/key-translator";
21
21
 
22
- import {
23
- getCursor,
24
- getCursorContext,
25
- maybeFindCommand,
26
- } from "./mathquill-helpers";
22
+ import {getCursorContext, maybeFindCommand} from "./mathquill-helpers";
27
23
  import {createMathField, mathQuillInstance} from "./mathquill-instance";
28
24
 
29
25
  import type {
@@ -31,7 +27,6 @@ import type {
31
27
  MathFieldUpdaterCallback,
32
28
  } from "./mathquill-types";
33
29
  import type Key from "../../data/keys";
34
- import type {Cursor} from "../../types";
35
30
 
36
31
  const mobileKeyTranslator: Record<Key, MathFieldUpdaterCallback> = {
37
32
  ...keyTranslator,
@@ -70,19 +65,17 @@ class MathWrapper {
70
65
  // HACK(charlie): We shouldn't reaching into MathQuill internals like
71
66
  // this, but it's the easiest way to allow us to manage the focus state
72
67
  // ourselves.
73
- const controller = this.mathField.__controller;
74
- controller.cursor.show();
68
+ this.mathField.cursor().show();
75
69
 
76
70
  // Set MathQuill's internal state to reflect the focus, otherwise it
77
71
  // will consistently try to hide the cursor on key-press and introduce
78
72
  // layout jank.
79
- controller.blurred = false;
73
+ this.mathField.focus();
80
74
  }
81
75
 
82
76
  blur() {
83
- const controller = this.mathField.__controller;
84
- controller.cursor.hide();
85
- controller.blurred = true;
77
+ this.mathField.cursor().hide();
78
+ this.mathField.blur();
86
79
  }
87
80
 
88
81
  /**
@@ -91,7 +84,7 @@ class MathWrapper {
91
84
  * @param {Key} key - an enum representing the key that was pressed
92
85
  * @returns {object} a cursor object, consisting of a cursor context
93
86
  */
94
- pressKey(key: Key): Cursor {
87
+ pressKey(key: Key) {
95
88
  const cursor = this.getCursor();
96
89
  const translator = mobileKeyTranslator[key];
97
90
 
@@ -135,10 +128,10 @@ class MathWrapper {
135
128
  if (el.hasAttribute("mq-root-block")) {
136
129
  // If we're in the empty area place the cursor at the right
137
130
  // end of the expression.
138
- cursor.insAtRightEnd(this.mathField.__controller.root);
131
+ cursor.insAtRightEnd(this.mathField.controller().root);
139
132
  } else {
140
133
  // Otherwise place beside the element at x, y.
141
- const controller = this.mathField.__controller;
134
+ const controller = this.mathField.controller();
142
135
 
143
136
  const pageX = x - document.body.scrollLeft;
144
137
  const pageY = y - document.body.scrollTop;
@@ -166,7 +159,7 @@ class MathWrapper {
166
159
  // note(Matthew): extracted this logic to share it elsewhere,
167
160
  // but it's part of the public MathWrapper API
168
161
  getCursor() {
169
- return getCursor(this.mathField);
162
+ return this.mathField.cursor();
170
163
  }
171
164
 
172
165
  // note(Matthew): extracted this logic to keep this file focused,
@@ -2,7 +2,7 @@ import {CursorContext} from "./cursor-contexts";
2
2
  import {mathQuillInstance} from "./mathquill-instance";
3
3
  import {MathFieldActionType} from "./mathquill-types";
4
4
 
5
- import type {MathFieldCursor, MathFieldInterface} from "./mathquill-types";
5
+ import type {MathFieldInterface} from "./mathquill-types";
6
6
 
7
7
  const Numerals = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
8
8
  const GreekLetters = ["\\theta", "\\pi"];
@@ -44,20 +44,19 @@ const ValidLeaves = [
44
44
  ...Letters.map((letter) => letter.toUpperCase()),
45
45
  ];
46
46
 
47
- export function getCursor(mathField: MathFieldInterface): MathFieldCursor {
48
- return mathField.__controller.cursor;
49
- }
47
+ const mqNodeHasClass = (node: any, className: string): boolean =>
48
+ node._el && (node._el as HTMLElement).classList.contains(className);
50
49
 
51
50
  export function isFraction(node): boolean {
52
- return node.jQ && node.jQ.hasClass("mq-fraction");
51
+ return mqNodeHasClass(node, "mq-fraction");
53
52
  }
54
53
 
55
54
  export function isNumerator(node): boolean {
56
- return node.jQ && node.jQ.hasClass("mq-numerator");
55
+ return mqNodeHasClass(node, "mq-numerator");
57
56
  }
58
57
 
59
58
  export function isDenominator(node): boolean {
60
- return node.jQ && node.jQ.hasClass("mq-denominator");
59
+ return mqNodeHasClass(node, "mq-denominator");
61
60
  }
62
61
 
63
62
  export function isSubScript(node): boolean {
@@ -65,8 +64,7 @@ export function isSubScript(node): boolean {
65
64
  // to be represented as a parent node with 'mq-sup-only' containing a
66
65
  // single child with 'mq-sup'.
67
66
  return (
68
- node.jQ &&
69
- (node.jQ.hasClass("mq-sub-only") || node.jQ.hasClass("mq-sub"))
67
+ mqNodeHasClass(node, "mq-sub-only") || mqNodeHasClass(node, "mq-sub")
70
68
  );
71
69
  }
72
70
 
@@ -75,8 +73,7 @@ export function isSuperScript(node): boolean {
75
73
  // to be represented as a parent node with 'mq-sup-only' containing a
76
74
  // single child with 'mq-sup'.
77
75
  return (
78
- node.jQ &&
79
- (node.jQ.hasClass("mq-sup-only") || node.jQ.hasClass("mq-sup"))
76
+ mqNodeHasClass(node, "mq-sup-only") || mqNodeHasClass(node, "mq-sup")
80
77
  );
81
78
  }
82
79
 
@@ -89,29 +86,21 @@ export function isLeaf(node): boolean {
89
86
  }
90
87
 
91
88
  export function isSquareRoot(node): boolean {
92
- return (
93
- node.blocks &&
94
- node.blocks[0].jQ &&
95
- node.blocks[0].jQ.hasClass("mq-sqrt-stem")
96
- );
89
+ return node.blocks && mqNodeHasClass(node.blocks[0], "mq-sqrt-stem");
97
90
  }
98
91
 
99
92
  export function isNthRoot(node): boolean {
100
- return (
101
- node.blocks &&
102
- node.blocks[0].jQ &&
103
- node.blocks[0].jQ.hasClass("mq-nthroot")
104
- );
93
+ return node.blocks && mqNodeHasClass(node.blocks[0], "mq-nthroot");
105
94
  }
106
95
 
107
96
  export function isNthRootIndex(node): boolean {
108
- return node.jQ && node.jQ.hasClass("mq-nthroot");
97
+ return mqNodeHasClass(node, "mq-nthroot");
109
98
  }
110
99
 
111
- export function isInsideLogIndex(cursor: MathFieldCursor): boolean {
100
+ export function isInsideLogIndex(cursor): boolean {
112
101
  const grandparent = cursor.parent.parent;
113
102
 
114
- if (grandparent && grandparent.jQ.hasClass("mq-supsub")) {
103
+ if (grandparent && mqNodeHasClass(grandparent, "mq-supsub")) {
115
104
  const command = maybeFindCommandBeforeParens(grandparent);
116
105
 
117
106
  if (command && command.name === "\\log") {
@@ -122,7 +111,7 @@ export function isInsideLogIndex(cursor: MathFieldCursor): boolean {
122
111
  return false;
123
112
  }
124
113
 
125
- export function isInsideEmptyNode(cursor: MathFieldCursor): boolean {
114
+ export function isInsideEmptyNode(cursor): boolean {
126
115
  return (
127
116
  cursor[mathQuillInstance.L] === MathFieldActionType.MQ_END &&
128
117
  cursor[mathQuillInstance.R] === MathFieldActionType.MQ_END
@@ -244,7 +233,7 @@ export function getCursorContext(
244
233
  }
245
234
 
246
235
  // First, try to find any fraction to the right, unimpeded.
247
- const cursor = getCursor(mathField);
236
+ const cursor = mathField.cursor();
248
237
  let visitor = cursor;
249
238
  while (visitor[mathQuillInstance.R] !== MathFieldActionType.MQ_END) {
250
239
  if (isFraction(visitor[mathQuillInstance.R])) {