@khanacademy/perseus-core 5.4.1 → 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 perseusCore = require('@khanacademy/perseus-core');
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,
@@ -2780,41 +2123,19 @@ const parsePerseusItem$1 = object({
2780
2123
  question: parsePerseusRenderer,
2781
2124
  hints: defaulted(array(parseHint), () => []),
2782
2125
  answerArea: parsePerseusAnswerArea,
2783
- itemDataVersion: optional(object({
2126
+ itemDataVersion: optional(nullable(object({
2784
2127
  major: number,
2785
2128
  minor: number
2786
- })),
2787
- // Deprecated field
2129
+ }))),
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,168 +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
- /**
2864
- * Adds the given perseus library version information to the __perseus_debug__
2865
- * object and ensures that the object is attached to `globalThis` (`window` in
2866
- * browser environments).
2867
- *
2868
- * This allows each library to provide runtime version information to assist in
2869
- * debugging in production environments.
2870
- */
2871
- const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
2872
- // If the library version is the default value, then we don't want to
2873
- // prefix it with a "v" to indicate that it is a version number.
2874
- let prefix = "v";
2875
- if (libraryVersion === "__lib_version__") {
2876
- prefix = "";
2877
- }
2878
- const formattedVersion = `${prefix}${libraryVersion}`;
2879
- if (typeof globalThis !== "undefined") {
2880
- globalThis.__perseus_debug__ = globalThis.__perseus_debug__ ?? {};
2881
- const existingVersionEntry = globalThis.__perseus_debug__[libraryName];
2882
- if (existingVersionEntry) {
2883
- // If we already have an entry and it doesn't match the registered
2884
- // version, we morph the entry into an array and log a warning.
2885
- if (existingVersionEntry !== formattedVersion) {
2886
- // Existing entry might be an array already (oops, at least 2
2887
- // versions of the library already loaded!).
2888
- const allVersions = Array.isArray(existingVersionEntry) ? existingVersionEntry : [existingVersionEntry];
2889
- allVersions.push(formattedVersion);
2890
- globalThis.__perseus_debug__[libraryName] = allVersions;
2891
-
2892
- // eslint-disable-next-line no-console
2893
- console.warn(`Multiple versions of ${libraryName} loaded on this page: ${allVersions.sort().join(", ")}`);
2894
- }
2895
- } else {
2896
- globalThis.__perseus_debug__[libraryName] = formattedVersion;
2897
- }
2898
- } else {
2899
- // eslint-disable-next-line no-console
2900
- console.warn(`globalThis not found found (${formattedVersion})`);
2901
- }
2902
- };
2903
-
2904
- // This file is processed by a Rollup plugin (replace) to inject the production
2905
2169
  const libName = "@khanacademy/perseus-core";
2906
- const libVersion = "5.4.1";
2170
+ const libVersion = "6.0.0";
2907
2171
  addLibraryVersionToPerseusDebug(libName, libVersion);
2908
2172
 
2909
- /**
2910
- * @typedef {Object} Errors utility for referencing the Perseus error taxonomy.
2911
- */
2912
2173
  const Errors = Object.freeze({
2913
- /**
2914
- * @property {ErrorKind} Unknown The kind of error is not known.
2915
- */
2916
2174
  Unknown: "Unknown",
2917
- /**
2918
- * @property {ErrorKind} Internal The error is internal to the executing code.
2919
- */
2920
2175
  Internal: "Internal",
2921
- /**
2922
- * @property {ErrorKind} InvalidInput There was a problem with the provided
2923
- * input, such as the wrong format or a null value.
2924
- */
2925
2176
  InvalidInput: "InvalidInput",
2926
- /**
2927
- * @property {ErrorKind} NotAllowed There was a problem due to the state of
2928
- * the system not matching the requested operation or input. For example,
2929
- * trying to create a username that is valid, but is already taken by
2930
- * another user. Use {@link InvalidInput} instead when the input isn't
2931
- * valid regardless of the state of the system. Use {@link NotFound} when
2932
- * the failure is due to not being able to find a resource.
2933
- */
2934
2177
  NotAllowed: "NotAllowed",
2935
- /**
2936
- * @property {ErrorKind} TransientService There was a problem when making a
2937
- * request to a service.
2938
- */
2939
2178
  TransientService: "TransientService",
2940
- /**
2941
- * @property {ErrorKind} Service There was a non-transient problem when
2942
- * making a request to service.
2943
- */
2944
2179
  Service: "Service"
2945
2180
  });
2946
2181
 
2947
- /**
2948
- * @type {ErrorKind} The kind of error being reported
2949
- */
2950
-
2951
2182
  class PerseusError extends Error {
2952
- kind;
2953
- metadata;
2954
2183
  constructor(message, kind, options) {
2955
2184
  super(message);
2185
+ this.kind = void 0;
2186
+ this.metadata = void 0;
2956
2187
  this.kind = kind;
2957
- this.metadata = options?.metadata;
2188
+ this.metadata = options == null ? void 0 : options.metadata;
2958
2189
  }
2959
2190
  }
2960
2191
 
2961
- /**
2962
- * _ utilities for objects
2963
- */
2964
-
2965
- /**
2966
- * Does a pluck on keys inside objects in an object
2967
- *
2968
- * Ex:
2969
- * tools = {
2970
- * translation: {
2971
- * enabled: true
2972
- * },
2973
- * rotation: {
2974
- * enabled: false
2975
- * }
2976
- * };
2977
- * pluckObject(tools, "enabled") returns {
2978
- * translation: true
2979
- * rotation: false
2980
- * }
2981
- */
2982
- const pluck = function (table, subKey) {
2983
- 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) {
2984
2194
  return [key, value[subKey]];
2985
2195
  }));
2986
2196
  };
