@khanacademy/perseus 49.2.0 → 49.2.2

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/es/index.js CHANGED
@@ -44,21 +44,13 @@ import cornersOutIcon from '@phosphor-icons/core/regular/corners-out.svg';
44
44
  import Link from '@khanacademy/wonder-blocks-link';
45
45
 
46
46
  function _extends() {
47
- _extends = Object.assign || function (target) {
48
- for (var i = 1; i < arguments.length; i++) {
49
- var source = arguments[i];
50
-
51
- for (var key in source) {
52
- if (Object.prototype.hasOwnProperty.call(source, key)) {
53
- target[key] = source[key];
54
- }
55
- }
47
+ return _extends = Object.assign ? Object.assign.bind() : function (n) {
48
+ for (var e = 1; e < arguments.length; e++) {
49
+ var t = arguments[e];
50
+ for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
56
51
  }
57
-
58
- return target;
59
- };
60
-
61
- return _extends.apply(this, arguments);
52
+ return n;
53
+ }, _extends.apply(null, arguments);
62
54
  }
63
55
 
64
56
  /**
@@ -4508,17 +4500,14 @@ const InteractiveUtil = {
4508
4500
  }
4509
4501
  };
4510
4502
 
4511
- function _classPrivateFieldBase(receiver, privateKey) {
4512
- if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
4513
- throw new TypeError("attempted to use private field on non-instance");
4514
- }
4515
-
4516
- return receiver;
4503
+ function _classPrivateFieldBase(e, t) {
4504
+ if (!{}.hasOwnProperty.call(e, t)) throw new TypeError("attempted to use private field on non-instance");
4505
+ return e;
4517
4506
  }
4518
4507
 
4519
4508
  var id = 0;
4520
- function _classPrivateFieldKey(name) {
4521
- return "__private_" + id++ + "_" + name;
4509
+ function _classPrivateFieldKey(e) {
4510
+ return "__private_" + id++ + "_" + e;
4522
4511
  }
4523
4512
 
4524
4513
  /*!
@@ -21145,19 +21134,14 @@ TextInput.defaultProps = {
21145
21134
  disabled: false
21146
21135
  };
21147
21136
 
21148
- function _objectWithoutPropertiesLoose(source, excluded) {
21149
- if (source == null) return {};
21150
- var target = {};
21151
- var sourceKeys = Object.keys(source);
21152
- var key, i;
21153
-
21154
- for (i = 0; i < sourceKeys.length; i++) {
21155
- key = sourceKeys[i];
21156
- if (excluded.indexOf(key) >= 0) continue;
21157
- target[key] = source[key];
21137
+ function _objectWithoutPropertiesLoose(r, e) {
21138
+ if (null == r) return {};
21139
+ var t = {};
21140
+ for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
21141
+ if (e.includes(n)) continue;
21142
+ t[n] = r[n];
21158
21143
  }
21159
-
21160
- return target;
21144
+ return t;
21161
21145
  }
21162
21146
 
21163
21147
  const _excluded$7 = ["position", "visibility", "left", "top"];
@@ -32370,7 +32354,7 @@ const LegacyGrid = ({
32370
32354
  * "new-awesomeness": MyAwesomeNewWidget;
32371
32355
  *
32372
32356
  * // A deprecated widget
32373
- * "super-old-widget": AutoCorrectWidget;
32357
+ * "super-old-widget": DeprecatedStandinWidget;
32374
32358
  * }
32375
32359
  * }
32376
32360
  *
@@ -32564,46 +32548,28 @@ function LockedLabel(props) {
32564
32548
 
32565
32549
  function GraphLockedLabelsLayer(props) {
32566
32550
  const {
32567
- flags,
32568
32551
  lockedFigures
32569
32552
  } = props;
32570
32553
  return lockedFigures.map((figure, i) => {
32571
- var _flags$mafs, _flags$mafs2, _flags$mafs3, _flags$mafs4, _flags$mafs5, _flags$mafs6;
32554
+ var _figure$labels, _figure$points$0$labe, _figure$points$1$labe;
32572
32555
  if (figure.type === "label") {
32573
32556
  return /*#__PURE__*/React.createElement(LockedLabel, _extends({
32574
32557
  key: `label-${i}`
32575
32558
  }, figure));
32576
32559
  }
