@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.css +2 -1
- package/dist/es/index.js +159 -219
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +23 -86
- package/dist/es/strings.js.map +1 -1
- package/dist/index.css +2 -1
- package/dist/index.js +140 -176
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +9 -7
- package/dist/server-item-renderer.d.ts +1 -1
- package/dist/strings.js +23 -86
- package/dist/strings.js.map +1 -1
- package/dist/types.d.ts +2 -5
- package/dist/util/parse-perseus-json/general-purpose-parsers/discriminated-union.d.ts +18 -5
- package/dist/util/parse-perseus-json/general-purpose-parsers/discriminated-union.typetest.d.ts +1 -0
- package/dist/util/parse-perseus-json/perseus-parsers/versioned-widget-options.d.ts +12 -21
- package/dist/widgets/expression/expression.d.ts +2 -2
- package/dist/widgets/interactive-graphs/graph-locked-labels-layer.d.ts +1 -3
- package/dist/widgets/interactive-graphs/graph-locked-layer.d.ts +0 -2
- package/dist/widgets/interactive-graphs/locked-figures/locked-ellipse.d.ts +1 -5
- package/dist/widgets/interactive-graphs/locked-figures/locked-function.d.ts +1 -5
- package/dist/widgets/interactive-graphs/locked-figures/locked-line.d.ts +0 -2
- package/dist/widgets/interactive-graphs/locked-figures/locked-point.d.ts +1 -5
- package/dist/widgets/interactive-graphs/locked-figures/locked-polygon.d.ts +1 -5
- package/dist/widgets/interactive-graphs/locked-figures/locked-vector.d.ts +1 -5
- package/package.json +38 -38
package/dist/index.js
CHANGED
|
@@ -32688,7 +32688,7 @@ const LegacyGrid = _ref => {
|
|
|
32688
32688
|
* "new-awesomeness": MyAwesomeNewWidget;
|
|
32689
32689
|
*
|
|
32690
32690
|
* // A deprecated widget
|
|
32691
|
-
* "super-old-widget":
|
|
32691
|
+
* "super-old-widget": DeprecatedStandinWidget;
|
|
32692
32692
|
* }
|
|
32693
32693
|
* }
|
|
32694
32694
|
*
|
|
@@ -32882,7 +32882,6 @@ function LockedLabel(props) {
|
|
|
32882
32882
|
|
|
32883
32883
|
function GraphLockedLabelsLayer(props) {
|
|
32884
32884
|
const {
|
|
32885
|
-
flags,
|
|
32886
32885
|
lockedFigures
|
|
32887
32886
|
} = props;
|
|
32888
32887
|
return lockedFigures.map((figure, i) => {
|
|
@@ -32891,30 +32890,15 @@ function GraphLockedLabelsLayer(props) {
|
|
|
32891
32890
|
key: `label-${i}`
|
|
32892
32891
|
}, figure));
|
|
32893
32892
|
}
|
|
32894
|
-
|
|
32895
|
-
|
|
32896
|
-
|
|
32897
|
-
|
|
32898
|
-
|
|
32899
|
-
|
|
32900
|
-
|
|
32901
|
-
|
|
32902
|
-
|
|
32903
|
-
// Polygon flag + polygon type
|
|
32904
|
-
flags?.["mafs"]?.["locked-polygon-labels"] && figure.type === "polygon" ||
|
|
32905
|
-
// Function flag + function type
|
|
32906
|
-
flags?.["mafs"]?.["locked-function-labels"] && figure.type === "function") {
|
|
32907
|
-
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, {
|
|
32908
|
-
key: i
|
|
32909
|
-
}, figure.labels?.map((label, j) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
32910
|
-
key: `${i}-label-${j}`
|
|
32911
|
-
}, label))), figure.type === "line" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, figure.points[0].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
32912
|
-
key: `locked-figure-${i}-point-1-label-${k}`
|
|
32913
|
-
}, label))), figure.points[1].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
32914
|
-
key: `locked-figure-${i}-point-2-label-${k}`
|
|
32915
|
-
}, label)))));
|
|
32916
|
-
}
|
|
32917
|
-
return null;
|
|
32893
|
+
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, {
|
|
32894
|
+
key: i
|
|
32895
|
+
}, figure.labels?.map((label, j) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
32896
|
+
key: `${i}-label-${j}`
|
|
32897
|
+
}, label))), figure.type === "line" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, figure.points[0].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
32898
|
+
key: `locked-figure-${i}-point-1-label-${k}`
|
|
32899
|
+
}, label))), figure.points[1].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
32900
|
+
key: `locked-figure-${i}-point-2-label-${k}`
|
|
32901
|
+
}, label)))));
|
|
32918
32902
|
});
|
|
32919
32903
|
}
|
|
32920
32904
|
|
|
@@ -32926,10 +32910,9 @@ const LockedEllipse = props => {
|
|
|
32926
32910
|
color,
|
|
32927
32911
|
fillStyle,
|
|
32928
32912
|
strokeStyle,
|
|
32929
|
-
ariaLabel
|
|
32930
|
-
flags
|
|
32913
|
+
ariaLabel
|
|
32931
32914
|
} = props;
|
|
32932
|
-
const hasAria = ariaLabel
|
|
32915
|
+
const hasAria = !!ariaLabel;
|
|
32933
32916
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32934
32917
|
className: "locked-ellipse",
|
|
32935
32918
|
"aria-label": hasAria ? ariaLabel : undefined,
|
|
@@ -32966,7 +32949,7 @@ const LockedFunction = props => {
|
|
|
32966
32949
|
style: strokeStyle,
|
|
32967
32950
|
domain
|
|
32968
32951
|
};
|
|
32969
|
-
const hasAria = props.ariaLabel
|
|
32952
|
+
const hasAria = !!props.ariaLabel;
|
|
32970
32953
|
React.useEffect(() => {
|
|
32971
32954
|
// Parsing the equation in a "useEffect" hook saves about 2ms each frame
|
|
32972
32955
|
// when the learner is interacting with the graph (i.e. moving points).
|
|
@@ -33093,11 +33076,10 @@ const LockedLine = props => {
|
|
|
33093
33076
|
showPoint1,
|
|
33094
33077
|
showPoint2,
|
|
33095
33078
|
ariaLabel,
|
|
33096
|
-
flags,
|
|
33097
33079
|
range
|
|
33098
33080
|
} = props;
|
|
33099
33081
|
const [point1, point2] = points;
|
|
33100
|
-
const hasAria = ariaLabel
|
|
33082
|
+
const hasAria = !!ariaLabel;
|
|
33101
33083
|
let line;
|
|
33102
33084
|
if (kind === "ray") {
|
|
33103
33085
|
// Rays extend to the end of the graph in one direction.
|
|
@@ -33165,14 +33147,13 @@ const LockedLine = props => {
|
|
|
33165
33147
|
|
|
33166
33148
|
const LockedPoint = props => {
|
|
33167
33149
|
const {
|
|
33168
|
-
flags,
|
|
33169
33150
|
color,
|
|
33170
33151
|
coord,
|
|
33171
33152
|
filled,
|
|
33172
33153
|
ariaLabel
|
|
33173
33154
|
} = props;
|
|
33174
33155
|
const [x, y] = coord;
|
|
33175
|
-
const hasAria = ariaLabel
|
|
33156
|
+
const hasAria = !!ariaLabel;
|
|
33176
33157
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
33177
33158
|
className: "locked-point",
|
|
33178
33159
|
"aria-label": hasAria ? ariaLabel : undefined,
|
|
@@ -33198,7 +33179,7 @@ const LockedPolygon = props => {
|
|
|
33198
33179
|
fillStyle,
|
|
33199
33180
|
strokeStyle
|
|
33200
33181
|
} = props;
|
|
33201
|
-
const hasAria = props.ariaLabel
|
|
33182
|
+
const hasAria = !!props.ariaLabel;
|
|
33202
33183
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
33203
33184
|
className: "locked-polygon",
|
|
33204
33185
|
"aria-label": hasAria ? props.ariaLabel : undefined,
|
|
@@ -33229,11 +33210,10 @@ const LockedVector = props => {
|
|
|
33229
33210
|
const {
|
|
33230
33211
|
color,
|
|
33231
33212
|
points,
|
|
33232
|
-
ariaLabel
|
|
33233
|
-
flags
|
|
33213
|
+
ariaLabel
|
|
33234
33214
|
} = props;
|
|
33235
33215
|
const [tail, tip] = points;
|
|
33236
|
-
const hasAria = ariaLabel
|
|
33216
|
+
const hasAria = !!ariaLabel;
|
|
33237
33217
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
33238
33218
|
className: "locked-vector",
|
|
33239
33219
|
"aria-label": hasAria ? ariaLabel : undefined,
|
|
@@ -33247,7 +33227,6 @@ const LockedVector = props => {
|
|
|
33247
33227
|
|
|
33248
33228
|
const GraphLockedLayer = props => {
|
|
33249
33229
|
const {
|
|
33250
|
-
flags,
|
|
33251
33230
|
lockedFigures
|
|
33252
33231
|
} = props;
|
|
33253
33232
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, lockedFigures.map((figure, index) => {
|
|
@@ -33255,40 +33234,28 @@ const GraphLockedLayer = props => {
|
|
|
33255
33234
|
case "point":
|
|
33256
33235
|
return /*#__PURE__*/React__namespace.createElement(LockedPoint, _extends({
|
|
33257
33236
|
key: `point-${index}`
|
|
33258
|
-
}, figure
|
|
33259
|
-
flags: flags
|
|
33260
|
-
}));
|
|
33237
|
+
}, figure));
|
|
33261
33238
|
case "line":
|
|
33262
33239
|
return /*#__PURE__*/React__namespace.createElement(LockedLine, _extends({
|
|
33263
33240
|
key: `line-${index}`,
|
|
33264
33241
|
range: props.range
|
|
33265
|
-
}, figure
|
|
33266
|
-
flags: flags
|
|
33267
|
-
}));
|
|
33242
|
+
}, figure));
|
|
33268
33243
|
case "vector":
|
|
33269
33244
|
return /*#__PURE__*/React__namespace.createElement(LockedVector, _extends({
|
|
33270
33245
|
key: `vector-${index}`
|
|
33271
|
-
}, figure
|
|
33272
|
-
flags: flags
|
|
33273
|
-
}));
|
|
33246
|
+
}, figure));
|
|
33274
33247
|
case "ellipse":
|
|
33275
33248
|
return /*#__PURE__*/React__namespace.createElement(LockedEllipse, _extends({
|
|
33276
33249
|
key: `ellipse-${index}`
|
|
33277
|
-
}, figure
|
|
33278
|
-
flags: flags
|
|
33279
|
-
}));
|
|
33250
|
+
}, figure));
|
|
33280
33251
|
case "polygon":
|
|
33281
33252
|
return /*#__PURE__*/React__namespace.createElement(LockedPolygon, _extends({
|
|
33282
33253
|
key: `polygon-${index}`
|
|
33283
|
-
}, figure
|
|
33284
|
-
flags: flags
|
|
33285
|
-
}));
|
|
33254
|
+
}, figure));
|
|
33286
33255
|
case "function":
|
|
33287
33256
|
return /*#__PURE__*/React__namespace.createElement(LockedFunction, _extends({
|
|
33288
33257
|
key: `function-${index}`
|
|
33289
|
-
}, figure
|
|
33290
|
-
flags: flags
|
|
33291
|
-
}));
|
|
33258
|
+
}, figure));
|
|
33292
33259
|
case "label":
|
|
33293
33260
|
// This is rendered outside the SVG element, since
|
|
33294
33261
|
// TeX cannot be rendered inside an SVG.
|
|
@@ -35930,11 +35897,9 @@ const MafsGraph = props => {
|
|
|
35930
35897
|
})),
|
|
35931
35898
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
35932
35899
|
props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
|
|
35933
|
-
flags: props.flags,
|
|
35934
35900
|
lockedFigures: props.lockedFigures,
|
|
35935
35901
|
range: state.range
|
|
35936
|
-
}))), props.
|
|
35937
|
-
flags: props.flags,
|
|
35902
|
+
}))), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
|
|
35938
35903
|
lockedFigures: props.lockedFigures
|
|
35939
35904
|
}), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
35940
35905
|
style: {
|
|
@@ -45944,7 +45909,7 @@ const init = function () {
|
|
|
45944
45909
|
|
|
45945
45910
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45946
45911
|
const libName = "@khanacademy/perseus";
|
|
45947
|
-
const libVersion = "49.2.
|
|
45912
|
+
const libVersion = "49.2.2";
|
|
45948
45913
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45949
45914
|
|
|
45950
45915
|
const apiVersion = {
|
|
@@ -48244,10 +48209,10 @@ class UnionBuilder {
|
|
|
48244
48209
|
this.parser = parser;
|
|
48245
48210
|
}
|
|
48246
48211
|
or(newBranch) {
|
|
48247
|
-
return new UnionBuilder(either
|
|
48212
|
+
return new UnionBuilder(either(this.parser, newBranch));
|
|
48248
48213
|
}
|
|
48249
48214
|
}
|
|
48250
|
-
function either
|
|
48215
|
+
function either(parseA, parseB) {
|
|
48251
48216
|
return (rawValue, ctx) => {
|
|
48252
48217
|
const resultA = parseA(rawValue, ctx);
|
|
48253
48218
|
if (isSuccess(resultA)) {
|
|
@@ -48300,9 +48265,9 @@ function parseWidgetWithVersion(parseVersion, parseType, parseOptions) {
|
|
|
48300
48265
|
const parseCategorizerWidget = parseWidget(constant("categorizer"), object({
|
|
48301
48266
|
items: array(string),
|
|
48302
48267
|
categories: array(string),
|
|
48303
|
-
randomizeItems: boolean,
|
|
48268
|
+
randomizeItems: defaulted(boolean, () => false),
|
|
48304
48269
|
static: defaulted(boolean, () => false),
|
|
48305
|
-
values: array(number),
|
|
48270
|
+
values: array(defaulted(number, () => 0)),
|
|
48306
48271
|
highlightLint: optional(boolean),
|
|
48307
48272
|
linterContext: optional(object({
|
|
48308
48273
|
contentType: string,
|
|
@@ -48322,7 +48287,7 @@ const parseCSProgramWidget = parseWidget(constant("cs-program"), object({
|
|
|
48322
48287
|
showButtons: boolean,
|
|
48323
48288
|
width: number,
|
|
48324
48289
|
height: number,
|
|
48325
|
-
static: boolean
|
|
48290
|
+
static: defaulted(boolean, () => false)
|
|
48326
48291
|
}));
|
|
48327
48292
|
|
|
48328
48293
|
const parseDefinitionWidget = parseWidget(constant("definition"), object({
|
|
@@ -48332,7 +48297,7 @@ const parseDefinitionWidget = parseWidget(constant("definition"), object({
|
|
|
48332
48297
|
}));
|
|
48333
48298
|
|
|
48334
48299
|
const parseDropdownWidget = parseWidget(constant("dropdown"), object({
|
|
48335
|
-
placeholder: string,
|
|
48300
|
+
placeholder: defaulted(string, () => ""),
|
|
48336
48301
|
ariaLabel: optional(string),
|
|
48337
48302
|
visibleLabel: optional(string),
|
|
48338
48303
|
static: defaulted(boolean, () => false),
|
|
@@ -48365,60 +48330,63 @@ function convert(f) {
|
|
|
48365
48330
|
* data does not match any of the versions.
|
|
48366
48331
|
*
|
|
48367
48332
|
* @example
|
|
48368
|
-
* const parseOptions = versionedWidgetOptions(parseOptionsV3)
|
|
48369
|
-
* .withMigrationFrom(parseOptionsV2, migrateV2ToV3)
|
|
48370
|
-
* .withMigrationFrom(parseOptionsV1, migrateV1ToV2)
|
|
48371
|
-
* .withMigrationFrom(parseOptionsV0, migrateV0ToV1)
|
|
48333
|
+
* const parseOptions = versionedWidgetOptions(3, parseOptionsV3)
|
|
48334
|
+
* .withMigrationFrom(2, parseOptionsV2, migrateV2ToV3)
|
|
48335
|
+
* .withMigrationFrom(1, parseOptionsV1, migrateV1ToV2)
|
|
48336
|
+
* .withMigrationFrom(0, parseOptionsV0, migrateV0ToV1)
|
|
48372
48337
|
* .parser;
|
|
48373
48338
|
*
|
|
48339
|
+
* @param latestMajorVersion the latest major version of the widget options.
|
|
48374
48340
|
* @param parseLatest a {@link Parser} for the latest version of the widget
|
|
48375
|
-
* options.
|
|
48376
|
-
* version.
|
|
48341
|
+
* options.
|
|
48377
48342
|
* @returns a builder object, to which migrations from earlier versions can be
|
|
48378
48343
|
* added. Migrations must be added in "reverse chronological" order as in the
|
|
48379
48344
|
* example above.
|
|
48380
48345
|
*/
|
|
48381
|
-
function versionedWidgetOptions(parseLatest) {
|
|
48382
|
-
return new VersionedWidgetOptionsParserBuilder(parseLatest, latest => latest);
|
|
48346
|
+
function versionedWidgetOptions(latestMajorVersion, parseLatest) {
|
|
48347
|
+
return new VersionedWidgetOptionsParserBuilder(latestMajorVersion, parseLatest, latest => latest, (raw, ctx) => ctx.failure("widget options with a known version number", raw));
|
|
48383
48348
|
}
|
|
48384
48349
|
class VersionedWidgetOptionsParserBuilder {
|
|
48385
|
-
|
|
48386
|
-
|
|
48387
|
-
this.
|
|
48350
|
+
parser;
|
|
48351
|
+
constructor(majorVersion, parseThisVersion, migrateToLatest, parseOtherVersions) {
|
|
48352
|
+
this.migrateToLatest = migrateToLatest;
|
|
48353
|
+
this.parseOtherVersions = parseOtherVersions;
|
|
48354
|
+
const parseThisVersionAndMigrateToLatest = pipeParsers(parseThisVersion).then(convert(this.migrateToLatest)).parser;
|
|
48355
|
+
this.parser = (raw, ctx) => {
|
|
48356
|
+
if (!isObject(raw)) {
|
|
48357
|
+
return ctx.failure("object", raw);
|
|
48358
|
+
}
|
|
48359
|
+
const versionParseResult = parseVersionedObject(raw, ctx);
|
|
48360
|
+
if (isFailure(versionParseResult)) {
|
|
48361
|
+
return versionParseResult;
|
|
48362
|
+
}
|
|
48363
|
+
if (versionParseResult.value.version.major !== majorVersion) {
|
|
48364
|
+
return this.parseOtherVersions(raw, ctx);
|
|
48365
|
+
}
|
|
48366
|
+
return parseThisVersionAndMigrateToLatest(raw, ctx);
|
|
48367
|
+
};
|
|
48388
48368
|
}
|
|
48389
48369
|
|
|
48390
48370
|
/**
|
|
48391
48371
|
* 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
48372
|
*/
|
|
48403
|
-
withMigrationFrom(parseOldVersion, migrateToNextVersion) {
|
|
48404
|
-
const
|
|
48405
|
-
const migrateToLatest = old => this.
|
|
48406
|
-
return new VersionedWidgetOptionsParserBuilder(
|
|
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);
|
|
48373
|
+
withMigrationFrom(majorVersion, parseOldVersion, migrateToNextVersion) {
|
|
48374
|
+
const parseOtherVersions = this.parser;
|
|
48375
|
+
const migrateToLatest = old => this.migrateToLatest(migrateToNextVersion(old));
|
|
48376
|
+
return new VersionedWidgetOptionsParserBuilder(majorVersion, parseOldVersion, migrateToLatest, parseOtherVersions);
|
|
48419
48377
|
}
|
|
48420
48378
|
}
|
|
48379
|
+
const parseVersionedObject = object({
|
|
48380
|
+
version: defaulted(object({
|
|
48381
|
+
major: number,
|
|
48382
|
+
minor: number
|
|
48383
|
+
}), () => ({
|
|
48384
|
+
major: 0,
|
|
48385
|
+
minor: 0
|
|
48386
|
+
}))
|
|
48387
|
+
});
|
|
48421
48388
|
|
|
48389
|
+
const stringOrNumberOrNullOrUndefined = union(string).or(number).or(constant(null)).or(constant(undefined)).parser;
|
|
48422
48390
|
const parsePossiblyInvalidAnswerForm = object({
|
|
48423
48391
|
// `value` is the possibly invalid part of this. It should always be a
|
|
48424
48392
|
// string, but some answer forms don't have it. The Expression widget
|
|
@@ -48427,7 +48395,7 @@ const parsePossiblyInvalidAnswerForm = object({
|
|
|
48427
48395
|
form: defaulted(boolean, () => false),
|
|
48428
48396
|
simplify: defaulted(boolean, () => false),
|
|
48429
48397
|
considered: enumeration("correct", "wrong", "ungraded"),
|
|
48430
|
-
key: pipeParsers(
|
|
48398
|
+
key: pipeParsers(stringOrNumberOrNullOrUndefined).then(convert(String)).parser
|
|
48431
48399
|
});
|
|
48432
48400
|
function removeInvalidAnswerForms(possiblyInvalid) {
|
|
48433
48401
|
const valid = [];
|
|
@@ -48445,6 +48413,11 @@ function removeInvalidAnswerForms(possiblyInvalid) {
|
|
|
48445
48413
|
}
|
|
48446
48414
|
return valid;
|
|
48447
48415
|
}
|
|
48416
|
+
|
|
48417
|
+
// NOTE(benchristel): I copied the default buttonSets from
|
|
48418
|
+
// expression.tsx. See the parse-perseus-json/README.md for
|
|
48419
|
+
// an explanation of why we want to duplicate the default here.
|
|
48420
|
+
const parseButtonSets = defaulted(array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")), () => ["basic", "trig", "prealgebra", "logarithms"]);
|
|
48448
48421
|
const version1 = object({
|
|
48449
48422
|
major: constant(1),
|
|
48450
48423
|
minor: number
|
|
@@ -48455,7 +48428,7 @@ const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expre
|
|
|
48455
48428
|
times: boolean,
|
|
48456
48429
|
visibleLabel: optional(string),
|
|
48457
48430
|
ariaLabel: optional(string),
|
|
48458
|
-
buttonSets:
|
|
48431
|
+
buttonSets: parseButtonSets,
|
|
48459
48432
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48460
48433
|
}));
|
|
48461
48434
|
const version0 = optional(object({
|
|
@@ -48470,7 +48443,7 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expre
|
|
|
48470
48443
|
form: boolean,
|
|
48471
48444
|
simplify: boolean,
|
|
48472
48445
|
value: string,
|
|
48473
|
-
buttonSets:
|
|
48446
|
+
buttonSets: parseButtonSets,
|
|
48474
48447
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48475
48448
|
}));
|
|
48476
48449
|
function migrateV0ToV1(widget) {
|
|
@@ -48499,7 +48472,7 @@ function migrateV0ToV1(widget) {
|
|
|
48499
48472
|
}
|
|
48500
48473
|
};
|
|
48501
48474
|
}
|
|
48502
|
-
const parseExpressionWidget = versionedWidgetOptions(parseExpressionWidgetV1).withMigrationFrom(parseExpressionWidgetV0, migrateV0ToV1).parser;
|
|
48475
|
+
const parseExpressionWidget = versionedWidgetOptions(1, parseExpressionWidgetV1).withMigrationFrom(0, parseExpressionWidgetV0, migrateV0ToV1).parser;
|
|
48503
48476
|
|
|
48504
48477
|
const parsePerseusRenderer = defaulted(object({
|
|
48505
48478
|
// TODO(benchristel): content is also defaulted to empty string in
|
|
@@ -48520,13 +48493,14 @@ const parsePerseusRenderer = defaulted(object({
|
|
|
48520
48493
|
images: {}
|
|
48521
48494
|
}));
|
|
48522
48495
|
|
|
48496
|
+
const falseToNull = pipeParsers(constant(false)).then(convert(() => null)).parser;
|
|
48523
48497
|
const parseGradedGroupWidgetOptions = object({
|
|
48524
48498
|
title: defaulted(string, () => ""),
|
|
48525
48499
|
hasHint: optional(nullable(boolean)),
|
|
48526
48500
|
// This module has an import cycle with parsePerseusRenderer.
|
|
48527
48501
|
// The anonymous function below ensures that we don't try to access
|
|
48528
48502
|
// parsePerseusRenderer before it's defined.
|
|
48529
|
-
hint:
|
|
48503
|
+
hint: union(falseToNull).or(constant(null)).or(constant(undefined)).or((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx)).parser,
|
|
48530
48504
|
content: string,
|
|
48531
48505
|
// This module has an import cycle with parseWidgetsMap.
|
|
48532
48506
|
// The anonymous function below ensures that we don't try to access
|
|
@@ -48545,28 +48519,41 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
|
|
|
48545
48519
|
gradedGroups: array(parseGradedGroupWidgetOptions)
|
|
48546
48520
|
}));
|
|
48547
48521
|
|
|
48548
|
-
|
|
48549
|
-
|
|
48550
|
-
|
|
48551
|
-
|
|
48552
|
-
|
|
48553
|
-
|
|
48554
|
-
|
|
48522
|
+
/**
|
|
48523
|
+
* discriminatedUnion() should be preferred over union() when parsing a
|
|
48524
|
+
* discriminated union type, because discriminatedUnion() produces more
|
|
48525
|
+
* understandable failure messages. It takes the discriminant as the source of
|
|
48526
|
+
* truth for which variant is to be parsed, and expects the other data to match
|
|
48527
|
+
* that variant.
|
|
48528
|
+
*/
|
|
48529
|
+
function discriminatedUnionOn(discriminantKey) {
|
|
48530
|
+
const noMoreBranches = (raw, ctx) => {
|
|
48531
|
+
if (!isObject(raw)) {
|
|
48532
|
+
return ctx.failure("object", raw);
|
|
48533
|
+
}
|
|
48534
|
+
return ctx.forSubtree(discriminantKey).failure("a valid value", raw[discriminantKey]);
|
|
48535
|
+
};
|
|
48536
|
+
return new DiscriminatedUnionBuilder(discriminantKey, noMoreBranches);
|
|
48555
48537
|
}
|
|
48556
48538
|
class DiscriminatedUnionBuilder {
|
|
48557
|
-
constructor(parser) {
|
|
48539
|
+
constructor(discriminantKey, parser) {
|
|
48540
|
+
this.discriminantKey = discriminantKey;
|
|
48558
48541
|
this.parser = parser;
|
|
48559
48542
|
}
|
|
48560
|
-
|
|
48561
|
-
|
|
48543
|
+
withBranch(discriminantValue, parseNewVariant) {
|
|
48544
|
+
const parseNewBranch = discriminatedUnionBranch(this.discriminantKey, discriminantValue, parseNewVariant, this.parser);
|
|
48545
|
+
return new DiscriminatedUnionBuilder(this.discriminantKey, parseNewBranch);
|
|
48562
48546
|
}
|
|
48563
48547
|
}
|
|
48564
|
-
function
|
|
48565
|
-
return (
|
|
48566
|
-
if (
|
|
48567
|
-
return
|
|
48548
|
+
function discriminatedUnionBranch(discriminantKey, discriminantValue, parseVariant, parseOtherBranches) {
|
|
48549
|
+
return (raw, ctx) => {
|
|
48550
|
+
if (!isObject(raw)) {
|
|
48551
|
+
return ctx.failure("object", raw);
|
|
48568
48552
|
}
|
|
48569
|
-
|
|
48553
|
+
if (raw[discriminantKey] === discriminantValue) {
|
|
48554
|
+
return parseVariant(raw, ctx);
|
|
48555
|
+
}
|
|
48556
|
+
return parseOtherBranches(raw, ctx);
|
|
48570
48557
|
};
|
|
48571
48558
|
}
|
|
48572
48559
|
|
|
@@ -48574,41 +48561,27 @@ const pairOfNumbers$3 = pair(number, number);
|
|
|
48574
48561
|
const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
|
|
48575
48562
|
const parseGrapherWidget = parseWidget(constant("grapher"), object({
|
|
48576
48563
|
availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
|
|
48577
|
-
correct:
|
|
48578
|
-
type: constant("absolute_value")
|
|
48579
|
-
}), object({
|
|
48564
|
+
correct: discriminatedUnionOn("type").withBranch("absolute_value", object({
|
|
48580
48565
|
type: constant("absolute_value"),
|
|
48581
48566
|
coords: pairOfPoints
|
|
48582
|
-
})).
|
|
48583
|
-
type: constant("exponential")
|
|
48584
|
-
}), object({
|
|
48567
|
+
})).withBranch("exponential", object({
|
|
48585
48568
|
type: constant("exponential"),
|
|
48586
48569
|
asymptote: pairOfPoints,
|
|
48587
48570
|
coords: pairOfPoints
|
|
48588
|
-
})).
|
|
48589
|
-
type: constant("linear")
|
|
48590
|
-
}), object({
|
|
48571
|
+
})).withBranch("linear", object({
|
|
48591
48572
|
type: constant("linear"),
|
|
48592
|
-
coords: pairOfPoints
|
|
48593
|
-
})).
|
|
48594
|
-
type: constant("logarithm")
|
|
48595
|
-
}), object({
|
|
48573
|
+
coords: defaulted(pairOfPoints, () => [[-5, 5], [5, 5]])
|
|
48574
|
+
})).withBranch("logarithm", object({
|
|
48596
48575
|
type: constant("logarithm"),
|
|
48597
48576
|
asymptote: pairOfPoints,
|
|
48598
48577
|
coords: pairOfPoints
|
|
48599
|
-
})).
|
|
48600
|
-
type: constant("quadratic")
|
|
48601
|
-
}), object({
|
|
48578
|
+
})).withBranch("quadratic", object({
|
|
48602
48579
|
type: constant("quadratic"),
|
|
48603
48580
|
coords: pairOfPoints
|
|
48604
|
-
})).
|
|
48605
|
-
type: constant("sinusoid")
|
|
48606
|
-
}), object({
|
|
48581
|
+
})).withBranch("sinusoid", object({
|
|
48607
48582
|
type: constant("sinusoid"),
|
|
48608
48583
|
coords: pairOfPoints
|
|
48609
|
-
})).
|
|
48610
|
-
type: constant("tangent")
|
|
48611
|
-
}), object({
|
|
48584
|
+
})).withBranch("tangent", object({
|
|
48612
48585
|
type: constant("tangent"),
|
|
48613
48586
|
coords: pairOfPoints
|
|
48614
48587
|
})).parser,
|
|
@@ -48856,23 +48829,7 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
|
48856
48829
|
rulerTicks: optional(number),
|
|
48857
48830
|
tickStep: pairOfNumbers$1
|
|
48858
48831
|
}),
|
|
48859
|
-
elements: array(
|
|
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)
|
|
48832
|
+
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)
|
|
48876
48833
|
}));
|
|
48877
48834
|
|
|
48878
48835
|
// Used to represent 2-D points and ranges
|
|
@@ -48930,7 +48887,7 @@ const parsePerseusGraphTypePolygon = object({
|
|
|
48930
48887
|
showAngles: optional(boolean),
|
|
48931
48888
|
showSides: optional(boolean),
|
|
48932
48889
|
snapTo: optional(enumeration("grid", "angles", "sides")),
|
|
48933
|
-
match: optional(enumeration("similar", "congruent", "approx")),
|
|
48890
|
+
match: optional(enumeration("similar", "congruent", "approx", "exact")),
|
|
48934
48891
|
startCoords: optional(array(pairOfNumbers)),
|
|
48935
48892
|
// TODO: remove coord? it's legacy.
|
|
48936
48893
|
coord: optional(pairOfNumbers)
|
|
@@ -48965,7 +48922,7 @@ const parsePerseusGraphTypeSinusoid = object({
|
|
|
48965
48922
|
// TODO: remove coord? it's legacy.
|
|
48966
48923
|
coord: optional(pairOfNumbers)
|
|
48967
48924
|
});
|
|
48968
|
-
const parsePerseusGraphType =
|
|
48925
|
+
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;
|
|
48969
48926
|
const parseLockedFigureColor = enumeration(...lockedFigureColorNames);
|
|
48970
48927
|
const parseLockedFigureFillType = enumeration("none", "white", "translucent", "solid");
|
|
48971
48928
|
const parseLockedLineStyle = enumeration("solid", "dashed");
|
|
@@ -49084,7 +49041,7 @@ const parseLabelImageWidget = parseWidget(constant("label-image"), object({
|
|
|
49084
49041
|
})),
|
|
49085
49042
|
hideChoicesFromInstructions: boolean,
|
|
49086
49043
|
multipleAnswers: boolean,
|
|
49087
|
-
static: boolean
|
|
49044
|
+
static: defaulted(boolean, () => false)
|
|
49088
49045
|
}));
|
|
49089
49046
|
|
|
49090
49047
|
const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
@@ -49095,7 +49052,8 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
|
49095
49052
|
padding: boolean
|
|
49096
49053
|
}));
|
|
49097
49054
|
|
|
49098
|
-
const
|
|
49055
|
+
const numberOrString = union(number).or(string).parser;
|
|
49056
|
+
const numeric = pipeParsers(defaulted(numberOrString, () => NaN)).then(stringToNumber).parser;
|
|
49099
49057
|
const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
|
|
49100
49058
|
prefix: optional(string),
|
|
49101
49059
|
suffix: optional(string),
|
|
@@ -49114,7 +49072,8 @@ const parseMeasurerWidget = parseWidget(constant("measurer"), object({
|
|
|
49114
49072
|
rulerPixels: number,
|
|
49115
49073
|
rulerLength: number,
|
|
49116
49074
|
box: pair(number, number),
|
|
49117
|
-
|
|
49075
|
+
// TODO(benchristel): static is not used. Remove it?
|
|
49076
|
+
static: defaulted(boolean, () => false)
|
|
49118
49077
|
}));
|
|
49119
49078
|
|
|
49120
49079
|
const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), object({
|
|
@@ -49123,9 +49082,10 @@ const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), o
|
|
|
49123
49082
|
smiles: optional(string)
|
|
49124
49083
|
}));
|
|
49125
49084
|
|
|
49085
|
+
const emptyStringToNull = pipeParsers(constant("")).then(convert(() => null)).parser;
|
|
49126
49086
|
const parseNumberLineWidget = parseWidget(constant("number-line"), object({
|
|
49127
49087
|
range: array(number),
|
|
49128
|
-
labelRange: array(nullable(number)),
|
|
49088
|
+
labelRange: array(nullable(union(number).or(emptyStringToNull).parser)),
|
|
49129
49089
|
labelStyle: string,
|
|
49130
49090
|
labelTicks: boolean,
|
|
49131
49091
|
isTickCtrl: optional(nullable(boolean)),
|
|
@@ -49137,10 +49097,10 @@ const parseNumberLineWidget = parseWidget(constant("number-line"), object({
|
|
|
49137
49097
|
snapDivisions: defaulted(number, () => 2),
|
|
49138
49098
|
tickStep: optional(nullable(number)),
|
|
49139
49099
|
correctRel: optional(nullable(string)),
|
|
49140
|
-
correctX: number,
|
|
49100
|
+
correctX: nullable(number),
|
|
49141
49101
|
initialX: optional(nullable(number)),
|
|
49142
49102
|
showTooltip: optional(boolean),
|
|
49143
|
-
static: boolean
|
|
49103
|
+
static: defaulted(boolean, () => false)
|
|
49144
49104
|
}));
|
|
49145
49105
|
|
|
49146
49106
|
const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "decimal", "percent", "pi");
|
|
@@ -49155,14 +49115,14 @@ const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
|
49155
49115
|
answerForms: optional(array(parseMathFormat)),
|
|
49156
49116
|
strict: boolean,
|
|
49157
49117
|
maxError: optional(nullable(number)),
|
|
49158
|
-
// TODO(benchristel): simplify should never be
|
|
49118
|
+
// TODO(benchristel): simplify should never be a boolean, but we
|
|
49159
49119
|
// have some content where it is anyway. If we ever backfill
|
|
49160
49120
|
// the data, we should simplify `simplify`.
|
|
49161
|
-
simplify: optional(nullable(union(string).or(pipeParsers(
|
|
49121
|
+
simplify: optional(nullable(union(string).or(pipeParsers(boolean).then(convert(String)).parser).parser))
|
|
49162
49122
|
})),
|
|
49163
49123
|
labelText: optional(string),
|
|
49164
49124
|
size: string,
|
|
49165
|
-
coefficient: boolean,
|
|
49125
|
+
coefficient: defaulted(boolean, () => false),
|
|
49166
49126
|
rightAlign: optional(boolean),
|
|
49167
49127
|
static: defaulted(boolean, () => false),
|
|
49168
49128
|
answerForms: optional(array(object({
|
|
@@ -49198,9 +49158,9 @@ const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
|
49198
49158
|
}));
|
|
49199
49159
|
|
|
49200
49160
|
const parsePassageWidget = parseWidget(constant("passage"), object({
|
|
49201
|
-
footnotes: string,
|
|
49161
|
+
footnotes: defaulted(string, () => ""),
|
|
49202
49162
|
passageText: string,
|
|
49203
|
-
passageTitle: string,
|
|
49163
|
+
passageTitle: defaulted(string, () => ""),
|
|
49204
49164
|
showLineNumbers: boolean,
|
|
49205
49165
|
static: defaulted(boolean, () => false)
|
|
49206
49166
|
}));
|
|
@@ -49385,6 +49345,10 @@ const parseWidgetsMapEntry = (_ref, widgetMap, ctx) => {
|
|
|
49385
49345
|
return parseAndAssign(`sequence ${id}`, parseDeprecatedWidget);
|
|
49386
49346
|
case "lights-puzzle":
|
|
49387
49347
|
return parseAndAssign(`lights-puzzle ${id}`, parseDeprecatedWidget);
|
|
49348
|
+
case "simulator":
|
|
49349
|
+
return parseAndAssign(`simulator ${id}`, parseDeprecatedWidget);
|
|
49350
|
+
case "transformer":
|
|
49351
|
+
return parseAndAssign(`transformer ${id}`, parseDeprecatedWidget);
|
|
49388
49352
|
default:
|
|
49389
49353
|
if (getWidget(type)) {
|
|
49390
49354
|
// @ts-expect-error - 'type' is not a valid widget type
|