2987
-
2988
- /**
2989
- * Maps an object to an object
2990
- *
2991
- * > mapObject({a: '1', b: '2'}, (value, key) => {
2992
- * return value + 1;
2993
- * });
2994
- * {a: 2, b: 3}
2995
- */
2996
- const mapObject = function (obj, lambda) {
2197
+ const mapObject = function mapObject(obj, lambda) {
2997
2198
  const result = {};
2998
2199
  Object.keys(obj).forEach(key => {
2999
- // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
3000
2200
  result[key] = lambda(obj[key], key);
3001
2201
  });
3002
2202
  return result;
3003
2203
  };
3004
2204
 
3005
- /**
3006
- * For details on the individual options, see the
3007
- * PerseusCategorizerWidgetOptions type
3008
- */
3009
-
3010
- /**
3011
- * Given a PerseusCategorizerWidgetOptions object, return a new object with only
3012
- * the public options that should be exposed to the client.
3013
- */
3014
2205
  function getCategorizerPublicWidgetOptions(options) {
3015
2206
  return {
3016
2207
  items: options.items,
@@ -3065,15 +2256,6 @@ const definitionWidgetLogic = {
3065
2256
  defaultAlignment: "inline"
3066
2257
  };
3067
2258
 
3068
- /**
3069
- * For details on the individual options, see the
3070
- * PerseusDropdownWidgetOptions type
3071
- */
3072
-
3073
- /**
3074
- * Given a PerseusDropdownWidgetOptions object, return a new object with only
3075
- * the public options that should be exposed to the client.
3076
- */
3077
2259
  function getDropdownPublicWidgetOptions(options) {
3078
2260
  return {
3079
2261
  choices: options.choices.map(choice => ({
@@ -3154,15 +2336,6 @@ const defaultWidgetOptions$p = {
3154
2336
  functions: ["f", "g", "h"]
3155
2337
  };
3156
2338
 
3157
- /**
3158
- * For details on the individual options, see the
3159
- * PerseusExpressionWidgetOptions type
3160
- */
3161
-
3162
- /**
3163
- * Given a PerseusExpressionWidgetOptions object, return a new object with only
3164
- * the public options that should be exposed to the client.
3165
- */
3166
2339
  function getExpressionPublicWidgetOptions(options) {
3167
2340
  return {
3168
2341
  buttonSets: options.buttonSets,
@@ -3204,11 +2377,9 @@ const gradedGroupSetWidgetLogic = {
3204
2377
  defaultWidgetOptions: defaultWidgetOptions$n
3205
2378
  };
3206
2379
 
2380
+ const _excluded$8 = ["correct"];
3207
2381
  function getGrapherPublicWidgetOptions(options) {
3208
- const {
3209
- correct: _,
3210
- ...publicOptions
3211
- } = options;
2382
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$8);
3212
2383
  return publicOptions;
3213
2384
  }
3214
2385
 
@@ -3320,11 +2491,9 @@ const interactionWidgetLogic = {
3320
2491
  defaultWidgetOptions: defaultWidgetOptions$h
3321
2492
  };
3322
2493
 
2494
+ const _excluded$7 = ["correct"];
3323
2495
  function getInteractiveGraphPublicWidgetOptions(options) {
3324
- const {
3325
- correct: _,
3326
- ...publicOptions
3327
- } = options;
2496
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$7);
3328
2497
  return publicOptions;
3329
2498
  }
3330
2499
 
@@ -3352,22 +2521,14 @@ const interactiveGraphWidgetLogic = {
3352
2521
  getPublicWidgetOptions: getInteractiveGraphPublicWidgetOptions
3353
2522
  };
3354
2523
 
3355
- /**
3356
- * For details on the individual options, see the
3357
- * PerseusLabelImageWidgetOptions type
3358
- */
3359
-
2524
+ const _excluded$6 = ["answers"];
3360
2525
  function getLabelImagePublicWidgetOptions(options) {
3361
- return {
3362
- ...options,
2526
+ return _extends({}, options, {
3363
2527
  markers: options.markers.map(getLabelImageMarkerPublicData)
3364
- };
2528
+ });
3365
2529
  }
3366
2530
  function getLabelImageMarkerPublicData(marker) {
3367
- const {
3368
- answers: _,
3369
- ...publicData
3370
- } = marker;
2531
+ const publicData = _objectWithoutPropertiesLoose(marker, _excluded$6);
3371
2532
  return publicData;
3372
2533
  }
3373
2534
 
@@ -3387,62 +2548,80 @@ const labelImageWidgetLogic = {
3387
2548
  getPublicWidgetOptions: getLabelImagePublicWidgetOptions
3388
2549
  };
3389
2550
 
3390
- // TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
2551
+ const seededRNG = function seededRNG(seed) {
2552
+ let randomSeed = seed;
2553
+ return function () {
2554
+ let seed = randomSeed;
2555
+ seed = seed + 0x7ed55d16 + (seed << 12) & 0xffffffff;
2556
+ seed = (seed ^ 0xc761c23c ^ seed >>> 19) & 0xffffffff;
2557
+ seed = seed + 0x165667b1 + (seed << 5) & 0xffffffff;
2558
+ seed = (seed + 0xd3a2646c ^ seed << 9) & 0xffffffff;
2559
+ seed = seed + 0xfd7046c5 + (seed << 3) & 0xffffffff;
2560
+ seed = (seed ^ 0xb55a4f09 ^ seed >>> 16) & 0xffffffff;
2561
+ return (randomSeed = seed & 0xfffffff) / 0x10000000;
2562
+ };
2563
+ };
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]);
2568
+ })) {
2569
+ return shuffled;
2570
+ }
2571
+ let random;
2572
+ if (typeof randomSeed === "function") {
2573
+ random = randomSeed;
2574
+ } else {
2575
+ random = seededRNG(randomSeed);
2576
+ }
2577
+ do {
2578
+ for (let top = shuffled.length; top > 0; top--) {
2579
+ const newEnd = Math.floor(random() * top);
2580
+ const temp = shuffled[newEnd];
2581
+ shuffled[newEnd] = shuffled[top - 1];
2582
+ shuffled[top - 1] = temp;
2583
+ }
2584
+ } while (ensurePermuted && _.isEqual(array, shuffled));
2585
+ return shuffled;
2586
+ }
2587
+ const random = seededRNG(new Date().getTime() & 0xffffffff);
3391
2588
 
3392
- // TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
3393
2589
  const shuffleMatcher = props => {
3394
- // Use the same random() function to shuffle both columns sequentially
3395
- const rng = perseusCore.seededRNG(props.problemNum);
2590
+ const rng = seededRNG(props.problemNum);
3396
2591
  let left;
3397
2592
  if (!props.orderMatters) {
3398
- // If the order doesn't matter, don't shuffle the left column
3399
2593
  left = props.left;
3400
2594
  } else {
3401
- left = perseusCore.shuffle(props.left, rng, /* ensurePermuted */true);
2595
+ left = shuffle(props.left, rng, true);
3402
2596
  }
3403
- const right = perseusCore.shuffle(props.right, rng, /* ensurePermuted */true);
2597
+ const right = shuffle(props.right, rng, true);
3404
2598
  return {
3405
2599
  left,
3406
2600
  right
3407
2601
  };
3408
2602
  };
3409
-
3410
- // TODO(LEMS-2841): Can shorten to shuffleMatcher after above function removed
3411
2603
  function shuffleMatcherWithRandom(data) {
3412
- // Use the same random() function to shuffle both columns sequentially
3413
2604
  let left;
3414
2605
  if (!data.orderMatters) {
3415
- // If the order doesn't matter, don't shuffle the left column
3416
2606
  left = data.left;
3417
2607
  } else {
3418
- left = perseusCore.shuffle(data.left, Math.random, /* ensurePermuted */true);
2608
+ left = shuffle(data.left, Math.random, true);
3419
2609
  }
3420
- const right = perseusCore.shuffle(data.right, Math.random, /* ensurePermuted */true);
2610
+ const right = shuffle(data.right, Math.random, true);
3421
2611
  return {
3422
2612
  left,
3423
2613
  right
3424
2614
  };
3425
2615
  }
3426
-
3427
- /**
3428
- * For details on the individual options, see the
3429
- * PerseusMatcherWidgetOptions type
3430
- */
3431
-
3432
- /**
3433
- * Given a PerseusMatcherWidgetOptions object, return a new object with only
3434
- * the public options that should be exposed to the client.
3435
- */
3436
2616
  function getMatcherPublicWidgetOptions(options) {
3437
2617
  const {
3438
2618
  left,
3439
2619
  right
3440
2620
  } = shuffleMatcherWithRandom(options);
3441
- return {
3442
- ...options,
2621
+ return _extends({}, options, {
3443
2622
  left: left,
3444
2623
  right: right
3445
- };
2624
+ });
3446
2625
  }
3447
2626
 
3448
2627
  const defaultWidgetOptions$e = {
@@ -3458,11 +2637,9 @@ const matcherWidgetLogic = {
3458
2637
  getPublicWidgetOptions: getMatcherPublicWidgetOptions
3459
2638
  };
3460
2639
 
2640
+ const _excluded$5 = ["answers"];
3461
2641
  function getMatrixPublicWidgetOptions(options) {
3462
- const {
3463
- answers: _,
3464
- ...publicOptions
3465
- } = options;
2642
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$5);
3466
2643
  return publicOptions;
3467
2644
  }
3468
2645
 
@@ -3479,6 +2656,7 @@ const matrixWidgetLogic = {
3479
2656
  getPublicWidgetOptions: getMatrixPublicWidgetOptions
3480
2657
  };
3481
2658
 
2659
+ const _excluded$4 = ["imageUrl", "imageTop", "imageLeft"];
3482
2660
  const currentVersion$1 = {
3483
2661
  major: 1,
3484
2662
  minor: 0
@@ -3486,19 +2664,18 @@ const currentVersion$1 = {
3486
2664
  const widgetOptionsUpgrades = {
3487
2665
  "1": v0options => {
3488
2666
  const {
3489
- imageUrl,
3490
- imageTop,
3491
- imageLeft,
3492
- ...rest
3493
- } = v0options;
3494
- return {
3495
- ...rest,
2667
+ imageUrl,
2668
+ imageTop,
2669
+ imageLeft
2670
+ } = v0options,
2671
+ rest = _objectWithoutPropertiesLoose(v0options, _excluded$4);
2672
+ return _extends({}, rest, {
3496
2673
  image: {
3497
2674
  url: imageUrl,
3498
2675
  top: imageTop,
3499
2676
  left: imageLeft
3500
2677
  }
3501
- };
2678
+ });
3502
2679
  }
3503
2680
  };
3504
2681
  const defaultWidgetOptions$c = {
@@ -3519,12 +2696,9 @@ const measurerWidgetLogic = {
3519
2696
  defaultWidgetOptions: defaultWidgetOptions$c
3520
2697
  };
3521
2698
 
2699
+ const _excluded$3 = ["correctX", "correctRel"];
3522
2700
  function getNumberLinePublicWidgetOptions(options) {
3523
- const {
3524
- correctX: _,
3525
- correctRel: __,
3526
- ...publicOptions
3527
- } = options;
2701
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$3);
3528
2702
  return publicOptions;
3529
2703
  }
3530
2704
 
@@ -3548,15 +2722,7 @@ const numberLineWidgetLogic = {
3548
2722
  getPublicWidgetOptions: getNumberLinePublicWidgetOptions
3549
2723
  };
3550
2724
 
3551
- /**
3552
- * For details on the individual options, see the
3553
- * PerseusNumericInputWidgetOptions type
3554
- */
3555
-
3556
- /**
3557
- * This data from `answers` is used pre-scoring to give hints
3558
- * to the learner regarding the format of accepted answers
3559
- */
2725
+ const _excluded$2 = ["answers"];
3560
2726
  function getNumericInputAnswerPublicData(answer) {
3561
2727
  const {
3562
2728
  answerForms,
@@ -3569,20 +2735,14 @@ function getNumericInputAnswerPublicData(answer) {
3569
2735
  status
3570
2736
  };
3571
2737
  }
3572
-
3573
- /**
3574
- * Given a PerseusNumericInputWidgetOptions object, return a new object with only
3575
- * the public options that should be exposed to the client.
3576
- */
3577
2738
  function getNumericInputPublicWidgetOptions(options) {
3578
2739
  const {
3579
- answers,
3580
- ...publicWidgetOptions
3581
- } = options;
3582
- return {
3583
- ...publicWidgetOptions,
2740
+ answers
2741
+ } = options,
2742
+ publicWidgetOptions = _objectWithoutPropertiesLoose(options, _excluded$2);
2743
+ return _extends({}, publicWidgetOptions, {
3584
2744
  answers: answers.map(getNumericInputAnswerPublicData)
3585
- };
2745
+ });
3586
2746
  }
3587
2747
 
3588
2748
  const defaultWidgetOptions$a = {
@@ -3607,15 +2767,6 @@ const numericInputWidgetLogic = {
3607
2767
  getPublicWidgetOptions: getNumericInputPublicWidgetOptions
3608
2768
  };
3609
2769
 
3610
- /**
3611
- * For details on the individual options, see the
3612
- * PerseusOrdererWidgetOptions type
3613
- */
3614
-
3615
- /**
3616
- * Given a PerseusOrdererWidgetOptions object, return a new object with only
3617
- * the public options that should be exposed to the client.
3618
- */
3619
2770
  function getOrdererPublicWidgetOptions(options) {
3620
2771
  return {
3621
2772
  options: options.options,
@@ -3686,20 +2837,9 @@ const phetSimulationWidgetLogic = {
3686
2837
  defaultWidgetOptions: defaultWidgetOptions$5
3687
2838
  };
3688
2839
 
3689
- /**
3690
- * For details on the individual options, see the
3691
- * PerseusPlotterWidgetOptions type
3692
- */
3693
-
3694
- /**
3695
- * Given a PerseusPlotterWidgetOptions object, return a new object with only
3696
- * the public options that should be exposed to the client.
3697
- */
2840
+ const _excluded$1 = ["correct"];
3698
2841
  function getPlotterPublicWidgetOptions(options) {
3699
- const {
3700
- correct: _,
3701
- ...publicOptions
3702
- } = options;
2842
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$1);
3703
2843
  return publicOptions;
3704
2844
  }
3705
2845
 
@@ -3733,19 +2873,6 @@ const pythonProgramWidgetLogic = {
3733
2873
  defaultWidgetOptions: defaultWidgetOptions$3
3734
2874
  };
3735
2875
 
3736
- /**
3737
- * For details on the individual options, see the
3738
- * PerseusRadioWidgetOptions type
3739
- */
3740
-
3741
- /**
3742
- * Only the options from each Radio choice that should be exposed to the client.
3743
- */
3744
-
3745
- /**
3746
- * Given a PerseusRadioChoice object, return a new object with only the public
3747
- * data that should be included in the Radio public widget options.
3748
- */
3749
2876
  function getRadioChoicePublicData(choice) {
3750
2877
  const {
3751
2878
  content,
@@ -3758,23 +2885,9 @@ function getRadioChoicePublicData(choice) {
3758
2885
  widgets
3759
2886
  };
3760
2887
  }
3761
-
3762
- /**
3763
- * Shared functionality to determine if numCorrect is used, because:
3764
- *
3765
- * 1. numCorrect is conditionally used for rendering pre-scoring
3766
- * 2. numCorrect also exposes information about answers
3767
- *
3768
- * So only include/use numCorrect when we know it's useful.
3769
- */
3770
2888
  function usesNumCorrect(multipleSelect, countChoices, numCorrect) {
3771
2889
  return multipleSelect && countChoices && numCorrect;
3772
2890
  }
3773
-
3774
- /**
3775
- * Given a PerseusRadioWidgetOptions object, return a new object with only
3776
- * the public options that should be exposed to the client.
3777
- */
3778
2891
  function getRadioPublicWidgetOptions(options) {
3779
2892
  const {
3780
2893
  numCorrect,
@@ -3782,11 +2895,10 @@ function getRadioPublicWidgetOptions(options) {
3782
2895
  multipleSelect,
3783
2896
  countChoices
3784
2897
  } = options;
3785
- return {
3786
- ...options,
2898
+ return _extends({}, options, {
3787
2899
  numCorrect: usesNumCorrect(multipleSelect, countChoices, numCorrect) ? numCorrect : undefined,
3788
2900
  choices: choices.map(getRadioChoicePublicData)
3789
- };
2901
+ });
3790
2902
  }
3791
2903
 
3792
2904
  const radioWidgetLogic = {
@@ -3797,28 +2909,12 @@ const radioWidgetLogic = {
3797
2909
  getPublicWidgetOptions: getRadioPublicWidgetOptions
3798
2910
  };
3799
2911
 
3800
- /**
3801
- * For details on the individual options, see the
3802
- * PerseusSorterWidgetOptions type
3803
- */
3804
-
3805
- /**
3806
- * Given a PerseusSorterWidgetOptions object, return a new object with only
3807
- * the public options that should be exposed to the client.
3808
- */
3809
2912
  function getSorterPublicWidgetOptions(options) {
3810
- const shuffledCorrect = perseusCore.shuffle(options.correct, Math.random, /* ensurePermuted */true);
3811
- return {
3812
- ...options,
3813
- // Note(Tamara): This does not provide correct answer information any longer.
3814
- // To maintain compatibility with the original widget options, we are
3815
- // 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, {
3816
2915
  correct: shuffledCorrect,
3817
- // Note(Tamara): This new key is only added here with "true". There isn't
3818
- // a place where it is set to false. It indicates that the correct field
3819
- // has been shuffled and no longer contains correct answer info.
3820
2916
  isCorrectShuffled: true
3821
- };
2917
+ });
3822
2918
  }
3823
2919
 
3824
2920
  const defaultWidgetOptions$2 = {
@@ -3832,19 +2928,14 @@ const sorterWidgetLogic = {
3832
2928
  getPublicWidgetOptions: getSorterPublicWidgetOptions
3833
2929
  };
3834
2930
 
2931
+ const _excluded = ["answers"];
3835
2932
  function getTablePublicWidgetOptions(options) {
3836
- const {
3837
- answers: _,
3838
- ...publicOptions
3839
- } = options;
2933
+ const publicOptions = _objectWithoutPropertiesLoose(options, _excluded);
3840
2934
  return publicOptions;
3841
2935
  }
3842
2936
 
3843
2937
  const defaultRows = 4;
3844
2938
  const defaultColumns = 1;
3845
-
3846
- // initialize a 2D array
3847
- // (defaultRows x defaultColumns) of empty strings
3848
2939
  const answers = new Array(defaultRows).fill(0).map(() => new Array(defaultColumns).fill(""));
3849
2940
  const defaultWidgetOptions$1 = {
3850
2941
  headers: [""],
@@ -3878,60 +2969,34 @@ function isWidgetRegistered(type) {
3878
2969
  }
3879
2970
  function getCurrentVersion(type) {
3880
2971
  const widgetLogic = widgets[type];
3881
- return widgetLogic?.version || {
2972
+ return (widgetLogic == null ? void 0 : widgetLogic.version) || {
3882
2973
  major: 0,
3883
2974
  minor: 0
3884
2975
  };
3885
2976
  }
3886
-
3887
- // TODO(LEMS-2870): getPublicWidgetOptionsFunction/PublicWidgetOptionsFunction
3888
- // need better types
3889
2977
  const getPublicWidgetOptionsFunction = name => {
3890
- 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;
3891
2980
  };
3892
2981
  function getWidgetOptionsUpgrades(type) {
3893
2982
  const widgetLogic = widgets[type];
3894
- return widgetLogic?.widgetOptionsUpgrades || {};
2983
+ return (widgetLogic == null ? void 0 : widgetLogic.widgetOptionsUpgrades) || {};
3895
2984
  }
3896
2985
  function getDefaultWidgetOptions(type) {
3897
2986
  const widgetLogic = widgets[type];
3898
- return widgetLogic?.defaultWidgetOptions || {};
2987
+ return (widgetLogic == null ? void 0 : widgetLogic.defaultWidgetOptions) || {};
3899
2988
  }
3900
-
3901
- /**
3902
- * Handling for the optional alignments for widgets
3903
- * See widget-container.jsx for details on how alignments are implemented.
3904
- */
3905
-
3906
- /**
3907
- * Returns the list of supported alignments for the given (string) widget
3908
- * type. This is used primarily at editing time to display the choices
3909
- * for the user.
3910
- *
3911
- * Supported alignments are given as an array of strings in the exports of
3912
- * a widget's module.
3913
- */
3914
2989
  const getSupportedAlignments = type => {
2990
+ var _widgetLogic$supporte;
3915
2991
  const widgetLogic = widgets[type];
3916
- if (!widgetLogic?.supportedAlignments?.[0]) {
3917
- // default alignments
2992
+ if (!(widgetLogic != null && (_widgetLogic$supporte = widgetLogic.supportedAlignments) != null && _widgetLogic$supporte[0])) {
3918
2993
  return ["default"];
3919
2994
  }
3920
- return widgetLogic?.supportedAlignments;
2995
+ return widgetLogic == null ? void 0 : widgetLogic.supportedAlignments;
3921
2996
  };
3922
-
3923
- /**
3924
- * For the given (string) widget type, determine the default alignment for
3925
- * the widget. This is used at rendering time to go from "default" alignment
3926
- * to the actual alignment displayed on the screen.
3927
- *
3928
- * The default alignment is given either as a string (called
3929
- * `defaultAlignment`) or a function (called `getDefaultAlignment`) on
3930
- * the exports of a widget's module.
3931
- */
3932
2997
  const getDefaultAlignment = type => {
3933
2998
  const widgetLogic = widgets[type];
3934
- if (!widgetLogic?.defaultAlignment) {
2999
+ if (!(widgetLogic != null && widgetLogic.defaultAlignment)) {
3935
3000
  return "block";
3936
3001
  }
3937
3002
  return widgetLogic.defaultAlignment;
@@ -3971,63 +3036,35 @@ registerWidget("video", videoWidgetLogic);
3971
3036
 
3972
3037
  var coreWidgetRegistry = /*#__PURE__*/Object.freeze({
3973
3038
  __proto__: null,
3974
- isWidgetRegistered: isWidgetRegistered,
3975
3039
  getCurrentVersion: getCurrentVersion,
3976
- getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
3977
- getWidgetOptionsUpgrades: getWidgetOptionsUpgrades,
3040
+ getDefaultAlignment: getDefaultAlignment,
3978
3041
  getDefaultWidgetOptions: getDefaultWidgetOptions,
3042
+ getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
3979
3043
  getSupportedAlignments: getSupportedAlignments,
3980
- getDefaultAlignment: getDefaultAlignment
3044
+ getWidgetOptionsUpgrades: getWidgetOptionsUpgrades,
3045
+ isWidgetRegistered: isWidgetRegistered
3981
3046
  });
3982
3047
 
3983
3048
  const DEFAULT_STATIC = false;
3984
3049
  const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
3985
3050
  const type = oldWidgetInfo.type;
3986
- // NOTE(jeremy): This looks like it could be replaced by fixing types so
3987
- // that `type` is non-optional. But we're seeing this in Sentry today so I
3988
- // suspect we have legacy data (potentially unpublished) and we should
3989
- // figure that out before depending solely on types.
3990
- if (!___default["default"].isString(type)) {
3051
+ if (!_.isString(type)) {
3991
3052
  throw new PerseusError("widget type must be a string, but was: " + type, Errors.Internal);
3992
3053
  }
3993
3054
  if (!isWidgetRegistered(type)) {
3994
- // If we have a widget that isn't registered, we can't upgrade it
3995
- // TODO(aria): Figure out what the best thing to do here would be
3996
3055
  return oldWidgetInfo;
3997
3056
  }
3998
-
3999
- // Unversioned widgets (pre-July 2014) are all implicitly 0.0
4000
3057
  const initialVersion = oldWidgetInfo.version || {
4001
3058
  major: 0,
4002
3059
  minor: 0
4003
3060
  };
4004
3061
  const latestVersion = getCurrentVersion(type);
4005
-
4006
- // If the widget version is later than what we understand (major
4007
- // version is higher than latest, or major versions are equal and minor
4008
- // version is higher than latest), don't perform any upgrades.
4009
3062
  if (initialVersion.major > latestVersion.major || initialVersion.major === latestVersion.major && initialVersion.minor > latestVersion.minor) {
4010
3063
  return oldWidgetInfo;
4011
3064
  }
4012
-
4013
- // We do a clone here so that it's safe to mutate the input parameter
4014
- // in propUpgrades functions (which I will probably accidentally do at
4015
- // some point, and we would like to not break when that happens).
4016
- let newEditorOptions = ___default["default"].clone(oldWidgetInfo.options) || {};
3065
+ let newEditorOptions = _.clone(oldWidgetInfo.options) || {};
4017
3066
  const upgradePropsMap = getWidgetOptionsUpgrades(type);
4018
-
4019
- // Empty props usually mean a newly created widget by the editor,
4020
- // and are always considerered up-to-date.
4021
- // Mostly, we'd rather not run upgrade functions on props that are
4022
- // not complete.
4023
- if (___default["default"].keys(newEditorOptions).length !== 0) {
4024
- // We loop through all the versions after the current version of
4025
- // the loaded widget, up to and including the latest version of the
4026
- // loaded widget, and run the upgrade function to bring our loaded
4027
- // widget's props up to that version.
4028
- // There is a little subtlety here in that we call
4029
- // upgradePropsMap[1] to upgrade *to* version 1,
4030
- // (not from version 1).
3067
+ if (_.keys(newEditorOptions).length !== 0) {
4031
3068
  for (let nextVersion = initialVersion.major + 1; nextVersion <= latestVersion.major; nextVersion++) {
4032
3069
  if (upgradePropsMap[String(nextVersion)]) {
4033
3070
  newEditorOptions = upgradePropsMap[String(nextVersion)](newEditorOptions);
@@ -4043,23 +3080,12 @@ const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
4043
3080
  }
4044
3081
  }
4045
3082
  }
4046
-
4047
- // Minor version upgrades (eg. new optional props) don't have
4048
- // transform functions. Instead, we fill in the new props with their
4049
- // defaults.
4050
3083
  const defaultOptions = getDefaultWidgetOptions(type);
4051
- newEditorOptions = {
4052
- ...defaultOptions,
4053
- ...newEditorOptions
4054
- };
3084
+ newEditorOptions = _extends({}, defaultOptions, newEditorOptions);
4055
3085
  let alignment = oldWidgetInfo.alignment;
4056
-
4057
- // Widgets that support multiple alignments will "lock in" the
4058
- // alignment to the alignment that would be listed first in the
4059
- // select box. If the widget only supports one alignment, the
4060
- // alignment value will likely just end up as "default".
4061
3086
  if (alignment == null || alignment === "default") {
4062
- alignment = getSupportedAlignments(type)?.[0];
3087
+ var _getSupportedAlignmen;
3088
+ alignment = (_getSupportedAlignmen = getSupportedAlignments(type)) == null ? void 0 : _getSupportedAlignmen[0];
4063
3089
  if (!alignment) {
4064
3090
  throw new PerseusError("No default alignment found when upgrading widget", Errors.Internal, {
4065
3091
  metadata: {
@@ -4072,200 +3098,46 @@ const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
4072
3098
  if (widgetStatic == null) {
4073
3099
  widgetStatic = DEFAULT_STATIC;
4074
3100
  }
4075
- return {
4076
- ...oldWidgetInfo,
4077
- // maintain other info, like type
4078
- // After upgrading we guarantee that the version is up-to-date
3101
+ return _extends({}, oldWidgetInfo, {
4079
3102
  version: latestVersion,
4080
- // Default graded to true (so null/undefined becomes true):
4081
3103
  graded: oldWidgetInfo.graded != null ? oldWidgetInfo.graded : true,
4082
3104
  alignment: alignment,
4083
3105
  static: widgetStatic,
4084
3106
  options: newEditorOptions
4085
- };
3107
+ });
4086
3108
  };
4087
3109
  function getUpgradedWidgetOptions(oldWidgetOptions) {
4088
3110
  return mapObject(oldWidgetOptions, (widgetInfo, widgetId) => {
4089
3111
  if (!widgetInfo.type || !widgetInfo.alignment) {
4090
3112
  const newValues = {};
4091
3113
  if (!widgetInfo.type) {
4092
- // TODO: why does widget have no type?
4093
- // We don't want to derive type from widget ID
4094
- // see: LEMS-1845
4095
3114
  newValues.type = widgetId.split(" ")[0];
4096
3115
  }
4097
3116
  if (!widgetInfo.alignment) {
4098
3117
  newValues.alignment = "default";
4099
3118
  }
4100
- widgetInfo = {
4101
- ...widgetInfo,
4102
- ...newValues
4103
- };
3119
+ widgetInfo = _extends({}, widgetInfo, newValues);
4104
3120
  }
4105
3121
  return upgradeWidgetInfoToLatestVersion(widgetInfo);
4106
3122
  });
4107
3123
  }
4108
3124
 
4109
- /**
4110
- * Return a copy of a Perseus item with rubric data removed (ie answers)
4111
- *
4112
- * @param originalItem - the original, full Perseus item (which includes the rubric - aka answer data)
4113
- */
4114
3125
  function splitPerseusItem(originalItem) {
4115
- const item = ___default["default"].clone(originalItem);
4116
- const originalWidgets = item.widgets ?? {};
3126
+ var _item$widgets;
3127
+ const item = _.clone(originalItem);
3128
+ const originalWidgets = (_item$widgets = item.widgets) != null ? _item$widgets : {};
4117
3129
  const upgradedWidgets = getUpgradedWidgetOptions(originalWidgets);
4118
3130
  const splitWidgets = {};
4119
3131
  for (const [id, widget] of Object.entries(upgradedWidgets)) {
4120
3132
  const publicWidgetOptionsFun = getPublicWidgetOptionsFunction(widget.type);
4121
- splitWidgets[id] = {
4122
- ...widget,
3133
+ splitWidgets[id] = _extends({}, widget, {
4123
3134
  options: publicWidgetOptionsFun(widget.options)
4124
- };
3135
+ });
4125
3136
  }
4126
- return {
4127
- ...item,
3137
+ return _extends({}, item, {
4128
3138
  widgets: splitWidgets
4129
- };
4130
- }
4131
-
4132
- /* Note(tamara): Brought over from the perseus package packages/perseus/src/util.ts file.
4133
- May be useful to bring other perseus package utilities here. Contains utility functions
4134
- and types used across multiple widgets for randomization and shuffling. */
4135
- const seededRNG = function (seed) {
4136
- let randomSeed = seed;
4137
- return function () {
4138
- // Robert Jenkins' 32 bit integer hash function.
4139
- let seed = randomSeed;
4140
- seed = seed + 0x7ed55d16 + (seed << 12) & 0xffffffff;
4141
- seed = (seed ^ 0xc761c23c ^ seed >>> 19) & 0xffffffff;
4142
- seed = seed + 0x165667b1 + (seed << 5) & 0xffffffff;
4143
- seed = (seed + 0xd3a2646c ^ seed << 9) & 0xffffffff;
4144
- seed = seed + 0xfd7046c5 + (seed << 3) & 0xffffffff;
4145
- seed = (seed ^ 0xb55a4f09 ^ seed >>> 16) & 0xffffffff;
4146
- return (randomSeed = seed & 0xfffffff) / 0x10000000;
4147
- };
4148
- };
4149
-
4150
- // Shuffle an array using a given random seed or function.
4151
- // If `ensurePermuted` is true, the input and output are guaranteed to be
4152
- // distinct permutations.
4153
- function shuffle(array, randomSeed) {
4154
- let ensurePermuted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
4155
- // Always return a copy of the input array
4156
- const shuffled = ___default["default"].clone(array);
4157
-
4158
- // Handle edge cases (input array is empty or uniform)
4159
- if (!shuffled.length || ___default["default"].all(shuffled, function (value) {
4160
- return ___default["default"].isEqual(value, shuffled[0]);
4161
- })) {
4162
- return shuffled;
4163
- }
4164
- let random;
4165
- if (typeof randomSeed === "function") {
4166
- random = randomSeed;
4167
- } else {
4168
- random = seededRNG(randomSeed);
4169
- }
4170
- do {
4171
- // Fischer-Yates shuffle
4172
- for (let top = shuffled.length; top > 0; top--) {
4173
- const newEnd = Math.floor(random() * top);
4174
- const temp = shuffled[newEnd];
4175
-
4176
- // @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
4177
- shuffled[newEnd] = shuffled[top - 1];
4178
- // @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
4179
- shuffled[top - 1] = temp;
4180
- }
4181
- } while (ensurePermuted && ___default["default"].isEqual(array, shuffled));
4182
- return shuffled;
3139
+ });
4183
3140
  }
4184
- const random = seededRNG(new Date().getTime() & 0xffffffff);
4185
3141
 
4186
- exports.CoreWidgetRegistry = coreWidgetRegistry;
4187
- exports.Errors = Errors;
4188
- exports.GrapherUtil = grapherUtil;
4189
- exports.ItemExtras = ItemExtras;
4190
- exports.PerseusError = PerseusError;
4191
- exports.PerseusExpressionAnswerFormConsidered = PerseusExpressionAnswerFormConsidered;
4192
- exports.addWidget = addWidget;
4193
- exports.approximateDeepEqual = approximateDeepEqual;
4194
- exports.approximateEqual = approximateEqual;
4195
- exports.categorizerLogic = categorizerWidgetLogic;
4196
- exports.csProgramLogic = csProgramWidgetLogic;
4197
- exports.deepClone = deepClone;
4198
- exports.definitionLogic = definitionWidgetLogic;
4199
- exports.deriveExtraKeys = deriveExtraKeys;
4200
- exports.deriveNumCorrect = deriveNumCorrect;
4201
- exports.dropdownLogic = dropdownWidgetLogic;
4202
- exports.explanationLogic = explanationWidgetLogic;
4203
- exports.expressionLogic = expressionWidgetLogic;
4204
- exports.getCSProgramPublicWidgetOptions = getCSProgramPublicWidgetOptions;
4205
- exports.getCategorizerPublicWidgetOptions = getCategorizerPublicWidgetOptions;
4206
- exports.getDecimalSeparator = getDecimalSeparator;
4207
- exports.getDropdownPublicWidgetOptions = getDropdownPublicWidgetOptions;
4208
- exports.getExpressionPublicWidgetOptions = getExpressionPublicWidgetOptions;
4209
- exports.getGrapherPublicWidgetOptions = getGrapherPublicWidgetOptions;
4210
- exports.getIFramePublicWidgetOptions = getIFramePublicWidgetOptions;
4211
- exports.getInteractiveGraphPublicWidgetOptions = getInteractiveGraphPublicWidgetOptions;
4212
- exports.getLabelImagePublicWidgetOptions = getLabelImagePublicWidgetOptions;
4213
- exports.getMatcherPublicWidgetOptions = getMatcherPublicWidgetOptions;
4214
- exports.getMatrixPublicWidgetOptions = getMatrixPublicWidgetOptions;
4215
- exports.getMatrixSize = getMatrixSize;
4216
- exports.getNumberLinePublicWidgetOptions = getNumberLinePublicWidgetOptions;
4217
- exports.getNumericInputPublicWidgetOptions = getNumericInputPublicWidgetOptions;
4218
- exports.getOrdererPublicWidgetOptions = getOrdererPublicWidgetOptions;
4219
- exports.getPlotterPublicWidgetOptions = getPlotterPublicWidgetOptions;
4220
- exports.getRadioPublicWidgetOptions = getRadioPublicWidgetOptions;
4221
- exports.getSorterPublicWidgetOptions = getSorterPublicWidgetOptions;
4222
- exports.getTablePublicWidgetOptions = getTablePublicWidgetOptions;
4223
- exports.getUpgradedWidgetOptions = getUpgradedWidgetOptions;
4224
- exports.getWidgetIdsFromContent = getWidgetIdsFromContent;
4225
- exports.getWidgetIdsFromContentByType = getWidgetIdsFromContentByType;
4226
- exports.gradedGroupLogic = gradedGroupWidgetLogic;
4227
- exports.gradedGroupSetLogic = gradedGroupSetWidgetLogic;
4228
- exports.grapherLogic = grapherWidgetLogic;
4229
- exports.groupLogic = groupWidgetLogic;
4230
- exports.iframeLogic = iframeWidgetLogic;
4231
- exports.imageLogic = imageWidgetLogic;
4232
- exports.inputNumberLogic = inputNumberWidgetLogic;
4233
- exports.interactionLogic = interactionWidgetLogic;
4234
- exports.interactiveGraphLogic = interactiveGraphWidgetLogic;
4235
- exports.isFailure = isFailure;
4236
- exports.isSuccess = isSuccess;
4237
- exports.labelImageLogic = labelImageWidgetLogic;
4238
- exports.libVersion = libVersion;
4239
- exports.lockedFigureColorNames = lockedFigureColorNames;
4240
- exports.lockedFigureColors = lockedFigureColors;
4241
- exports.lockedFigureFillStyles = lockedFigureFillStyles;
4242
- exports.mapObject = mapObject;
4243
- exports.matcherLogic = matcherWidgetLogic;
4244
- exports.matrixLogic = matrixWidgetLogic;
4245
- exports.measurerLogic = measurerWidgetLogic;
4246
- exports.numberLineLogic = numberLineWidgetLogic;
4247
- exports.numericInputLogic = numericInputWidgetLogic;
4248
- exports.ordererLogic = ordererWidgetLogic;
4249
- exports.parseAndMigratePerseusArticle = parseAndMigratePerseusArticle;
4250
- exports.parseAndMigratePerseusItem = parseAndMigratePerseusItem;
4251
- exports.parsePerseusItem = parsePerseusItem;
4252
- exports.passageLogic = passageWidgetLogic;
4253
- exports.passageRefLogic = passageRefWidgetLogic;
4254
- exports.passageRefTargetLogic = passageRefTargetWidgetLogic;
4255
- exports.phetSimulationLogic = phetSimulationWidgetLogic;
4256
- exports.plotterLogic = plotterWidgetLogic;
4257
- exports.plotterPlotTypes = plotterPlotTypes;
4258
- exports.pluck = pluck;
4259
- exports.pythonProgramLogic = pythonProgramWidgetLogic;
4260
- exports.radioLogic = radioWidgetLogic;
4261
- exports.random = random;
4262
- exports.seededRNG = seededRNG;
4263
- exports.shuffle = shuffle;
4264
- exports.shuffleMatcher = shuffleMatcher;
4265
- exports.sorterLogic = sorterWidgetLogic;
4266
- exports.splitPerseusItem = splitPerseusItem;
4267
- exports.tableLogic = tableWidgetLogic;
4268
- exports.upgradeWidgetInfoToLatestVersion = upgradeWidgetInfoToLatestVersion;
4269
- exports.usesNumCorrect = usesNumCorrect;
4270
- 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 };
4271
3143
  //# sourceMappingURL=index.js.map