32577
- if (
32578
- // Point flag + point type
32579
- flags != null && (_flags$mafs = flags["mafs"]) != null && _flags$mafs["locked-point-labels"] && figure.type === "point" ||
32580
- // Line flag + line type
32581
- flags != null && (_flags$mafs2 = flags["mafs"]) != null && _flags$mafs2["locked-line-labels"] && figure.type === "line" ||
32582
- // Vector flag + vector type
32583
- flags != null && (_flags$mafs3 = flags["mafs"]) != null && _flags$mafs3["locked-vector-labels"] && figure.type === "vector" ||
32584
- // Ellipse flag + ellipse type
32585
- flags != null && (_flags$mafs4 = flags["mafs"]) != null && _flags$mafs4["locked-ellipse-labels"] && figure.type === "ellipse" ||
32586
- // Polygon flag + polygon type
32587
- flags != null && (_flags$mafs5 = flags["mafs"]) != null && _flags$mafs5["locked-polygon-labels"] && figure.type === "polygon" ||
32588
- // Function flag + function type
32589
- flags != null && (_flags$mafs6 = flags["mafs"]) != null && _flags$mafs6["locked-function-labels"] && figure.type === "function") {
32590
- var _figure$labels, _figure$points$0$labe, _figure$points$1$labe;
32591
- return /*#__PURE__*/React.createElement(React.Fragment, {
32592
- key: i
32593
- }, (_figure$labels = figure.labels) == null ? void 0 : _figure$labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32594
- key: `${i}-label-${j}`
32595
- }, label))), figure.type === "line" && /*#__PURE__*/React.createElement(React.Fragment, null, (_figure$points$0$labe = figure.points[0].labels) == null ? void 0 : _figure$points$0$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32596
- key: `locked-figure-${i}-point-1-label-${k}`
32597
- }, label))), (_figure$points$1$labe = figure.points[1].labels) == null ? void 0 : _figure$points$1$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32598
- key: `locked-figure-${i}-point-2-label-${k}`
32599
- }, label)))));
32600
- }
32601
- return null;
32560
+ return /*#__PURE__*/React.createElement(React.Fragment, {
32561
+ key: i
32562
+ }, (_figure$labels = figure.labels) == null ? void 0 : _figure$labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32563
+ key: `${i}-label-${j}`
32564
+ }, label))), figure.type === "line" && /*#__PURE__*/React.createElement(React.Fragment, null, (_figure$points$0$labe = figure.points[0].labels) == null ? void 0 : _figure$points$0$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32565
+ key: `locked-figure-${i}-point-1-label-${k}`
32566
+ }, label))), (_figure$points$1$labe = figure.points[1].labels) == null ? void 0 : _figure$points$1$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32567
+ key: `locked-figure-${i}-point-2-label-${k}`
32568
+ }, label)))));
32602
32569
  });
32603
32570
  }
32604
32571
 
