@khanacademy/perseus 49.1.6 → 49.2.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
@@ -487,12 +487,12 @@ const mockStrings = {
487
487
  angleMeasure,
488
488
  vertexX,
489
489
  vertexY,
490
- isX,
491
- isY,
492
- tsX,
493
- tsY
490
+ startingSideX,
491
+ startingSideY,
492
+ endingSideX,
493
+ endingSideY
494
494
  } = _ref40;
495
- return `The angle measure is ${angleMeasure} degrees with a vertex at ${vertexX} comma ${vertexY}, a point on the initial side at ${isX} comma ${isY} and a point on the terminal side at ${tsX} comma ${tsY}.`;
495
+ return `The angle measure is ${angleMeasure} degrees with a vertex at ${vertexX} comma ${vertexY}, a point on the starting side at ${startingSideX} comma ${startingSideY} and a point on the ending side at ${endingSideX} comma ${endingSideY}.`;
496
496
  }
497
497
  // The above strings are used for interactive graph SR descriptions.
498
498
  };
@@ -897,6 +897,9 @@ const mapButtonSets = buttonSets => {
897
897
  case "trig":
898
898
  keypadButtonSets.trigonometry = true;
899
899
  break;
900
+ case "scientific":
901
+ keypadButtonSets.scientific = true;
902
+ break;
900
903
  }
901
904
  });
902
905
  return keypadButtonSets;
@@ -3971,7 +3974,9 @@ ZoomServiceClass.prototype._scrollHandler = function (e) {
3971
3974
  }
3972
3975
  };
3973
3976
  ZoomServiceClass.prototype._keyHandler = function (e) {
3974
- if (e.keyCode === 27) {
3977
+ // 27: Esc, 13: Enter, 32: Space
3978
+ const keyCodes = [27, 13, 32];
3979
+ if (keyCodes.includes(e.keyCode)) {
3975
3980
  this._activeZoomClose();
3976
3981
  }
3977
3982
  };
@@ -4060,6 +4065,7 @@ Zoom.prototype.zoomImage = function () {
4060
4065
  }.bind(this);
4061
4066
  img.src = this._targetImage.src;
4062
4067
  img.alt = this._targetImage.alt;
4068
+ img.tabIndex = 0;
4063
4069
  this.$zoomedImage = $zoomedImage;
4064
4070
  };
