@khanacademy/perseus-core 5.4.2 → 6.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/dist/index.js CHANGED
@@ -1,51 +1,19 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var _ = require('underscore');
6
- var KAS = require('@khanacademy/kas');
7
- var perseusUtils = require('@khanacademy/perseus-utils');
8
-
9
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
-
11
- function _interopNamespace(e) {
12
- if (e && e.__esModule) return e;
13
- var n = Object.create(null);
14
- if (e) {
15
- Object.keys(e).forEach(function (k) {
16
- if (k !== 'default') {
17
- var d = Object.getOwnPropertyDescriptor(e, k);
18
- Object.defineProperty(n, k, d.get ? d : {
19
- enumerable: true,
20
- get: function () { return e[k]; }
21
- });
22
- }
23
- });
24
- }
25
- n["default"] = e;
26
- return Object.freeze(n);
27
- }
28
-
29
- var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
30
- var KAS__namespace = /*#__PURE__*/_interopNamespace(KAS);
1
+ import _ from 'underscore';
2
+ import _extends from '@babel/runtime/helpers/extends';
3
+ import * as KAS from '@khanacademy/kas';
4
+ import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/objectWithoutPropertiesLoose';
5
+ import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-utils';
31
6
 
32
7
  function getMatrixSize(matrix) {
33
8
  const matrixSize = [1, 1];
34
-
35
- // We need to find the widest row and tallest column to get the correct
36
- // matrix size.
37
- ___default["default"](matrix).each((matrixRow, row) => {
9
+ _(matrix).each((matrixRow, row) => {
38
10
  let rowWidth = 0;
39
- ___default["default"](matrixRow).each((matrixCol, col) => {
11
+ _(matrixRow).each((matrixCol, col) => {
40
12
  if (matrixCol != null && matrixCol.toString().length) {
41
13
  rowWidth = col + 1;
42
14
  }
43
15
  });
44
-
45
- // Matrix width:
46
16
  matrixSize[1] = Math.max(matrixSize[1], rowWidth);
47
-
48
- // Matrix height:
49
17
  if (rowWidth > 0) {
50
18
  matrixSize[0] = Math.max(matrixSize[0], row + 1);
51
19
  }
@@ -53,47 +21,24 @@ function getMatrixSize(matrix) {
53
21
  return matrixSize;
54
22
  }
55
23
 
56
- /**
57
- * Get the character used for separating decimals.
58
- */
59
24
  const getDecimalSeparator = locale => {
25
+ var _match$;
60
26
  switch (locale) {
61
- // TODO(somewhatabstract): Remove this when Chrome supports the `ka`
62
- // locale properly.
63
- // https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
64
- //
65
- // Supported locales in Chrome:
66
- // https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
67
27
  case "ka":
68
28
  return ",";
69
29
  default:
70
30
  const numberWithDecimalSeparator = 1.1;
71
- // TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
72
- // support Safari 12.
73
- const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
74
- // 0x661 is ARABIC-INDIC DIGIT ONE
75
- // 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
76
- // 0x967 is DEVANAGARI DIGIT ONE
77
- // 0x9e7 is BENGALI/BANGLA DIGIT ONE
78
- .match(/[^\d\u0661\u06F1\u0967\u09e7]/);
79
- return match?.[0] ?? ".";
31
+ const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator).match(/[^\d\u0661\u06F1\u0967\u09e7]/);
32
+ return (_match$ = match == null ? void 0 : match[0]) != null ? _match$ : ".";
80
33
  }
81
34
  };
82
35
 
83
- /**
84
- * APPROXIMATE equality on numbers and primitives.
85
- */
86
36
  function approximateEqual(x, y) {
87
37
  if (typeof x === "number" && typeof y === "number") {
88
38
  return Math.abs(x - y) < 1e-9;
89
39
  }
90
40
  return x === y;
91
41
  }
92
-
93
- /**
94
- * Deep APPROXIMATE equality on primitives, numbers, arrays, and objects.
95
- * Recursive.
96
- */
97
42
  function approximateDeepEqual(x, y) {
98
43
  if (Array.isArray(x) && Array.isArray(y)) {
99
44
  if (x.length !== y.length) {
@@ -116,11 +61,9 @@ function approximateDeepEqual(x, y) {
116
61
  return false;
117
62
  }
118
63
  if (typeof x === "object" && typeof y === "object" && !!x && !!y) {
119
- return x === y || ___default["default"].all(x, function (v, k) {
120
- // @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
64
+ return x === y || _.all(x, function (v, k) {
121
65
  return approximateDeepEqual(y[k], v);
122
- }) && ___default["default"].all(y, function (v, k) {
123
- // @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
66
+ }) && _.all(y, function (v, k) {
124
67
  return approximateDeepEqual(x[k], v);
125
68
  });
126
69
  }
@@ -130,38 +73,12 @@ function approximateDeepEqual(x, y) {
130
73
  return approximateEqual(x, y);
131
74
  }
132
75
 
133
- /**
134
- * Add a widget placeholder using the widget ID.
135
- * ex. addWidget("radio 1") => "[[☃ radio 1]]"
136
- *
137
- * @param {string} id
138
- * @returns {string}
139
- */
140
76
  function addWidget(id) {
141
77
  return `[[☃ ${id}]]`;
142
78
  }
143
-
144
- /**
145
- * Regex for widget placeholders in a string.
146
- *
147
- * First capture group is the widget ID (ex. 'radio 1')
148
- * Second capture group is the widget type (ex. "radio)
149
- * exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
150
- */
151
79
  function getWidgetRegex() {
152
80
  return /\[\[☃ ([A-Za-z0-9- ]+)\]\]/g;
153
81
  }
154
-
155
- /**
156
- * Extract all widget IDs, which includes the widget type and instance number.
157
- * example output: ['radio 1', 'categorizer 1', 'categorizor 2']
158
- *
159
- * Content should contain Perseus widget placeholders,
160
- * which look like: '[[☃ radio 1]]'.
161
- *
162
- * @param {string} content
163
- * @returns {ReadonlyArray<string>} widgetIds
164
- */
165
82
  function getWidgetIdsFromContent(content) {
166
83
  const widgets = [];
167
84
  const localWidgetRegex = getWidgetRegex();
@@ -172,31 +89,18 @@ function getWidgetIdsFromContent(content) {
172
89
  }
173
90
  return widgets;
174
91
  }
175
-
176
- /**
177
- * Get a list of widget IDs from content,
178
- * but only for specific widget types
179
- *
180
- * @param {string} type the type of widget (ie "radio")
181
- * @param {string} content the string to parse
182
- * @param {PerseusWidgetsMap} widgetMap widget ID to widget map
183
- * @returns {ReadonlyArray<string>} the widget type (ie "radio")
184
- */
185
92
  function getWidgetIdsFromContentByType(type, content, widgetMap) {
186
93
  const rv = [];
187
94
  const widgetIdsInContent = getWidgetIdsFromContent(content);
188
95
  widgetIdsInContent.forEach(widgetId => {
189
96
  const widget = widgetMap[widgetId];
190
- if (widget?.type === type) {
97
+ if ((widget == null ? void 0 : widget.type) === type) {
191
98
  rv.push(widgetId);
192
99
  }
193
100
  });
194
101
  return rv;
195
102
  }
196
103
 
197
- // TODO(benchristel): in the future, we may want to make deepClone work for
198
- // Record<string, Cloneable> as well. Currently, it only does arrays.
199
-
200
104
  function deepClone(obj) {
201
105
  if (Array.isArray(obj)) {
202
106
  return obj.map(deepClone);
@@ -209,33 +113,22 @@ const MOVABLES = {
209
113
  PARABOLA: "PARABOLA",
210
114
  SINUSOID: "SINUSOID"
211
115
  };
212
-
213
- // TODO(charlie): These really need to go into a utility file as they're being
214
- // used by both interactive-graph and now grapher.
215
116
  function canonicalSineCoefficients(coeffs) {
216
- // For a curve of the form f(x) = a * Sin(b * x - c) + d,
217
- // this function ensures that a, b > 0, and c is its
218
- // smallest possible positive value.
219
117
  let amplitude = coeffs[0];
220
118
  let angularFrequency = coeffs[1];
221
119
  let phase = coeffs[2];
222
120
  const verticalOffset = coeffs[3];
223
-
224
- // Guarantee a > 0
225
121
  if (amplitude < 0) {
226
122
  amplitude *= -1;
227
123
  angularFrequency *= -1;
228
124
  phase *= -1;
229
125
  }
230
126
  const period = 2 * Math.PI;
231
- // Guarantee b > 0
232
127
  if (angularFrequency < 0) {
233
128
  angularFrequency *= -1;
234
129
  phase *= -1;
235
130
  phase += period / 2;
236
131
  }
237
-
238
- // Guarantee c is smallest possible positive value
239
132
  while (phase > 0) {
240
133
  phase -= period;
241
134
  }
@@ -245,29 +138,21 @@ function canonicalSineCoefficients(coeffs) {
245
138
  return [amplitude, angularFrequency, phase, verticalOffset];
246
139
  }
247
140
  function canonicalTangentCoefficients(coeffs) {
248
- // For a curve of the form f(x) = a * Tan(b * x - c) + d,
249
- // this function ensures that a, b > 0, and c is its
250
- // smallest possible positive value.
251
141
  let amplitude = coeffs[0];
252
142
  let angularFrequency = coeffs[1];
253
143
  let phase = coeffs[2];
254
144
  const verticalOffset = coeffs[3];
255
-
256
- // Guarantee a > 0
257
145
  if (amplitude < 0) {
258
146
  amplitude *= -1;
259
147
  angularFrequency *= -1;
260
148
  phase *= -1;
261
149
  }
262
150
  const period = Math.PI;
263
- // Guarantee b > 0
264
151
  if (angularFrequency < 0) {
265
152
  angularFrequency *= -1;
266
153
  phase *= -1;
267
154
  phase += period / 2;
268
155
  }
269
-
270
- // Guarantee c is smallest possible positive value
271
156
  while (phase > 0) {
272
157
  phase -= period;
273
158
  }
@@ -283,12 +168,11 @@ const PlotDefaults = {
283
168
  movable: MOVABLES.PLOT,
284
169
  getPropsForCoeffs: function (coeffs) {
285
170
  return {
286
- // @ts-expect-error - TS2339 - Property 'getFunctionForCoeffs' does not exist on type '{ readonly areEqual: (coeffs1: any, coeffs2: any) => boolean; readonly Movable: any; readonly getPropsForCoeffs: (coeffs: any) => any; }'.
287
- fn: ___default["default"].partial(this.getFunctionForCoeffs, coeffs)
171
+ fn: _.partial(this.getFunctionForCoeffs, coeffs)
288
172
  };
289
173
  }
290
174
  };
291
- const Linear = ___default["default"].extend({}, PlotDefaults, {
175
+ const Linear = _.extend({}, PlotDefaults, {
292
176
  url: "https://ka-perseus-graphie.s3.amazonaws.com/67aaf581e6d9ef9038c10558a1f70ac21c11c9f8.png",
293
177
  defaultCoords: [[0.25, 0.75], [0.75, 0.75]],
294
178
  getCoefficients: function (coords) {
@@ -315,19 +199,15 @@ const Linear = ___default["default"].extend({}, PlotDefaults, {
315
199
  return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
316
200
  }
317
201
  });
318
- const Quadratic = ___default["default"].extend({}, PlotDefaults, {
202
+ const Quadratic = _.extend({}, PlotDefaults, {
319
203
  url: "https://ka-perseus-graphie.s3.amazonaws.com/e23d36e6fc29ee37174e92c9daba2a66677128ab.png",
320
204
  defaultCoords: [[0.5, 0.5], [0.75, 0.75]],
321
205
  movable: MOVABLES.PARABOLA,
322
206
  getCoefficients: function (coords) {
323
207
  const p1 = coords[0];
324
208
  const p2 = coords[1];
325
-
326
- // Parabola with vertex (h, k) has form: y = a * (h - k)^2 + k
327
209
  const h = p1[0];
328
210
  const k = p1[1];
329
-
330
- // Use these to calculate familiar a, b, c
331
211
  const a = (p2[1] - k) / ((p2[0] - h) * (p2[0] - h));
332
212
  const b = -2 * h * a;
333
213
  const c = a * h * h + k;
@@ -354,7 +234,7 @@ const Quadratic = ___default["default"].extend({}, PlotDefaults, {
354
234
  return "y = " + a.toFixed(3) + "x^2 + " + b.toFixed(3) + "x + " + c.toFixed(3);
355
235
  }
356
236
  });
357
- const Sinusoid = ___default["default"].extend({}, PlotDefaults, {
237
+ const Sinusoid = _.extend({}, PlotDefaults, {
358
238
  url: "https://ka-perseus-graphie.s3.amazonaws.com/3d68e7718498475f53b206c2ab285626baf8857e.png",
359
239
  defaultCoords: [[0.5, 0.5], [0.6, 0.6]],
360
240
  movable: MOVABLES.SINUSOID,
@@ -394,7 +274,7 @@ const Sinusoid = ___default["default"].extend({}, PlotDefaults, {
394
274
  return approximateDeepEqual(canonicalSineCoefficients(coeffs1), canonicalSineCoefficients(coeffs2));
395
275
  }
396
276
  });
397
- const Tangent = ___default["default"].extend({}, PlotDefaults, {
277
+ const Tangent = _.extend({}, PlotDefaults, {
398
278
  url: "https://ka-perseus-graphie.s3.amazonaws.com/7db80d23c35214f98659fe1cf0765811c1bbfbba.png",
399
279
  defaultCoords: [[0.5, 0.5], [0.75, 0.75]],
400
280
  getCoefficients: function (coords) {
@@ -425,46 +305,27 @@ const Tangent = ___default["default"].extend({}, PlotDefaults, {
425
305
  return approximateDeepEqual(canonicalTangentCoefficients(coeffs1), canonicalTangentCoefficients(coeffs2));
426
306
  }
427
307
  });
428
- const Exponential = ___default["default"].extend({}, PlotDefaults, {
308
+ const Exponential = _.extend({}, PlotDefaults, {
429
309
  url: "https://ka-perseus-graphie.s3.amazonaws.com/9cbfad55525e3ce755a31a631b074670a5dad611.png",
430
310
  defaultCoords: [[0.5, 0.55], [0.75, 0.75]],
431
311
  defaultAsymptote: [[0, 0.5], [1.0, 0.5]],
432
- /**
433
- * Add extra constraints for movement of the points or asymptote (below):
434
- * newCoord: [x, y]
435
- * The end position of the point or asymptote endpoint
436
- * oldCoord: [x, y]
437
- * The old position of the point or asymptote endpoint
438
- * coords:
439
- * An array of coordinates representing the proposed end configuration
440
- * of the plot coordinates.
441
- * asymptote:
442
- * An array of coordinates representing the proposed end configuration
443
- * of the asymptote.
444
- *
445
- * Return: either a coordinate (to be used as the resulting coordinate of
446
- * the move) or a boolean, where `true` uses newCoord as the resulting
447
- * coordinate, and `false` uses oldCoord as the resulting coordinate.
448
- */
449
312
  extraCoordConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
450
313
  const y = asymptote[0][1];
451
- return ___default["default"].all(coords, coord => coord[1] !== y);
314
+ return _.all(coords, coord => coord[1] !== y);
452
315
  },
453
316
  extraAsymptoteConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
454
317
  const y = newCoord[1];
455
- const isValid = ___default["default"].all(coords, coord => coord[1] > y) || ___default["default"].all(coords, coord => coord[1] < y);
318
+ const isValid = _.all(coords, coord => coord[1] > y) || _.all(coords, coord => coord[1] < y);
456
319
  if (isValid) {
457
320
  return [oldCoord[0], y];
458
321
  }
459
- // Snap the asymptote as close as possible, i.e., if the user moves
460
- // the mouse really quickly into an invalid region
461
322
  const oldY = oldCoord[1];
462
- const wasBelow = ___default["default"].all(coords, coord => coord[1] > oldY);
323
+ const wasBelow = _.all(coords, coord => coord[1] > oldY);
463
324
  if (wasBelow) {
464
- const bottomMost = ___default["default"].min(___default["default"].map(coords, coord => coord[1]));
325
+ const bottomMost = _.min(_.map(coords, coord => coord[1]));
465
326
  return [oldCoord[0], bottomMost - graph.snapStep[1]];
466
327
  }
467
- const topMost = ___default["default"].max(___default["default"].map(coords, coord => coord[1]));
328
+ const topMost = _.max(_.map(coords, coord => coord[1]));
468
329
  return [oldCoord[0], topMost + graph.snapStep[1]];
469
330
  },
470
331
  allowReflectOverAsymptote: true,
@@ -493,39 +354,33 @@ const Exponential = ___default["default"].extend({}, PlotDefaults, {
493
354
  return "y = " + a.toFixed(3) + "e^(" + b.toFixed(3) + "x) + " + c.toFixed(3);
494
355
  }
495
356
  });
496
- const Logarithm = ___default["default"].extend({}, PlotDefaults, {
357
+ const Logarithm = _.extend({}, PlotDefaults, {
497
358
  url: "https://ka-perseus-graphie.s3.amazonaws.com/f6491e99d34af34d924bfe0231728ad912068dc3.png",
498
359
  defaultCoords: [[0.55, 0.5], [0.75, 0.75]],
499
360
  defaultAsymptote: [[0.5, 0], [0.5, 1.0]],
500
361
  extraCoordConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
501
362
  const x = asymptote[0][0];
502
- return ___default["default"].all(coords, coord => coord[0] !== x) && coords[0][1] !== coords[1][1];
363
+ return _.all(coords, coord => coord[0] !== x) && coords[0][1] !== coords[1][1];
503
364
  },
504
365
  extraAsymptoteConstraint: function (newCoord, oldCoord, coords, asymptote, graph) {
505
366
  const x = newCoord[0];
506
- const isValid = ___default["default"].all(coords, coord => coord[0] > x) || ___default["default"].all(coords, coord => coord[0] < x);
367
+ const isValid = _.all(coords, coord => coord[0] > x) || _.all(coords, coord => coord[0] < x);
507
368
  if (isValid) {
508
369
  return [x, oldCoord[1]];
509
370
  }
510
- // Snap the asymptote as close as possible, i.e., if the user moves
511
- // the mouse really quickly into an invalid region
512
371
  const oldX = oldCoord[0];
513
- const wasLeft = ___default["default"].all(coords, coord => coord[0] > oldX);
372
+ const wasLeft = _.all(coords, coord => coord[0] > oldX);
514
373
  if (wasLeft) {
515
- const leftMost = ___default["default"].min(___default["default"].map(coords, coord => coord[0]));
374
+ const leftMost = _.min(_.map(coords, coord => coord[0]));
516
375
  return [leftMost - graph.snapStep[0], oldCoord[1]];
517
376
  }
518
- const rightMost = ___default["default"].max(___default["default"].map(coords, coord => coord[0]));
377
+ const rightMost = _.max(_.map(coords, coord => coord[0]));
519
378
  return [rightMost + graph.snapStep[0], oldCoord[1]];
520
379
  },
521
380
  allowReflectOverAsymptote: true,
522
381
  getCoefficients: function (coords, asymptote) {
523
- // It's easiest to calculate the logarithm's coefficients by thinking
524
- // about it as the inverse of the exponential, so we flip x and y and
525
- // perform some algebra on the coefficients. This also unifies the
526
- // logic between the two 'models'.
527
382
  const flip = coord => [coord[1], coord[0]];
528
- const inverseCoeffs = Exponential.getCoefficients(___default["default"].map(coords, flip), ___default["default"].map(asymptote, flip));
383
+ const inverseCoeffs = Exponential.getCoefficients(_.map(coords, flip), _.map(asymptote, flip));
529
384
  if (inverseCoeffs) {
530
385
  const c = -inverseCoeffs[2] / inverseCoeffs[0];
531
386
  const b = 1 / inverseCoeffs[0];
@@ -550,7 +405,7 @@ const Logarithm = ___default["default"].extend({}, PlotDefaults, {
550
405
  return "y = ln(" + a.toFixed(3) + "x + " + b.toFixed(3) + ") + " + c.toFixed(3);
551
406
  }
552
407
  });
553
- const AbsoluteValue = ___default["default"].extend({}, PlotDefaults, {
408
+ const AbsoluteValue = _.extend({}, PlotDefaults, {
554
409
  url: "https://ka-perseus-graphie.s3.amazonaws.com/8256a630175a0cb1d11de223d6de0266daf98721.png",
555
410
  defaultCoords: [[0.5, 0.5], [0.75, 0.75]],
556
411
  getCoefficients: function (coords) {
@@ -583,8 +438,6 @@ const AbsoluteValue = ___default["default"].extend({}, PlotDefaults, {
583
438
  return "y = " + m.toFixed(3) + "| x - " + horizontalOffset.toFixed(3) + "| + " + verticalOffset.toFixed(3);
584
439
  }
585
440
  });
586
-
587
- /* Utility functions for dealing with graphing interfaces. */
588
441
  const functionTypeMapping = {
589
442
  linear: Linear,
590
443
  quadratic: Quadratic,
@@ -594,10 +447,8 @@ const functionTypeMapping = {
594
447
  logarithm: Logarithm,
595
448
  absolute_value: AbsoluteValue
596
449
  };
597
- const allTypes = ___default["default"].keys(functionTypeMapping);
450
+ const allTypes = _.keys(functionTypeMapping);
598
451
  function functionForType(type) {
599
- // @ts-expect-error: TypeScript doesn't know how to use deal with generics
600
- // and conditional types in this way.
601
452
  return functionTypeMapping[type];
602
453
  }
603
454
 
@@ -666,8 +517,7 @@ function isRealJSONParse(jsonParse) {
666
517
  const parsedTestItemData = parsedTestJSON.data.assessmentItem.item.itemData;
667
518
  return approximateDeepEqual(parsedTestItemData, testingObject);
668
519
  }
669
- function buildRandomString() {
670
- let capitalize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
520
+ function buildRandomString(capitalize = false) {
671
521
  let randomString = "";
672
522
  const randomLength = Math.floor(Math.random() * 8) + 3;
673
523
  for (let i = 0; i < randomLength; i++) {
@@ -691,8 +541,6 @@ function buildTestData(testObject) {
691
541
  return `{"data":{"assessmentItem":{"__typename":"AssessmentItemOrError","error":null,"item":{"__typename":"AssessmentItem","id":"x890b3c70f3e8f4a6","itemData":"${testObject}","problemType":"Type 1","sha":"c7284a3ad65214b4e62bccce236d92f7f5d35941"}}}}`;
692
542
  }
693
543
 
694
- process.env.NODE_ENV === 'production';
695
-
696
544
  function success(value) {
697
545
  return {
698
546
  type: "success",
@@ -711,11 +559,7 @@ function isFailure(result) {
711
559
  function isSuccess(result) {
712
560
  return result.type === "success";
713
561
  }
714
-
715
- // Result's `all` function is similar to Promise.all: given an array of
716
- // results, it returns success if all succeeded, and failure if any failed.
717
- function all(results) {
718
- let combineFailureDetails = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : a => a;
562
+ function all(results, combineFailureDetails = a => a) {
719
563
  const values = [];
720
564
  const failureDetails = [];
721
565
  for (const result of results) {
@@ -772,7 +616,6 @@ function message(failure) {
772
616
  }
773
617
  function conjoin(items) {
774
618
  switch (items.length) {
775
- // TODO(benchristel): handle 0 if this is reused elsewhere.
776
619
  case 1:
777
620
  return items[0];
778
621
  case 2:
@@ -825,10 +668,7 @@ function constant(acceptedValue) {
825
668
  };
826
669
  }
827
670
 
828
- function enumeration() {
829
- for (var _len = arguments.length, acceptedValues = new Array(_len), _key = 0; _key < _len; _key++) {
830
- acceptedValues[_key] = arguments[_key];
831
- }
671
+ function enumeration(...acceptedValues) {
832
672
  return (rawValue, ctx) => {
833
673
  if (typeof rawValue === "string") {
834
674
  const index = acceptedValues.indexOf(rawValue);
@@ -866,9 +706,7 @@ function object(schema) {
866
706
  if (!isObject(rawValue)) {
867
707
  return ctx.failure("object", rawValue);
868
708
  }
869
- const ret = {
870
- ...rawValue
871
- };
709
+ const ret = _extends({}, rawValue);
872
710
  const mismatches = [];
873
711
  for (const [prop, propParser] of Object.entries(schema)) {
874
712
  const result = propParser(rawValue[prop], ctx.forSubtree(prop));
@@ -1106,54 +944,26 @@ const parseExplanationWidget = parseWidget(constant("explanation"), object({
1106
944
  showPrompt: string,
1107
945
  hidePrompt: string,
1108
946
  explanation: string,
1109
- // We wrap parseWidgetsMap in a function here to make sure it is not
1110
- // referenced before it is defined. There is an import cycle between
1111
- // this file and widgets-map.ts that could cause it to be undefined.
1112
947
  widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
1113
948
  static: defaulted(boolean, () => false)
1114
949
  }));
1115
950
 
1116
- const KeypadKeys = ["PLUS", "MINUS", "NEGATIVE", "TIMES", "DIVIDE", "DECIMAL", "PERIOD", "PERCENT", "CDOT", "EQUAL", "NEQ", "GT", "LT", "GEQ", "LEQ",
1117
- // mobile native only
1118
- "FRAC_INCLUSIVE",
1119
- // mobile native only
1120
- "FRAC_EXCLUSIVE",
1121
- // mobile native only
1122
- "FRAC", "EXP", "EXP_2", "EXP_3", "SQRT", "CUBE_ROOT", "RADICAL", "LEFT_PAREN", "RIGHT_PAREN", "LN", "LOG", "LOG_N", "SIN", "COS",
1123
- // TODO(charlie): Add in additional Greek letters.,
1124
- "TAN", "PI", "THETA", "UP", "RIGHT", "DOWN", "LEFT", "BACKSPACE", "DISMISS", "JUMP_OUT_PARENTHESES", "JUMP_OUT_EXPONENT", "JUMP_OUT_BASE", "JUMP_INTO_NUMERATOR", "JUMP_OUT_NUMERATOR", "JUMP_OUT_DENOMINATOR",
1125
- // Multi-functional keys.
1126
- "NUM_0", "NUM_1", "NUM_2", "NUM_3", "NUM_4", "NUM_5", "NUM_6", "NUM_7", "NUM_8", "NUM_9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
1127
-
1128
- // Used by KeypadContext to pass around a renderer reference
1129
-
1130
- /**
1131
- * Scrape the answer forms for any variables or contants (like Pi)
1132
- * that need to be included as keys on the keypad.
1133
- */
951
+ const KeypadKeys = ["PLUS", "MINUS", "NEGATIVE", "TIMES", "DIVIDE", "DECIMAL", "PERIOD", "PERCENT", "CDOT", "EQUAL", "NEQ", "GT", "LT", "GEQ", "LEQ", "FRAC_INCLUSIVE", "FRAC_EXCLUSIVE", "FRAC", "EXP", "EXP_2", "EXP_3", "SQRT", "CUBE_ROOT", "RADICAL", "LEFT_PAREN", "RIGHT_PAREN", "LN", "LOG", "LOG_N", "SIN", "COS", "TAN", "PI", "THETA", "UP", "RIGHT", "DOWN", "LEFT", "BACKSPACE", "DISMISS", "JUMP_OUT_PARENTHESES", "JUMP_OUT_EXPONENT", "JUMP_OUT_BASE", "JUMP_INTO_NUMERATOR", "JUMP_OUT_NUMERATOR", "JUMP_OUT_DENOMINATOR", "NUM_0", "NUM_1", "NUM_2", "NUM_3", "NUM_4", "NUM_5", "NUM_6", "NUM_7", "NUM_8", "NUM_9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
952
+
1134
953
  function deriveExtraKeys(widgetOptions) {
1135
954
  if (widgetOptions.extraKeys) {
1136
955
  return widgetOptions.extraKeys;
1137
956
  }
1138
-
1139
- // If there are no extra symbols available, we include Pi anyway, so
1140
- // that the "extra symbols" button doesn't appear empty.
1141
957
  const defaultKeys = ["PI"];
1142
958
  if (widgetOptions.answerForms == null) {
1143
959
  return defaultKeys;
1144
960
  }
1145
-
1146
- // Extract any and all variables and constants from the answer forms.
1147
961
  const uniqueExtraVariables = {};
1148
962
  const uniqueExtraConstants = {};
1149
963
  for (const answerForm of widgetOptions.answerForms) {
1150
- const maybeExpr = KAS__namespace.parse(answerForm.value, widgetOptions);
964
+ const maybeExpr = KAS.parse(answerForm.value, widgetOptions);
1151
965
  if (maybeExpr.parsed) {
1152
966
  const expr = maybeExpr.expr;
1153
-
1154
- // The keypad expects Greek letters to be capitalized (e.g., it
1155
- // requires `PI` instead of `pi`). Right now, it only supports Pi
1156
- // and Theta, so we special-case.
1157
967
  const isGreek = symbol => symbol === "pi" || symbol === "theta";
1158
968
  const toKey = symbol => isGreek(symbol) ? symbol.toUpperCase() : symbol;
1159
969
  const isKey = key => KeypadKeys.includes(key);
@@ -1171,9 +981,6 @@ function deriveExtraKeys(widgetOptions) {
1171
981
  }
1172
982
  }
1173
983
  }
1174
-
1175
- // TODO(charlie): Alert the keypad as to which of these symbols should be
1176
- // treated as functions.
1177
984
  const extraVariables = Object.keys(uniqueExtraVariables).sort();
1178
985
  const extraConstants = Object.keys(uniqueExtraConstants).sort();
1179
986
  const extraKeys = [...extraVariables, ...extraConstants];
@@ -1183,44 +990,19 @@ function deriveExtraKeys(widgetOptions) {
1183
990
  return extraKeys;
1184
991
  }
1185
992
 
1186
- // Given a function, creates a PartialParser that converts one type to another
1187
- // using that function. The returned parser never fails.
1188
993
  function convert(f) {
1189
994
  return (rawValue, ctx) => ctx.success(f(rawValue));
1190
995
  }
1191
996
 
1192
997
  const parseLegacyButtonSet = enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations", "scientific");
1193
- const parseLegacyButtonSets = defaulted(array(parseLegacyButtonSet),
1194
- // NOTE(benchristel): I copied the default buttonSets from
1195
- // expression.tsx. See the parse-perseus-json/README.md for
1196
- // an explanation of why we want to duplicate the default here.
1197
- () => ["basic", "trig", "prealgebra", "logarithms"]);
1198
-
1199
- /**
1200
- * Creates a parser for a widget options type with multiple major versions. Old
1201
- * versions are migrated to the latest version. The parse fails if the input
1202
- * data does not match any of the versions.
1203
- *
1204
- * @example
1205
- * const parseOptions = versionedWidgetOptions(3, parseOptionsV3)
1206
- * .withMigrationFrom(2, parseOptionsV2, migrateV2ToV3)
1207
- * .withMigrationFrom(1, parseOptionsV1, migrateV1ToV2)
1208
- * .withMigrationFrom(0, parseOptionsV0, migrateV0ToV1)
1209
- * .parser;
1210
- *
1211
- * @param latestMajorVersion the latest major version of the widget options.
1212
- * @param parseLatest a {@link Parser} for the latest version of the widget
1213
- * options.
1214
- * @returns a builder object, to which migrations from earlier versions can be
1215
- * added. Migrations must be added in "reverse chronological" order as in the
1216
- * example above.
1217
- */
998
+ const parseLegacyButtonSets = defaulted(array(parseLegacyButtonSet), () => ["basic", "trig", "prealgebra", "logarithms"]);
999
+
1218
1000
  function versionedWidgetOptions(latestMajorVersion, parseLatest) {
1219
1001
  return new VersionedWidgetOptionsParserBuilder(latestMajorVersion, parseLatest, latest => latest, (raw, ctx) => ctx.failure("widget options with a known version number", raw));
1220
1002
  }
1221
1003
  class VersionedWidgetOptionsParserBuilder {
1222
- parser;
1223
1004
  constructor(majorVersion, parseThisVersion, migrateToLatest, parseOtherVersions) {
1005
+ this.parser = void 0;
1224
1006
  this.migrateToLatest = migrateToLatest;
1225
1007
  this.parseOtherVersions = parseOtherVersions;
1226
1008
  const parseThisVersionAndMigrateToLatest = pipeParsers(parseThisVersion).then(convert(this.migrateToLatest)).parser;
@@ -1238,10 +1020,6 @@ class VersionedWidgetOptionsParserBuilder {
1238
1020
  return parseThisVersionAndMigrateToLatest(raw, ctx);
1239
1021
  };
1240
1022
  }
1241
-
1242
- /**
1243
- * Add a migration from an old version of the widget options.
1244
- */
1245
1023
  withMigrationFrom(majorVersion, parseOldVersion, migrateToNextVersion) {
1246
1024
  const parseOtherVersions = this.parser;
1247
1025
  const migrateToLatest = old => this.migrateToLatest(migrateToNextVersion(old));
@@ -1260,9 +1038,6 @@ const parseVersionedObject = object({
1260
1038
 
1261
1039
  const stringOrNumberOrNullOrUndefined = union(string).or(number).or(constant(null)).or(constant(undefined)).parser;
1262
1040
  const parsePossiblyInvalidAnswerForm = object({
1263
- // `value` is the possibly invalid part of this. It should always be a
1264
- // string, but some answer forms don't have it. The Expression widget
1265
- // ignores invalid values, so we can safely filter them out during parsing.
1266
1041
  value: optional(string),
1267
1042
  form: defaulted(boolean, () => false),
1268
1043
  simplify: defaulted(boolean, () => false),
@@ -1276,11 +1051,9 @@ function removeInvalidAnswerForms(possiblyInvalid) {
1276
1051
  value
1277
1052
  } = answerForm;
1278
1053
  if (value != null) {
1279
- // Copying the object seems to be needed to make TypeScript happy
1280
- valid.push({
1281
- ...answerForm,
1054
+ valid.push(_extends({}, answerForm, {
1282
1055
  value
1283
- });
1056
+ }));
1284
1057
  }
1285
1058
  }
1286
1059
  return valid;
@@ -1316,8 +1089,7 @@ function migrateV1ToV2$1(widget) {
1316
1089
  const {
1317
1090
  options
1318
1091
  } = widget;
1319
- return {
1320
- ...widget,
1092
+ return _extends({}, widget, {
1321
1093
  version: {
1322
1094
  major: 2,
1323
1095
  minor: 0
@@ -1332,7 +1104,7 @@ function migrateV1ToV2$1(widget) {
1332
1104
  answerForms: options.answerForms,
1333
1105
  extraKeys: deriveExtraKeys(options)
1334
1106
  }
1335
- };
1107
+ });
1336
1108
  }
1337
1109
  const version0$1 = optional(object({
1338
1110
  major: constant(0),
@@ -1353,8 +1125,7 @@ function migrateV0ToV1$1(widget) {
1353
1125
  const {
1354
1126
  options
1355
1127
  } = widget;
1356
- return {
1357
- ...widget,
1128
+ return _extends({}, widget, {
1358
1129
  version: {
1359
1130
  major: 1,
1360
1131
  minor: 0
@@ -1373,7 +1144,7 @@ function migrateV0ToV1$1(widget) {
1373
1144
  value: options.value
1374
1145
  }]
1375
1146
  }
1376
- };
1147
+ });
1377
1148
  }
1378
1149
  const parseExpressionWidget = versionedWidgetOptions(2, parseExpressionWidgetV2).withMigrationFrom(1, parseExpressionWidgetV1, migrateV1ToV2$1).withMigrationFrom(0, parseExpressionWidgetV0, migrateV0ToV1$1).parser;
1379
1150
 
@@ -1381,14 +1152,8 @@ const falseToNull = pipeParsers(constant(false)).then(convert(() => null)).parse
1381
1152
  const parseGradedGroupWidgetOptions = object({
1382
1153
  title: defaulted(string, () => ""),
1383
1154
  hasHint: optional(nullable(boolean)),
1384
- // This module has an import cycle with parsePerseusRenderer.
1385
- // The anonymous function below ensures that we don't try to access
1386
- // parsePerseusRenderer before it's defined.
1387
1155
  hint: union(falseToNull).or(constant(null)).or(constant(undefined)).or((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx)).parser,
1388
1156
  content: string,
1389
- // This module has an import cycle with parseWidgetsMap.
1390
- // The anonymous function below ensures that we don't try to access
1391
- // parseWidgetsMap before it's defined.
1392
1157
  widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
1393
1158
  widgetEnabled: optional(nullable(boolean)),
1394
1159
  immutableWidgets: optional(nullable(boolean)),
@@ -1403,13 +1168,6 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
1403
1168
  gradedGroups: array(parseGradedGroupWidgetOptions)
1404
1169
  }));
1405
1170
 
1406
- /**
1407
- * discriminatedUnion() should be preferred over union() when parsing a
1408
- * discriminated union type, because discriminatedUnion() produces more
1409
- * understandable failure messages. It takes the discriminant as the source of
1410
- * truth for which variant is to be parsed, and expects the other data to match
1411
- * that variant.
1412
- */
1413
1171
  function discriminatedUnionOn(discriminantKey) {
1414
1172
  const noMoreBranches = (raw, ctx) => {
1415
1173
  if (!isObject(raw)) {
@@ -1495,11 +1253,7 @@ const parseGrapherWidget = parseWidget(constant("grapher"), object({
1495
1253
  })
1496
1254
  }));
1497
1255
 
1498
- const parseGroupWidget = parseWidget(constant("group"),
1499
- // This module has an import cycle with parsePerseusRenderer.
1500
- // The anonymous function below ensures that we don't try to access
1501
- // parsePerseusRenderer before it's defined.
1502
- (rawVal, ctx) => parsePerseusRenderer(rawVal, ctx));
1256
+ const parseGroupWidget = parseWidget(constant("group"), (rawVal, ctx) => parsePerseusRenderer(rawVal, ctx));
1503
1257
 
1504
1258
  const parseIframeWidget = parseWidget(constant("iframe"), object({
1505
1259
  url: string,
@@ -1528,11 +1282,7 @@ const stringToNumber = (rawValue, ctx) => {
1528
1282
  function emptyToZero(x) {
1529
1283
  return x === "" ? 0 : x;
1530
1284
  }
1531
- const imageDimensionToNumber = pipeParsers(union(number).or(string).parser)
1532
- // In this specific case, empty string is equivalent to zero. An empty
1533
- // string parses to either NaN (using parseInt) or 0 (using unary +) and
1534
- // CSS will treat NaN as invalid and default to 0 instead.
1535
- .then(convert(emptyToZero)).then(stringToNumber).parser;
1285
+ const imageDimensionToNumber = pipeParsers(union(number).or(string).parser).then(convert(emptyToZero)).then(stringToNumber).parser;
1536
1286
  const dimensionOrUndefined = defaulted(imageDimensionToNumber, () => undefined);
1537
1287
  const parsePerseusImageBackground = object({
1538
1288
  url: optional(nullable(string)),
@@ -1573,10 +1323,6 @@ const parseInputNumberWidget = parseWidget(constant("input-number"), object({
1573
1323
  rightAlign: optional(boolean),
1574
1324
  simplify: enumeration("required", "optional", "enforced"),
1575
1325
  size: enumeration("normal", "small"),
1576
- // TODO(benchristel): there are some content items where value is a
1577
- // boolean, even though that makes no sense. We should figure out if
1578
- // those content items are actually published anywhere, and consider
1579
- // updating them.
1580
1326
  value: union(number).or(string).or(booleanToString).parser,
1581
1327
  customKeypad: optional(boolean)
1582
1328
  }));
@@ -1710,270 +1456,8 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
1710
1456
  elements: array(discriminatedUnionOn("type").withBranch("function", parseFunctionElement).withBranch("label", parseLabelElement).withBranch("line", parseLineElement).withBranch("movable-line", parseMovableLineElement).withBranch("movable-point", parseMovablePointElement).withBranch("parametric", parseParametricElement).withBranch("point", parsePointElement).withBranch("rectangle", parseRectangleElement).parser)
1711
1457
  }));
1712
1458
 
1713
- /**
1714
- * The Perseus "data schema" file.
1715
- *
1716
- * This file, and the types in it, represents the "data schema" that Perseus
1717
- * uses. The @khanacademy/perseus-editor package edits and produces objects
1718
- * that conform to the types in this file. Similarly, the top-level renderers
1719
- * in @khanacademy/perseus, consume objects that conform to these types.
1720
- *
1721
- * WARNING: This file should not import any types from elsewhere so that it is
1722
- * easy to reason about changes that alter the Perseus schema. This helps
1723
- * ensure that it is not changed accidentally when upgrading a dependant
1724
- * package or other part of Perseus code. Note that TypeScript does type
1725
- * checking via something called "structural typing". This means that as long
1726
- * as the shape of a type matches, the name it goes by doesn't matter. As a
1727
- * result, a `Coord` type that looks like this `[x: number, y: number]` is
1728
- * _identical_, in TypeScript's eyes, to this `Vector2` type `[x: number, y:
1729
- * number]`. Also, with tuples, the labels for each entry is ignored, so `[x:
1730
- * number, y: number]` is compatible with `[min: number, max: number]`. The
1731
- * labels are for humans, not TypeScript. :)
1732
- *
1733
- * If you make changes to types in this file, be very sure that:
1734
- *
1735
- * a) the changes are backwards compatible. If they are not, old data from
1736
- * previous versions of the "schema" could become unrenderable, or worse,
1737
- * introduce hard-to-diagnose bugs.
1738
- * b) the parsing code (`util/parse-perseus-json/`) is updated to handle
1739
- * the new format _as well as_ the old format.
1740
- */
1741
-
1742
- // TODO(FEI-4010): Remove `Perseus` prefix for all types here
1743
-
1744
- // Same name as Mafs
1745
-
1746
- /**
1747
- * A utility type that constructs a widget map from a "registry interface".
1748
- * The keys of the registry should be the widget type (aka, "categorizer" or
1749
- * "radio", etc) and the value should be the option type stored in the value
1750
- * of the map.
1751
- *
1752
- * You can think of this as a type that generates another type. We use
1753
- * "registry interfaces" as a way to keep a set of widget types to their data
1754
- * type in several places in Perseus. This type then allows us to generate a
1755
- * map type that maps a widget id to its data type and keep strong typing by
1756
- * widget id.
1757
- *
1758
- * For example, given a fictitious registry such as this:
1759
- *
1760
- * ```
1761
- * interface DummyRegistry {
1762
- * categorizer: { categories: ReadonlyArray<string> };
1763
- * dropdown: { choices: ReadonlyArray<string> }:
1764
- * }
1765
- * ```
1766
- *
1767
- * If we create a DummyMap using this helper:
1768
- *
1769
- * ```
1770
- * type DummyMap = MakeWidgetMap<DummyRegistry>;
1771
- * ```
1772
- *
1773
- * We'll get a map that looks like this:
1774
- *
1775
- * ```
1776
- * type DummyMap = {
1777
- * `categorizer ${number}`: { categories: ReadonlyArray<string> };
1778
- * `dropdown ${number}`: { choices: ReadonlyArray<string> };
1779
- * }
1780
- * ```
1781
- *
1782
- * We use interfaces for the registries so that they can be extended in cases
1783
- * where the consuming app brings along their own widgets. Interfaces in
1784
- * TypeScript are always open (ie. you can extend them) whereas types aren't.
1785
- */
1786
-
1787
- /**
1788
- * Our core set of Perseus widgets.
1789
- *
1790
- * This interface is the basis for "registering" all Perseus widget types.
1791
- * There should be one key/value pair for each supported widget. If you create
1792
- * a new widget, an entry should be added to this interface. Note that this
1793
- * only registers the widget options type, you'll also need to register the
1794
- * widget so that it's available at runtime (@see
1795
- * {@link file://./widgets.ts#registerWidget}).
1796
- *
1797
- * Importantly, the key should be the name that is used in widget IDs. For most
1798
- * widgets that is the same as the widget option's `type` field. In cases where
1799
- * a widget has been deprecated and replaced with the deprecated-standin
1800
- * widget, it should be the original widget type!
1801
- *
1802
- * If you define the widget outside of this package, you can still add the new
1803
- * widget to this interface by writing the following in that package that
1804
- * contains the widget. TypeScript will merge that definition of the
1805
- * `PerseusWidgets` with the one defined below.
1806
- *
1807
- * ```typescript
1808
- * declare module "@khanacademy/perseus-core" {
1809
- * interface PerseusWidgetTypes {
1810
- * // A new widget
1811
- * "new-awesomeness": MyAwesomeNewWidget;
1812
- *
1813
- * // A deprecated widget
1814
- * "super-old-widget": DeprecatedStandinWidget;
1815
- * }
1816
- * }
1817
- *
1818
- * // The new widget's options definition
1819
- * type MyAwesomeNewWidget = WidgetOptions<'new-awesomeness', MyAwesomeNewWidgetOptions>;
1820
- *
1821
- * // The deprecated widget's options definition
1822
- * type SuperOldWidget = WidgetOptions<'super-old-widget', object>;
1823
- * ```
1824
- *
1825
- * This interface can be extended through the magic of TypeScript "Declaration
1826
- * merging". Specifically, we augment this module and extend this interface.
1827
- *
1828
- * @see {@link https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation}
1829
- */
1830
-
1831
- /**
1832
- * A map of widget IDs to widget options. This is most often used as the type
1833
- * for a set of widgets defined in a `PerseusItem` but can also be useful to
1834
- * represent a function parameter where only `widgets` from a `PerseusItem` are
1835
- * needed. Today Widget IDs are made up of the widget type and an incrementing
1836
- * integer (eg. `interactive-graph 1` or `radio 3`). It is suggested to avoid
1837
- * reading/parsing the widget id to derive any information from it, except in
1838
- * the case of this map.
1839
- *
1840
- * @see {@link PerseusWidgetTypes} additional widgets can be added to this map type
1841
- * by augmenting the PerseusWidgetTypes with new widget types!
1842
- */
1843
-
1844
- /**
1845
- * PerseusWidget is a union of all the different types of widget options that
1846
- * Perseus knows about.
1847
- *
1848
- * Thanks to it being based on PerseusWidgetTypes interface, this union is
1849
- * automatically extended to include widgets used in tests without those widget
1850
- * option types seeping into our production types.
1851
- *
1852
- * @see MockWidget for an example
1853
- */
1854
-
1855
- /**
1856
- * A "PerseusItem" is a classic Perseus item. It is rendered by the
1857
- * `ServerItemRenderer` and the layout is pre-set.
1858
- *
1859
- * To render more complex Perseus items, see the `Item` type in the multi item
1860
- * area.
1861
- */
1862
-
1863
- /**
1864
- * A "PerseusArticle" is an item that is meant to be rendered as an article.
1865
- * This item is never scored and is rendered by the `ArticleRenderer`.
1866
- */
1867
-
1868
- const ItemExtras = [
1869
- // The user might benefit from using a Scientific Calculator. Provided on Khan Academy when true
1870
- "calculator",
1871
- // The user might benefit from using a statistics Chi Squared Table like https://people.richland.edu/james/lecture/m170/tbl-chi.html
1872
- "chi2Table",
1873
- // The user might benefit from a monthly payments calculator. Provided on Khan Academy when true
1874
- "financialCalculatorMonthlyPayment",
1875
- // The user might benefit from a total amount calculator. Provided on Khan Academy when true
1876
- "financialCalculatorTotalAmount",
1877
- // The user might benefit from a time to pay off calculator. Provided on Khan Academy when true
1878
- "financialCalculatorTimeToPayOff",
1879
- // The user might benefit from using a Periodic Table of Elements. Provided on Khan Academy when true
1880
- "periodicTable",
1881
- // The user might benefit from using a Periodic Table of Elements with key. Provided on Khan Academy when true
1882
- "periodicTableWithKey",
1883
- // The user might benefit from using a statistics T Table like https://www.statisticshowto.com/tables/t-distribution-table/
1884
- "tTable",
1885
- // The user might benefit from using a statistics Z Table like https://www.ztable.net/
1886
- "zTable"];
1887
-
1888
- /**
1889
- * The type representing the common structure of all widget's options. The
1890
- * `Options` generic type represents the widget-specific option data.
1891
- */
1892
-
1893
- // prettier-ignore
1894
-
1895
- // prettier-ignore
1896
-
1897
- // prettier-ignore
1898
-
1899
- // prettier-ignore
1900
-
1901
- // prettier-ignore
1902
-
1903
- // prettier-ignore
1904
-
1905
- // prettier-ignore
1906
-
1907
- // prettier-ignore
1908
-
1909
- // prettier-ignore
1910
-
1911
- // prettier-ignore
1912
-
1913
- // prettier-ignore
1914
-
1915
- // prettier-ignore
1916
-
1917
- // prettier-ignore
1918
-
1919
- // prettier-ignore
1920
-
1921
- // prettier-ignore
1922
-
1923
- // prettier-ignore
1924
-
1925
- // prettier-ignore
1926
-
1927
- // prettier-ignore
1928
-
1929
- // prettier-ignore
1930
-
1931
- // prettier-ignore
1932
-
1933
- // prettier-ignore
1934
-
1935
- // prettier-ignore
1936
-
1937
- // prettier-ignore
1938
-
1939
- // prettier-ignore
1940
-
1941
- // prettier-ignore
1942
-
1943
- // prettier-ignore
1944
-
1945
- // prettier-ignore
1946
-
1947
- // prettier-ignore
1948
-
1949
- // prettier-ignore
1950
-
1951
- // prettier-ignore
1952
-
1953
- // prettier-ignore
1954
-
1955
- // prettier-ignore
1956
-
1957
- // prettier-ignore
1958
-
1959
- //prettier-ignore
1960
-
1961
- /**
1962
- * A background image applied to various widgets.
1963
- */
1964
-
1965
- /**
1966
- * The type of markings to display on the graph.
1967
- * - axes: shows the axes without the gride lines
1968
- * - graph: shows the axes and the grid lines
1969
- * - grid: shows only the grid lines
1970
- * - none: shows no markings
1971
- */
1972
-
1459
+ const ItemExtras = ["calculator", "chi2Table", "financialCalculatorMonthlyPayment", "financialCalculatorTotalAmount", "financialCalculatorTimeToPayOff", "periodicTable", "periodicTableWithKey", "tTable", "zTable"];
1973
1460
  const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
1974
-
1975
- // 2D range: xMin, xMax, yMin, yMax
1976
-
1977
1461
  const lockedFigureColorNames = ["blue", "green", "grayH", "purple", "pink", "orange", "red"];
1978
1462
  const lockedFigureColors = {
1979
1463
  blue: "#3D7586",
@@ -1990,21 +1474,8 @@ const lockedFigureFillStyles = {
1990
1474
  translucent: 0.4,
1991
1475
  solid: 1
1992
1476
  };
1993
-
1994
- // Not associated with a specific figure
1995
-
1996
- /**
1997
- * Determines how unsimplified fractions are scored.
1998
- *
1999
- * - "required" means unsimplified fractions are considered invalid input, and
2000
- * the learner can try again.
2001
- * - "enforced" means unsimplified fractions are marked incorrect.
2002
- * - "optional" means unsimplified fractions are accepted.
2003
- */
2004
-
2005
1477
  const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
2006
1478
 
2007
- // Used to represent 2-D points and ranges
2008
1479
  const pairOfNumbers = pair(number, number);
2009
1480
  const parsePerseusGraphTypeAngle = object({
2010
1481
  type: constant("angle"),
@@ -2024,22 +1495,18 @@ const parsePerseusGraphTypeCircle = object({
2024
1495
  center: pairOfNumbers,
2025
1496
  radius: number
2026
1497
  })),
2027
- // TODO: remove coord? it's legacy.
2028
1498
  coord: optional(pairOfNumbers)
2029
1499
  });
2030
1500
  const parsePerseusGraphTypeLinear = object({
2031
1501
  type: constant("linear"),
2032
1502
  coords: optional(nullable(pair(pairOfNumbers, pairOfNumbers))),
2033
1503
  startCoords: optional(pair(pairOfNumbers, pairOfNumbers)),
2034
- // TODO: remove coord? it's legacy.
2035
1504
  coord: optional(pairOfNumbers)
2036
1505
  });
2037
1506
  const parsePerseusGraphTypeLinearSystem = object({
2038
1507
  type: constant("linear-system"),
2039
- // TODO(benchristel): default coords to empty array?
2040
1508
  coords: optional(nullable(array(pair(pairOfNumbers, pairOfNumbers)))),
2041
1509
  startCoords: optional(array(pair(pairOfNumbers, pairOfNumbers))),
2042
- // TODO: remove coord? it's legacy.
2043
1510
  coord: optional(pairOfNumbers)
2044
1511
  });
2045
1512
  const parsePerseusGraphTypeNone = object({
@@ -2050,7 +1517,6 @@ const parsePerseusGraphTypePoint = object({
2050
1517
  numPoints: optional(union(number).or(constant("unlimited")).parser),
2051
1518
  coords: optional(nullable(array(pairOfNumbers))),
2052
1519
  startCoords: optional(array(pairOfNumbers)),
2053
- // TODO: remove coord? it's legacy.
2054
1520
  coord: optional(pairOfNumbers)
2055
1521
  });
2056
1522
  const parsePerseusGraphTypePolygon = object({
@@ -2061,37 +1527,31 @@ const parsePerseusGraphTypePolygon = object({
2061
1527
  snapTo: optional(enumeration("grid", "angles", "sides")),
2062
1528
  match: optional(enumeration("similar", "congruent", "approx", "exact")),
2063
1529
  startCoords: optional(array(pairOfNumbers)),
2064
- // TODO: remove coord? it's legacy.
2065
1530
  coord: optional(pairOfNumbers)
2066
1531
  });
2067
1532
  const parsePerseusGraphTypeQuadratic = object({
2068
1533
  type: constant("quadratic"),
2069
1534
  coords: optional(nullable(trio(pairOfNumbers, pairOfNumbers, pairOfNumbers))),
2070
1535
  startCoords: optional(trio(pairOfNumbers, pairOfNumbers, pairOfNumbers)),
2071
- // TODO: remove coord? it's legacy.
2072
1536
  coord: optional(pairOfNumbers)
2073
1537
  });
2074
1538
  const parsePerseusGraphTypeRay = object({
2075
1539
  type: constant("ray"),
2076
1540
  coords: optional(nullable(pair(pairOfNumbers, pairOfNumbers))),
2077
1541
  startCoords: optional(pair(pairOfNumbers, pairOfNumbers)),
2078
- // TODO: remove coord? it's legacy.
2079
1542
  coord: optional(pairOfNumbers)
2080
1543
  });
2081
1544
  const parsePerseusGraphTypeSegment = object({
2082
1545
  type: constant("segment"),
2083
- // TODO(benchristel): default numSegments?
2084
1546
  numSegments: optional(number),
2085
1547
  coords: optional(nullable(array(pair(pairOfNumbers, pairOfNumbers)))),
2086
1548
  startCoords: optional(array(pair(pairOfNumbers, pairOfNumbers))),
2087
- // TODO: remove coord? it's legacy.
2088
1549
  coord: optional(pairOfNumbers)
2089
1550
  });
2090
1551
  const parsePerseusGraphTypeSinusoid = object({
2091
1552
  type: constant("sinusoid"),
2092
1553
  coords: optional(nullable(array(pairOfNumbers))),
2093
1554
  startCoords: optional(array(pairOfNumbers)),
2094
- // TODO: remove coord? it's legacy.
2095
1555
  coord: optional(pairOfNumbers)
2096
1556
  });
2097
1557
  const parsePerseusGraphType = discriminatedUnionOn("type").withBranch("angle", parsePerseusGraphTypeAngle).withBranch("circle", parsePerseusGraphTypeCircle).withBranch("linear", parsePerseusGraphTypeLinear).withBranch("linear-system", parsePerseusGraphTypeLinearSystem).withBranch("none", parsePerseusGraphTypeNone).withBranch("point", parsePerseusGraphTypePoint).withBranch("polygon", parsePerseusGraphTypePolygon).withBranch("quadratic", parsePerseusGraphTypeQuadratic).withBranch("ray", parsePerseusGraphTypeRay).withBranch("segment", parsePerseusGraphTypeSegment).withBranch("sinusoid", parsePerseusGraphTypeSinusoid).parser;
@@ -2110,7 +1570,6 @@ const parseLockedPointType = object({
2110
1570
  coord: pairOfNumbers,
2111
1571
  color: parseLockedFigureColor,
2112
1572
  filled: boolean,
2113
- // TODO(benchristel): default labels to empty array?
2114
1573
  labels: optional(array(parseLockedLabelType)),
2115
1574
  ariaLabel: optional(string)
2116
1575
  });
@@ -2122,7 +1581,6 @@ const parseLockedLineType = object({
2122
1581
  lineStyle: parseLockedLineStyle,
2123
1582
  showPoint1: defaulted(boolean, () => false),
2124
1583
  showPoint2: defaulted(boolean, () => false),
2125
- // TODO(benchristel): default labels to empty array?
2126
1584
  labels: optional(array(parseLockedLabelType)),
2127
1585
  ariaLabel: optional(string)
2128
1586
  });
@@ -2130,7 +1588,6 @@ const parseLockedVectorType = object({
2130
1588
  type: constant("vector"),
2131
1589
  points: pair(pairOfNumbers, pairOfNumbers),
2132
1590
  color: parseLockedFigureColor,
2133
- // TODO(benchristel): default labels to empty array?
2134
1591
  labels: optional(array(parseLockedLabelType)),
2135
1592
  ariaLabel: optional(string)
2136
1593
  });
@@ -2142,7 +1599,6 @@ const parseLockedEllipseType = object({
2142
1599
  color: parseLockedFigureColor,
2143
1600
  fillStyle: parseLockedFigureFillType,
2144
1601
  strokeStyle: parseLockedLineStyle,
2145
- // TODO(benchristel): default labels to empty array?
2146
1602
  labels: optional(array(parseLockedLabelType)),
2147
1603
  ariaLabel: optional(string)
2148
1604
  });
@@ -2153,12 +1609,9 @@ const parseLockedPolygonType = object({
2153
1609
  showVertices: boolean,
2154
1610
  fillStyle: parseLockedFigureFillType,
2155
1611
  strokeStyle: parseLockedLineStyle,
2156
- // TODO(benchristel): default labels to empty array?
2157
1612
  labels: optional(array(parseLockedLabelType)),
2158
1613
  ariaLabel: optional(string)
2159
1614
  });
2160
-
2161
- // Exported for testing.
2162
1615
  const parseLockedFunctionDomain = defaulted(pair(defaulted(number, () => -Infinity), defaulted(number, () => Infinity)), () => [-Infinity, Infinity]);
2163
1616
  const parseLockedFunctionType = object({
2164
1617
  type: constant("function"),
@@ -2167,17 +1620,12 @@ const parseLockedFunctionType = object({
2167
1620
  equation: string,
2168
1621
  directionalAxis: enumeration("x", "y"),
2169
1622
  domain: parseLockedFunctionDomain,
2170
- // TODO(benchristel): default labels to empty array?
2171
1623
  labels: optional(array(parseLockedLabelType)),
2172
1624
  ariaLabel: optional(string)
2173
1625
  });
2174
1626
  const parseLockedFigure = discriminatedUnionOn("type").withBranch("point", parseLockedPointType).withBranch("line", parseLockedLineType).withBranch("vector", parseLockedVectorType).withBranch("ellipse", parseLockedEllipseType).withBranch("polygon", parseLockedPolygonType).withBranch("function", parseLockedFunctionType).withBranch("label", parseLockedLabelType).parser;
2175
1627
  const parseInteractiveGraphWidget = parseWidget(constant("interactive-graph"), object({
2176
1628
  step: pairOfNumbers,
2177
- // TODO(benchristel): rather than making gridStep and snapStep
2178
- // optional, we should duplicate the defaulting logic from the
2179
- // InteractiveGraph component. See parse-perseus-json/README.md for
2180
- // why.
2181
1629
  gridStep: optional(pairOfNumbers),
2182
1630
  snapStep: optional(pairOfNumbers),
2183
1631
  backgroundImage: optional(parsePerseusImageBackground),
@@ -2189,14 +1637,10 @@ const parseInteractiveGraphWidget = parseWidget(constant("interactive-graph"), o
2189
1637
  rulerLabel: optional(string),
2190
1638
  rulerTicks: optional(number),
2191
1639
  range: pair(pairOfNumbers, pairOfNumbers),
2192
- // NOTE(benchristel): I copied the default graph from
2193
- // interactive-graph.tsx. See the parse-perseus-json/README.md for
2194
- // an explanation of why we want to duplicate the default here.
2195
1640
  graph: defaulted(parsePerseusGraphType, () => ({
2196
1641
  type: "linear"
2197
1642
  })),
2198
1643
  correct: parsePerseusGraphType,
2199
- // TODO(benchristel): default lockedFigures to empty array
2200
1644
  lockedFigures: optional(array(parseLockedFigure)),
2201
1645
  fullGraphLabel: optional(string),
2202
1646
  fullGraphAriaDescription: optional(string)
@@ -2239,9 +1683,6 @@ const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matri
2239
1683
  }));
2240
1684
 
2241
1685
  const parseMeasurerWidget = parseWidget(constant("measurer"), object({
2242
- // The default value for image comes from measurer.tsx.
2243
- // See parse-perseus-json/README.md for why we want to duplicate the
2244
- // defaults here.
2245
1686
  image: defaulted(parsePerseusImageBackground, () => ({
2246
1687
  url: null,
2247
1688
  top: 0,
@@ -2254,7 +1695,6 @@ const parseMeasurerWidget = parseWidget(constant("measurer"), object({
2254
1695
  rulerPixels: number,
2255
1696
  rulerLength: number,
2256
1697
  box: pair(number, number),
2257
- // TODO(benchristel): static is not used. Remove it?
2258
1698
  static: defaulted(boolean, () => false)
2259
1699
  }));
2260
1700
 
@@ -2273,9 +1713,6 @@ const parseNumberLineWidget = parseWidget(constant("number-line"), object({
2273
1713
  isTickCtrl: optional(nullable(boolean)),
2274
1714
  divisionRange: array(number),
2275
1715
  numDivisions: optional(nullable(number)),
2276
- // NOTE(benchristel): I copied the default snapDivisions from
2277
- // number-line.tsx. See the parse-perseus-json/README.md for
2278
- // an explanation of why we want to duplicate the default here.
2279
1716
  snapDivisions: defaulted(number, () => 2),
2280
1717
  tickStep: optional(nullable(number)),
2281
1718
  correctRel: optional(nullable(string)),
@@ -2293,10 +1730,6 @@ function deprecatedSimplifyValuesToRequired(simplify) {
2293
1730
  case "required":
2294
1731
  case "optional":
2295
1732
  return simplify;
2296
- // NOTE(benchristel): "accepted", "correct", true, false, undefined, and
2297
- // null are all treated the same as "required" during scoring, so we
2298
- // convert them to "required" here to preserve behavior. See the tests
2299
- // in score-numeric-input.test.ts
2300
1733
  default:
2301
1734
  return "required";
2302
1735
  }
@@ -2304,17 +1737,11 @@ function deprecatedSimplifyValuesToRequired(simplify) {
2304
1737
  const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
2305
1738
  answers: array(object({
2306
1739
  message: string,
2307
- // TODO(benchristel): value should never be null or undefined,
2308
- // but we have some content where it is anyway. If we backfill
2309
- // the data, simplify this.
2310
1740
  value: optional(nullable(number)),
2311
1741
  status: string,
2312
1742
  answerForms: defaulted(array(parseMathFormat), () => undefined),
2313
1743
  strict: boolean,
2314
1744
  maxError: optional(nullable(number)),
2315
- // TODO(benchristel): simplify should never be a boolean, but we
2316
- // have some content where it is anyway. If we ever backfill
2317
- // the data, we should simplify `simplify`.
2318
1745
  simplify: parseSimplify
2319
1746
  })),
2320
1747
  labelText: optional(string),
@@ -2328,9 +1755,6 @@ const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
2328
1755
  })))
2329
1756
  }));
2330
1757
 
2331
- // There is an import cycle between orderer-widget.ts and perseus-renderer.ts.
2332
- // This wrapper ensures that we don't refer to parsePerseusRenderer before
2333
- // it's defined.
2334
1758
  function parseRenderer(rawValue, ctx) {
2335
1759
  return parsePerseusRenderer(rawValue, ctx);
2336
1760
  }
@@ -2372,23 +1796,14 @@ const parsePlotterWidget = parseWidget(constant("plotter"), object({
2372
1796
  categories: array(string),
2373
1797
  type: enumeration(...plotterPlotTypes),
2374
1798
  maxY: number,
2375
- // The default value for scaleY comes from plotter.tsx.
2376
- // See parse-perseus-json/README.md for why we want to duplicate the
2377
- // defaults here.
2378
1799
  scaleY: defaulted(number, () => 1),
2379
1800
  labelInterval: optional(nullable(number)),
2380
- // The default value for snapsPerLine comes from plotter.tsx.
2381
- // See parse-perseus-json/README.md for why we want to duplicate the
2382
- // defaults here.
2383
1801
  snapsPerLine: defaulted(number, () => 2),
2384
1802
  starting: array(number),
2385
1803
  correct: array(number),
2386
1804
  picUrl: optional(nullable(string)),
2387
1805
  picSize: optional(nullable(number)),
2388
1806
  picBoxHeight: optional(nullable(number)),
2389
- // NOTE(benchristel): I copied the default plotDimensions from
2390
- // plotter.tsx. See the parse-perseus-json/README.md for an explanation
2391
- // of why we want to duplicate the defaults here.
2392
1807
  plotDimensions: defaulted(array(number), () => [380, 300])
2393
1808
  }));
2394
1809
 
@@ -2397,6 +1812,7 @@ const parsePythonProgramWidget = parseWidget(constant("python-program"), object(
2397
1812
  height: number
2398
1813
  }));
2399
1814
 
1815
+ const _excluded$a = ["noneOfTheAbove"];
2400
1816
  const currentVersion$3 = {
2401
1817
  major: 2,
2402
1818
  minor: 0
@@ -2406,28 +1822,26 @@ function deriveNumCorrect(options) {
2406
1822
  choices,
2407
1823
  numCorrect
2408
1824
  } = options;
2409
- return numCorrect ?? choices.filter(c => c.correct).length;
1825
+ return numCorrect != null ? numCorrect : choices.filter(c => c.correct).length;
2410
1826
  }
2411
1827
  const widgetOptionsUpgrades$2 = {
2412
1828
  "2": v1props => {
2413
- const upgraded = {
2414
- ...v1props,
1829
+ const upgraded = _extends({}, v1props, {
2415
1830
  numCorrect: deriveNumCorrect(v1props)
2416
- };
1831
+ });
2417
1832
  return upgraded;
2418
1833
  },
2419
1834
  "1": v0props => {
2420
1835
  const {
2421
- noneOfTheAbove,
2422
- ...rest
2423
- } = v0props;
1836
+ noneOfTheAbove
1837
+ } = v0props,
1838
+ rest = _objectWithoutPropertiesLoose(v0props, _excluded$a);
2424
1839
  if (noneOfTheAbove) {
2425
1840
  throw new Error("radio widget v0 no longer supports auto noneOfTheAbove");
2426
1841
  }
2427
- return {
2428
- ...rest,
1842
+ return _extends({}, rest, {
2429
1843
  hasNoneOfTheAbove: false
2430
- };
1844
+ });
2431
1845
  }
2432
1846
  };
2433
1847
  const defaultWidgetOptions$v = {
@@ -2440,6 +1854,7 @@ const defaultWidgetOptions$v = {
2440
1854
  deselectEnabled: false
2441
1855
  };
2442
1856
 
1857
+ const _excluded$9 = ["noneOfTheAbove"];
2443
1858
  const version2 = optional(object({
2444
1859
  major: constant(2),
2445
1860
  minor: number
@@ -2451,10 +1866,6 @@ const parseRadioWidgetV2 = parseWidgetWithVersion(version2, constant("radio"), o
2451
1866
  clue: optional(string),
2452
1867
  correct: optional(boolean),
2453
1868
  isNoneOfTheAbove: optional(boolean),
2454
- // deprecated
2455
- // There is an import cycle between radio-widget.ts and
2456
- // widgets-map.ts. The anonymous function below ensures that we
2457
- // don't refer to parseWidgetsMap before it's defined.
2458
1869
  widgets: optional((rawVal, ctx) => parseWidgetsMap(rawVal, ctx))
2459
1870
  })),
2460
1871
  hasNoneOfTheAbove: optional(boolean),
@@ -2462,12 +1873,8 @@ const parseRadioWidgetV2 = parseWidgetWithVersion(version2, constant("radio"), o
2462
1873
  randomize: optional(boolean),
2463
1874
  multipleSelect: optional(boolean),
2464
1875
  deselectEnabled: optional(boolean),
2465
- // deprecated
2466
1876
  onePerLine: optional(boolean),
2467
- // deprecated
2468
1877
  displayCount: optional(any),
2469
- // v0 props
2470
- // `noneOfTheAbove` is still in use (but only set to `false`).
2471
1878
  noneOfTheAbove: optional(constant(false))
2472
1879
  }));
2473
1880
  const version1 = optional(object({
@@ -2480,10 +1887,6 @@ const parseRadioWidgetV1 = parseWidgetWithVersion(version1, constant("radio"), o
2480
1887
  clue: optional(string),
2481
1888
  correct: optional(boolean),
2482
1889
  isNoneOfTheAbove: optional(boolean),
2483
- // deprecated
2484
- // There is an import cycle between radio-widget.ts and
2485
- // widgets-map.ts. The anonymous function below ensures that we
2486
- // don't refer to parseWidgetsMap before it's defined.
2487
1890
  widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => undefined)
2488
1891
  })),
2489
1892
  hasNoneOfTheAbove: optional(boolean),
@@ -2491,29 +1894,23 @@ const parseRadioWidgetV1 = parseWidgetWithVersion(version1, constant("radio"), o
2491
1894
  randomize: optional(boolean),
2492
1895
  multipleSelect: optional(boolean),
2493
1896
  deselectEnabled: optional(boolean),
2494
- // deprecated
2495
1897
  onePerLine: optional(boolean),
2496
- // deprecated
2497
1898
  displayCount: optional(any),
2498
- // v0 props
2499
- // `noneOfTheAbove` is still in use (but only set to `false`).
2500
1899
  noneOfTheAbove: optional(constant(false))
2501
1900
  }));
2502
1901
  function migrateV1ToV2(widget) {
2503
1902
  const {
2504
1903
  options
2505
1904
  } = widget;
2506
- return {
2507
- ...widget,
1905
+ return _extends({}, widget, {
2508
1906
  version: {
2509
1907
  major: 2,
2510
1908
  minor: 0
2511
1909
  },
2512
- options: {
2513
- ...options,
1910
+ options: _extends({}, options, {
2514
1911
  numCorrect: deriveNumCorrect(options)
2515
- }
2516
- };
1912
+ })
1913
+ });
2517
1914
  }
2518
1915
  const version0 = optional(object({
2519
1916
  major: constant(0),
@@ -2525,10 +1922,6 @@ const parseRadioWidgetV0 = parseWidgetWithVersion(version0, constant("radio"), o
2525
1922
  clue: optional(string),
2526
1923
  correct: optional(boolean),
2527
1924
  isNoneOfTheAbove: optional(boolean),
2528
- // deprecated
2529
- // There is an import cycle between radio-widget.ts and
2530
- // widgets-map.ts. The anonymous function below ensures that we
2531
- // don't refer to parseWidgetsMap before it's defined.
2532
1925
  widgets: optional((rawVal, ctx) => parseWidgetsMap(rawVal, ctx))
2533
1926
  })),
2534
1927
  hasNoneOfTheAbove: optional(boolean),
@@ -2536,33 +1929,24 @@ const parseRadioWidgetV0 = parseWidgetWithVersion(version0, constant("radio"), o
2536
1929
  randomize: optional(boolean),
2537
1930
  multipleSelect: optional(boolean),
2538
1931
  deselectEnabled: optional(boolean),
2539
- // deprecated
2540
1932
  onePerLine: optional(boolean),
2541
- // deprecated
2542
1933
  displayCount: optional(any),
2543
- // v0 props
2544
- // `noneOfTheAbove` is still in use (but only set to `false`).
2545
1934
  noneOfTheAbove: optional(constant(false))
2546
1935
  }));
2547
1936
  function migrateV0ToV1(widget) {
2548
1937
  const {
2549
1938
  options
2550
1939
  } = widget;
2551
- const {
2552
- noneOfTheAbove: _,
2553
- ...rest
2554
- } = options;
2555
- return {
2556
- ...widget,
1940
+ const rest = _objectWithoutPropertiesLoose(options, _excluded$9);
1941
+ return _extends({}, widget, {
2557
1942
  version: {
2558
1943
  major: 1,
2559
1944
  minor: 0
2560
1945
  },
2561
- options: {
2562
- ...rest,
1946
+ options: _extends({}, rest, {
2563
1947
  hasNoneOfTheAbove: false
2564
- }
2565
- };
1948
+ })
1949
+ });
2566
1950
  }
2567
1951
  const parseRadioWidget = versionedWidgetOptions(2, parseRadioWidgetV2).withMigrationFrom(1, parseRadioWidgetV1, migrateV1ToV2).withMigrationFrom(0, parseRadioWidgetV0, migrateV0ToV1).parser;
2568
1952
 
@@ -2590,9 +1974,6 @@ const parseWidgetsMap = (rawValue, ctx) => {
2590
1974
  }
2591
1975
  const widgetsMap = {};
2592
1976
  for (const key of Object.keys(rawValue)) {
2593
- // parseWidgetsMapEntry modifies the widgetsMap. This is kind of gross,
2594
- // but it's the only way I could find to make TypeScript check the key
2595
- // against the widget type.
2596
1977
  const entryResult = parseWidgetsMapEntry([key, rawValue[key]], widgetsMap, ctx.forSubtree(key));
2597
1978
  if (isFailure(entryResult)) {
2598
1979
  return entryResult;
@@ -2600,8 +1981,7 @@ const parseWidgetsMap = (rawValue, ctx) => {
2600
1981
  }
2601
1982
  return ctx.success(widgetsMap);
2602
1983
  };
2603
- const parseWidgetsMapEntry = (_ref, widgetMap, ctx) => {
2604
- let [id, widget] = _ref;
1984
+ const parseWidgetsMapEntry = ([id, widget], widgetMap, ctx) => {
2605
1985
  const idComponentsResult = parseWidgetIdComponents(id.split(" "), ctx.forSubtree("(widget ID)"));
2606
1986
  if (isFailure(idComponentsResult)) {
2607
1987
  return idComponentsResult;
@@ -2667,9 +2047,6 @@ const parseWidgetsMapEntry = (_ref, widgetMap, ctx) => {
2667
2047
  case "passage-ref":
2668
2048
  return parseAndAssign(`passage-ref ${n}`, parsePassageRefWidget);
2669
2049
  case "passage-ref-target":
2670
- // NOTE(benchristel): as of 2024-11-12, passage-ref-target is only
2671
- // used in test content. See:
2672
- // https://www.khanacademy.org/devadmin/content/search?query=widget:passage-ref-target
2673
2050
  return parseAndAssign(`passage-ref-target ${n}`, any);
2674
2051
  case "phet-simulation":
2675
2052
  return parseAndAssign(`phet-simulation ${n}`, parsePhetSimulationWidget);
@@ -2686,8 +2063,6 @@ const parseWidgetsMapEntry = (_ref, widgetMap, ctx) => {
2686
2063
  case "video":
2687
2064
  return parseAndAssign(`video ${n}`, parseVideoWidget);
2688
2065
  case "sequence":
2689
- // sequence is a deprecated widget type, and the corresponding
2690
- // widget component no longer exists.
2691
2066
  return parseAndAssign(`sequence ${n}`, parseDeprecatedWidget);
2692
2067
  case "lights-puzzle":
2693
2068
  return parseAndAssign(`lights-puzzle ${n}`, parseDeprecatedWidget);
@@ -2699,16 +2074,8 @@ const parseWidgetsMapEntry = (_ref, widgetMap, ctx) => {
2699
2074
  return parseAndAssign(`${type} ${n}`, parseWidget(constant(type), any));
2700
2075
  }
2701
2076
  };
2702
- const parseDeprecatedWidget = parseWidget(
2703
- // Ignore the incoming widget type and hardcode "deprecated-standin"
2704
- (_, ctx) => ctx.success("deprecated-standin"),
2705
- // Allow any widget options
2706
- object({}));
2077
+ const parseDeprecatedWidget = parseWidget((_, ctx) => ctx.success("deprecated-standin"), object({}));
2707
2078
  const parseStringToNonNegativeInt = (rawValue, ctx) => {
2708
- // The article renderer seems to allow the numeric part of a widget ID to
2709
- // be 0, at least for image widgets. However, if widget IDs in an exercise
2710
- // contain 0, the exercise renderer will blow up. We allow 0 here for
2711
- // compatibility with articles.
2712
2079
  if (typeof rawValue !== "string" || !/^(0|[1-9][0-9]*)$/.test(rawValue)) {
2713
2080
  return ctx.failure("a string representing a non-negative integer", rawValue);
2714
2081
  }
@@ -2717,20 +2084,11 @@ const parseStringToNonNegativeInt = (rawValue, ctx) => {
2717
2084
  const parseWidgetIdComponents = pair(string, parseStringToNonNegativeInt);
2718
2085
 
2719
2086
  const parsePerseusRenderer = defaulted(object({
2720
- // TODO(benchristel): content is also defaulted to empty string in
2721
- // renderer.tsx. See if we can remove one default or the other.
2722
2087
  content: defaulted(string, () => ""),
2723
- // This module has an import cycle with parseWidgetsMap, because the
2724
- // `group` widget can contain another renderer.
2725
- // The anonymous function below ensures that we don't try to access
2726
- // parseWidgetsMap before it's defined.
2727
2088
  widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
2728
2089
  images: parseImages,
2729
- // deprecated
2730
2090
  metadata: any
2731
- }),
2732
- // Default value
2733
- () => ({
2091
+ }), () => ({
2734
2092
  content: "",
2735
2093
  widgets: {},
2736
2094
  images: {}
@@ -2743,25 +2101,10 @@ const parseHint = object({
2743
2101
  content: string,
2744
2102
  widgets: defaulted(parseWidgetsMap, () => ({})),
2745
2103
  images: parseImages,
2746
- // deprecated
2747
2104
  metadata: any
2748
2105
  });
2749
2106
 
2750
2107
  const parsePerseusAnswerArea = pipeParsers(defaulted(object({}), () => ({}))).then(convert(toAnswerArea)).parser;
2751
-
2752
- // Some answerAreas have extra, bogus fields, like:
2753
- //
2754
- // "answerArea": {
2755
- // "type": "multiple",
2756
- // "options": {},
2757
- // "version": null,
2758
- // "static": false,
2759
- // "graded": false,
2760
- // "alignment": "",
2761
- // }
2762
- //
2763
- // This function filters the fields of an answerArea object, keeping only the
2764
- // known ones, and converts `undefined` and `null` values to `false`.
2765
2108
  function toAnswerArea(raw) {
2766
2109
  return {
2767
2110
  zTable: !!raw.zTable,
@@ -2784,37 +2127,15 @@ const parsePerseusItem$1 = object({
2784
2127
  major: number,
2785
2128
  minor: number
2786
2129
  }))),
2787
- // Deprecated field
2788
2130
  answer: any
2789
2131
  });
2790
2132
 
2791
- /**
2792
- * Helper to parse PerseusItem JSON
2793
- * Why not just use JSON.parse? We want:
2794
- * - To make sure types are correct
2795
- * - To give us a central place to validate/transform output if needed
2796
- * @deprecated - use parseAndMigratePerseusItem instead
2797
- * @param {string} json - the stringified PerseusItem JSON
2798
- * @returns {PerseusItem} the parsed PerseusItem object
2799
- */
2800
2133
  function parsePerseusItem(json) {
2801
- // Try to block a cheating vector which relies on monkey-patching
2802
- // JSON.parse
2803
2134
  if (isRealJSONParse(JSON.parse)) {
2804
2135
  return JSON.parse(json);
2805
2136
  }
2806
2137
  throw new Error("Something went wrong.");
2807
2138
  }
2808
- /**
2809
- * Parses a PerseusItem from a JSON string, migrates old formats to the latest
2810
- * schema, and runtime-typechecks the result. Use this to parse assessmentItem
2811
- * data.
2812
- *
2813
- * @returns a {@link Result} of the parsed PerseusItem. If the result is a
2814
- * failure, it will contain an error message describing where in the tree
2815
- * parsing failed.
2816
- * @throws SyntaxError if the argument is not well-formed JSON.
2817
- */
2818
2139
  function parseAndMigratePerseusItem(json) {
2819
2140
  throwErrorIfCheatingDetected();
2820
2141
  const object = JSON.parse(json);
@@ -2827,16 +2148,6 @@ function parseAndMigratePerseusItem(json) {
2827
2148
  }
2828
2149
  return result;
2829
2150
  }
2830
-
2831
- /**
2832
- * Parses a PerseusArticle from a JSON string, migrates old formats to the
2833
- * latest schema, and runtime-typechecks the result.
2834
- *
2835
- * @returns a {@link Result} of the parsed PerseusArticle. If the result is a
2836
- * failure, it will contain an error message describing where in the tree
2837
- * parsing failed.
2838
- * @throws SyntaxError if the argument is not well-formed JSON.
2839
- */
2840
2151
  function parseAndMigratePerseusArticle(json) {
2841
2152
  throwErrorIfCheatingDetected();
2842
2153
  const object = JSON.parse(json);
@@ -2849,127 +2160,48 @@ function parseAndMigratePerseusArticle(json) {
2849
2160
  }
2850
2161
  return result;
2851
2162
  }
2852
-
2853
- /**
2854
- * Tries to block a cheating vector that relies on monkey-patching JSON.parse.
2855
- */
2856
- // TODO(LEMS-2331): delete this function once server-side scoring is done.
2857
2163
  function throwErrorIfCheatingDetected() {
2858
2164
  if (!isRealJSONParse(JSON.parse)) {
2859
2165
  throw new Error("Something went wrong.");
2860
2166
  }
2861
2167
  }
2862
2168
 
2863
- // This file is processed by a Rollup plugin (replace) to inject the production
2864
2169
  const libName = "@khanacademy/perseus-core";
2865
- const libVersion = "5.4.2";
2866
- perseusUtils.addLibraryVersionToPerseusDebug(libName, libVersion);
2170
+ const libVersion = "6.0.0";
2171
+ addLibraryVersionToPerseusDebug(libName, libVersion);
2867
2172
 
2868
- /**
2869
- * @typedef {Object} Errors utility for referencing the Perseus error taxonomy.
2870
- */
2871
2173
  const Errors = Object.freeze({
2872
- /**
2873
- * @property {ErrorKind} Unknown The kind of error is not known.
2874
- */
2875
2174
  Unknown: "Unknown",
2876
- /**
2877
- * @property {ErrorKind} Internal The error is internal to the executing code.
2878
- */
2879
2175
  Internal: "Internal",
2880
- /**
2881
- * @property {ErrorKind} InvalidInput There was a problem with the provided
2882
- * input, such as the wrong format or a null value.
2883
- */
2884
2176
  InvalidInput: "InvalidInput",
2885
- /**
2886
- * @property {ErrorKind} NotAllowed There was a problem due to the state of
2887
- * the system not matching the requested operation or input. For example,
2888
- * trying to create a username that is valid, but is already taken by
2889
- * another user. Use {@link InvalidInput} instead when the input isn't
2890
- * valid regardless of the state of the system. Use {@link NotFound} when
2891
- * the failure is due to not being able to find a resource.
2892
- */
2893
2177
  NotAllowed: "NotAllowed",
2894
- /**
2895
- * @property {ErrorKind} TransientService There was a problem when making a
2896
- * request to a service.
2897
- */
2898
2178
  TransientService: "TransientService",
2899
- /**
2900
- * @property {ErrorKind} Service There was a non-transient problem when
2901
- * making a request to service.
2902
- */
2903
2179
  Service: "Service"
2904
2180
  });
2905
2181
 
2906
- /**
2907
- * @type {ErrorKind} The kind of error being reported
2908
- */
2909
-
2910
2182
  class PerseusError extends Error {
2911
- kind;
2912
- metadata;
2913
2183
  constructor(message, kind, options) {
2914
2184
  super(message);
2185
+ this.kind = void 0;
2186
+ this.metadata = void 0;
2915
2187
  this.kind = kind;
2916
- this.metadata = options?.metadata;
2188
+ this.metadata = options == null ? void 0 : options.metadata;
2917
2189
  }
2918
2190
  }
2919
2191
 
2920
- /**
2921
- * _ utilities for objects
2922
- */
2923
-
2924
- /**
2925
- * Does a pluck on keys inside objects in an object
2926
- *
2927
- * Ex:
2928
- * tools = {
2929
- * translation: {
2930
- * enabled: true
2931
- * },
2932
- * rotation: {
2933
- * enabled: false
2934
- * }
2935
- * };
2936
- * pluckObject(tools, "enabled") returns {
2937
- * translation: true
2938
- * rotation: false
2939
- * }
2940
- */
2941
- const pluck = function (table, subKey) {
2942
- return ___default["default"].object(___default["default"].map(table, function (value, key) {
2192
+ const pluck = function pluck(table, subKey) {
2193
+ return _.object(_.map(table, function (value, key) {
2943
2194
  return [key, value[subKey]];
2944
2195
  }));
2945
2196
  };
2946
-
2947
- /**
2948
- * Maps an object to an object
2949
- *
2950
- * > mapObject({a: '1', b: '2'}, (value, key) => {
2951
- * return value + 1;
2952
- * });
2953
- * {a: 2, b: 3}
2954
- */
2955
- const mapObject = function (obj, lambda) {
2197
+ const mapObject = function mapObject(obj, lambda) {
2956
2198
  const result = {};
2957
2199
  Object.keys(obj).forEach(key => {
2958
- // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
2959
2200
  result[key] = lambda(obj[key], key);
2960
2201
  });
2961
2202
  return result;
2962
2203
  };
2963
2204
 
2964
- /**
2965
- * For details on the individual options, see the
2966
- * PerseusCategorizerWidgetOptions type
2967
- */
2968
-
2969
- /**
2970
- * Given a PerseusCategorizerWidgetOptions object, return a new object with only
2971
- * the public options that should be exposed to the client.
2972
- */
2973
2205
  function getCategorizerPublicWidgetOptions(options) {
2974
2206
  return {
2975
2207
  items: options.items,
@@ -3024,15 +2256,6 @@ const definitionWidgetLogic = {
3024
2256
  defaultAlignment: "inline"
3025
2257
  };
3026
2258
 
3027
- /**
3028
- * For details on the individual options, see the
3029
- * PerseusDropdownWidgetOptions type
3030
- */
3031
-
3032
- /**
3033
- * Given a PerseusDropdownWidgetOptions object, return a new object with only
3034
- * the public options that should be exposed to the client.
3035
- */
3036
2259
  function getDropdownPublicWidgetOptions(options) {
3037
2260
  return {
3038
2261
  choices: options.choices.map(choice => ({
@@ -3113,15 +2336,6 @@ const defaultWidgetOptions$p = {
3113
2336
  functions: ["f", "g", "h"]
3114
2337
  };
3115
2338
 
3116
- /**
3117
- * For details on the individual options, see the
3118
- * PerseusExpressionWidgetOptions type
3119
- */
3120
-
3121
- /**
3122
- * Given a PerseusExpressionWidgetOptions object, return a new object with only
3123
- * the public options that should be exposed to the client.
3124
- */
3125
2339
  function getExpressionPublicWidgetOptions(options) {
3126
2340
  return {
3127
2341
  buttonSets: options.buttonSets,
@@ -3163,11 +2377,9 @@ const gradedGroupSetWidgetLogic = {
3163
2377
  defaultWidgetOptions: defaultWidgetOptions$n
3164
2378
  };
3165
2379
 
2380
+ const _excluded$8 = ["correct"];
3166
2381
  function getGrapherPublicWidgetOptions(options) {
3167
- const {
3168
- correct: _,
3169
- ...publicOptions
3170
- } = options;
2382
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$8);
3171
2383
  return publicOptions;
3172
2384
  }
3173
2385
 
@@ -3279,11 +2491,9 @@ const interactionWidgetLogic = {
3279
2491
  defaultWidgetOptions: defaultWidgetOptions$h
3280
2492
  };
3281
2493
 
2494
+ const _excluded$7 = ["correct"];
3282
2495
  function getInteractiveGraphPublicWidgetOptions(options) {
3283
- const {
3284
- correct: _,
3285
- ...publicOptions
3286
- } = options;
2496
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$7);
3287
2497
  return publicOptions;
3288
2498
  }
3289
2499
 
@@ -3311,22 +2521,14 @@ const interactiveGraphWidgetLogic = {
3311
2521
  getPublicWidgetOptions: getInteractiveGraphPublicWidgetOptions
3312
2522
  };
3313
2523
 
3314
- /**
3315
- * For details on the individual options, see the
3316
- * PerseusLabelImageWidgetOptions type
3317
- */
3318
-
2524
+ const _excluded$6 = ["answers"];
3319
2525
  function getLabelImagePublicWidgetOptions(options) {
3320
- return {
3321
- ...options,
2526
+ return _extends({}, options, {
3322
2527
  markers: options.markers.map(getLabelImageMarkerPublicData)
3323
- };
2528
+ });
3324
2529
  }
3325
2530
  function getLabelImageMarkerPublicData(marker) {
3326
- const {
3327
- answers: _,
3328
- ...publicData
3329
- } = marker;
2531
+ const publicData = _objectWithoutPropertiesLoose(marker, _excluded$6);
3330
2532
  return publicData;
3331
2533
  }
3332
2534
 
@@ -3346,13 +2548,9 @@ const labelImageWidgetLogic = {
3346
2548
  getPublicWidgetOptions: getLabelImagePublicWidgetOptions
3347
2549
  };
3348
2550
 
3349
- /* Note(tamara): Brought over from the perseus package packages/perseus/src/util.ts file.
3350
- May be useful to bring other perseus package utilities here. Contains utility functions
3351
- and types used across multiple widgets for randomization and shuffling. */
3352
- const seededRNG = function (seed) {
2551
+ const seededRNG = function seededRNG(seed) {
3353
2552
  let randomSeed = seed;
3354
2553
  return function () {
3355
- // Robert Jenkins' 32 bit integer hash function.
3356
2554
  let seed = randomSeed;
3357
2555
  seed = seed + 0x7ed55d16 + (seed << 12) & 0xffffffff;
3358
2556
  seed = (seed ^ 0xc761c23c ^ seed >>> 19) & 0xffffffff;
@@ -3363,18 +2561,10 @@ const seededRNG = function (seed) {
3363
2561
  return (randomSeed = seed & 0xfffffff) / 0x10000000;
3364
2562
  };
3365
2563
  };
3366
-
3367
- // Shuffle an array using a given random seed or function.
3368
- // If `ensurePermuted` is true, the input and output are guaranteed to be
3369
- // distinct permutations.
3370
- function shuffle(array, randomSeed) {
3371
- let ensurePermuted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
3372
- // Always return a copy of the input array
3373
- const shuffled = ___default["default"].clone(array);
3374
-
3375
- // Handle edge cases (input array is empty or uniform)
3376
- if (!shuffled.length || ___default["default"].all(shuffled, function (value) {
3377
- return ___default["default"].isEqual(value, shuffled[0]);
2564
+ function shuffle(array, randomSeed, ensurePermuted = false) {
2565
+ const shuffled = _.clone(array);
2566
+ if (!shuffled.length || _.all(shuffled, function (value) {
2567
+ return _.isEqual(value, shuffled[0]);
3378
2568
  })) {
3379
2569
  return shuffled;
3380
2570
  }
@@ -3385,77 +2575,53 @@ function shuffle(array, randomSeed) {
3385
2575
  random = seededRNG(randomSeed);
3386
2576
  }
3387
2577
  do {
3388
- // Fischer-Yates shuffle
3389
2578
  for (let top = shuffled.length; top > 0; top--) {
3390
2579
  const newEnd = Math.floor(random() * top);
3391
2580
  const temp = shuffled[newEnd];
3392
-
3393
- // @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
3394
2581
  shuffled[newEnd] = shuffled[top - 1];
3395
- // @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
3396
2582
  shuffled[top - 1] = temp;
3397
2583
  }
3398
- } while (ensurePermuted && ___default["default"].isEqual(array, shuffled));
2584
+ } while (ensurePermuted && _.isEqual(array, shuffled));
3399
2585
  return shuffled;
3400
2586
  }
3401
2587
  const random = seededRNG(new Date().getTime() & 0xffffffff);
3402
2588
 
3403
- // TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
3404
-
3405
- // TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
3406
2589
  const shuffleMatcher = props => {
3407
- // Use the same random() function to shuffle both columns sequentially
3408
2590
  const rng = seededRNG(props.problemNum);
3409
2591
  let left;
3410
2592
  if (!props.orderMatters) {
3411
- // If the order doesn't matter, don't shuffle the left column
3412
2593
  left = props.left;
3413
2594
  } else {
3414
- left = shuffle(props.left, rng, /* ensurePermuted */true);
2595
+ left = shuffle(props.left, rng, true);
3415
2596
  }
3416
- const right = shuffle(props.right, rng, /* ensurePermuted */true);
2597
+ const right = shuffle(props.right, rng, true);
3417
2598
  return {
3418
2599
  left,
3419
2600
  right
3420
2601
  };
3421
2602
  };
3422
-
3423
- // TODO(LEMS-2841): Can shorten to shuffleMatcher after above function removed
3424
2603
  function shuffleMatcherWithRandom(data) {
3425
- // Use the same random() function to shuffle both columns sequentially
3426
2604
  let left;
3427
2605
  if (!data.orderMatters) {
3428
- // If the order doesn't matter, don't shuffle the left column
3429
2606
  left = data.left;
3430
2607
  } else {
3431
- left = shuffle(data.left, Math.random, /* ensurePermuted */true);
2608
+ left = shuffle(data.left, Math.random, true);
3432
2609
  }
3433
- const right = shuffle(data.right, Math.random, /* ensurePermuted */true);
2610
+ const right = shuffle(data.right, Math.random, true);
3434
2611
  return {
3435
2612
  left,
3436
2613
  right
3437
2614
  };
3438
2615
  }
3439
-
3440
- /**
3441
- * For details on the individual options, see the
3442
- * PerseusMatcherWidgetOptions type
3443
- */
3444
-
3445
- /**
3446
- * Given a PerseusMatcherWidgetOptions object, return a new object with only
3447
- * the public options that should be exposed to the client.
3448
- */
3449
2616
  function getMatcherPublicWidgetOptions(options) {
3450
2617
  const {
3451
2618
  left,
3452
2619
  right
3453
2620
  } = shuffleMatcherWithRandom(options);
3454
- return {
3455
- ...options,
2621
+ return _extends({}, options, {
3456
2622
  left: left,
3457
2623
  right: right
3458
- };
2624
+ });
3459
2625
  }
3460
2626
 
3461
2627
  const defaultWidgetOptions$e = {
@@ -3471,11 +2637,9 @@ const matcherWidgetLogic = {
3471
2637
  getPublicWidgetOptions: getMatcherPublicWidgetOptions
3472
2638
  };
3473
2639
 
2640
+ const _excluded$5 = ["answers"];
3474
2641
  function getMatrixPublicWidgetOptions(options) {
3475
- const {
3476
- answers: _,
3477
- ...publicOptions
3478
- } = options;
2642
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$5);
3479
2643
  return publicOptions;
3480
2644
  }
3481
2645
 
@@ -3492,6 +2656,7 @@ const matrixWidgetLogic = {
3492
2656
  getPublicWidgetOptions: getMatrixPublicWidgetOptions
3493
2657
  };
3494
2658
 
2659
+ const _excluded$4 = ["imageUrl", "imageTop", "imageLeft"];
3495
2660
  const currentVersion$1 = {
3496
2661
  major: 1,
3497
2662
  minor: 0
@@ -3499,19 +2664,18 @@ const currentVersion$1 = {
3499
2664
  const widgetOptionsUpgrades = {
3500
2665
  "1": v0options => {
3501
2666
  const {
3502
- imageUrl,
3503
- imageTop,
3504
- imageLeft,
3505
- ...rest
3506
- } = v0options;
3507
- return {
3508
- ...rest,
2667
+ imageUrl,
2668
+ imageTop,
2669
+ imageLeft
2670
+ } = v0options,
2671
+ rest = _objectWithoutPropertiesLoose(v0options, _excluded$4);
2672
+ return _extends({}, rest, {
3509
2673
  image: {
3510
2674
  url: imageUrl,
3511
2675
  top: imageTop,
3512
2676
  left: imageLeft
3513
2677
  }
3514
- };
2678
+ });
3515
2679
  }
3516
2680
  };
3517
2681
  const defaultWidgetOptions$c = {
@@ -3532,12 +2696,9 @@ const measurerWidgetLogic = {
3532
2696
  defaultWidgetOptions: defaultWidgetOptions$c
3533
2697
  };
3534
2698
 
2699
+ const _excluded$3 = ["correctX", "correctRel"];
3535
2700
  function getNumberLinePublicWidgetOptions(options) {
3536
- const {
3537
- correctX: _,
3538
- correctRel: __,
3539
- ...publicOptions
3540
- } = options;
2701
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$3);
3541
2702
  return publicOptions;
3542
2703
  }
3543
2704
 
@@ -3561,15 +2722,7 @@ const numberLineWidgetLogic = {
3561
2722
  getPublicWidgetOptions: getNumberLinePublicWidgetOptions
3562
2723
  };
3563
2724
 
3564
- /**
3565
- * For details on the individual options, see the
3566
- * PerseusNumericInputWidgetOptions type
3567
- */
3568
-
3569
- /**
3570
- * This data from `answers` is used pre-scoring to give hints
3571
- * to the learner regarding the format of accepted answers
3572
- */
2725
+ const _excluded$2 = ["answers"];
3573
2726
  function getNumericInputAnswerPublicData(answer) {
3574
2727
  const {
3575
2728
  answerForms,
@@ -3582,20 +2735,14 @@ function getNumericInputAnswerPublicData(answer) {
3582
2735
  status
3583
2736
  };
3584
2737
  }
3585
-
3586
- /**
3587
- * Given a PerseusNumericInputWidgetOptions object, return a new object with only
3588
- * the public options that should be exposed to the client.
3589
- */
3590
2738
  function getNumericInputPublicWidgetOptions(options) {
3591
2739
  const {
3592
- answers,
3593
- ...publicWidgetOptions
3594
- } = options;
3595
- return {
3596
- ...publicWidgetOptions,
2740
+ answers
2741
+ } = options,
2742
+ publicWidgetOptions = _objectWithoutPropertiesLoose(options, _excluded$2);
2743
+ return _extends({}, publicWidgetOptions, {
3597
2744
  answers: answers.map(getNumericInputAnswerPublicData)
3598
- };
2745
+ });
3599
2746
  }
3600
2747
 
3601
2748
  const defaultWidgetOptions$a = {
@@ -3620,15 +2767,6 @@ const numericInputWidgetLogic = {
3620
2767
  getPublicWidgetOptions: getNumericInputPublicWidgetOptions
3621
2768
  };
3622
2769
 
3623
- /**
3624
- * For details on the individual options, see the
3625
- * PerseusOrdererWidgetOptions type
3626
- */
3627
-
3628
- /**
3629
- * Given a PerseusOrdererWidgetOptions object, return a new object with only
3630
- * the public options that should be exposed to the client.
3631
- */
3632
2770
  function getOrdererPublicWidgetOptions(options) {
3633
2771
  return {
3634
2772
  options: options.options,
@@ -3699,20 +2837,9 @@ const phetSimulationWidgetLogic = {
3699
2837
  defaultWidgetOptions: defaultWidgetOptions$5
3700
2838
  };
3701
2839
 
3702
- /**
3703
- * For details on the individual options, see the
3704
- * PerseusPlotterWidgetOptions type
3705
- */
3706
-
3707
- /**
3708
- * Given a PerseusPlotterWidgetOptions object, return a new object with only
3709
- * the public options that should be exposed to the client.
3710
- */
2840
+ const _excluded$1 = ["correct"];
3711
2841
  function getPlotterPublicWidgetOptions(options) {
3712
- const {
3713
- correct: _,
3714
- ...publicOptions
3715
- } = options;
2842
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$1);
3716
2843
  return publicOptions;
3717
2844
  }
3718
2845
 
@@ -3746,19 +2873,6 @@ const pythonProgramWidgetLogic = {
3746
2873
  defaultWidgetOptions: defaultWidgetOptions$3
3747
2874
  };
3748
2875
 
3749
- /**
3750
- * For details on the individual options, see the
3751
- * PerseusRadioWidgetOptions type
3752
- */
3753
-
3754
- /**
3755
- * Only the options from each Radio choice that should be exposed to the client.
3756
- */
3757
-
3758
- /**
3759
- * Given a PerseusRadioChoice object, return a new object with only the public
3760
- * data that should be included in the Radio public widget options.
3761
- */
3762
2876
  function getRadioChoicePublicData(choice) {
3763
2877
  const {
3764
2878
  content,
@@ -3771,23 +2885,9 @@ function getRadioChoicePublicData(choice) {
3771
2885
  widgets
3772
2886
  };
3773
2887
  }
3774
-
3775
- /**
3776
- * Shared functionality to determine if numCorrect is used, because:
3777
- *
3778
- * 1. numCorrect is conditionally used for rendering pre-scoring
3779
- * 2. numCorrect also exposes information about answers
3780
- *
3781
- * So only include/use numCorrect when we know it's useful.
3782
- */
3783
2888
  function usesNumCorrect(multipleSelect, countChoices, numCorrect) {
3784
2889
  return multipleSelect && countChoices && numCorrect;
3785
2890
  }
3786
-
3787
- /**
3788
- * Given a PerseusRadioWidgetOptions object, return a new object with only
3789
- * the public options that should be exposed to the client.
3790
- */
3791
2891
  function getRadioPublicWidgetOptions(options) {
3792
2892
  const {
3793
2893
  numCorrect,
@@ -3795,11 +2895,10 @@ function getRadioPublicWidgetOptions(options) {
3795
2895
  multipleSelect,
3796
2896
  countChoices
3797
2897
  } = options;
3798
- return {
3799
- ...options,
2898
+ return _extends({}, options, {
3800
2899
  numCorrect: usesNumCorrect(multipleSelect, countChoices, numCorrect) ? numCorrect : undefined,
3801
2900
  choices: choices.map(getRadioChoicePublicData)
3802
- };
2901
+ });
3803
2902
  }
3804
2903
 
3805
2904
  const radioWidgetLogic = {
@@ -3810,28 +2909,12 @@ const radioWidgetLogic = {
3810
2909
  getPublicWidgetOptions: getRadioPublicWidgetOptions
3811
2910
  };
3812
2911
 
3813
- /**
3814
- * For details on the individual options, see the
3815
- * PerseusSorterWidgetOptions type
3816
- */
3817
-
3818
- /**
3819
- * Given a PerseusSorterWidgetOptions object, return a new object with only
3820
- * the public options that should be exposed to the client.
3821
- */
3822
2912
  function getSorterPublicWidgetOptions(options) {
3823
- const shuffledCorrect = shuffle(options.correct, Math.random, /* ensurePermuted */true);
3824
- return {
3825
- ...options,
3826
- // Note(Tamara): This does not provide correct answer information any longer.
3827
- // To maintain compatibility with the original widget options, we are
3828
- // keeping the key the same. Represents initial state of the cards here.
2913
+ const shuffledCorrect = shuffle(options.correct, Math.random, true);
2914
+ return _extends({}, options, {
3829
2915
  correct: shuffledCorrect,
3830
- // Note(Tamara): This new key is only added here with "true". There isn't
3831
- // a place where it is set to false. It indicates that the correct field
3832
- // has been shuffled and no longer contains correct answer info.
3833
2916
  isCorrectShuffled: true
3834
- };
2917
+ });
3835
2918
  }
3836
2919
 
3837
2920
  const defaultWidgetOptions$2 = {
@@ -3845,19 +2928,14 @@ const sorterWidgetLogic = {
3845
2928
  getPublicWidgetOptions: getSorterPublicWidgetOptions
3846
2929
  };
3847
2930
 
2931
+ const _excluded = ["answers"];
3848
2932
  function getTablePublicWidgetOptions(options) {
3849
- const {
3850
- answers: _,
3851
- ...publicOptions
3852
- } = options;
2933
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded);
3853
2934
  return publicOptions;
3854
2935
  }
3855
2936
 
3856
2937
  const defaultRows = 4;
3857
2938
  const defaultColumns = 1;
3858
-
3859
- // initialize a 2D array
3860
- // (defaultRows x defaultColumns) of empty strings
3861
2939
  const answers = new Array(defaultRows).fill(0).map(() => new Array(defaultColumns).fill(""));
3862
2940
  const defaultWidgetOptions$1 = {
3863
2941
  headers: [""],
@@ -3891,60 +2969,34 @@ function isWidgetRegistered(type) {
3891
2969
  }
3892
2970
  function getCurrentVersion(type) {
3893
2971
  const widgetLogic = widgets[type];
3894
- return widgetLogic?.version || {
2972
+ return (widgetLogic == null ? void 0 : widgetLogic.version) || {
3895
2973
  major: 0,
3896
2974
  minor: 0
3897
2975
  };
3898
2976
  }
3899
-
3900
- // TODO(LEMS-2870): getPublicWidgetOptionsFunction/PublicWidgetOptionsFunction
3901
- // need better types
3902
2977
  const getPublicWidgetOptionsFunction = name => {
3903
- return widgets[name]?.getPublicWidgetOptions ?? (i => i);
2978
+ var _widgets$name$getPubl, _widgets$name;
2979
+ return (_widgets$name$getPubl = (_widgets$name = widgets[name]) == null ? void 0 : _widgets$name.getPublicWidgetOptions) != null ? _widgets$name$getPubl : i => i;
3904
2980
  };
3905
2981
  function getWidgetOptionsUpgrades(type) {
3906
2982
  const widgetLogic = widgets[type];
3907
- return widgetLogic?.widgetOptionsUpgrades || {};
2983
+ return (widgetLogic == null ? void 0 : widgetLogic.widgetOptionsUpgrades) || {};
3908
2984
  }
3909
2985
  function getDefaultWidgetOptions(type) {
3910
2986
  const widgetLogic = widgets[type];
3911
- return widgetLogic?.defaultWidgetOptions || {};
2987
+ return (widgetLogic == null ? void 0 : widgetLogic.defaultWidgetOptions) || {};
3912
2988
  }
3913
-
3914
- /**
3915
- * Handling for the optional alignments for widgets
3916
- * See widget-container.jsx for details on how alignments are implemented.
3917
- */
3918
-
3919
- /**
3920
- * Returns the list of supported alignments for the given (string) widget
3921
- * type. This is used primarily at editing time to display the choices
3922
- * for the user.
3923
- *
3924
- * Supported alignments are given as an array of strings in the exports of
3925
- * a widget's module.
3926
- */
3927
2989
  const getSupportedAlignments = type => {
2990
+ var _widgetLogic$supporte;
3928
2991
  const widgetLogic = widgets[type];
3929
- if (!widgetLogic?.supportedAlignments?.[0]) {
3930
- // default alignments
2992
+ if (!(widgetLogic != null && (_widgetLogic$supporte = widgetLogic.supportedAlignments) != null && _widgetLogic$supporte[0])) {
3931
2993
  return ["default"];
3932
2994
  }
3933
- return widgetLogic?.supportedAlignments;
2995
+ return widgetLogic == null ? void 0 : widgetLogic.supportedAlignments;
3934
2996
  };
3935
-
3936
- /**
3937
- * For the given (string) widget type, determine the default alignment for
3938
- * the widget. This is used at rendering time to go from "default" alignment
3939
- * to the actual alignment displayed on the screen.
3940
- *
3941
- * The default alignment is given either as a string (called
3942
- * `defaultAlignment`) or a function (called `getDefaultAlignment`) on
3943
- * the exports of a widget's module.
3944
- */
3945
2997
  const getDefaultAlignment = type => {
3946
2998
  const widgetLogic = widgets[type];
3947
- if (!widgetLogic?.defaultAlignment) {
2999
+ if (!(widgetLogic != null && widgetLogic.defaultAlignment)) {
3948
3000
  return "block";
3949
3001
  }
3950
3002
  return widgetLogic.defaultAlignment;
@@ -3984,63 +3036,35 @@ registerWidget("video", videoWidgetLogic);
3984
3036
 
3985
3037
  var coreWidgetRegistry = /*#__PURE__*/Object.freeze({
3986
3038
  __proto__: null,
3987
- isWidgetRegistered: isWidgetRegistered,
3988
3039
  getCurrentVersion: getCurrentVersion,
3989
- getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
3990
- getWidgetOptionsUpgrades: getWidgetOptionsUpgrades,
3040
+ getDefaultAlignment: getDefaultAlignment,
3991
3041
  getDefaultWidgetOptions: getDefaultWidgetOptions,
3042
+ getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
3992
3043
  getSupportedAlignments: getSupportedAlignments,
3993
- getDefaultAlignment: getDefaultAlignment
3044
+ getWidgetOptionsUpgrades: getWidgetOptionsUpgrades,
3045
+ isWidgetRegistered: isWidgetRegistered
3994
3046
  });
3995
3047
 
3996
3048
  const DEFAULT_STATIC = false;
3997
3049
  const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
3998
3050
  const type = oldWidgetInfo.type;
3999
- // NOTE(jeremy): This looks like it could be replaced by fixing types so
4000
- // that `type` is non-optional. But we're seeing this in Sentry today so I
4001
- // suspect we have legacy data (potentially unpublished) and we should
4002
- // figure that out before depending solely on types.
4003
- if (!___default["default"].isString(type)) {
3051
+ if (!_.isString(type)) {
4004
3052
  throw new PerseusError("widget type must be a string, but was: " + type, Errors.Internal);
4005
3053
  }
4006
3054
  if (!isWidgetRegistered(type)) {
4007
- // If we have a widget that isn't registered, we can't upgrade it
4008
- // TODO(aria): Figure out what the best thing to do here would be
4009
3055
  return oldWidgetInfo;
4010
3056
  }
4011
-
4012
- // Unversioned widgets (pre-July 2014) are all implicitly 0.0
4013
3057
  const initialVersion = oldWidgetInfo.version || {
4014
3058
  major: 0,
4015
3059
  minor: 0
4016
3060
  };
4017
3061
  const latestVersion = getCurrentVersion(type);
4018
-
4019
- // If the widget version is later than what we understand (major
4020
- // version is higher than latest, or major versions are equal and minor
4021
- // version is higher than latest), don't perform any upgrades.
4022
3062
  if (initialVersion.major > latestVersion.major || initialVersion.major === latestVersion.major && initialVersion.minor > latestVersion.minor) {
4023
3063
  return oldWidgetInfo;
4024
3064
  }
4025
-
4026
- // We do a clone here so that it's safe to mutate the input parameter
4027
- // in propUpgrades functions (which I will probably accidentally do at
4028
- // some point, and we would like to not break when that happens).
4029
- let newEditorOptions = ___default["default"].clone(oldWidgetInfo.options) || {};
3065
+ let newEditorOptions = _.clone(oldWidgetInfo.options) || {};
4030
3066
  const upgradePropsMap = getWidgetOptionsUpgrades(type);
4031
-
4032
- // Empty props usually mean a newly created widget by the editor,
4033
- // and are always considerered up-to-date.
4034
- // Mostly, we'd rather not run upgrade functions on props that are
4035
- // not complete.
4036
- if (___default["default"].keys(newEditorOptions).length !== 0) {
4037
- // We loop through all the versions after the current version of
4038
- // the loaded widget, up to and including the latest version of the
4039
- // loaded widget, and run the upgrade function to bring our loaded
4040
- // widget's props up to that version.
4041
- // There is a little subtlety here in that we call
4042
- // upgradePropsMap[1] to upgrade *to* version 1,
4043
- // (not from version 1).
3067
+ if (_.keys(newEditorOptions).length !== 0) {
4044
3068
  for (let nextVersion = initialVersion.major + 1; nextVersion <= latestVersion.major; nextVersion++) {
4045
3069
  if (upgradePropsMap[String(nextVersion)]) {
4046
3070
  newEditorOptions = upgradePropsMap[String(nextVersion)](newEditorOptions);
@@ -4056,23 +3080,12 @@ const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
4056
3080
  }
4057
3081
  }
4058
3082
  }
4059
-
4060
- // Minor version upgrades (eg. new optional props) don't have
4061
- // transform functions. Instead, we fill in the new props with their
4062
- // defaults.
4063
3083
  const defaultOptions = getDefaultWidgetOptions(type);
4064
- newEditorOptions = {
4065
- ...defaultOptions,
4066
- ...newEditorOptions
4067
- };
3084
+ newEditorOptions = _extends({}, defaultOptions, newEditorOptions);
4068
3085
  let alignment = oldWidgetInfo.alignment;
4069
-
4070
- // Widgets that support multiple alignments will "lock in" the
4071
- // alignment to the alignment that would be listed first in the
4072
- // select box. If the widget only supports one alignment, the
4073
- // alignment value will likely just end up as "default".
4074
3086
  if (alignment == null || alignment === "default") {
4075
- alignment = getSupportedAlignments(type)?.[0];
3087
+ var _getSupportedAlignmen;
3088
+ alignment = (_getSupportedAlignmen = getSupportedAlignments(type)) == null ? void 0 : _getSupportedAlignmen[0];
4076
3089
  if (!alignment) {
4077
3090
  throw new PerseusError("No default alignment found when upgrading widget", Errors.Internal, {
4078
3091
  metadata: {
@@ -4085,146 +3098,46 @@ const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
4085
3098
  if (widgetStatic == null) {
4086
3099
  widgetStatic = DEFAULT_STATIC;
4087
3100
  }
4088
- return {
4089
- ...oldWidgetInfo,
4090
- // maintain other info, like type
4091
- // After upgrading we guarantee that the version is up-to-date
3101
+ return _extends({}, oldWidgetInfo, {
4092
3102
  version: latestVersion,
4093
- // Default graded to true (so null/undefined becomes true):
4094
3103
  graded: oldWidgetInfo.graded != null ? oldWidgetInfo.graded : true,
4095
3104
  alignment: alignment,
4096
3105
  static: widgetStatic,
4097
3106
  options: newEditorOptions
4098
- };
3107
+ });
4099
3108
  };
4100
3109
  function getUpgradedWidgetOptions(oldWidgetOptions) {
4101
3110
  return mapObject(oldWidgetOptions, (widgetInfo, widgetId) => {
4102
3111
  if (!widgetInfo.type || !widgetInfo.alignment) {
4103
3112
  const newValues = {};
4104
3113
  if (!widgetInfo.type) {
4105
- // TODO: why does widget have no type?
4106
- // We don't want to derive type from widget ID
4107
- // see: LEMS-1845
4108
3114
  newValues.type = widgetId.split(" ")[0];
4109
3115
  }
4110
3116
  if (!widgetInfo.alignment) {
4111
3117
  newValues.alignment = "default";
4112
3118
  }
4113
- widgetInfo = {
4114
- ...widgetInfo,
4115
- ...newValues
4116
- };
3119
+ widgetInfo = _extends({}, widgetInfo, newValues);
4117
3120
  }
4118
3121
  return upgradeWidgetInfoToLatestVersion(widgetInfo);
4119
3122
  });
4120
3123
  }
4121
3124
 
4122
- /**
4123
- * Return a copy of a Perseus item with rubric data removed (ie answers)
4124
- *
4125
- * @param originalItem - the original, full Perseus item (which includes the rubric - aka answer data)
4126
- */
4127
3125
  function splitPerseusItem(originalItem) {
4128
- const item = ___default["default"].clone(originalItem);
4129
- const originalWidgets = item.widgets ?? {};
3126
+ var _item$widgets;
3127
+ const item = _.clone(originalItem);
3128
+ const originalWidgets = (_item$widgets = item.widgets) != null ? _item$widgets : {};
4130
3129
  const upgradedWidgets = getUpgradedWidgetOptions(originalWidgets);
4131
3130
  const splitWidgets = {};
4132
3131
  for (const [id, widget] of Object.entries(upgradedWidgets)) {
4133
3132
  const publicWidgetOptionsFun = getPublicWidgetOptionsFunction(widget.type);
4134
- splitWidgets[id] = {
4135
- ...widget,
3133
+ splitWidgets[id] = _extends({}, widget, {
4136
3134
  options: publicWidgetOptionsFun(widget.options)
4137
- };
3135
+ });
4138
3136
  }
4139
- return {
4140
- ...item,
3137
+ return _extends({}, item, {
4141
3138
  widgets: splitWidgets
4142
- };
3139
+ });
4143
3140
  }
4144
3141
 
4145
- exports.CoreWidgetRegistry = coreWidgetRegistry;
4146
- exports.Errors = Errors;
4147
- exports.GrapherUtil = grapherUtil;
4148
- exports.ItemExtras = ItemExtras;
4149
- exports.PerseusError = PerseusError;
4150
- exports.PerseusExpressionAnswerFormConsidered = PerseusExpressionAnswerFormConsidered;
4151
- exports.addWidget = addWidget;
4152
- exports.approximateDeepEqual = approximateDeepEqual;
4153
- exports.approximateEqual = approximateEqual;
4154
- exports.categorizerLogic = categorizerWidgetLogic;
4155
- exports.csProgramLogic = csProgramWidgetLogic;
4156
- exports.deepClone = deepClone;
4157
- exports.definitionLogic = definitionWidgetLogic;
4158
- exports.deriveExtraKeys = deriveExtraKeys;
4159
- exports.deriveNumCorrect = deriveNumCorrect;
4160
- exports.dropdownLogic = dropdownWidgetLogic;
4161
- exports.explanationLogic = explanationWidgetLogic;
4162
- exports.expressionLogic = expressionWidgetLogic;
4163
- exports.getCSProgramPublicWidgetOptions = getCSProgramPublicWidgetOptions;
4164
- exports.getCategorizerPublicWidgetOptions = getCategorizerPublicWidgetOptions;
4165
- exports.getDecimalSeparator = getDecimalSeparator;
4166
- exports.getDropdownPublicWidgetOptions = getDropdownPublicWidgetOptions;
4167
- exports.getExpressionPublicWidgetOptions = getExpressionPublicWidgetOptions;
4168
- exports.getGrapherPublicWidgetOptions = getGrapherPublicWidgetOptions;
4169
- exports.getIFramePublicWidgetOptions = getIFramePublicWidgetOptions;
4170
- exports.getInteractiveGraphPublicWidgetOptions = getInteractiveGraphPublicWidgetOptions;
4171
- exports.getLabelImagePublicWidgetOptions = getLabelImagePublicWidgetOptions;
4172
- exports.getMatcherPublicWidgetOptions = getMatcherPublicWidgetOptions;
4173
- exports.getMatrixPublicWidgetOptions = getMatrixPublicWidgetOptions;
4174
- exports.getMatrixSize = getMatrixSize;
4175
- exports.getNumberLinePublicWidgetOptions = getNumberLinePublicWidgetOptions;
4176
- exports.getNumericInputPublicWidgetOptions = getNumericInputPublicWidgetOptions;
4177
- exports.getOrdererPublicWidgetOptions = getOrdererPublicWidgetOptions;
4178
- exports.getPlotterPublicWidgetOptions = getPlotterPublicWidgetOptions;
4179
- exports.getRadioPublicWidgetOptions = getRadioPublicWidgetOptions;
4180
- exports.getSorterPublicWidgetOptions = getSorterPublicWidgetOptions;
4181
- exports.getTablePublicWidgetOptions = getTablePublicWidgetOptions;
4182
- exports.getUpgradedWidgetOptions = getUpgradedWidgetOptions;
4183
- exports.getWidgetIdsFromContent = getWidgetIdsFromContent;
4184
- exports.getWidgetIdsFromContentByType = getWidgetIdsFromContentByType;
4185
- exports.gradedGroupLogic = gradedGroupWidgetLogic;
4186
- exports.gradedGroupSetLogic = gradedGroupSetWidgetLogic;
4187
- exports.grapherLogic = grapherWidgetLogic;
4188
- exports.groupLogic = groupWidgetLogic;
4189
- exports.iframeLogic = iframeWidgetLogic;
4190
- exports.imageLogic = imageWidgetLogic;
4191
- exports.inputNumberLogic = inputNumberWidgetLogic;
4192
- exports.interactionLogic = interactionWidgetLogic;
4193
- exports.interactiveGraphLogic = interactiveGraphWidgetLogic;
4194
- exports.isFailure = isFailure;
4195
- exports.isSuccess = isSuccess;
4196
- exports.labelImageLogic = labelImageWidgetLogic;
4197
- exports.libVersion = libVersion;
4198
- exports.lockedFigureColorNames = lockedFigureColorNames;
4199
- exports.lockedFigureColors = lockedFigureColors;
4200
- exports.lockedFigureFillStyles = lockedFigureFillStyles;
4201
- exports.mapObject = mapObject;
4202
- exports.matcherLogic = matcherWidgetLogic;
4203
- exports.matrixLogic = matrixWidgetLogic;
4204
- exports.measurerLogic = measurerWidgetLogic;
4205
- exports.numberLineLogic = numberLineWidgetLogic;
4206
- exports.numericInputLogic = numericInputWidgetLogic;
4207
- exports.ordererLogic = ordererWidgetLogic;
4208
- exports.parseAndMigratePerseusArticle = parseAndMigratePerseusArticle;
4209
- exports.parseAndMigratePerseusItem = parseAndMigratePerseusItem;
4210
- exports.parsePerseusItem = parsePerseusItem;
4211
- exports.passageLogic = passageWidgetLogic;
4212
- exports.passageRefLogic = passageRefWidgetLogic;
4213
- exports.passageRefTargetLogic = passageRefTargetWidgetLogic;
4214
- exports.phetSimulationLogic = phetSimulationWidgetLogic;
4215
- exports.plotterLogic = plotterWidgetLogic;
4216
- exports.plotterPlotTypes = plotterPlotTypes;
4217
- exports.pluck = pluck;
4218
- exports.pythonProgramLogic = pythonProgramWidgetLogic;
4219
- exports.radioLogic = radioWidgetLogic;
4220
- exports.random = random;
4221
- exports.seededRNG = seededRNG;
4222
- exports.shuffle = shuffle;
4223
- exports.shuffleMatcher = shuffleMatcher;
4224
- exports.sorterLogic = sorterWidgetLogic;
4225
- exports.splitPerseusItem = splitPerseusItem;
4226
- exports.tableLogic = tableWidgetLogic;
4227
- exports.upgradeWidgetInfoToLatestVersion = upgradeWidgetInfoToLatestVersion;
4228
- exports.usesNumCorrect = usesNumCorrect;
4229
- exports.videoLogic = videoWidgetLogic;
3142
+ export { coreWidgetRegistry as CoreWidgetRegistry, Errors, grapherUtil as GrapherUtil, ItemExtras, PerseusError, PerseusExpressionAnswerFormConsidered, addWidget, approximateDeepEqual, approximateEqual, categorizerWidgetLogic as categorizerLogic, csProgramWidgetLogic as csProgramLogic, deepClone, definitionWidgetLogic as definitionLogic, deriveExtraKeys, deriveNumCorrect, dropdownWidgetLogic as dropdownLogic, explanationWidgetLogic as explanationLogic, expressionWidgetLogic as expressionLogic, getCSProgramPublicWidgetOptions, getCategorizerPublicWidgetOptions, getDecimalSeparator, getDropdownPublicWidgetOptions, getExpressionPublicWidgetOptions, getGrapherPublicWidgetOptions, getIFramePublicWidgetOptions, getInteractiveGraphPublicWidgetOptions, getLabelImagePublicWidgetOptions, getMatcherPublicWidgetOptions, getMatrixPublicWidgetOptions, getMatrixSize, getNumberLinePublicWidgetOptions, getNumericInputPublicWidgetOptions, getOrdererPublicWidgetOptions, getPlotterPublicWidgetOptions, getRadioPublicWidgetOptions, getSorterPublicWidgetOptions, getTablePublicWidgetOptions, getUpgradedWidgetOptions, getWidgetIdsFromContent, getWidgetIdsFromContentByType, gradedGroupWidgetLogic as gradedGroupLogic, gradedGroupSetWidgetLogic as gradedGroupSetLogic, grapherWidgetLogic as grapherLogic, groupWidgetLogic as groupLogic, iframeWidgetLogic as iframeLogic, imageWidgetLogic as imageLogic, inputNumberWidgetLogic as inputNumberLogic, interactionWidgetLogic as interactionLogic, interactiveGraphWidgetLogic as interactiveGraphLogic, isFailure, isSuccess, labelImageWidgetLogic as labelImageLogic, libVersion, lockedFigureColorNames, lockedFigureColors, lockedFigureFillStyles, mapObject, matcherWidgetLogic as matcherLogic, matrixWidgetLogic as matrixLogic, measurerWidgetLogic as measurerLogic, numberLineWidgetLogic as numberLineLogic, numericInputWidgetLogic as numericInputLogic, ordererWidgetLogic as ordererLogic, parseAndMigratePerseusArticle, parseAndMigratePerseusItem, parsePerseusItem, passageWidgetLogic as passageLogic, passageRefWidgetLogic as passageRefLogic, passageRefTargetWidgetLogic as passageRefTargetLogic, phetSimulationWidgetLogic as phetSimulationLogic, plotterWidgetLogic as plotterLogic, plotterPlotTypes, pluck, pythonProgramWidgetLogic as pythonProgramLogic, radioWidgetLogic as radioLogic, random, seededRNG, shuffle, shuffleMatcher, sorterWidgetLogic as sorterLogic, splitPerseusItem, tableWidgetLogic as tableLogic, upgradeWidgetInfoToLatestVersion, usesNumCorrect, videoWidgetLogic as videoLogic };
4230
3143
  //# sourceMappingURL=index.js.map