@khanacademy/perseus 6.2.1 → 6.3.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.
Files changed (133) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/all-widgets.d.ts +2 -2
  3. package/dist/all-widgets.js.flow +2 -2
  4. package/dist/basic-widgets.d.ts +2 -2
  5. package/dist/basic-widgets.js.flow +2 -2
  6. package/dist/components/math-input.d.ts +4 -1
  7. package/dist/components/math-input.js.flow +4 -1
  8. package/dist/components/tex-buttons.d.ts +5 -1
  9. package/dist/components/tex-buttons.js.flow +1 -1
  10. package/dist/es/index.js +172 -205
  11. package/dist/es/index.js.map +1 -1
  12. package/dist/extra-widgets.d.ts +2 -2
  13. package/dist/extra-widgets.js.flow +2 -2
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +170 -201
  16. package/dist/index.js.flow +2 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/strings.js +23 -0
  19. package/dist/types.d.ts +2 -2
  20. package/dist/types.js.flow +2 -2
  21. package/dist/util/geometry.d.ts +25 -0
  22. package/dist/util/geometry.js.flow +37 -0
  23. package/dist/widgets/categorizer.d.ts +2 -2
  24. package/dist/widgets/categorizer.js.flow +2 -2
  25. package/dist/widgets/cs-program.d.ts +2 -2
  26. package/dist/widgets/cs-program.js.flow +2 -2
  27. package/dist/widgets/definition.d.ts +2 -2
  28. package/dist/widgets/definition.js.flow +2 -2
  29. package/dist/widgets/dropdown.d.ts +2 -2
  30. package/dist/widgets/dropdown.js.flow +2 -2
  31. package/dist/widgets/explanation.d.ts +2 -2
  32. package/dist/widgets/explanation.js.flow +2 -2
  33. package/dist/widgets/expression.d.ts +2 -2
  34. package/dist/widgets/expression.js.flow +2 -2
  35. package/dist/widgets/graded-group-set.d.ts +2 -2
  36. package/dist/widgets/graded-group-set.js.flow +2 -2
  37. package/dist/widgets/graded-group.d.ts +2 -2
  38. package/dist/widgets/graded-group.js.flow +2 -2
  39. package/dist/widgets/grapher.d.ts +2 -2
  40. package/dist/widgets/grapher.js.flow +2 -2
  41. package/dist/widgets/group.d.ts +2 -2
  42. package/dist/widgets/group.js.flow +2 -2
  43. package/dist/widgets/iframe.d.ts +2 -2
  44. package/dist/widgets/iframe.js.flow +2 -2
  45. package/dist/widgets/image.d.ts +2 -2
  46. package/dist/widgets/image.js.flow +2 -2
  47. package/dist/widgets/input-number.d.ts +2 -2
  48. package/dist/widgets/input-number.js.flow +2 -2
  49. package/dist/widgets/interaction.d.ts +2 -2
  50. package/dist/widgets/interaction.js.flow +2 -2
  51. package/dist/widgets/interactive-graph.d.ts +4 -12
  52. package/dist/widgets/interactive-graph.js.flow +4 -7
  53. package/dist/widgets/label-image.d.ts +2 -2
  54. package/dist/widgets/label-image.js.flow +2 -2
  55. package/dist/widgets/lights-puzzle.d.ts +2 -2
  56. package/dist/widgets/lights-puzzle.js.flow +2 -2
  57. package/dist/widgets/matcher.d.ts +2 -2
  58. package/dist/widgets/matcher.js.flow +2 -2
  59. package/dist/widgets/matrix.d.ts +2 -2
  60. package/dist/widgets/matrix.js.flow +2 -2
  61. package/dist/widgets/measurer.d.ts +2 -2
  62. package/dist/widgets/measurer.js.flow +2 -2
  63. package/dist/widgets/molecule.d.ts +2 -2
  64. package/dist/widgets/molecule.js.flow +2 -2
  65. package/dist/widgets/number-line.d.ts +2 -2
  66. package/dist/widgets/number-line.js.flow +2 -2
  67. package/dist/widgets/numeric-input.d.ts +2 -2
  68. package/dist/widgets/numeric-input.js.flow +2 -2
  69. package/dist/widgets/orderer.d.ts +2 -2
  70. package/dist/widgets/orderer.js.flow +2 -2
  71. package/dist/widgets/passage-ref-target.d.ts +2 -2
  72. package/dist/widgets/passage-ref-target.js.flow +2 -2
  73. package/dist/widgets/passage-ref.d.ts +2 -2
  74. package/dist/widgets/passage-ref.js.flow +2 -2
  75. package/dist/widgets/passage.d.ts +2 -2
  76. package/dist/widgets/passage.js.flow +2 -2
  77. package/dist/widgets/plotter.d.ts +2 -2
  78. package/dist/widgets/plotter.js.flow +2 -2
  79. package/dist/widgets/radio.d.ts +2 -2
  80. package/dist/widgets/radio.js.flow +2 -2
  81. package/dist/widgets/reaction-diagram.d.ts +2 -2
  82. package/dist/widgets/reaction-diagram.js.flow +2 -2
  83. package/dist/widgets/sequence.d.ts +2 -2
  84. package/dist/widgets/sequence.js.flow +2 -2
  85. package/dist/widgets/simulator.d.ts +2 -2
  86. package/dist/widgets/simulator.js.flow +2 -2
  87. package/dist/widgets/sorter.d.ts +2 -2
  88. package/dist/widgets/sorter.js.flow +2 -2
  89. package/dist/widgets/table.d.ts +2 -2
  90. package/dist/widgets/table.js.flow +2 -2
  91. package/dist/widgets/transformer.d.ts +2 -2
  92. package/dist/widgets/transformer.js.flow +2 -2
  93. package/dist/widgets/unit.d.ts +2 -2
  94. package/dist/widgets/unit.js.flow +2 -2
  95. package/dist/widgets/video.d.ts +2 -2
  96. package/dist/widgets/video.js.flow +2 -2
  97. package/package.json +4 -5
  98. package/src/components/__tests__/math-input.test.tsx +60 -0
  99. package/src/components/math-input.tsx +88 -118
  100. package/src/components/prop-check-box.tsx +0 -1
  101. package/src/components/tex-buttons.tsx +73 -12
  102. package/src/index.ts +2 -0
  103. package/src/types.ts +19 -2
  104. package/src/util/geometry.ts +293 -0
  105. package/src/widgets/__tests__/numeric-input.test.ts +38 -0
  106. package/src/widgets/expression.tsx +0 -1
  107. package/src/widgets/grapher.tsx +0 -1
  108. package/src/widgets/image.tsx +0 -1
  109. package/src/widgets/input-number.tsx +0 -1
  110. package/src/widgets/interactive-graph.tsx +20 -288
  111. package/src/widgets/matrix.tsx +0 -1
  112. package/src/widgets/numeric-input.tsx +6 -2
  113. package/src/widgets/transformer.tsx +0 -1
  114. package/tsconfig-build.json +0 -1
  115. package/tsconfig-build.tsbuildinfo +1 -1
  116. package/dist/__tests__/mock-asset-loading-widget.d.ts +0 -59
  117. package/dist/__tests__/mock-asset-loading-widget.js.flow +0 -70
  118. package/dist/__tests__/mock-widget.d.ts +0 -30
  119. package/dist/__tests__/mock-widget.js.flow +0 -39
  120. package/dist/__tests__/test-items/image-item.d.ts +0 -14
  121. package/dist/__tests__/test-items/image-item.js.flow +0 -20
  122. package/dist/__tests__/test-items/input-number-1-item.d.ts +0 -9
  123. package/dist/__tests__/test-items/input-number-1-item.js.flow +0 -15
  124. package/dist/__tests__/test-items/input-number-2-item.d.ts +0 -9
  125. package/dist/__tests__/test-items/input-number-2-item.js.flow +0 -15
  126. package/dist/__tests__/test-items/table-item.d.ts +0 -9
  127. package/dist/__tests__/test-items/table-item.js.flow +0 -15
  128. package/dist/interactive2/__tests__/movable-mock.d.ts +0 -5
  129. package/dist/interactive2/__tests__/movable-mock.js.flow +0 -11
  130. package/dist/widgets/__stories__/test-keypad-context-wrapper.d.ts +0 -6
  131. package/dist/widgets/__stories__/test-keypad-context-wrapper.js.flow +0 -12
  132. package/dist/widgets/__tests__/renderQuestion.d.ts +0 -13
  133. package/dist/widgets/__tests__/renderQuestion.js.flow +0 -28