4065
4071
  Zoom.prototype._zoomOriginal = function () {
@@ -17242,6 +17248,7 @@ class ImageLoader extends React__namespace.Component {
17242
17248
  }
17243
17249
  const staticUrl = getDependencies().staticUrl;
17244
17250
  return /*#__PURE__*/React__namespace.createElement("img", _extends({
17251
+ tabIndex: 0,
17245
17252
  src: staticUrl(src),
17246
17253
  onKeyUp: onKeyUp,
17247
17254
  onKeyDown: onKeyDown
@@ -22456,7 +22463,7 @@ function scoreNumericInput(userInput, rubric, strings) {
22456
22463
  // We may have received TeX; try to parse it before grading.
22457
22464
  // If `currentValue` is not TeX, this should be a no-op.
22458
22465
  const currentValue = ParseTex(userInput.currentValue);
22459
- const normalizedAnswerExpected = rubric.answers.filter(answer => answer.status === "correct").every(answer => answer.value !== undefined && Math.abs(answer.value) <= 1);
22466
+ const normalizedAnswerExpected = rubric.answers.filter(answer => answer.status === "correct").every(answer => answer.value != null && Math.abs(answer.value) <= 1);
22460
22467
 
22461
22468
  // The coefficient is an attribute of the widget
22462
22469
  let localValue = currentValue;
@@ -27704,8 +27711,7 @@ class Dropdown extends React__namespace.Component {
27704
27711
  disabled: true,
27705
27712
  label: /*#__PURE__*/React__namespace.createElement(Renderer, {
27706
27713
  content: this.props.placeholder,
27707
- strings: this.context.strings,
27708
- inline: true
27714
+ strings: this.context.strings
27709
27715
  }),
27710
27716
  labelAsText: this.props.placeholder
27711
27717
  }), ...this.props.choices.map((choice, i) => /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
@@ -27713,8 +27719,7 @@ class Dropdown extends React__namespace.Component {
27713
27719
  value: String(i + 1),
27714
27720
  label: /*#__PURE__*/React__namespace.createElement(Renderer, {
27715
27721
  content: choice,
27716
- strings: this.context.strings,
27717
- inline: true
27722
+ strings: this.context.strings
27718
27723
  }),
27719
27724
  labelAsText: choice
27720
27725
  }))];
@@ -27734,6 +27739,7 @@ class Dropdown extends React__namespace.Component {
27734
27739
  }, this.props.visibleLabel), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
27735
27740
  id: dropdownId,
27736
27741
  placeholder: "",
27742
+ className: "perseus-dropdown",
27737
27743
  onChange: value => this._handleChange(parseInt(value)),
27738
27744
  selectedValue: String(this.props.selected),
27739
27745
  disabled: this.props.apiOptions.readOnly,
@@ -34461,10 +34467,10 @@ function AngleGraph(props) {
34461
34467
  angleMeasure,
34462
34468
  vertexX: srFormatNumber(coords[1][X], locale),
34463
34469
  vertexY: srFormatNumber(coords[1][Y], locale),
34464
- isX: srFormatNumber(coords[2][X], locale),
34465
- isY: srFormatNumber(coords[2][Y], locale),
34466
- tsX: srFormatNumber(coords[0][X], locale),
34467
- tsY: srFormatNumber(coords[0][Y], locale)
34470
+ startingSideX: srFormatNumber(coords[2][X], locale),
34471
+ startingSideY: srFormatNumber(coords[2][Y], locale),
34472
+ endingSideX: srFormatNumber(coords[0][X], locale),
34473
+ endingSideY: srFormatNumber(coords[0][Y], locale)
34468
34474
  });