32605
32572
  const LockedEllipse = props => {
32606
- var _flags$mafs;
32607
32573
  const {
32608
32574
  center,
32609
32575
  radius,
@@ -32611,10 +32577,9 @@ const LockedEllipse = props => {
32611
32577
  color: color$1,
32612
32578
  fillStyle,
32613
32579
  strokeStyle,
32614
- ariaLabel,
32615
- flags
32580
+ ariaLabel
32616
32581
  } = props;
32617
- const hasAria = ariaLabel && (flags == null || (_flags$mafs = flags["mafs"]) == null ? void 0 : _flags$mafs["locked-figures-aria"]);
32582
+ const hasAria = !!ariaLabel;
32618
32583
  return /*#__PURE__*/React.createElement("g", {
32619
32584
  className: "locked-ellipse",
32620
32585
  "aria-label": hasAria ? ariaLabel : undefined,
@@ -32639,7 +32604,6 @@ const LockedEllipse = props => {
32639
32604
  };
32640
32605
 
32641
32606
  const LockedFunction = props => {
32642
- var _props$flags;
32643
32607
  const [equation, setEquation] = useState();
32644
32608
  const {
32645
32609
  color,
@@ -32652,7 +32616,7 @@ const LockedFunction = props => {
32652
32616
  style: strokeStyle,
32653
32617
  domain
32654
32618
  };
32655
- const hasAria = props.ariaLabel && ((_props$flags = props.flags) == null || (_props$flags = _props$flags["mafs"]) == null ? void 0 : _props$flags["locked-figures-aria"]);
32619
+ const hasAria = !!props.ariaLabel;
32656
32620
  useEffect(() => {
32657
32621
  // Parsing the equation in a "useEffect" hook saves about 2ms each frame
32658
32622
  // when the learner is interacting with the graph (i.e. moving points).
@@ -32768,7 +32732,6 @@ function getArrayWithoutDuplicates(array) {
32768
32732
  }
32769
32733
 
32770
32734
  const LockedLine = props => {
32771
- var _flags$mafs;
32772
32735
  const {
32773
32736
  color: color$1,
32774
32737
  lineStyle,
@@ -32777,11 +32740,10 @@ const LockedLine = props => {
32777
32740
  showPoint1,
32778
32741
  showPoint2,
32779
32742
  ariaLabel,
32780
- flags,
32781
32743
  range
32782
32744
  } = props;
32783
32745
  const [point1, point2] = points;
32784
- const hasAria = ariaLabel && (flags == null || (_flags$mafs = flags["mafs"]) == null ? void 0 : _flags$mafs["locked-figures-aria"]);
32746
+ const hasAria = !!ariaLabel;
32785
32747
  let line;
32786
32748
  if (kind === "ray") {
32787
32749
  // Rays extend to the end of the graph in one direction.
@@ -32848,16 +32810,14 @@ const LockedLine = props => {
32848
32810
  };
32849
32811
 
32850
32812
  const LockedPoint = props => {
32851
- var _flags$mafs;
32852
32813
  const {
32853
- flags,
32854
32814
  color: color$1,
32855
32815
  coord,
32856
32816
  filled,
32857
32817
  ariaLabel
32858
32818
  } = props;
32859
32819
  const [x, y] = coord;
32860
- const hasAria = ariaLabel && (flags == null || (_flags$mafs = flags["mafs"]) == null ? void 0 : _flags$mafs["locked-figures-aria"]);
32820
+ const hasAria = !!ariaLabel;
32861
32821
  return /*#__PURE__*/React.createElement("g", {
32862
32822
  className: "locked-point",
32863
32823
  "aria-label": hasAria ? ariaLabel : undefined,
@@ -32876,7 +32836,6 @@ const LockedPoint = props => {
32876
32836
  };
32877
32837
 
32878
32838
  const LockedPolygon = props => {
32879
- var _props$flags;
32880
32839
  const {
32881
32840
  points,
32882
32841
  color: color$1,
@@ -32884,7 +32843,7 @@ const LockedPolygon = props => {
32884
32843
  fillStyle,
32885
32844
  strokeStyle
32886
32845
  } = props;
32887
- const hasAria = props.ariaLabel && ((_props$flags = props.flags) == null || (_props$flags = _props$flags["mafs"]) == null ? void 0 : _props$flags["locked-figures-aria"]);
32846
+ const hasAria = !!props.ariaLabel;
32888
32847
  return /*#__PURE__*/React.createElement("g", {
32889
32848
  className: "locked-polygon",
32890
32849
  "aria-label": hasAria ? props.ariaLabel : undefined,
@@ -32912,15 +32871,13 @@ const LockedPolygon = props => {
32912
32871
  };
32913
32872
 
32914
32873
  const LockedVector = props => {
32915
- var _flags$mafs;
32916
32874
  const {
32917
32875
  color,
32918
32876
  points,
32919
- ariaLabel,
32920
- flags
32877
+ ariaLabel
32921
32878
  } = props;
32922
32879
  const [tail, tip] = points;
32923
- const hasAria = ariaLabel && (flags == null || (_flags$mafs = flags["mafs"]) == null ? void 0 : _flags$mafs["locked-figures-aria"]);
32880
+ const hasAria = !!ariaLabel;
32924
32881
  return /*#__PURE__*/React.createElement("g", {
32925
32882
  className: "locked-vector",
32926
32883
  "aria-label": hasAria ? ariaLabel : undefined,
@@ -32934,7 +32891,6 @@ const LockedVector = props => {
32934
32891
 
32935
32892
  const GraphLockedLayer = props => {
32936
32893
  const {
32937
- flags,
32938
32894
  lockedFigures
32939
32895
  } = props;
32940
32896
  return /*#__PURE__*/React.createElement(React.Fragment, null, lockedFigures.map((figure, index) => {
@@ -32942,40 +32898,28 @@ const GraphLockedLayer = props => {
32942
32898
  case "point":
32943
32899
  return /*#__PURE__*/React.createElement(LockedPoint, _extends({
32944
32900
  key: `point-${index}`
32945
- }, figure, {
32946
- flags: flags
32947
- }));
32901
+ }, figure));
32948
32902
  case "line":
32949
32903
  return /*#__PURE__*/React.createElement(LockedLine, _extends({
32950
32904
  key: `line-${index}`,
32951
32905
  range: props.range
32952
- }, figure, {
32953
- flags: flags
32954
- }));
32906
+ }, figure));
32955
32907
  case "vector":
32956
32908
  return /*#__PURE__*/React.createElement(LockedVector, _extends({
32957
32909
  key: `vector-${index}`
32958
- }, figure, {
32959
- flags: flags
32960
- }));
32910
+ }, figure));
32961
32911
  case "ellipse":
32962
32912
  return /*#__PURE__*/React.createElement(LockedEllipse, _extends({
32963
32913
  key: `ellipse-${index}`
32964
- }, figure, {
32965
- flags: flags
32966
- }));
32914
+ }, figure));
32967
32915
  case "polygon":
32968
32916
  return /*#__PURE__*/React.createElement(LockedPolygon, _extends({
32969
32917
  key: `polygon-${index}`
32970
- }, figure, {
32971
- flags: flags
32972
- }));
32918
+ }, figure));
32973
32919
  case "function":
32974
32920
  return /*#__PURE__*/React.createElement(LockedFunction, _extends({
32975
32921
  key: `function-${index}`
32976
- }, figure, {
32977
- flags: flags
32978
- }));
32922
+ }, figure));
32979
32923
  case "label":
32980
32924
  // This is rendered outside the SVG element, since
32981
32925
  // TeX cannot be rendered inside an SVG.
@@ -35446,7 +35390,6 @@ function useDraggablePx(args) {
35446
35390
  * - Interactive Graph Elements
35447
35391
  */
35448
35392
  const MafsGraph = props => {
35449
- var _props$flags;
35450
35393
  const {
35451
35394
  state,
35452
35395
  dispatch,
@@ -35583,11 +35526,9 @@ const MafsGraph = props => {
35583
35526
  })),
35584
35527
  // Only render the axis ticks and arrows if the markings are set to a full "graph"
35585
35528
  props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
35586
- flags: props.flags,
35587
35529
  lockedFigures: props.lockedFigures,
35588
35530
  range: state.range
35589
- }))), ((_props$flags = props.flags) == null || (_props$flags = _props$flags["mafs"]) == null ? void 0 : _props$flags["interactive-graph-locked-features-labels"]) && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
35590
- flags: props.flags,
35531
+ }))), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
35591
35532
  lockedFigures: props.lockedFigures
35592
35533
  }), /*#__PURE__*/React.createElement(View, {
35593
35534
  style: {
@@ -45560,7 +45501,7 @@ const init = function init() {
45560
45501
 
45561
45502
  // This file is processed by a Rollup plugin (replace) to inject the production
45562
45503
  const libName = "@khanacademy/perseus";
45563
- const libVersion = "49.2.0";
45504
+ const libVersion = "49.2.2";
45564
45505
  addLibraryVersionToPerseusDebug(libName, libVersion);
45565
45506
 
45566
45507
  const apiVersion = {
@@ -47845,10 +47786,10 @@ class UnionBuilder {
47845
47786
  this.parser = parser;
47846
47787
  }
47847
47788
  or(newBranch) {
47848
- return new UnionBuilder(either$1(this.parser, newBranch));
47789
+ return new UnionBuilder(either(this.parser, newBranch));
47849
47790
  }
47850
47791
  }
47851
- function either$1(parseA, parseB) {
47792
+ function either(parseA, parseB) {
47852
47793
  return (rawValue, ctx) => {
47853
47794
  const resultA = parseA(rawValue, ctx);
47854
47795
  if (isSuccess(resultA)) {
@@ -47901,9 +47842,9 @@ function parseWidgetWithVersion(parseVersion, parseType, parseOptions) {
47901
47842
  const parseCategorizerWidget = parseWidget(constant("categorizer"), object({
47902
47843
  items: array(string),
47903
47844
  categories: array(string),
47904
- randomizeItems: boolean,
47845
+ randomizeItems: defaulted(boolean, () => false),
47905
47846
  static: defaulted(boolean, () => false),
47906
- values: array(number),
47847
+ values: array(defaulted(number, () => 0)),
47907
47848
  highlightLint: optional(boolean),
47908
47849
  linterContext: optional(object({
47909
47850
  contentType: string,
@@ -47923,7 +47864,7 @@ const parseCSProgramWidget = parseWidget(constant("cs-program"), object({
47923
47864
  showButtons: boolean,
47924
47865
  width: number,
47925
47866
  height: number,
47926
- static: boolean
47867
+ static: defaulted(boolean, () => false)
47927
47868
  }));
47928
47869
 
47929
47870
  const parseDefinitionWidget = parseWidget(constant("definition"), object({
@@ -47933,7 +47874,7 @@ const parseDefinitionWidget = parseWidget(constant("definition"), object({
47933
47874
  }));
47934
47875
 
47935
47876
  const parseDropdownWidget = parseWidget(constant("dropdown"), object({
47936
- placeholder: string,
47877
+ placeholder: defaulted(string, () => ""),
47937
47878
  ariaLabel: optional(string),
47938
47879
  visibleLabel: optional(string),
47939
47880
  static: defaulted(boolean, () => false),
@@ -47966,60 +47907,63 @@ function convert(f) {
47966
47907
  * data does not match any of the versions.
47967
47908
  *
47968
47909
  * @example
47969
- * const parseOptions = versionedWidgetOptions(parseOptionsV3)
47970
- * .withMigrationFrom(parseOptionsV2, migrateV2ToV3)
47971
- * .withMigrationFrom(parseOptionsV1, migrateV1ToV2)
47972
- * .withMigrationFrom(parseOptionsV0, migrateV0ToV1)
47910
+ * const parseOptions = versionedWidgetOptions(3, parseOptionsV3)
47911
+ * .withMigrationFrom(2, parseOptionsV2, migrateV2ToV3)
47912
+ * .withMigrationFrom(1, parseOptionsV1, migrateV1ToV2)
47913
+ * .withMigrationFrom(0, parseOptionsV0, migrateV0ToV1)
47973
47914
  * .parser;
47974
47915
  *
47916
+ * @param latestMajorVersion the latest major version of the widget options.
47975
47917
  * @param parseLatest a {@link Parser} for the latest version of the widget
47976
- * options. This should check version.major and fail if it's not the latest
47977
- * version.
47918
+ * options.
47978
47919
  * @returns a builder object, to which migrations from earlier versions can be
47979
47920
  * added. Migrations must be added in "reverse chronological" order as in the
47980
47921
  * example above.
47981
47922
  */
47982
- function versionedWidgetOptions(parseLatest) {
47983
- return new VersionedWidgetOptionsParserBuilder(parseLatest, latest => latest);
47923
+ function versionedWidgetOptions(latestMajorVersion, parseLatest) {
47924
+ return new VersionedWidgetOptionsParserBuilder(latestMajorVersion, parseLatest, latest => latest, (raw, ctx) => ctx.failure("widget options with a known version number", raw));
47984
47925
  }
47985
47926
  class VersionedWidgetOptionsParserBuilder {
47986
- constructor(parser, migrate) {
47987
- this.parser = parser;
47988
- this.migrate = migrate;
47927
+ constructor(majorVersion, parseThisVersion, migrateToLatest, parseOtherVersions) {
47928
+ this.parser = void 0;
47929
+ this.migrateToLatest = migrateToLatest;
47930
+ this.parseOtherVersions = parseOtherVersions;
47931
+ const parseThisVersionAndMigrateToLatest = pipeParsers(parseThisVersion).then(convert(this.migrateToLatest)).parser;
47932
+ this.parser = (raw, ctx) => {
47933
+ if (!isObject(raw)) {
47934
+ return ctx.failure("object", raw);
47935
+ }
47936
+ const versionParseResult = parseVersionedObject(raw, ctx);
47937
+ if (isFailure(versionParseResult)) {
47938
+ return versionParseResult;
47939
+ }
47940
+ if (versionParseResult.value.version.major !== majorVersion) {
47941
+ return this.parseOtherVersions(raw, ctx);
47942
+ }
47943
+ return parseThisVersionAndMigrateToLatest(raw, ctx);
47944
+ };
47989
47945
  }
47990
47946
 
47991
47947
  /**
47992
47948
  * Add a migration from an old version of the widget options.
47993
- *
47994
- * @returns a VersionedWidgetOptionsParserBuilder whose `parser` function
47995
- * is capable of migrating the old version to the latest version. The parser
47996
- * will always return the latest version of the widget options on a
47997
- * successful parse.
47998
- * @param parseOldVersion should be a {@link Parser} for the old options
47999
- * type. It should fail if version.major isn't correct.
48000
- * @param migrateToNextVersion should migrate the `Old` data to the
48001
- * `Migratable` version of the current VersionedWidgetOptionsParserBuilder.
48002
- * Usually, this means migrating to the next major version.
48003
47949
  */
48004
- withMigrationFrom(parseOldVersion, migrateToNextVersion) {
48005
- const parseLatestVersion = this.parser;
48006
- const migrateToLatest = old => this.migrate(migrateToNextVersion(old));
48007
- return new VersionedWidgetOptionsParserBuilder((raw, ctx) => {
48008
- const resultOfParsingLatest = parseLatestVersion(raw, ctx);
48009
- if (isSuccess(resultOfParsingLatest)) {
48010
- return resultOfParsingLatest;
48011
- }
48012
- const resultOfParsingOld = parseOldVersion(raw, ctx);
48013
- if (isSuccess(resultOfParsingOld)) {
48014
- return success(migrateToLatest(resultOfParsingOld.value));
48015
- }
48016
-
48017
- // If we're here, neither parse succeeded. Return the failure.
48018
- return resultOfParsingOld;
48019
- }, migrateToLatest);
47950
+ withMigrationFrom(majorVersion, parseOldVersion, migrateToNextVersion) {
47951
+ const parseOtherVersions = this.parser;
47952
+ const migrateToLatest = old => this.migrateToLatest(migrateToNextVersion(old));
47953
+ return new VersionedWidgetOptionsParserBuilder(majorVersion, parseOldVersion, migrateToLatest, parseOtherVersions);
48020
47954
  }
48021
47955
  }
47956
+ const parseVersionedObject = object({
47957
+ version: defaulted(object({
47958
+ major: number,
47959
+ minor: number
47960
+ }), () => ({
47961
+ major: 0,
47962
+ minor: 0
47963
+ }))
47964
+ });
48022
47965
 
47966
+ const stringOrNumberOrNullOrUndefined = union(string).or(number).or(constant(null)).or(constant(undefined)).parser;
48023
47967
  const parsePossiblyInvalidAnswerForm = object({
48024
47968
  // `value` is the possibly invalid part of this. It should always be a
48025
47969
  // string, but some answer forms don't have it. The Expression widget
@@ -48028,7 +47972,7 @@ const parsePossiblyInvalidAnswerForm = object({
48028
47972
  form: defaulted(boolean, () => false),
48029
47973
  simplify: defaulted(boolean, () => false),
48030
47974
  considered: enumeration("correct", "wrong", "ungraded"),
48031
- key: pipeParsers(optional(union(string).or(number).parser)).then((key, ctx) => ctx.success(String(key))).parser
47975
+ key: pipeParsers(stringOrNumberOrNullOrUndefined).then(convert(String)).parser
48032
47976
  });
48033
47977
  function removeInvalidAnswerForms(possiblyInvalid) {
48034
47978
  const valid = [];
@@ -48045,6 +47989,11 @@ function removeInvalidAnswerForms(possiblyInvalid) {
48045
47989
  }
48046
47990
  return valid;
48047
47991
  }
47992
+
47993
+ // NOTE(benchristel): I copied the default buttonSets from
47994
+ // expression.tsx. See the parse-perseus-json/README.md for
47995
+ // an explanation of why we want to duplicate the default here.
47996
+ const parseButtonSets = defaulted(array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")), () => ["basic", "trig", "prealgebra", "logarithms"]);
48048
47997
  const version1 = object({
48049
47998
  major: constant(1),
48050
47999
  minor: number
@@ -48055,7 +48004,7 @@ const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expre
48055
48004
  times: boolean,
48056
48005
  visibleLabel: optional(string),
48057
48006
  ariaLabel: optional(string),
48058
- buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
48007
+ buttonSets: parseButtonSets,
48059
48008
  buttonsVisible: optional(enumeration("always", "never", "focused"))
48060
48009
  }));
48061
48010
  const version0 = optional(object({
@@ -48070,7 +48019,7 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expre
48070
48019
  form: boolean,
48071
48020
  simplify: boolean,
48072
48021
  value: string,
48073
- buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
48022
+ buttonSets: parseButtonSets,
48074
48023
  buttonsVisible: optional(enumeration("always", "never", "focused"))
48075
48024
  }));
48076
48025
  function migrateV0ToV1(widget) {
@@ -48098,7 +48047,7 @@ function migrateV0ToV1(widget) {
48098
48047
  }
48099
48048
  });
48100
48049
  }
48101
- const parseExpressionWidget = versionedWidgetOptions(parseExpressionWidgetV1).withMigrationFrom(parseExpressionWidgetV0, migrateV0ToV1).parser;
48050
+ const parseExpressionWidget = versionedWidgetOptions(1, parseExpressionWidgetV1).withMigrationFrom(0, parseExpressionWidgetV0, migrateV0ToV1).parser;
48102
48051
 
48103
48052
  const parsePerseusRenderer = defaulted(object({
48104
48053
  // TODO(benchristel): content is also defaulted to empty string in
@@ -48119,13 +48068,14 @@ const parsePerseusRenderer = defaulted(object({
48119
48068
  images: {}
48120
48069
  }));
48121
48070
 
48071
+ const falseToNull = pipeParsers(constant(false)).then(convert(() => null)).parser;
48122
48072
  const parseGradedGroupWidgetOptions = object({
48123
48073
  title: defaulted(string, () => ""),
48124
48074
  hasHint: optional(nullable(boolean)),
48125
48075
  // This module has an import cycle with parsePerseusRenderer.
48126
48076
  // The anonymous function below ensures that we don't try to access
48127
48077
  // parsePerseusRenderer before it's defined.
48128
- hint: optional(nullable((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx))),
48078
+ hint: union(falseToNull).or(constant(null)).or(constant(undefined)).or((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx)).parser,
48129
48079
  content: string,
48130
48080
  // This module has an import cycle with parseWidgetsMap.
48131
48081
  // The anonymous function below ensures that we don't try to access
@@ -48144,28 +48094,41 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
48144
48094
  gradedGroups: array(parseGradedGroupWidgetOptions)
48145
48095
  }));
48146
48096
 
48147
- // discriminatedUnion() should be preferred over union() when parsing a
48148
- // discriminated union type, because discriminatedUnion() produces more
48149
- // understandable failure messages. It takes the discriminant as the source of
48150
- // truth for which variant is to be parsed, and expects the other data to match
48151
- // that variant.
48152
- function discriminatedUnion(narrow, parseVariant) {
48153
- return new DiscriminatedUnionBuilder(pipeParsers(narrow).then(parseVariant).parser);
48097
+ /**
48098
+ * discriminatedUnion() should be preferred over union() when parsing a
48099
+ * discriminated union type, because discriminatedUnion() produces more
48100
+ * understandable failure messages. It takes the discriminant as the source of
48101
+ * truth for which variant is to be parsed, and expects the other data to match
48102
+ * that variant.
48103
+ */
48104
+ function discriminatedUnionOn(discriminantKey) {
48105
+ const noMoreBranches = (raw, ctx) => {
48106
+ if (!isObject(raw)) {
48107
+ return ctx.failure("object", raw);
48108
+ }
48109
+ return ctx.forSubtree(discriminantKey).failure("a valid value", raw[discriminantKey]);
48110
+ };
48111
+ return new DiscriminatedUnionBuilder(discriminantKey, noMoreBranches);
48154
48112
  }
48155
48113
  class DiscriminatedUnionBuilder {
48156
- constructor(parser) {
48114
+ constructor(discriminantKey, parser) {
48115
+ this.discriminantKey = discriminantKey;
48157
48116
  this.parser = parser;
48158
48117
  }
48159
- or(narrow, parseVariant) {
48160
- return new DiscriminatedUnionBuilder(either(narrow, parseVariant, this.parser));
48118
+ withBranch(discriminantValue, parseNewVariant) {
48119
+ const parseNewBranch = discriminatedUnionBranch(this.discriminantKey, discriminantValue, parseNewVariant, this.parser);
48120
+ return new DiscriminatedUnionBuilder(this.discriminantKey, parseNewBranch);
48161
48121
  }
48162
48122
  }
48163
- function either(narrowToA, parseA, parseB) {
48164
- return (rawValue, ctx) => {
48165
- if (isSuccess(narrowToA(rawValue, ctx))) {
48166
- return parseA(rawValue, ctx);
48123
+ function discriminatedUnionBranch(discriminantKey, discriminantValue, parseVariant, parseOtherBranches) {
48124
+ return (raw, ctx) => {
48125
+ if (!isObject(raw)) {
48126
+ return ctx.failure("object", raw);
48167
48127
  }
48168
- return parseB(rawValue, ctx);
48128
+ if (raw[discriminantKey] === discriminantValue) {
48129
+ return parseVariant(raw, ctx);
48130
+ }
48131
+ return parseOtherBranches(raw, ctx);
48169
48132
  };
48170
48133
  }
48171
48134
 
@@ -48173,41 +48136,27 @@ const pairOfNumbers$3 = pair(number, number);
48173
48136
  const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
48174
48137
  const parseGrapherWidget = parseWidget(constant("grapher"), object({
48175
48138
  availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
48176
- correct: discriminatedUnion(object({
48177
- type: constant("absolute_value")
48178
- }), object({
48139
+ correct: discriminatedUnionOn("type").withBranch("absolute_value", object({
48179
48140
  type: constant("absolute_value"),
48180
48141
  coords: pairOfPoints
48181
- })).or(object({
48182
- type: constant("exponential")
48183
- }), object({
48142
+ })).withBranch("exponential", object({
48184
48143
  type: constant("exponential"),
48185
48144
  asymptote: pairOfPoints,
48186
48145
  coords: pairOfPoints
48187
- })).or(object({
48188
- type: constant("linear")
48189
- }), object({
48146
+ })).withBranch("linear", object({
48190
48147
  type: constant("linear"),
48191
- coords: pairOfPoints
48192
- })).or(object({
48193
- type: constant("logarithm")
48194
- }), object({
48148
+ coords: defaulted(pairOfPoints, () => [[-5, 5], [5, 5]])
48149
+ })).withBranch("logarithm", object({
48195
48150
  type: constant("logarithm"),
48196
48151
  asymptote: pairOfPoints,
48197
48152
  coords: pairOfPoints
48198
- })).or(object({
48199
- type: constant("quadratic")
48200
- }), object({
48153
+ })).withBranch("quadratic", object({
48201
48154
  type: constant("quadratic"),
48202
48155
  coords: pairOfPoints
48203
- })).or(object({
48204
- type: constant("sinusoid")
48205
- }), object({
48156
+ })).withBranch("sinusoid", object({
48206
48157
  type: constant("sinusoid"),
48207
48158
  coords: pairOfPoints
48208
- })).or(object({
48209
- type: constant("tangent")
48210
- }), object({
48159
+ })).withBranch("tangent", object({
48211
48160
  type: constant("tangent"),
48212
48161
  coords: pairOfPoints
48213
48162
  })).parser,
@@ -48455,23 +48404,7 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
48455
48404
  rulerTicks: optional(number),
48456
48405
  tickStep: pairOfNumbers$1
48457
48406
  }),
48458
- elements: array(discriminatedUnion(object({
48459
- type: parseFunctionType
48460
- }), parseFunctionElement).or(object({
48461
- type: parseLabelType
48462
- }), parseLabelElement).or(object({
48463
- type: parseLineType
48464
- }), parseLineElement).or(object({
48465
- type: parseMovableLineType
48466
- }), parseMovableLineElement).or(object({
48467
- type: parseMovablePointType
48468
- }), parseMovablePointElement).or(object({
48469
- type: parseParametricType
48470
- }), parseParametricElement).or(object({
48471
- type: parsePointType
48472
- }), parsePointElement).or(object({
48473
- type: parseRectangleType
48474
- }), parseRectangleElement).parser)
48407
+ 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)
48475
48408
  }));
48476
48409
 
48477
48410
  // Used to represent 2-D points and ranges
@@ -48529,7 +48462,7 @@ const parsePerseusGraphTypePolygon = object({
48529
48462
  showAngles: optional(boolean),
48530
48463
  showSides: optional(boolean),
48531
48464
  snapTo: optional(enumeration("grid", "angles", "sides")),
48532
- match: optional(enumeration("similar", "congruent", "approx")),
48465
+ match: optional(enumeration("similar", "congruent", "approx", "exact")),
48533
48466
  startCoords: optional(array(pairOfNumbers)),
48534
48467
  // TODO: remove coord? it's legacy.
48535
48468
  coord: optional(pairOfNumbers)
@@ -48564,7 +48497,7 @@ const parsePerseusGraphTypeSinusoid = object({
48564
48497
  // TODO: remove coord? it's legacy.
48565
48498
  coord: optional(pairOfNumbers)
48566
48499
  });
48567
- const parsePerseusGraphType = union(parsePerseusGraphTypeAngle).or(parsePerseusGraphTypeAngle).or(parsePerseusGraphTypeCircle).or(parsePerseusGraphTypeLinear).or(parsePerseusGraphTypeLinearSystem).or(parsePerseusGraphTypeNone).or(parsePerseusGraphTypePoint).or(parsePerseusGraphTypePolygon).or(parsePerseusGraphTypeQuadratic).or(parsePerseusGraphTypeRay).or(parsePerseusGraphTypeSegment).or(parsePerseusGraphTypeSinusoid).parser;
48500
+ 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;
48568
48501
  const parseLockedFigureColor = enumeration(...lockedFigureColorNames);
48569
48502
  const parseLockedFigureFillType = enumeration("none", "white", "translucent", "solid");
48570
48503
  const parseLockedLineStyle = enumeration("solid", "dashed");
@@ -48683,7 +48616,7 @@ const parseLabelImageWidget = parseWidget(constant("label-image"), object({
48683
48616
  })),
48684
48617
  hideChoicesFromInstructions: boolean,
48685
48618
  multipleAnswers: boolean,
48686
- static: boolean
48619
+ static: defaulted(boolean, () => false)
48687
48620
  }));
48688
48621
 
48689
48622
  const parseMatcherWidget = parseWidget(constant("matcher"), object({
@@ -48694,7 +48627,8 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
48694
48627
  padding: boolean
48695
48628
  }));
48696
48629
 
48697
- const numeric = pipeParsers(union(number).or(string).parser).then(stringToNumber).parser;
48630
+ const numberOrString = union(number).or(string).parser;
48631
+ const numeric = pipeParsers(defaulted(numberOrString, () => NaN)).then(stringToNumber).parser;
48698
48632
  const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
48699
48633
  prefix: optional(string),
48700
48634
  suffix: optional(string),
@@ -48713,7 +48647,8 @@ const parseMeasurerWidget = parseWidget(constant("measurer"), object({
48713
48647
  rulerPixels: number,
48714
48648
  rulerLength: number,
48715
48649
  box: pair(number, number),
48716
- static: boolean
48650
+ // TODO(benchristel): static is not used. Remove it?
48651
+ static: defaulted(boolean, () => false)
48717
48652
  }));
48718
48653
 
48719
48654
  const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), object({
@@ -48722,9 +48657,10 @@ const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), o
48722
48657
  smiles: optional(string)
48723
48658
  }));
48724
48659
 
48660
+ const emptyStringToNull = pipeParsers(constant("")).then(convert(() => null)).parser;
48725
48661
  const parseNumberLineWidget = parseWidget(constant("number-line"), object({
48726
48662
  range: array(number),
48727
- labelRange: array(nullable(number)),
48663
+ labelRange: array(nullable(union(number).or(emptyStringToNull).parser)),
48728
48664
  labelStyle: string,
48729
48665
  labelTicks: boolean,
48730
48666
  isTickCtrl: optional(nullable(boolean)),
@@ -48736,10 +48672,10 @@ const parseNumberLineWidget = parseWidget(constant("number-line"), object({
48736
48672
  snapDivisions: defaulted(number, () => 2),
48737
48673
  tickStep: optional(nullable(number)),
48738
48674
  correctRel: optional(nullable(string)),
48739
- correctX: number,
48675
+ correctX: nullable(number),
48740
48676
  initialX: optional(nullable(number)),
48741
48677
  showTooltip: optional(boolean),
48742
- static: boolean
48678
+ static: defaulted(boolean, () => false)
48743
48679
  }));
48744
48680
 
48745
48681
  const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "decimal", "percent", "pi");
@@ -48754,14 +48690,14 @@ const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
48754
48690
  answerForms: optional(array(parseMathFormat)),
48755
48691
  strict: boolean,
48756
48692
  maxError: optional(nullable(number)),
48757
- // TODO(benchristel): simplify should never be `true`, but we
48693
+ // TODO(benchristel): simplify should never be a boolean, but we
48758
48694
  // have some content where it is anyway. If we ever backfill
48759
48695
  // the data, we should simplify `simplify`.
48760
- simplify: optional(nullable(union(string).or(pipeParsers(constant(true)).then(convert(String)).parser).parser))
48696
+ simplify: optional(nullable(union(string).or(pipeParsers(boolean).then(convert(String)).parser).parser))
48761
48697
  })),
48762
48698
  labelText: optional(string),
48763
48699
  size: string,
48764
- coefficient: boolean,
48700
+ coefficient: defaulted(boolean, () => false),
48765
48701
  rightAlign: optional(boolean),
48766
48702
  static: defaulted(boolean, () => false),
48767
48703
  answerForms: optional(array(object({
@@ -48797,9 +48733,9 @@ const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
48797
48733
  }));
48798
48734
 
48799
48735
  const parsePassageWidget = parseWidget(constant("passage"), object({
48800
- footnotes: string,
48736
+ footnotes: defaulted(string, () => ""),
48801
48737
  passageText: string,
48802
- passageTitle: string,
48738
+ passageTitle: defaulted(string, () => ""),
48803
48739
  showLineNumbers: boolean,
48804
48740
  static: defaulted(boolean, () => false)
48805
48741
  }));
@@ -48983,6 +48919,10 @@ const parseWidgetsMapEntry = ([key, widget], widgetMap, ctx) => {
48983
48919
  return parseAndAssign(`sequence ${id}`, parseDeprecatedWidget);
48984
48920
  case "lights-puzzle":
48985
48921
  return parseAndAssign(`lights-puzzle ${id}`, parseDeprecatedWidget);
48922
+ case "simulator":
48923
+ return parseAndAssign(`simulator ${id}`, parseDeprecatedWidget);
48924
+ case "transformer":
48925
+ return parseAndAssign(`transformer ${id}`, parseDeprecatedWidget);
48986
48926
  default:
48987
48927
  if (getWidget(type)) {
48988
48928
  // @ts-expect-error - 'type' is not a valid widget type