package/dist/index.js CHANGED
@@ -10,7 +10,6 @@ var classNames = require('classnames');
10
10
  var React = require('react');
11
11
  var _ = require('underscore');
12
12
  var $ = require('jquery');
13
- var MathQuill = require('mathquill');
14
13
  var ReactDOM = require('react-dom');
15
14
  var PropTypes = require('prop-types');
16
15
  var kmath = require('@khanacademy/kmath');
@@ -59,7 +58,6 @@ var classNames__default = /*#__PURE__*/_interopDefaultLegacy(classNames);
59
58
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
60
59
  var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
61
60
  var $__default = /*#__PURE__*/_interopDefaultLegacy($);
62
- var MathQuill__default = /*#__PURE__*/_interopDefaultLegacy(MathQuill);
63
61
  var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
64
62
  var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
65
63
  var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
@@ -209,10 +207,10 @@ const symbStyle = {
209
207
  const basic = [() => [/*#__PURE__*/React__namespace.createElement("span", {
210
208
  key: "plus",
211
209
  style: slightlyBig
212
- }, "+"), "PLUS"], () => [/*#__PURE__*/React__namespace.createElement("span", {
210
+ }, "+"), "PLUS", i18n__namespace._("Plus")], () => [/*#__PURE__*/React__namespace.createElement("span", {
213
211
  key: "minus",
214
212
  style: prettyBig
215
- }, "-"), "MINUS"],
213
+ }, "-"), "MINUS", i18n__namespace._("Minus")],
216
214
  // TODO(joel) - display as \cdot when appropriate
217
215
  props => {
218
216
  const {
@@ -222,12 +220,12 @@ props => {
222
220
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
223
221
  key: "times",
224
222
  style: prettyBig
225
- }, i18n__namespace.doNotTranslate("\\times")), "TIMES"];
223
+ }, i18n__namespace.doNotTranslate("\\times")), "TIMES", i18n__namespace._("Multiply")];
226
224
  }
227
225
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
228
226
  key: "times",
229
227
  style: prettyBig
230
- }, i18n__namespace.doNotTranslate("\\cdot")), "CDOT"];
228
+ }, i18n__namespace.doNotTranslate("\\cdot")), "CDOT", i18n__namespace._("Multiply")];
231
229
  }, () => {
232
230
  const {
233
231
  TeX
@@ -235,7 +233,7 @@ props => {
235
233
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
236
234
  key: "frac",
237
235
  style: prettyBig
238
- }, i18n__namespace.doNotTranslate("\\frac{x}{y}")), "FRAC"];
236
+ }, i18n__namespace.doNotTranslate("\\frac{x}{y}")), "FRAC", i18n__namespace._("Fraction, excluding the current expression")];
239
237
  }];