34469
34475
  const formatCoordinates = (x, y) => ({
34470
34476
  x: srFormatNumber(x, locale),
@@ -45938,7 +45944,7 @@ const init = function () {
45938
45944
 
45939
45945
  // This file is processed by a Rollup plugin (replace) to inject the production
45940
45946
  const libName = "@khanacademy/perseus";
45941
- const libVersion = "49.1.6";
45947
+ const libVersion = "49.2.0";
45942
45948
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45943
45949
 
45944
45950
  const apiVersion = {
@@ -48238,10 +48244,10 @@ class UnionBuilder {
48238
48244
  this.parser = parser;
48239
48245
  }
48240
48246
  or(newBranch) {
48241
- return new UnionBuilder(either(this.parser, newBranch));
48247
+ return new UnionBuilder(either$1(this.parser, newBranch));
48242
48248
  }
48243
48249
  }
48244
- function either(parseA, parseB) {
48250
+ function either$1(parseA, parseB) {
48245
48251
  return (rawValue, ctx) => {
48246
48252
  const resultA = parseA(rawValue, ctx);
48247
48253
  if (isSuccess(resultA)) {
@@ -48343,22 +48349,108 @@ const parseExplanationWidget = parseWidget(constant("explanation"), object({
48343
48349
  // We wrap parseWidgetsMap in a function here to make sure it is not
48344
48350
  // referenced before it is defined. There is an import cycle between
48345
48351
  // this file and widgets-map.ts that could cause it to be undefined.
48346
- widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
48347
- static: boolean
48352
+ widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
48353
+ static: defaulted(boolean, () => false)
48348
48354
  }));
48349
48355
 
48350
- const parseAnswerForm = object({
48351
- value: string,
48352
- form: boolean,
48353
- simplify: boolean,
48356
+ // Given a function, creates a PartialParser that converts one type to another
48357
+ // using that function. The returned parser never fails.
48358
+ function convert(f) {
48359
+ return (rawValue, ctx) => ctx.success(f(rawValue));
48360
+ }
48361
+
48362
+ /**
48363
+ * Creates a parser for a widget options type with multiple major versions. Old
48364
+ * versions are migrated to the latest version. The parse fails if the input
48365
+ * data does not match any of the versions.
48366
+ *
48367
+ * @example
48368
+ * const parseOptions = versionedWidgetOptions(parseOptionsV3)
48369
+ * .withMigrationFrom(parseOptionsV2, migrateV2ToV3)
48370
+ * .withMigrationFrom(parseOptionsV1, migrateV1ToV2)
48371
+ * .withMigrationFrom(parseOptionsV0, migrateV0ToV1)
48372
+ * .parser;
48373
+ *
48374
+ * @param parseLatest a {@link Parser} for the latest version of the widget
48375
+ * options. This should check version.major and fail if it's not the latest
48376
+ * version.
48377
+ * @returns a builder object, to which migrations from earlier versions can be
48378
+ * added. Migrations must be added in "reverse chronological" order as in the
48379
+ * example above.
48380
+ */
48381
+ function versionedWidgetOptions(parseLatest) {
48382
+ return new VersionedWidgetOptionsParserBuilder(parseLatest, latest => latest);
48383
+ }
48384
+ class VersionedWidgetOptionsParserBuilder {
48385
+ constructor(parser, migrate) {
48386
+ this.parser = parser;
48387
+ this.migrate = migrate;
48388
+ }
48389
+
48390
+ /**
48391
+ * Add a migration from an old version of the widget options.
48392
+ *
48393
+ * @returns a VersionedWidgetOptionsParserBuilder whose `parser` function
48394
+ * is capable of migrating the old version to the latest version. The parser
48395
+ * will always return the latest version of the widget options on a
48396
+ * successful parse.
48397
+ * @param parseOldVersion should be a {@link Parser} for the old options
48398
+ * type. It should fail if version.major isn't correct.
48399
+ * @param migrateToNextVersion should migrate the `Old` data to the
48400
+ * `Migratable` version of the current VersionedWidgetOptionsParserBuilder.
48401
+ * Usually, this means migrating to the next major version.
48402
+ */
48403
+ withMigrationFrom(parseOldVersion, migrateToNextVersion) {
48404
+ const parseLatestVersion = this.parser;
48405
+ const migrateToLatest = old => this.migrate(migrateToNextVersion(old));
48406
+ return new VersionedWidgetOptionsParserBuilder((raw, ctx) => {
48407
+ const resultOfParsingLatest = parseLatestVersion(raw, ctx);
48408
+ if (isSuccess(resultOfParsingLatest)) {
48409
+ return resultOfParsingLatest;
48410
+ }
48411
+ const resultOfParsingOld = parseOldVersion(raw, ctx);
48412
+ if (isSuccess(resultOfParsingOld)) {
48413
+ return success(migrateToLatest(resultOfParsingOld.value));
48414
+ }
48415
+
48416
+ // If we're here, neither parse succeeded. Return the failure.
48417
+ return resultOfParsingOld;
48418
+ }, migrateToLatest);
48419
+ }
48420
+ }
48421
+
48422
+ const parsePossiblyInvalidAnswerForm = object({
48423
+ // `value` is the possibly invalid part of this. It should always be a
48424
+ // string, but some answer forms don't have it. The Expression widget
48425
+ // ignores invalid values, so we can safely filter them out during parsing.
48426
+ value: optional(string),
48427
+ form: defaulted(boolean, () => false),
48428
+ simplify: defaulted(boolean, () => false),
48354
48429
  considered: enumeration("correct", "wrong", "ungraded"),
48355
48430
  key: pipeParsers(optional(union(string).or(number).parser)).then((key, ctx) => ctx.success(String(key))).parser
48356
48431
  });
48357
- const parseExpressionWidgetV1 = parseWidgetWithVersion(object({
48432
+ function removeInvalidAnswerForms(possiblyInvalid) {
48433
+ const valid = [];
48434
+ for (const answerForm of possiblyInvalid) {
48435
+ const {
48436
+ value
48437
+ } = answerForm;
48438
+ if (value != null) {
48439
+ // Copying the object seems to be needed to make TypeScript happy
48440
+ valid.push({
48441
+ ...answerForm,
48442
+ value
48443
+ });
48444
+ }
48445
+ }
48446
+ return valid;
48447
+ }
48448
+ const version1 = object({
48358
48449
  major: constant(1),
48359
48450
  minor: number
48360
- }), constant("expression"), object({
48361
- answerForms: array(parseAnswerForm),
48451
+ });
48452
+ const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expression"), object({
48453
+ answerForms: pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,
48362
48454
  functions: array(string),
48363
48455
  times: boolean,
48364
48456
  visibleLabel: optional(string),
@@ -48366,10 +48458,11 @@ const parseExpressionWidgetV1 = parseWidgetWithVersion(object({
48366
48458
  buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
48367
48459
  buttonsVisible: optional(enumeration("always", "never", "focused"))
48368
48460
  }));
48369
- const parseExpressionWidgetV0 = parseWidgetWithVersion(optional(object({
48461
+ const version0 = optional(object({
48370
48462
  major: constant(0),
48371
48463
  minor: number
48372
- })), constant("expression"), object({
48464
+ }));
48465
+ const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expression"), object({
48373
48466
  functions: array(string),
48374
48467
  times: boolean,
48375
48468
  visibleLabel: optional(string),
@@ -48380,11 +48473,11 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(optional(object({
48380
48473
  buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
48381
48474
  buttonsVisible: optional(enumeration("always", "never", "focused"))
48382
48475
  }));
48383
- function migrateV0ToV1(widget, ctx) {
48476
+ function migrateV0ToV1(widget) {
48384
48477
  const {
48385
48478
  options
48386
48479
  } = widget;
48387
- return ctx.success({
48480
+ return {
48388
48481
  ...widget,
48389
48482
  version: {
48390
48483
  major: 1,
@@ -48404,9 +48497,9 @@ function migrateV0ToV1(widget, ctx) {
48404
48497
  value: options.value
48405
48498
  }]
48406
48499
  }
48407
- });
48500
+ };
48408
48501
  }
48409
- const parseExpressionWidget = union(parseExpressionWidgetV1).or(pipeParsers(parseExpressionWidgetV0).then(migrateV0ToV1).parser).parser;
48502
+ const parseExpressionWidget = versionedWidgetOptions(parseExpressionWidgetV1).withMigrationFrom(parseExpressionWidgetV0, migrateV0ToV1).parser;
48410
48503
 
48411
48504
  const parsePerseusRenderer = defaulted(object({
48412
48505
  // TODO(benchristel): content is also defaulted to empty string in
@@ -48452,31 +48545,70 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
48452
48545
  gradedGroups: array(parseGradedGroupWidgetOptions)
48453
48546
  }));
48454
48547
 
48548
+ // discriminatedUnion() should be preferred over union() when parsing a
48549
+ // discriminated union type, because discriminatedUnion() produces more
48550
+ // understandable failure messages. It takes the discriminant as the source of
48551
+ // truth for which variant is to be parsed, and expects the other data to match
48552
+ // that variant.
48553
+ function discriminatedUnion(narrow, parseVariant) {
48554
+ return new DiscriminatedUnionBuilder(pipeParsers(narrow).then(parseVariant).parser);
48555
+ }
48556
+ class DiscriminatedUnionBuilder {
48557
+ constructor(parser) {
48558
+ this.parser = parser;
48559
+ }
48560
+ or(narrow, parseVariant) {
48561
+ return new DiscriminatedUnionBuilder(either(narrow, parseVariant, this.parser));
48562
+ }
48563
+ }
48564
+ function either(narrowToA, parseA, parseB) {
48565
+ return (rawValue, ctx) => {
48566
+ if (isSuccess(narrowToA(rawValue, ctx))) {
48567
+ return parseA(rawValue, ctx);
48568
+ }
48569
+ return parseB(rawValue, ctx);
48570
+ };
48571
+ }
48572
+
48455
48573
  const pairOfNumbers$3 = pair(number, number);
48456
48574
  const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
48457
48575
  const parseGrapherWidget = parseWidget(constant("grapher"), object({
48458
48576
  availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
48459
- correct: union(object({
48577
+ correct: discriminatedUnion(object({
48578
+ type: constant("absolute_value")
48579
+ }), object({
48460
48580
  type: constant("absolute_value"),
48461
48581
  coords: pairOfPoints
48462
48582
  })).or(object({
48583
+ type: constant("exponential")
48584
+ }), object({
48463
48585
  type: constant("exponential"),
48464
48586
  asymptote: pairOfPoints,
48465
48587
  coords: pairOfPoints
48466
48588
  })).or(object({
48589
+ type: constant("linear")
48590
+ }), object({
48467
48591
  type: constant("linear"),
48468
48592
  coords: pairOfPoints
48469
48593
  })).or(object({
48594
+ type: constant("logarithm")
48595
+ }), object({
48470
48596
  type: constant("logarithm"),
48471
48597
  asymptote: pairOfPoints,
48472
48598
  coords: pairOfPoints
48473
48599
  })).or(object({
48600
+ type: constant("quadratic")
48601
+ }), object({
48474
48602
  type: constant("quadratic"),
48475
48603
  coords: pairOfPoints
48476
48604
  })).or(object({
48605
+ type: constant("sinusoid")
48606
+ }), object({
48477
48607
  type: constant("sinusoid"),
48478
48608
  coords: pairOfPoints
48479
48609
  })).or(object({
48610
+ type: constant("tangent")
48611
+ }), object({
48480
48612
  type: constant("tangent"),
48481
48613
  coords: pairOfPoints
48482
48614
  })).parser,
@@ -48536,15 +48668,22 @@ const stringToNumber = (rawValue, ctx) => {
48536
48668
  return ctx.success(parsedNumber);
48537
48669
  };
48538
48670
 
48539
- const numericToNumber = pipeParsers(union(number).or(string).parser).then(stringToNumber).parser;
48671
+ function emptyToZero(x) {
48672
+ return x === "" ? 0 : x;
48673
+ }
48674
+ const imageDimensionToNumber = pipeParsers(union(number).or(string).parser)
48675
+ // In this specific case, empty string is equivalent to zero. An empty
48676
+ // string parses to either NaN (using parseInt) or 0 (using unary +) and
48677
+ // CSS will treat NaN as invalid and default to 0 instead.
48678
+ .then(convert(emptyToZero)).then(stringToNumber).parser;
48540
48679
  const parsePerseusImageBackground = object({
48541
48680
  url: optional(nullable(string)),
48542
- width: optional(numericToNumber),
48543
- height: optional(numericToNumber),
48544
- top: optional(numericToNumber),
48545
- left: optional(numericToNumber),
48546
- bottom: optional(numericToNumber),
48547
- scale: optional(numericToNumber)
48681
+ width: optional(imageDimensionToNumber),
48682
+ height: optional(imageDimensionToNumber),
48683
+ top: optional(imageDimensionToNumber),
48684
+ left: optional(imageDimensionToNumber),
48685
+ bottom: optional(imageDimensionToNumber),
48686
+ scale: optional(imageDimensionToNumber)
48548
48687
  });
48549
48688
 
48550
48689
  const pairOfNumbers$2 = pair(number, number);
@@ -48585,8 +48724,10 @@ const parseInputNumberWidget = parseWidget(constant("input-number"), object({
48585
48724
  }));
48586
48725
 
48587
48726
  const pairOfNumbers$1 = pair(number, number);
48727
+ const stringOrEmpty = defaulted(string, () => "");
48728
+ const parseFunctionType = constant("function");
48588
48729
  const parseFunctionElement = object({
48589
- type: constant("function"),
48730
+ type: parseFunctionType,
48590
48731
  key: string,
48591
48732
  options: object({
48592
48733
  value: string,
@@ -48598,8 +48739,9 @@ const parseFunctionElement = object({
48598
48739
  strokeWidth: number
48599
48740
  })
48600
48741
  });
48742
+ const parseLabelType = constant("label");
48601
48743
  const parseLabelElement = object({
48602
- type: constant("label"),
48744
+ type: parseLabelType,
48603
48745
  key: string,
48604
48746
  options: object({
48605
48747
  label: string,
@@ -48608,8 +48750,9 @@ const parseLabelElement = object({
48608
48750
  coordY: string
48609
48751
  })
48610
48752
  });
48753
+ const parseLineType = constant("line");
48611
48754
  const parseLineElement = object({
48612
- type: constant("line"),
48755
+ type: parseLineType,
48613
48756
  key: string,
48614
48757
  options: object({
48615
48758
  color: string,
@@ -48622,8 +48765,9 @@ const parseLineElement = object({
48622
48765
  arrows: string
48623
48766
  })
48624
48767
  });
48768
+ const parseMovableLineType = constant("movable-line");
48625
48769
  const parseMovableLineElement = object({
48626
- type: constant("movable-line"),
48770
+ type: parseMovableLineType,
48627
48771
  key: string,
48628
48772
  options: object({
48629
48773
  startX: string,
@@ -48641,8 +48785,9 @@ const parseMovableLineElement = object({
48641
48785
  constraintYMax: string
48642
48786
  })
48643
48787
  });
48788
+ const parseMovablePointType = constant("movable-point");
48644
48789
  const parseMovablePointElement = object({
48645
- type: constant("movable-point"),
48790
+ type: parseMovablePointType,
48646
48791
  key: string,
48647
48792
  options: object({
48648
48793
  startX: string,
@@ -48651,14 +48796,15 @@ const parseMovablePointElement = object({
48651
48796
  constraint: string,
48652
48797
  snap: number,
48653
48798
  constraintFn: string,
48654
- constraintXMin: string,
48655
- constraintXMax: string,
48656
- constraintYMin: string,
48657
- constraintYMax: string
48799
+ constraintXMin: stringOrEmpty,
48800
+ constraintXMax: stringOrEmpty,
48801
+ constraintYMin: stringOrEmpty,
48802
+ constraintYMax: stringOrEmpty
48658
48803
  })
48659
48804
  });
48805
+ const parseParametricType = constant("parametric");
48660
48806
  const parseParametricElement = object({
48661
- type: constant("parametric"),
48807
+ type: parseParametricType,
48662
48808
  key: string,
48663
48809
  options: object({
48664
48810
  x: string,
@@ -48670,8 +48816,9 @@ const parseParametricElement = object({
48670
48816
  strokeWidth: number
48671
48817
  })
48672
48818
  });
48819
+ const parsePointType = constant("point");
48673
48820
  const parsePointElement = object({
48674
- type: constant("point"),
48821
+ type: parsePointType,
48675
48822
  key: string,
48676
48823
  options: object({
48677
48824
  color: string,
@@ -48679,8 +48826,9 @@ const parsePointElement = object({
48679
48826
  coordY: string
48680
48827
  })
48681
48828
  });
48829
+ const parseRectangleType = constant("rectangle");
48682
48830
  const parseRectangleElement = object({
48683
- type: constant("rectangle"),
48831
+ type: parseRectangleType,
48684
48832
  key: string,
48685
48833
  options: object({
48686
48834
  color: string,
@@ -48691,7 +48839,7 @@ const parseRectangleElement = object({
48691
48839
  })
48692
48840
  });
48693
48841
  const parseInteractionWidget = parseWidget(constant("interaction"), object({
48694
- static: boolean,
48842
+ static: defaulted(boolean, () => false),
48695
48843
  graph: object({
48696
48844
  editableSettings: optional(array(enumeration("canvas", "graph"))),
48697
48845
  box: pairOfNumbers$1,
@@ -48708,7 +48856,23 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
48708
48856
  rulerTicks: optional(number),
48709
48857
  tickStep: pairOfNumbers$1
48710
48858
  }),
48711
- elements: array(union(parseFunctionElement).or(parseLabelElement).or(parseLineElement).or(parseMovableLineElement).or(parseMovablePointElement).or(parseParametricElement).or(parsePointElement).or(parseRectangleElement).parser)
48859
+ elements: array(discriminatedUnion(object({
48860
+ type: parseFunctionType
48861
+ }), parseFunctionElement).or(object({
48862
+ type: parseLabelType
48863
+ }), parseLabelElement).or(object({
48864
+ type: parseLineType
48865
+ }), parseLineElement).or(object({
48866
+ type: parseMovableLineType
48867
+ }), parseMovableLineElement).or(object({
48868
+ type: parseMovablePointType
48869
+ }), parseMovablePointElement).or(object({
48870
+ type: parseParametricType
48871
+ }), parseParametricElement).or(object({
48872
+ type: parsePointType
48873
+ }), parsePointElement).or(object({
48874
+ type: parseRectangleType
48875
+ }), parseRectangleElement).parser)
48712
48876
  }));
48713
48877
 
48714
48878
  // Used to represent 2-D points and ranges
@@ -48931,10 +49095,11 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
48931
49095
  padding: boolean
48932
49096
  }));
48933
49097
 
49098
+ const numeric = pipeParsers(union(number).or(string).parser).then(stringToNumber).parser;
48934
49099
  const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
48935
49100
  prefix: optional(string),
48936
49101
  suffix: optional(string),
48937
- answers: array(array(number)),
49102
+ answers: array(array(numeric)),
48938
49103
  cursorPosition: optional(array(number)),
48939
49104
  matrixBoardSize: array(number),
48940
49105
  static: optional(boolean)
@@ -48982,12 +49147,18 @@ const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "d
48982
49147
  const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
48983
49148
  answers: array(object({
48984
49149
  message: string,
48985
- value: optional(number),
49150
+ // TODO(benchristel): value should never be null or undefined,
49151
+ // but we have some content where it is anyway. If we backfill
49152
+ // the data, simplify this.
49153
+ value: optional(nullable(number)),
48986
49154
  status: string,
48987
49155
  answerForms: optional(array(parseMathFormat)),
48988
49156
  strict: boolean,
48989
49157
  maxError: optional(nullable(number)),
48990
- simplify: optional(nullable(string))
49158
+ // TODO(benchristel): simplify should never be `true`, but we
49159
+ // have some content where it is anyway. If we ever backfill
49160
+ // the data, we should simplify `simplify`.
49161
+ simplify: optional(nullable(union(string).or(pipeParsers(constant(true)).then(convert(String)).parser).parser))
48991
49162
  })),
48992
49163
  labelText: optional(string),
48993
49164
  size: string,
@@ -49023,7 +49194,7 @@ const parseOrdererWidget = parseWidget(constant("orderer"), object({
49023
49194
  const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
49024
49195
  passageNumber: number,
49025
49196
  referenceNumber: number,
49026
- summaryText: string
49197
+ summaryText: optional(string)
49027
49198
  }));
49028
49199
 
49029
49200
  const parsePassageWidget = parseWidget(constant("passage"), object({
@@ -49212,6 +49383,8 @@ const parseWidgetsMapEntry = (_ref, widgetMap, ctx) => {
49212
49383
  // sequence is a deprecated widget type, and the corresponding
49213
49384
  // widget component no longer exists.
49214
49385
  return parseAndAssign(`sequence ${id}`, parseDeprecatedWidget);
49386
+ case "lights-puzzle":
49387
+ return parseAndAssign(`lights-puzzle ${id}`, parseDeprecatedWidget);
49215
49388
  default:
49216
49389
  if (getWidget(type)) {
49217
49390
  // @ts-expect-error - 'type' is not a valid widget type