240
238
  const buttonSets = {
241
239
  basic,
@@ -245,7 +243,7 @@ const buttonSets = {
245
243
  } = getDependencies();
246
244
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
247
245
  key: "div"
248
- }, i18n__namespace.doNotTranslate("\\div")), "DIVIDE"];
246
+ }, i18n__namespace.doNotTranslate("\\div")), "DIVIDE", i18n__namespace._("Divide")];
249
247
  }]),
250
248
  trig: [() => {
251
249
  const {
@@ -253,21 +251,21 @@ const buttonSets = {
253
251
  } = getDependencies();
254
252
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
255
253
  key: "sin"
256
- }, i18n__namespace.doNotTranslate("\\sin")), "SIN"];
254
+ }, i18n__namespace.doNotTranslate("\\sin")), "SIN", i18n__namespace._("Sine")];
257
255
  }, () => {
258
256
  const {
259
257
  TeX
260
258
  } = getDependencies();
261
259
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
262
260
  key: "cos"
263
- }, i18n__namespace.doNotTranslate("\\cos")), "COS"];
261
+ }, i18n__namespace.doNotTranslate("\\cos")), "COS", i18n__namespace._("Cosine")];
264
262
  }, () => {
265
263
  const {
266
264
  TeX
267
265
  } = getDependencies();
268
266
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
269
267
  key: "tan"
270
- }, i18n__namespace.doNotTranslate("\\tan")), "TAN"];
268
+ }, i18n__namespace.doNotTranslate("\\tan")), "TAN", i18n__namespace._("Tangent")];
271
269
  }, () => {
272
270
  const {
273
271
  TeX
@@ -275,7 +273,7 @@ const buttonSets = {
275
273
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
276
274
  key: "theta",
277
275
  style: symbStyle
278
- }, i18n__namespace.doNotTranslate("\\theta")), "THETA"];
276
+ }, i18n__namespace.doNotTranslate("\\theta")), "THETA", i18n__namespace._("Theta")];
279
277
  }, () => {
280
278
  const {
281
279
  TeX
@@ -287,7 +285,7 @@ const buttonSets = {
287
285
  }, i18n__namespace.doNotTranslate("phi")), /*#__PURE__*/React__namespace.createElement(TeX, {
288
286
  key: "pi",
289
287
  style: symbStyle
290
- }, i18n__namespace.doNotTranslate("\\phi"))), "PHI"];
288
+ }, i18n__namespace.doNotTranslate("\\phi"))), "PHI", i18n__namespace._("Phi")];
291
289
  }],
292
290
  prealgebra: [() => {
293
291
  const {
@@ -295,7 +293,7 @@ const buttonSets = {
295
293
  } = getDependencies();
296
294
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
297
295
  key: "sqrt"
298
- }, i18n__namespace.doNotTranslate("\\sqrt{x}")), "SQRT"];
296
+ }, i18n__namespace.doNotTranslate("\\sqrt{x}")), "SQRT", i18n__namespace._("Square root")];
299
297
  },
300
298
  // TODO(joel) - how does desmos do this?
301
299
  () => {
@@ -304,7 +302,7 @@ const buttonSets = {
304
302
  } = getDependencies();
305
303
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
306
304
  key: "nthroot"
307
- }, i18n__namespace.doNotTranslate("\\sqrt[3]{x}")), "NTHROOT3"];
305
+ }, i18n__namespace.doNotTranslate("\\sqrt[3]{x}")), "NTHROOT3", i18n__namespace._("Cube root")];
308
306
  }, () => {
309
307
  const {
310
308
  TeX
@@ -312,7 +310,7 @@ const buttonSets = {
312
310
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
313
311
  key: "pow",
314
312
  style: slightlyBig
315
- }, i18n__namespace.doNotTranslate("a^b")), "POW"];
313
+ }, i18n__namespace.doNotTranslate("a^b")), "POW", i18n__namespace._("Exponent")];
316
314
  }, () => {
317
315
  const {
318
316
  TeX
@@ -320,7 +318,7 @@ const buttonSets = {
320
318
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
321
319
  key: "pi",
322
320
  style: slightlyBig
323
- }, i18n__namespace.doNotTranslate("\\pi")), "PI"];
321
+ }, i18n__namespace.doNotTranslate("\\pi")), "PI", i18n__namespace._("Pi")];
324
322
  }],
325
323
  logarithms: [() => {
326
324
  const {
@@ -328,21 +326,21 @@ const buttonSets = {
328
326
  } = getDependencies();
329
327
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
330
328
  key: "log"
331
- }, i18n__namespace.doNotTranslate("\\log")), "LOG"];
329
+ }, i18n__namespace.doNotTranslate("\\log")), "LOG", i18n__namespace._("Logarithm with base 10")];
332
330
  }, () => {
333
331
  const {
334
332
  TeX
335
333
  } = getDependencies();
336
334
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
337
335
  key: "ln"
338
- }, i18n__namespace.doNotTranslate("\\ln")), "LN"];
336
+ }, i18n__namespace.doNotTranslate("\\ln")), "LN", i18n__namespace._("Natural logarithm")];
339
337
  }, () => {
340
338
  const {
341
339
  TeX
342
340
  } = getDependencies();
343
341
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
344
342
  key: "log_b"
345
- }, i18n__namespace.doNotTranslate("\\log_b")), "LOG_B"];
343
+ }, i18n__namespace.doNotTranslate("\\log_b")), "LOG_B", i18n__namespace._("Logarithm with custom base")];
346
344
  }],
347
345
  "basic relations": [() => {
348
346
  const {
@@ -350,21 +348,21 @@ const buttonSets = {
350
348
  } = getDependencies();
351
349
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
352
350
  key: "eq"
353
- }, "="), "EQUAL"];
351
+ }, "="), "EQUAL", i18n__namespace._("Equals sign")];
354
352
  }, () => {
355
353
  const {
356
354
  TeX
357
355
  } = getDependencies();
358
356
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
359
357
  key: "lt"
360
- }, i18n__namespace.doNotTranslate("\\lt")), "LT"];
358
+ }, i18n__namespace.doNotTranslate("\\lt")), "LT", i18n__namespace._("Less than sign")];
361
359
  }, () => {
362
360
  const {
363
361
  TeX
364
362
  } = getDependencies();
365
363
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
366
364
  key: "gt"
367
- }, i18n__namespace.doNotTranslate("\\gt")), "GT"];
365
+ }, i18n__namespace.doNotTranslate("\\gt")), "GT", i18n__namespace._("Greater than sign")];
368
366
  }],
369
367
  "advanced relations": [() => {
370
368
  const {
@@ -372,21 +370,21 @@ const buttonSets = {
372
370
  } = getDependencies();
373
371
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
374
372
  key: "neq"
375
- }, i18n__namespace.doNotTranslate("\\neq")), "NEQ"];
373
+ }, i18n__namespace.doNotTranslate("\\neq")), "NEQ", i18n__namespace._("Not-equals sign")];
376
374
  }, () => {
377
375
  const {
378
376
  TeX
379
377
  } = getDependencies();
380
378
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
381
379
  key: "leq"
382
- }, i18n__namespace.doNotTranslate("\\leq")), "LEQ"];
380
+ }, i18n__namespace.doNotTranslate("\\leq")), "LEQ", i18n__namespace._("Less than or equal to sign")];
383
381
  }, () => {
384
382
  const {
385
383
  TeX
386
384
  } = getDependencies();
387
385
  return [/*#__PURE__*/React__namespace.createElement(TeX, {
388
386
  key: "geq"
389
- }, i18n__namespace.doNotTranslate("\\geq")), "GEQ"];
387
+ }, i18n__namespace.doNotTranslate("\\geq")), "GEQ", i18n__namespace._("Greater than or equal to sign")];
390
388
  }]
391
389
  };
392
390
  const buttonSetsPropType = PropTypes__default["default"].arrayOf(PropTypes__default["default"].oneOf(___default["default"](buttonSets).keys()));
@@ -408,7 +406,8 @@ class TexButtons extends React__namespace.Component {
408
406
  // @ts-expect-error [FEI-5003] - TS2533 - Object is possibly 'null' or 'undefined'. | TS2339 - Property 'key' does not exist on type 'boolean | ReactChild | ReactFragment | ReactPortal'.
409
407
  ,
410
408
  key: symbol[0].key,
411
- type: "button"
409
+ type: "button",
410
+ "aria-label": symbol[2]
412
411
  }, symbol[0]);
413
412
  }));
414
413
  const buttonPopup = ___default["default"](buttonRows).map((row, i) => {
@@ -444,6 +443,8 @@ class MathInput extends React__namespace.Component {
444
443
  constructor() {
445
444
  super(...arguments);
446
445
  _defineProperty(this, "mouseDown", void 0);
446
+ _defineProperty(this, "__mathFieldWrapperRef", null);
447
+ _defineProperty(this, "__mathField", null);
447
448
  _defineProperty(this, "state", {
448
449
  focused: false
449
450
  });
@@ -498,12 +499,11 @@ class MathInput extends React__namespace.Component {
498
499
  return this.state.focused;
499
500
  });
500
501
  _defineProperty(this, "insert", value => {
501
- // @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
502
502
  const input = this.mathField();
503
503
  const inputModifier = customKeyTranslator[value];
504
504
  if (inputModifier) {
505
505
  inputModifier(input, value);
506
- input.focus();
506
+ input === null || input === void 0 ? void 0 : input.focus();
507
507
  return;
508
508
  }
509
509
 
@@ -514,131 +514,99 @@ class MathInput extends React__namespace.Component {
514
514
  if (___default["default"](value).isFunction()) {
515
515
  value(input);
516
516
  } else if (value[0] === "\\") {
517
- input.cmd(value).focus();
517
+ input === null || input === void 0 ? void 0 : input.cmd(value).focus();
518
518
  } else {
519
- input.write(value).focus();
519
+ input === null || input === void 0 ? void 0 : input.write(value).focus();
520
+ }
521
+ input === null || input === void 0 ? void 0 : input.focus();
522
+ });
523
+ _defineProperty(this, "mathField", () => {
524
+ if (!this.__mathField && this.__mathFieldWrapperRef) {
525
+ // Initialize MathQuill.MathField instance
526
+ this.__mathField = mathInput.createMathField(this.__mathFieldWrapperRef, baseConfig => ({
527
+ ...baseConfig,
528
+ handlers: {
529
+ edit: mathField => {
530
+ // This handler is guaranteed to be called on change, but
531
+ // unlike React it sometimes generates false positives.
532
+ // One of these is on initialization (with an empty string
533
+ // value), so we have to guard against that below.
534
+ let value = mathField.latex();
535
+
536
+ // Provide a MathQuill-compatible way to generate the
537
+ // not-equals sign without pasting unicode or typing TeX
538
+ value = value.replace(/<>/g, "\\ne");
539
+
540
+ // Use the specified symbol to represent multiplication
541
+ // TODO(alex): Add an option to disallow variables, in
542
+ // which case 'x' should get converted to '\\times'
543
+ if (this.props.convertDotToTimes) {
544
+ value = value.replace(/\\cdot/g, "\\times");
545
+
546
+ // Preserve cursor position in the common case:
547
+ // typing '*' to insert a multiplication sign.
548
+ // We do this by modifying internal MathQuill state
549
+ // directly, instead of waiting for `.latex()` to be
550
+ // called in `componentDidUpdate()`.
551
+ const left = mathField.__controller.cursor[mathInput.mathQuillInstance.L];
552
+ if (left && left.ctrlSeq === "\\cdot ") {
553
+ mathField.__controller.backspace();
554
+ mathField.cmd("\\times");
555
+ }
556
+ } else {
557
+ value = value.replace(/\\times/g, "\\cdot");
558
+ }
559
+ if (this.props.value !== value) {
560
+ this.props.onChange(value);
561
+ }
562
+ },
563
+ enter: () => {
564
+ // NOTE(kevinb): This isn't how answers to exercises are
565
+ // submitted. The actual mechanism for this can be found
566
+ // in exercise-problem-template.jsx, see `handleSubmit`.
567
+
568
+ // This handler is called when the user presses the enter
569
+ // key. Since this isn't an actual <input> element, we have
570
+ // to manually trigger the usually automatic form submit.
571
+ if (this.__mathFieldWrapperRef) {
572
+ $__default["default"](this.__mathFieldWrapperRef).submit();
573
+ }
574
+ },
575
+ upOutOf: mathField => {
576
+ // This handler is called when the user presses the up
577
+ // arrow key, but there is nowhere in the expression to go
578
+ // up to (no numerator or exponent). For ease of use,
579
+ // interpret this as an attempt to create an exponent.
580
+ mathField.typedText("^");
581
+ }
582
+ }
583
+ }));
520
584
  }
521
- input.focus();
522
- });
523
- _defineProperty(this, "mathField", options => {
524
- // The MathQuill API is now "versioned" through its own "InterVer"
525
- // system.
526
- // See: https://github.com/mathquill/mathquill/pull/459
527
- const MQ = MathQuill__default["default"].getInterface(2);
528
-
529
- // MathQuill.MathField takes a DOM node, MathQuill-ifies it if it's
530
- // seeing that node for the first time, then returns the associated
531
- // MathQuill object for that node. It is stable - will always return
532
- // the same object when called on the same DOM node.
533
- // eslint-disable-next-line react/no-string-refs
534
- return MQ.MathField(ReactDOM__default["default"].findDOMNode(this.refs.mathinput), options);
585
+ return this.__mathField;
535
586
  });
536
587
  _defineProperty(this, "focus", () => {
537
- // @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
538
- this.mathField().focus();
588
+ var _this$mathField;
589
+ (_this$mathField = this.mathField()) === null || _this$mathField === void 0 ? void 0 : _this$mathField.focus();
539
590
  this.setState({
540
591
  focused: true
541
592
  });
542
593
  });
543
594
  _defineProperty(this, "blur", () => {
544
- // @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
545
- this.mathField().blur();
595
+ var _this$mathField2;
596
+ (_this$mathField2 = this.mathField()) === null || _this$mathField2 === void 0 ? void 0 : _this$mathField2.blur();
546
597
  this.setState({
547
598
  focused: false
548
599
  });
549
600
  });
550
601
  }
551
602
  componentDidMount() {
603
+ var _this$mathField3;
552
604
  window.addEventListener("mousedown", this.handleMouseDown);
553
605
  window.addEventListener("mouseup", this.handleMouseUp);
554
- let initialized = false;
555
-
556
- // Initialize MathQuill.MathField instance
557
- this.mathField({
558
- // LaTeX commands that, when typed, are immediately replaced by the
559
- // appropriate symbol. This does not include ln, log, or any of the
560
- // trig functions; those are always interpreted as commands.
561
- autoCommands: "pi theta phi sqrt nthroot",
562
- // Pop the cursor out of super/subscripts on arithmetic operators
563
- // or (in)equalities.
564
- charsThatBreakOutOfSupSub: "+-*/=<>≠≤≥",
565
- // Prevent excessive super/subscripts or fractions from being
566
- // created without operands, e.g. when somebody holds down a key
567
- supSubsRequireOperand: true,
568
- // The name of this option is somewhat misleading, as tabbing in
569
- // MathQuill breaks you out of a nested context (fraction/script)
570
- // if you're in one, but moves focus to the next input if you're
571
- // not. Spaces (with this option enabled) are just ignored in the
572
- // latter case.
573
- //
574
- // TODO(alex): In order to allow inputting mixed numbers, we will
575
- // have to accept spaces in certain cases. The desired behavior is
576
- // still to escape nested contexts if currently in one, but to
577
- // insert a space if not (we don't expect mixed numbers in nested
578
- // contexts). We should also limit to one consecutive space.
579
- spaceBehavesLikeTab: true,
580
- handlers: {
581
- edited: mathField => {
582
- // This handler is guaranteed to be called on change, but
583
- // unlike React it sometimes generates false positives.
584
- // One of these is on initialization (with an empty string
585
- // value), so we have to guard against that below.
586
- let value = mathField.latex();
587
-
588
- // Provide a MathQuill-compatible way to generate the
589
- // not-equals sign without pasting unicode or typing TeX
590
- value = value.replace(/<>/g, "\\ne");
591
-
592
- // Use the specified symbol to represent multiplication
593
- // TODO(alex): Add an option to disallow variables, in
594
- // which case 'x' should get converted to '\\times'
595
- if (this.props.convertDotToTimes) {
596
- value = value.replace(/\\cdot/g, "\\times");
597
-
598
- // Preserve cursor position in the common case:
599
- // typing '*' to insert a multiplication sign.
600
- // We do this by modifying internal MathQuill state
601
- // directly, instead of waiting for `.latex()` to be
602
- // called in `componentDidUpdate()`.
603
- const left = mathField.__controller.cursor[MathQuill__default["default"].L];
604
- if (left && left.ctrlSeq === "\\cdot ") {
605
- mathField.__controller.backspace();
606
- mathField.cmd("\\times");
607
- }
608
- } else {
609
- value = value.replace(/\\times/g, "\\cdot");
610
- }
611
- if (initialized && this.props.value !== value) {
612
- this.props.onChange(value);
613
- }
614
- },
615
- enter: () => {
616
- // NOTE(kevinb): This isn't how answers to exercises are
617
- // submitted. The actual mechanism for this can be found
618
- // in exercise-problem-template.jsx, see `handleSubmit`.
619
-
620
- // This handler is called when the user presses the enter
621
- // key. Since this isn't an actual <input> element, we have
622
- // to manually trigger the usually automatic form submit.
623
- // eslint-disable-next-line react/no-string-refs
624
- // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call. | TS2339 - Property 'submit' does not exist on type 'JQueryStatic'.
625
- $__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.mathinput)).submit();
626
- },
627
- upOutOf: mathField => {
628
- // This handler is called when the user presses the up
629
- // arrow key, but there is nowhere in the expression to go
630
- // up to (no numerator or exponent). For ease of use,
631
- // interpret this as an attempt to create an exponent.
632
- mathField.typedText("^");
633
- }
634
- }
635
- });
636
606
 
637
607
  // Ideally, we would be able to pass an initial value directly into
638
- // the constructor above
639
- // @ts-expect-error [FEI-5003] - TS2554 - Expected 1 arguments, but got 0.
640
- this.mathField().latex(this.props.value);
641
- initialized = true;
608
+ // the constructor
609
+ (_this$mathField3 = this.mathField()) === null || _this$mathField3 === void 0 ? void 0 : _this$mathField3.latex(this.props.value);
642
610
  }
643
611
  componentWillUnmount() {
644
612
  window.removeEventListener("mousedown", this.handleMouseDown);
@@ -681,10 +649,8 @@ class MathInput extends React__namespace.Component {
681
649
  display: "inline-block"
682
650
  }
683
651
  }, /*#__PURE__*/React__namespace.createElement("span", {
684
- className: className
685
- // eslint-disable-next-line react/no-string-refs
686
- ,
687
- ref: "mathinput",
652
+ className: className,
653
+ ref: ref => this.__mathFieldWrapperRef = ref,
688
654
  "aria-label": this.props.labelText,
689
655
  onFocus: this.handleFocus,
690
656
  onBlur: this.handleBlur
@@ -3456,7 +3422,7 @@ const DeprecationMixin$1 = {
3456
3422
  /**
3457
3423
  * Approximate equality on numbers and primitives.
3458
3424
  */
3459
- function eq$1(x, y) {
3425
+ function eq$2(x, y) {
3460
3426
  if (typeof x === "number" && typeof y === "number") {
3461
3427
  return Math.abs(x - y) < 1e-9;
3462
3428
  }
@@ -3467,13 +3433,13 @@ function eq$1(x, y) {
3467
3433
  * Deep approximate equality on primitives, numbers, arrays, and objects.
3468
3434
  * Recursive.
3469
3435
  */
3470
- function deepEq$5(x, y) {
3436
+ function deepEq$6(x, y) {
3471
3437
  if (Array.isArray(x) && Array.isArray(y)) {
3472
3438
  if (x.length !== y.length) {
3473
3439
  return false;
3474
3440
  }
3475
3441
  for (let i = 0; i < x.length; i++) {
3476
- if (!deepEq$5(x[i], y[i])) {
3442
+ if (!deepEq$6(x[i], y[i])) {
3477
3443
  return false;
3478
3444
  }
3479
3445
  }
@@ -3483,7 +3449,7 @@ function deepEq$5(x, y) {
3483
3449
  return false;
3484
3450
  }
3485
3451
  if (typeof x === "function" && typeof y === "function") {
3486
- return eq$1(x, y);
3452
+ return eq$2(x, y);
3487
3453
  }
3488
3454
  if (typeof x === "function" || typeof y === "function") {
3489
3455
  return false;
@@ -3491,16 +3457,16 @@ function deepEq$5(x, y) {
3491
3457
  if (typeof x === "object" && typeof y === "object" && !!x && !!y) {
3492
3458
  return x === y || ___default["default"].all(x, function (v, k) {
3493
3459
  // @ts-expect-error [FEI-5003] - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
3494
- return deepEq$5(y[k], v);
3460
+ return deepEq$6(y[k], v);
3495
3461
  }) && ___default["default"].all(y, function (v, k) {
3496
3462
  // @ts-expect-error [FEI-5003] - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
3497
- return deepEq$5(x[k], v);
3463
+ return deepEq$6(x[k], v);
3498
3464
  });
3499
3465
  }
3500
3466
  if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
3501
3467
  return false;
3502
3468
  }
3503
- return eq$1(x, y);
3469
+ return eq$2(x, y);
3504
3470
  }
3505
3471
 
3506
3472
  /**
@@ -3818,8 +3784,8 @@ const Util = {
3818
3784
  tickStepFromNumTicks,
3819
3785
  constrainedTickStepsFromTickSteps,
3820
3786
  DeprecationMixin: DeprecationMixin$1,
3821
- eq: eq$1,
3822
- deepEq: deepEq$5,
3787
+ eq: eq$2,
3788
+ deepEq: deepEq$6,
3823
3789
  parseQueryString,
3824
3790
  updateQueryString: updateQueryString$2,
3825
3791
  strongEncodeURIComponent,
@@ -14076,7 +14042,7 @@ ___default["default"].extend(MovableAngle.prototype, {
14076
14042
 
14077
14043
  /* eslint-disable @babel/no-invalid-this */
14078
14044
  const nestedMap$1 = Util.nestedMap;
14079
- const deepEq$4 = Util.deepEq;
14045
+ const deepEq$5 = Util.deepEq;
14080
14046
 
14081
14047
  /**
14082
14048
  * A base class for all Graphie Movables
@@ -14173,7 +14139,7 @@ const createSimpleClass = function (addFunction) {
14173
14139
  this._prevProps = this.props;
14174
14140
  },
14175
14141
  modify: function (graphie) {
14176
- if (!deepEq$4(this.props, this._prevProps)) {
14142
+ if (!deepEq$5(this.props, this._prevProps)) {
14177
14143
  this.remove();
14178
14144
  this.add(graphie);
14179
14145
  this._prevProps = this.props;
@@ -16448,7 +16414,7 @@ var Movables = {
16448
16414
  const GraphieMovable = GraphieClasses.GraphieMovable;
16449
16415
  const createGraphie = GraphUtils.createGraphie;
16450
16416
  const {
16451
- deepEq: deepEq$3,
16417
+ deepEq: deepEq$4,
16452
16418
  nestedMap
16453
16419
  } = Util;
16454
16420
  const {
@@ -16683,7 +16649,7 @@ class Graphie extends React__namespace.Component {
16683
16649
  if (this.props.setup !== prevProps.setup) {
16684
16650
  Log.error("<Graphie> was given a new setup function. " + "This is a bad idea; please refactor your code to give " + "the same setup function reference to <Graphie> on " + "every render.", Errors.Internal);
16685
16651
  }
16686
- if (!deepEq$3(this.props.options, prevProps.options) || !deepEq$3(this.props.box, prevProps.box) || !deepEq$3(this.props.range, prevProps.range)) {
16652
+ if (!deepEq$4(this.props.options, prevProps.options) || !deepEq$4(this.props.box, prevProps.box) || !deepEq$4(this.props.range, prevProps.range)) {
16687
16653
  this._setupGraphie();
16688
16654
  }
16689
16655
  this._updateMovables();
@@ -21612,7 +21578,11 @@ class NumericInput extends React__namespace.Component {
21612
21578
  return answerStrings[0];
21613
21579
  }
21614
21580
  static validate(useInput, rubric) {
21615
- const allAnswerForms = answerFormButtons.map(e => e["value"]);
21581
+ const allAnswerForms = answerFormButtons.map(e => e["value"])
21582
+ // Don't default to validating the answer as a pi answer
21583
+ // if answerForm isn't set on the answer
21584
+ // https://khanacademy.atlassian.net/browse/LC-691
21585
+ .filter(e => e !== "pi");
21616
21586
  const createValidator = answer => {
21617
21587
  const stringAnswer = "".concat(answer.value);
21618
21588
  return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
@@ -27849,36 +27819,29 @@ _defineProperty(Graph, "defaultProps", {
27849
27819
  isMobile: false
27850
27820
  });
27851
27821
 
27822
+ /**
27823
+ * A collection of geomtry-related utility functions
27824
+ */
27852
27825
  const {
27853
- DeprecationMixin
27826
+ eq: eq$1,
27827
+ deepEq: deepEq$3
27854
27828
  } = Util;
27855
- const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
27856
- const defaultBackgroundImage$1 = {
27857
- url: null
27858
- };
27859
- const eq = Util.eq;
27860
- const deepEq$2 = Util.deepEq;
27861
- const UNLIMITED = "unlimited";
27862
27829
 
27863
- // Sample background image:
27864
- // https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
27830
+ // This should really be a readonly tuple of [number, number]
27831
+
27832
+ // a, b, c
27865
27833
 
27866
- function ccw(a, b, c) {
27867
- return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
27868
- }
27869
- function collinear(a, b, c) {
27870
- return eq(ccw(a, b, c), 0);
27871
- }
27872
27834
  function sign(val) {
27873
- if (eq(val, 0)) {
27835
+ if (eq$1(val, 0)) {
27874
27836
  return 0;
27875
27837
  }
27876
27838
  return val > 0 ? 1 : -1;
27877
27839
  }
27878
-
27879
- // default to defaultValue if actual is null or undefined
27880
- function defaultVal(actual, defaultValue) {
27881
- return actual == null ? defaultValue : actual;
27840
+ function ccw(a, b, c) {
27841
+ return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
27842
+ }
27843
+ function collinear(a, b, c) {
27844
+ return eq$1(ccw(a, b, c), 0);
27882
27845
  }
27883
27846
 
27884
27847
  // Given rect bounding points A and B, whether point C is inside the rect
@@ -27891,14 +27854,12 @@ function pointInRect(a, b, c) {
27891
27854
  function intersects(ab, cd) {
27892
27855
  const triplets = [[ab[0], ab[1], cd[0]], [ab[0], ab[1], cd[1]], [cd[0], cd[1], ab[0]], [cd[0], cd[1], ab[1]]];
27893
27856
  const orientations = ___default["default"].map(triplets, function (triplet) {
27894
- // @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
27895
27857
  return sign(ccw(...triplet));
27896
27858
  });
27897
27859
  if (orientations[0] !== orientations[1] && orientations[2] !== orientations[3]) {
27898
27860
  return true;
27899
27861
  }
27900
27862
  for (let i = 0; i < 4; i++) {
27901
- // @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
27902
27863
  if (orientations[i] === 0 && pointInRect(...triplets[i])) {
27903
27864
  return true;
27904
27865
  }
@@ -27976,13 +27937,13 @@ function similar(coords1, coords2, tolerance) {
27976
27937
  angles = rotate(angles, i);
27977
27938
  // @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
27978
27939
  sides = rotate(sides, i);
27979
- if (deepEq$2(angles1, angles)) {
27940
+ if (deepEq$3(angles1, angles)) {
27980
27941
  const sidePairs = ___default["default"].zip(sides1, sides);
27981
27942
  const factors = ___default["default"].map(sidePairs, function (pair) {
27982
27943
  return pair[0] / pair[1];
27983
27944
  });
27984
27945
  const same = ___default["default"].all(factors, function (factor) {
27985
- return eq(factors[0], factor);
27946
+ return eq$1(factors[0], factor);
27986
27947
  });
27987
27948
  const congruentEnough = ___default["default"].all(sidePairs, function (pair) {
27988
27949
  return kmath.number.equal(pair[0], pair[1], tolerance);
@@ -27995,11 +27956,6 @@ function similar(coords1, coords2, tolerance) {
27995
27956
  return false;
27996
27957
  }
27997
27958
 
27998
- // Less than or approximately equal
27999
- function leq(a, b) {
28000
- return a < b || eq(a, b);
28001
- }
28002
-
28003
27959
  // Given triangle with sides ABC return angle opposite side C in degrees
28004
27960
  function lawOfCosines(a, b, c) {
28005
27961
  return Math.acos((a * a + b * b - c * c) / (2 * a * b)) * 180 / Math.PI;
@@ -28039,13 +27995,8 @@ function rotate(array, n) {
28039
27995
  n = typeof n === "undefined" ? 1 : n % array.length;
28040
27996
  return array.slice(n).concat(array.slice(0, n));
28041
27997
  }
28042
- function capitalize(str) {
28043
- return str.replace(/(?:^|-)(.)/g, function (match, letter) {
28044
- return letter.toUpperCase();
28045
- });
28046
- }
28047
27998
  function getLineEquation(first, second) {
28048
- if (eq(first[0], second[0])) {
27999
+ if (eq$1(first[0], second[0])) {
28049
28000
  return "x = " + first[0].toFixed(3);
28050
28001
  }
28051
28002
  const m = (second[1] - first[1]) / (second[0] - first[0]);
@@ -28074,6 +28025,35 @@ firstPoints, secondPoints) {
28074
28025
  const y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / determinant;
28075
28026
  return "Intersection: (" + x.toFixed(3) + ", " + y.toFixed(3) + ")";
28076
28027
  }
28028
+
28029
+ const {
28030
+ DeprecationMixin
28031
+ } = Util;
28032
+ const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
28033
+ const defaultBackgroundImage$1 = {
28034
+ url: null
28035
+ };
28036
+ const eq = Util.eq;
28037
+ const deepEq$2 = Util.deepEq;
28038
+ const UNLIMITED = "unlimited";
28039
+
28040
+ // Sample background image:
28041
+ // https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
28042
+
28043
+ // default to defaultValue if actual is null or undefined
28044
+ function defaultVal(actual, defaultValue) {
28045
+ return actual == null ? defaultValue : actual;
28046
+ }
28047
+
28048
+ // Less than or approximately equal
28049
+ function leq(a, b) {
28050
+ return a < b || eq(a, b);
28051
+ }
28052
+ function capitalize(str) {
28053
+ return str.replace(/(?:^|-)(.)/g, function (match, letter) {
28054
+ return letter.toUpperCase();
28055
+ });
28056
+ }
28077
28057
  function numSteps(range, step) {
28078
28058
  return Math.floor((range[1] - range[0]) / step);
28079
28059
  }
@@ -29236,17 +29216,6 @@ class InteractiveGraph extends React__namespace.Component {
29236
29216
  ___default["default"].invoke(this.points, "remove");
29237
29217
  (_this$angle2 = this.angle) === null || _this$angle2 === void 0 ? void 0 : _this$angle2.remove();
29238
29218
  });
29239
- _defineProperty(this, "toggleShowAngles", () => {
29240
- if (this.props.graph.type !== "polygon") {
29241
- throw makeInvalidTypeError("toggleShowAngles", "polygon");
29242
- }
29243
- const graph = ___default["default"].extend({}, this.props.graph, {
29244
- showAngles: !this.props.graph.showAngles
29245
- });
29246
- this.onChange({
29247
- graph: graph
29248
- });
29249
- });
29250
29219
  _defineProperty(this, "toggleShowSides", () => {
29251
29220
  if (this.props.graph.type !== "polygon") {
29252
29221
  throw makeInvalidTypeError("toggleShowSides", "polygon");
@@ -45840,7 +45809,7 @@ const styles$1 = aphrodite.StyleSheet.create({
45840
45809
  }
45841
45810
  });
45842
45811
 
45843
- /* eslint-disable react/prop-types */
45812
+ /* eslint-disable @babel/no-invalid-this */
45844
45813
 
45845
45814
  /* A checkbox that syncs its value to props using the
45846
45815
  * renderer's onChange method, and gets the prop name