@khanacademy/perseus 49.1.7 → 49.2.1
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/components/math-input.d.ts +2 -1
- package/dist/es/index.css +6 -0
- package/dist/es/index.js +277 -147
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +16 -12
- package/dist/es/strings.js.map +1 -1
- package/dist/index.css +6 -0
- package/dist/index.js +277 -138
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +12 -9
- package/dist/server-item-renderer.d.ts +1 -1
- package/dist/strings.d.ts +6 -5
- package/dist/strings.js +16 -12
- package/dist/strings.js.map +1 -1
- package/dist/types.d.ts +2 -5
- package/dist/util/parse-perseus-json/general-purpose-parsers/convert.d.ts +2 -0
- package/dist/util/parse-perseus-json/general-purpose-parsers/discriminated-union.d.ts +21 -0
- 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 +36 -0
- package/dist/widgets/expression/expression.d.ts +3 -3
- 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/dist/widgets/passage-ref/passage-ref.d.ts +7 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -487,12 +487,12 @@ const mockStrings = {
|
|
|
487
487
|
angleMeasure,
|
|
488
488
|
vertexX,
|
|
489
489
|
vertexY,
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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;
|
|
@@ -27732,6 +27739,7 @@ class Dropdown extends React__namespace.Component {
|
|
|
27732
27739
|
}, this.props.visibleLabel), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
|
|
27733
27740
|
id: dropdownId,
|
|
27734
27741
|
placeholder: "",
|
|
27742
|
+
className: "perseus-dropdown",
|
|
27735
27743
|
onChange: value => this._handleChange(parseInt(value)),
|
|
27736
27744
|
selectedValue: String(this.props.selected),
|
|
27737
27745
|
disabled: this.props.apiOptions.readOnly,
|
|
@@ -32680,7 +32688,7 @@ const LegacyGrid = _ref => {
|
|
|
32680
32688
|
* "new-awesomeness": MyAwesomeNewWidget;
|
|
32681
32689
|
*
|
|
32682
32690
|
* // A deprecated widget
|
|
32683
|
-
* "super-old-widget":
|
|
32691
|
+
* "super-old-widget": DeprecatedStandinWidget;
|
|
32684
32692
|
* }
|
|
32685
32693
|
* }
|
|
32686
32694
|
*
|
|
@@ -32874,7 +32882,6 @@ function LockedLabel(props) {
|
|
|
32874
32882
|
|
|
32875
32883
|
function GraphLockedLabelsLayer(props) {
|
|
32876
32884
|
const {
|
|
32877
|
-
flags,
|
|
32878
32885
|
lockedFigures
|
|
32879
32886
|
} = props;
|
|
32880
32887
|
return lockedFigures.map((figure, i) => {
|
|
@@ -32883,30 +32890,15 @@ function GraphLockedLabelsLayer(props) {
|
|
|
32883
32890
|
key: `label-${i}`
|
|
32884
32891
|
}, figure));
|
|
32885
32892
|
}
|
|
32886
|
-
|
|
32887
|
-
|
|
32888
|
-
|
|
32889
|
-
|
|
32890
|
-
|
|
32891
|
-
|
|
32892
|
-
|
|
32893
|
-
|
|
32894
|
-
|
|
32895
|
-
// Polygon flag + polygon type
|
|
32896
|
-
flags?.["mafs"]?.["locked-polygon-labels"] && figure.type === "polygon" ||
|
|
32897
|
-
// Function flag + function type
|
|
32898
|
-
flags?.["mafs"]?.["locked-function-labels"] && figure.type === "function") {
|
|
32899
|
-
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, {
|
|
32900
|
-
key: i
|
|
32901
|
-
}, figure.labels?.map((label, j) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
32902
|
-
key: `${i}-label-${j}`
|
|
32903
|
-
}, 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({
|
|
32904
|
-
key: `locked-figure-${i}-point-1-label-${k}`
|
|
32905
|
-
}, label))), figure.points[1].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
32906
|
-
key: `locked-figure-${i}-point-2-label-${k}`
|
|
32907
|
-
}, label)))));
|
|
32908
|
-
}
|
|
32909
|
-
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)))));
|
|
32910
32902
|
});
|
|
32911
32903
|
}
|
|
32912
32904
|
|
|
@@ -32918,10 +32910,9 @@ const LockedEllipse = props => {
|
|
|
32918
32910
|
color,
|
|
32919
32911
|
fillStyle,
|
|
32920
32912
|
strokeStyle,
|
|
32921
|
-
ariaLabel
|
|
32922
|
-
flags
|
|
32913
|
+
ariaLabel
|
|
32923
32914
|
} = props;
|
|
32924
|
-
const hasAria = ariaLabel
|
|
32915
|
+
const hasAria = !!ariaLabel;
|
|
32925
32916
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
32926
32917
|
className: "locked-ellipse",
|
|
32927
32918
|
"aria-label": hasAria ? ariaLabel : undefined,
|
|
@@ -32958,7 +32949,7 @@ const LockedFunction = props => {
|
|
|
32958
32949
|
style: strokeStyle,
|
|
32959
32950
|
domain
|
|
32960
32951
|
};
|
|
32961
|
-
const hasAria = props.ariaLabel
|
|
32952
|
+
const hasAria = !!props.ariaLabel;
|
|
32962
32953
|
React.useEffect(() => {
|
|
32963
32954
|
// Parsing the equation in a "useEffect" hook saves about 2ms each frame
|
|
32964
32955
|
// when the learner is interacting with the graph (i.e. moving points).
|
|
@@ -33085,11 +33076,10 @@ const LockedLine = props => {
|
|
|
33085
33076
|
showPoint1,
|
|
33086
33077
|
showPoint2,
|
|
33087
33078
|
ariaLabel,
|
|
33088
|
-
flags,
|
|
33089
33079
|
range
|
|
33090
33080
|
} = props;
|
|
33091
33081
|
const [point1, point2] = points;
|
|
33092
|
-
const hasAria = ariaLabel
|
|
33082
|
+
const hasAria = !!ariaLabel;
|
|
33093
33083
|
let line;
|
|
33094
33084
|
if (kind === "ray") {
|
|
33095
33085
|
// Rays extend to the end of the graph in one direction.
|
|
@@ -33157,14 +33147,13 @@ const LockedLine = props => {
|
|
|
33157
33147
|
|
|
33158
33148
|
const LockedPoint = props => {
|
|
33159
33149
|
const {
|
|
33160
|
-
flags,
|
|
33161
33150
|
color,
|
|
33162
33151
|
coord,
|
|
33163
33152
|
filled,
|
|
33164
33153
|
ariaLabel
|
|
33165
33154
|
} = props;
|
|
33166
33155
|
const [x, y] = coord;
|
|
33167
|
-
const hasAria = ariaLabel
|
|
33156
|
+
const hasAria = !!ariaLabel;
|
|
33168
33157
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
33169
33158
|
className: "locked-point",
|
|
33170
33159
|
"aria-label": hasAria ? ariaLabel : undefined,
|
|
@@ -33190,7 +33179,7 @@ const LockedPolygon = props => {
|
|
|
33190
33179
|
fillStyle,
|
|
33191
33180
|
strokeStyle
|
|
33192
33181
|
} = props;
|
|
33193
|
-
const hasAria = props.ariaLabel
|
|
33182
|
+
const hasAria = !!props.ariaLabel;
|
|
33194
33183
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
33195
33184
|
className: "locked-polygon",
|
|
33196
33185
|
"aria-label": hasAria ? props.ariaLabel : undefined,
|
|
@@ -33221,11 +33210,10 @@ const LockedVector = props => {
|
|
|
33221
33210
|
const {
|
|
33222
33211
|
color,
|
|
33223
33212
|
points,
|
|
33224
|
-
ariaLabel
|
|
33225
|
-
flags
|
|
33213
|
+
ariaLabel
|
|
33226
33214
|
} = props;
|
|
33227
33215
|
const [tail, tip] = points;
|
|
33228
|
-
const hasAria = ariaLabel
|
|
33216
|
+
const hasAria = !!ariaLabel;
|
|
33229
33217
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
33230
33218
|
className: "locked-vector",
|
|
33231
33219
|
"aria-label": hasAria ? ariaLabel : undefined,
|
|
@@ -33239,7 +33227,6 @@ const LockedVector = props => {
|
|
|
33239
33227
|
|
|
33240
33228
|
const GraphLockedLayer = props => {
|
|
33241
33229
|
const {
|
|
33242
|
-
flags,
|
|
33243
33230
|
lockedFigures
|
|
33244
33231
|
} = props;
|
|
33245
33232
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, lockedFigures.map((figure, index) => {
|
|
@@ -33247,40 +33234,28 @@ const GraphLockedLayer = props => {
|
|
|
33247
33234
|
case "point":
|
|
33248
33235
|
return /*#__PURE__*/React__namespace.createElement(LockedPoint, _extends({
|
|
33249
33236
|
key: `point-${index}`
|
|
33250
|
-
}, figure
|
|
33251
|
-
flags: flags
|
|
33252
|
-
}));
|
|
33237
|
+
}, figure));
|
|
33253
33238
|
case "line":
|
|
33254
33239
|
return /*#__PURE__*/React__namespace.createElement(LockedLine, _extends({
|
|
33255
33240
|
key: `line-${index}`,
|
|
33256
33241
|
range: props.range
|
|
33257
|
-
}, figure
|
|
33258
|
-
flags: flags
|
|
33259
|
-
}));
|
|
33242
|
+
}, figure));
|
|
33260
33243
|
case "vector":
|
|
33261
33244
|
return /*#__PURE__*/React__namespace.createElement(LockedVector, _extends({
|
|
33262
33245
|
key: `vector-${index}`
|
|
33263
|
-
}, figure
|
|
33264
|
-
flags: flags
|
|
33265
|
-
}));
|
|
33246
|
+
}, figure));
|
|
33266
33247
|
case "ellipse":
|
|
33267
33248
|
return /*#__PURE__*/React__namespace.createElement(LockedEllipse, _extends({
|
|
33268
33249
|
key: `ellipse-${index}`
|
|
33269
|
-
}, figure
|
|
33270
|
-
flags: flags
|
|
33271
|
-
}));
|
|
33250
|
+
}, figure));
|
|
33272
33251
|
case "polygon":
|
|
33273
33252
|
return /*#__PURE__*/React__namespace.createElement(LockedPolygon, _extends({
|
|
33274
33253
|
key: `polygon-${index}`
|
|
33275
|
-
}, figure
|
|
33276
|
-
flags: flags
|
|
33277
|
-
}));
|
|
33254
|
+
}, figure));
|
|
33278
33255
|
case "function":
|
|
33279
33256
|
return /*#__PURE__*/React__namespace.createElement(LockedFunction, _extends({
|
|
33280
33257
|
key: `function-${index}`
|
|
33281
|
-
}, figure
|
|
33282
|
-
flags: flags
|
|
33283
|
-
}));
|
|
33258
|
+
}, figure));
|
|
33284
33259
|
case "label":
|
|
33285
33260
|
// This is rendered outside the SVG element, since
|
|
33286
33261
|
// TeX cannot be rendered inside an SVG.
|
|
@@ -34459,10 +34434,10 @@ function AngleGraph(props) {
|
|
|
34459
34434
|
angleMeasure,
|
|
34460
34435
|
vertexX: srFormatNumber(coords[1][X], locale),
|
|
34461
34436
|
vertexY: srFormatNumber(coords[1][Y], locale),
|
|
34462
|
-
|
|
34463
|
-
|
|
34464
|
-
|
|
34465
|
-
|
|
34437
|
+
startingSideX: srFormatNumber(coords[2][X], locale),
|
|
34438
|
+
startingSideY: srFormatNumber(coords[2][Y], locale),
|
|
34439
|
+
endingSideX: srFormatNumber(coords[0][X], locale),
|
|
34440
|
+
endingSideY: srFormatNumber(coords[0][Y], locale)
|
|
34466
34441
|
});
|
|
34467
34442
|
const formatCoordinates = (x, y) => ({
|
|
34468
34443
|
x: srFormatNumber(x, locale),
|
|
@@ -35922,11 +35897,9 @@ const MafsGraph = props => {
|
|
|
35922
35897
|
})),
|
|
35923
35898
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
35924
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, {
|
|
35925
|
-
flags: props.flags,
|
|
35926
35900
|
lockedFigures: props.lockedFigures,
|
|
35927
35901
|
range: state.range
|
|
35928
|
-
}))), props.
|
|
35929
|
-
flags: props.flags,
|
|
35902
|
+
}))), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
|
|
35930
35903
|
lockedFigures: props.lockedFigures
|
|
35931
35904
|
}), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
35932
35905
|
style: {
|
|
@@ -45936,7 +45909,7 @@ const init = function () {
|
|
|
45936
45909
|
|
|
45937
45910
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45938
45911
|
const libName = "@khanacademy/perseus";
|
|
45939
|
-
const libVersion = "49.1
|
|
45912
|
+
const libVersion = "49.2.1";
|
|
45940
45913
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45941
45914
|
|
|
45942
45915
|
const apiVersion = {
|
|
@@ -48292,9 +48265,9 @@ function parseWidgetWithVersion(parseVersion, parseType, parseOptions) {
|
|
|
48292
48265
|
const parseCategorizerWidget = parseWidget(constant("categorizer"), object({
|
|
48293
48266
|
items: array(string),
|
|
48294
48267
|
categories: array(string),
|
|
48295
|
-
randomizeItems: boolean,
|
|
48268
|
+
randomizeItems: defaulted(boolean, () => false),
|
|
48296
48269
|
static: defaulted(boolean, () => false),
|
|
48297
|
-
values: array(number),
|
|
48270
|
+
values: array(defaulted(number, () => 0)),
|
|
48298
48271
|
highlightLint: optional(boolean),
|
|
48299
48272
|
linterContext: optional(object({
|
|
48300
48273
|
contentType: string,
|
|
@@ -48314,7 +48287,7 @@ const parseCSProgramWidget = parseWidget(constant("cs-program"), object({
|
|
|
48314
48287
|
showButtons: boolean,
|
|
48315
48288
|
width: number,
|
|
48316
48289
|
height: number,
|
|
48317
|
-
static: boolean
|
|
48290
|
+
static: defaulted(boolean, () => false)
|
|
48318
48291
|
}));
|
|
48319
48292
|
|
|
48320
48293
|
const parseDefinitionWidget = parseWidget(constant("definition"), object({
|
|
@@ -48324,7 +48297,7 @@ const parseDefinitionWidget = parseWidget(constant("definition"), object({
|
|
|
48324
48297
|
}));
|
|
48325
48298
|
|
|
48326
48299
|
const parseDropdownWidget = parseWidget(constant("dropdown"), object({
|
|
48327
|
-
placeholder: string,
|
|
48300
|
+
placeholder: defaulted(string, () => ""),
|
|
48328
48301
|
ariaLabel: optional(string),
|
|
48329
48302
|
visibleLabel: optional(string),
|
|
48330
48303
|
static: defaulted(boolean, () => false),
|
|
@@ -48341,33 +48314,128 @@ const parseExplanationWidget = parseWidget(constant("explanation"), object({
|
|
|
48341
48314
|
// We wrap parseWidgetsMap in a function here to make sure it is not
|
|
48342
48315
|
// referenced before it is defined. There is an import cycle between
|
|
48343
48316
|
// this file and widgets-map.ts that could cause it to be undefined.
|
|
48344
|
-
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
48345
|
-
static: boolean
|
|
48317
|
+
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
|
|
48318
|
+
static: defaulted(boolean, () => false)
|
|
48346
48319
|
}));
|
|
48347
48320
|
|
|
48348
|
-
|
|
48349
|
-
|
|
48350
|
-
|
|
48351
|
-
|
|
48321
|
+
// Given a function, creates a PartialParser that converts one type to another
|
|
48322
|
+
// using that function. The returned parser never fails.
|
|
48323
|
+
function convert(f) {
|
|
48324
|
+
return (rawValue, ctx) => ctx.success(f(rawValue));
|
|
48325
|
+
}
|
|
48326
|
+
|
|
48327
|
+
/**
|
|
48328
|
+
* Creates a parser for a widget options type with multiple major versions. Old
|
|
48329
|
+
* versions are migrated to the latest version. The parse fails if the input
|
|
48330
|
+
* data does not match any of the versions.
|
|
48331
|
+
*
|
|
48332
|
+
* @example
|
|
48333
|
+
* const parseOptions = versionedWidgetOptions(3, parseOptionsV3)
|
|
48334
|
+
* .withMigrationFrom(2, parseOptionsV2, migrateV2ToV3)
|
|
48335
|
+
* .withMigrationFrom(1, parseOptionsV1, migrateV1ToV2)
|
|
48336
|
+
* .withMigrationFrom(0, parseOptionsV0, migrateV0ToV1)
|
|
48337
|
+
* .parser;
|
|
48338
|
+
*
|
|
48339
|
+
* @param latestMajorVersion the latest major version of the widget options.
|
|
48340
|
+
* @param parseLatest a {@link Parser} for the latest version of the widget
|
|
48341
|
+
* options.
|
|
48342
|
+
* @returns a builder object, to which migrations from earlier versions can be
|
|
48343
|
+
* added. Migrations must be added in "reverse chronological" order as in the
|
|
48344
|
+
* example above.
|
|
48345
|
+
*/
|
|
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));
|
|
48348
|
+
}
|
|
48349
|
+
class VersionedWidgetOptionsParserBuilder {
|
|
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
|
+
};
|
|
48368
|
+
}
|
|
48369
|
+
|
|
48370
|
+
/**
|
|
48371
|
+
* Add a migration from an old version of the widget options.
|
|
48372
|
+
*/
|
|
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);
|
|
48377
|
+
}
|
|
48378
|
+
}
|
|
48379
|
+
const parseVersionedObject = object({
|
|
48380
|
+
version: defaulted(object({
|
|
48381
|
+
major: number,
|
|
48382
|
+
minor: number
|
|
48383
|
+
}), () => ({
|
|
48384
|
+
major: 0,
|
|
48385
|
+
minor: 0
|
|
48386
|
+
}))
|
|
48387
|
+
});
|
|
48388
|
+
|
|
48389
|
+
const stringOrNumberOrNullOrUndefined = union(string).or(number).or(constant(null)).or(constant(undefined)).parser;
|
|
48390
|
+
const parsePossiblyInvalidAnswerForm = object({
|
|
48391
|
+
// `value` is the possibly invalid part of this. It should always be a
|
|
48392
|
+
// string, but some answer forms don't have it. The Expression widget
|
|
48393
|
+
// ignores invalid values, so we can safely filter them out during parsing.
|
|
48394
|
+
value: optional(string),
|
|
48395
|
+
form: defaulted(boolean, () => false),
|
|
48396
|
+
simplify: defaulted(boolean, () => false),
|
|
48352
48397
|
considered: enumeration("correct", "wrong", "ungraded"),
|
|
48353
|
-
key: pipeParsers(
|
|
48398
|
+
key: pipeParsers(stringOrNumberOrNullOrUndefined).then(convert(String)).parser
|
|
48354
48399
|
});
|
|
48355
|
-
|
|
48400
|
+
function removeInvalidAnswerForms(possiblyInvalid) {
|
|
48401
|
+
const valid = [];
|
|
48402
|
+
for (const answerForm of possiblyInvalid) {
|
|
48403
|
+
const {
|
|
48404
|
+
value
|
|
48405
|
+
} = answerForm;
|
|
48406
|
+
if (value != null) {
|
|
48407
|
+
// Copying the object seems to be needed to make TypeScript happy
|
|
48408
|
+
valid.push({
|
|
48409
|
+
...answerForm,
|
|
48410
|
+
value
|
|
48411
|
+
});
|
|
48412
|
+
}
|
|
48413
|
+
}
|
|
48414
|
+
return valid;
|
|
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"]);
|
|
48421
|
+
const version1 = object({
|
|
48356
48422
|
major: constant(1),
|
|
48357
48423
|
minor: number
|
|
48358
|
-
})
|
|
48359
|
-
|
|
48424
|
+
});
|
|
48425
|
+
const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expression"), object({
|
|
48426
|
+
answerForms: pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,
|
|
48360
48427
|
functions: array(string),
|
|
48361
48428
|
times: boolean,
|
|
48362
48429
|
visibleLabel: optional(string),
|
|
48363
48430
|
ariaLabel: optional(string),
|
|
48364
|
-
buttonSets:
|
|
48431
|
+
buttonSets: parseButtonSets,
|
|
48365
48432
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48366
48433
|
}));
|
|
48367
|
-
const
|
|
48434
|
+
const version0 = optional(object({
|
|
48368
48435
|
major: constant(0),
|
|
48369
48436
|
minor: number
|
|
48370
|
-
}))
|
|
48437
|
+
}));
|
|
48438
|
+
const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expression"), object({
|
|
48371
48439
|
functions: array(string),
|
|
48372
48440
|
times: boolean,
|
|
48373
48441
|
visibleLabel: optional(string),
|
|
@@ -48375,14 +48443,14 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(optional(object({
|
|
|
48375
48443
|
form: boolean,
|
|
48376
48444
|
simplify: boolean,
|
|
48377
48445
|
value: string,
|
|
48378
|
-
buttonSets:
|
|
48446
|
+
buttonSets: parseButtonSets,
|
|
48379
48447
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48380
48448
|
}));
|
|
48381
|
-
function migrateV0ToV1(widget
|
|
48449
|
+
function migrateV0ToV1(widget) {
|
|
48382
48450
|
const {
|
|
48383
48451
|
options
|
|
48384
48452
|
} = widget;
|
|
48385
|
-
return
|
|
48453
|
+
return {
|
|
48386
48454
|
...widget,
|
|
48387
48455
|
version: {
|
|
48388
48456
|
major: 1,
|
|
@@ -48402,9 +48470,9 @@ function migrateV0ToV1(widget, ctx) {
|
|
|
48402
48470
|
value: options.value
|
|
48403
48471
|
}]
|
|
48404
48472
|
}
|
|
48405
|
-
}
|
|
48473
|
+
};
|
|
48406
48474
|
}
|
|
48407
|
-
const parseExpressionWidget =
|
|
48475
|
+
const parseExpressionWidget = versionedWidgetOptions(1, parseExpressionWidgetV1).withMigrationFrom(0, parseExpressionWidgetV0, migrateV0ToV1).parser;
|
|
48408
48476
|
|
|
48409
48477
|
const parsePerseusRenderer = defaulted(object({
|
|
48410
48478
|
// TODO(benchristel): content is also defaulted to empty string in
|
|
@@ -48425,13 +48493,14 @@ const parsePerseusRenderer = defaulted(object({
|
|
|
48425
48493
|
images: {}
|
|
48426
48494
|
}));
|
|
48427
48495
|
|
|
48496
|
+
const falseToNull = pipeParsers(constant(false)).then(convert(() => null)).parser;
|
|
48428
48497
|
const parseGradedGroupWidgetOptions = object({
|
|
48429
48498
|
title: defaulted(string, () => ""),
|
|
48430
48499
|
hasHint: optional(nullable(boolean)),
|
|
48431
48500
|
// This module has an import cycle with parsePerseusRenderer.
|
|
48432
48501
|
// The anonymous function below ensures that we don't try to access
|
|
48433
48502
|
// parsePerseusRenderer before it's defined.
|
|
48434
|
-
hint:
|
|
48503
|
+
hint: union(falseToNull).or(constant(null)).or(constant(undefined)).or((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx)).parser,
|
|
48435
48504
|
content: string,
|
|
48436
48505
|
// This module has an import cycle with parseWidgetsMap.
|
|
48437
48506
|
// The anonymous function below ensures that we don't try to access
|
|
@@ -48450,31 +48519,69 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
|
|
|
48450
48519
|
gradedGroups: array(parseGradedGroupWidgetOptions)
|
|
48451
48520
|
}));
|
|
48452
48521
|
|
|
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);
|
|
48537
|
+
}
|
|
48538
|
+
class DiscriminatedUnionBuilder {
|
|
48539
|
+
constructor(discriminantKey, parser) {
|
|
48540
|
+
this.discriminantKey = discriminantKey;
|
|
48541
|
+
this.parser = parser;
|
|
48542
|
+
}
|
|
48543
|
+
withBranch(discriminantValue, parseNewVariant) {
|
|
48544
|
+
const parseNewBranch = discriminatedUnionBranch(this.discriminantKey, discriminantValue, parseNewVariant, this.parser);
|
|
48545
|
+
return new DiscriminatedUnionBuilder(this.discriminantKey, parseNewBranch);
|
|
48546
|
+
}
|
|
48547
|
+
}
|
|
48548
|
+
function discriminatedUnionBranch(discriminantKey, discriminantValue, parseVariant, parseOtherBranches) {
|
|
48549
|
+
return (raw, ctx) => {
|
|
48550
|
+
if (!isObject(raw)) {
|
|
48551
|
+
return ctx.failure("object", raw);
|
|
48552
|
+
}
|
|
48553
|
+
if (raw[discriminantKey] === discriminantValue) {
|
|
48554
|
+
return parseVariant(raw, ctx);
|
|
48555
|
+
}
|
|
48556
|
+
return parseOtherBranches(raw, ctx);
|
|
48557
|
+
};
|
|
48558
|
+
}
|
|
48559
|
+
|
|
48453
48560
|
const pairOfNumbers$3 = pair(number, number);
|
|
48454
48561
|
const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
|
|
48455
48562
|
const parseGrapherWidget = parseWidget(constant("grapher"), object({
|
|
48456
48563
|
availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
|
|
48457
|
-
correct:
|
|
48564
|
+
correct: discriminatedUnionOn("type").withBranch("absolute_value", object({
|
|
48458
48565
|
type: constant("absolute_value"),
|
|
48459
48566
|
coords: pairOfPoints
|
|
48460
|
-
})).
|
|
48567
|
+
})).withBranch("exponential", object({
|
|
48461
48568
|
type: constant("exponential"),
|
|
48462
48569
|
asymptote: pairOfPoints,
|
|
48463
48570
|
coords: pairOfPoints
|
|
48464
|
-
})).
|
|
48571
|
+
})).withBranch("linear", object({
|
|
48465
48572
|
type: constant("linear"),
|
|
48466
|
-
coords: pairOfPoints
|
|
48467
|
-
})).
|
|
48573
|
+
coords: defaulted(pairOfPoints, () => [[-5, 5], [5, 5]])
|
|
48574
|
+
})).withBranch("logarithm", object({
|
|
48468
48575
|
type: constant("logarithm"),
|
|
48469
48576
|
asymptote: pairOfPoints,
|
|
48470
48577
|
coords: pairOfPoints
|
|
48471
|
-
})).
|
|
48578
|
+
})).withBranch("quadratic", object({
|
|
48472
48579
|
type: constant("quadratic"),
|
|
48473
48580
|
coords: pairOfPoints
|
|
48474
|
-
})).
|
|
48581
|
+
})).withBranch("sinusoid", object({
|
|
48475
48582
|
type: constant("sinusoid"),
|
|
48476
48583
|
coords: pairOfPoints
|
|
48477
|
-
})).
|
|
48584
|
+
})).withBranch("tangent", object({
|
|
48478
48585
|
type: constant("tangent"),
|
|
48479
48586
|
coords: pairOfPoints
|
|
48480
48587
|
})).parser,
|
|
@@ -48534,15 +48641,22 @@ const stringToNumber = (rawValue, ctx) => {
|
|
|
48534
48641
|
return ctx.success(parsedNumber);
|
|
48535
48642
|
};
|
|
48536
48643
|
|
|
48537
|
-
|
|
48644
|
+
function emptyToZero(x) {
|
|
48645
|
+
return x === "" ? 0 : x;
|
|
48646
|
+
}
|
|
48647
|
+
const imageDimensionToNumber = pipeParsers(union(number).or(string).parser)
|
|
48648
|
+
// In this specific case, empty string is equivalent to zero. An empty
|
|
48649
|
+
// string parses to either NaN (using parseInt) or 0 (using unary +) and
|
|
48650
|
+
// CSS will treat NaN as invalid and default to 0 instead.
|
|
48651
|
+
.then(convert(emptyToZero)).then(stringToNumber).parser;
|
|
48538
48652
|
const parsePerseusImageBackground = object({
|
|
48539
48653
|
url: optional(nullable(string)),
|
|
48540
|
-
width: optional(
|
|
48541
|
-
height: optional(
|
|
48542
|
-
top: optional(
|
|
48543
|
-
left: optional(
|
|
48544
|
-
bottom: optional(
|
|
48545
|
-
scale: optional(
|
|
48654
|
+
width: optional(imageDimensionToNumber),
|
|
48655
|
+
height: optional(imageDimensionToNumber),
|
|
48656
|
+
top: optional(imageDimensionToNumber),
|
|
48657
|
+
left: optional(imageDimensionToNumber),
|
|
48658
|
+
bottom: optional(imageDimensionToNumber),
|
|
48659
|
+
scale: optional(imageDimensionToNumber)
|
|
48546
48660
|
});
|
|
48547
48661
|
|
|
48548
48662
|
const pairOfNumbers$2 = pair(number, number);
|
|
@@ -48583,8 +48697,10 @@ const parseInputNumberWidget = parseWidget(constant("input-number"), object({
|
|
|
48583
48697
|
}));
|
|
48584
48698
|
|
|
48585
48699
|
const pairOfNumbers$1 = pair(number, number);
|
|
48700
|
+
const stringOrEmpty = defaulted(string, () => "");
|
|
48701
|
+
const parseFunctionType = constant("function");
|
|
48586
48702
|
const parseFunctionElement = object({
|
|
48587
|
-
type:
|
|
48703
|
+
type: parseFunctionType,
|
|
48588
48704
|
key: string,
|
|
48589
48705
|
options: object({
|
|
48590
48706
|
value: string,
|
|
@@ -48596,8 +48712,9 @@ const parseFunctionElement = object({
|
|
|
48596
48712
|
strokeWidth: number
|
|
48597
48713
|
})
|
|
48598
48714
|
});
|
|
48715
|
+
const parseLabelType = constant("label");
|
|
48599
48716
|
const parseLabelElement = object({
|
|
48600
|
-
type:
|
|
48717
|
+
type: parseLabelType,
|
|
48601
48718
|
key: string,
|
|
48602
48719
|
options: object({
|
|
48603
48720
|
label: string,
|
|
@@ -48606,8 +48723,9 @@ const parseLabelElement = object({
|
|
|
48606
48723
|
coordY: string
|
|
48607
48724
|
})
|
|
48608
48725
|
});
|
|
48726
|
+
const parseLineType = constant("line");
|
|
48609
48727
|
const parseLineElement = object({
|
|
48610
|
-
type:
|
|
48728
|
+
type: parseLineType,
|
|
48611
48729
|
key: string,
|
|
48612
48730
|
options: object({
|
|
48613
48731
|
color: string,
|
|
@@ -48620,8 +48738,9 @@ const parseLineElement = object({
|
|
|
48620
48738
|
arrows: string
|
|
48621
48739
|
})
|
|
48622
48740
|
});
|
|
48741
|
+
const parseMovableLineType = constant("movable-line");
|
|
48623
48742
|
const parseMovableLineElement = object({
|
|
48624
|
-
type:
|
|
48743
|
+
type: parseMovableLineType,
|
|
48625
48744
|
key: string,
|
|
48626
48745
|
options: object({
|
|
48627
48746
|
startX: string,
|
|
@@ -48639,8 +48758,9 @@ const parseMovableLineElement = object({
|
|
|
48639
48758
|
constraintYMax: string
|
|
48640
48759
|
})
|
|
48641
48760
|
});
|
|
48761
|
+
const parseMovablePointType = constant("movable-point");
|
|
48642
48762
|
const parseMovablePointElement = object({
|
|
48643
|
-
type:
|
|
48763
|
+
type: parseMovablePointType,
|
|
48644
48764
|
key: string,
|
|
48645
48765
|
options: object({
|
|
48646
48766
|
startX: string,
|
|
@@ -48649,14 +48769,15 @@ const parseMovablePointElement = object({
|
|
|
48649
48769
|
constraint: string,
|
|
48650
48770
|
snap: number,
|
|
48651
48771
|
constraintFn: string,
|
|
48652
|
-
constraintXMin:
|
|
48653
|
-
constraintXMax:
|
|
48654
|
-
constraintYMin:
|
|
48655
|
-
constraintYMax:
|
|
48772
|
+
constraintXMin: stringOrEmpty,
|
|
48773
|
+
constraintXMax: stringOrEmpty,
|
|
48774
|
+
constraintYMin: stringOrEmpty,
|
|
48775
|
+
constraintYMax: stringOrEmpty
|
|
48656
48776
|
})
|
|
48657
48777
|
});
|
|
48778
|
+
const parseParametricType = constant("parametric");
|
|
48658
48779
|
const parseParametricElement = object({
|
|
48659
|
-
type:
|
|
48780
|
+
type: parseParametricType,
|
|
48660
48781
|
key: string,
|
|
48661
48782
|
options: object({
|
|
48662
48783
|
x: string,
|
|
@@ -48668,8 +48789,9 @@ const parseParametricElement = object({
|
|
|
48668
48789
|
strokeWidth: number
|
|
48669
48790
|
})
|
|
48670
48791
|
});
|
|
48792
|
+
const parsePointType = constant("point");
|
|
48671
48793
|
const parsePointElement = object({
|
|
48672
|
-
type:
|
|
48794
|
+
type: parsePointType,
|
|
48673
48795
|
key: string,
|
|
48674
48796
|
options: object({
|
|
48675
48797
|
color: string,
|
|
@@ -48677,8 +48799,9 @@ const parsePointElement = object({
|
|
|
48677
48799
|
coordY: string
|
|
48678
48800
|
})
|
|
48679
48801
|
});
|
|
48802
|
+
const parseRectangleType = constant("rectangle");
|
|
48680
48803
|
const parseRectangleElement = object({
|
|
48681
|
-
type:
|
|
48804
|
+
type: parseRectangleType,
|
|
48682
48805
|
key: string,
|
|
48683
48806
|
options: object({
|
|
48684
48807
|
color: string,
|
|
@@ -48689,7 +48812,7 @@ const parseRectangleElement = object({
|
|
|
48689
48812
|
})
|
|
48690
48813
|
});
|
|
48691
48814
|
const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
48692
|
-
static: boolean,
|
|
48815
|
+
static: defaulted(boolean, () => false),
|
|
48693
48816
|
graph: object({
|
|
48694
48817
|
editableSettings: optional(array(enumeration("canvas", "graph"))),
|
|
48695
48818
|
box: pairOfNumbers$1,
|
|
@@ -48706,7 +48829,7 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
|
48706
48829
|
rulerTicks: optional(number),
|
|
48707
48830
|
tickStep: pairOfNumbers$1
|
|
48708
48831
|
}),
|
|
48709
|
-
elements: array(
|
|
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)
|
|
48710
48833
|
}));
|
|
48711
48834
|
|
|
48712
48835
|
// Used to represent 2-D points and ranges
|
|
@@ -48764,7 +48887,7 @@ const parsePerseusGraphTypePolygon = object({
|
|
|
48764
48887
|
showAngles: optional(boolean),
|
|
48765
48888
|
showSides: optional(boolean),
|
|
48766
48889
|
snapTo: optional(enumeration("grid", "angles", "sides")),
|
|
48767
|
-
match: optional(enumeration("similar", "congruent", "approx")),
|
|
48890
|
+
match: optional(enumeration("similar", "congruent", "approx", "exact")),
|
|
48768
48891
|
startCoords: optional(array(pairOfNumbers)),
|
|
48769
48892
|
// TODO: remove coord? it's legacy.
|
|
48770
48893
|
coord: optional(pairOfNumbers)
|
|
@@ -48799,7 +48922,7 @@ const parsePerseusGraphTypeSinusoid = object({
|
|
|
48799
48922
|
// TODO: remove coord? it's legacy.
|
|
48800
48923
|
coord: optional(pairOfNumbers)
|
|
48801
48924
|
});
|
|
48802
|
-
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;
|
|
48803
48926
|
const parseLockedFigureColor = enumeration(...lockedFigureColorNames);
|
|
48804
48927
|
const parseLockedFigureFillType = enumeration("none", "white", "translucent", "solid");
|
|
48805
48928
|
const parseLockedLineStyle = enumeration("solid", "dashed");
|
|
@@ -48918,7 +49041,7 @@ const parseLabelImageWidget = parseWidget(constant("label-image"), object({
|
|
|
48918
49041
|
})),
|
|
48919
49042
|
hideChoicesFromInstructions: boolean,
|
|
48920
49043
|
multipleAnswers: boolean,
|
|
48921
|
-
static: boolean
|
|
49044
|
+
static: defaulted(boolean, () => false)
|
|
48922
49045
|
}));
|
|
48923
49046
|
|
|
48924
49047
|
const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
@@ -48929,10 +49052,12 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
|
48929
49052
|
padding: boolean
|
|
48930
49053
|
}));
|
|
48931
49054
|
|
|
49055
|
+
const numberOrString = union(number).or(string).parser;
|
|
49056
|
+
const numeric = pipeParsers(defaulted(numberOrString, () => NaN)).then(stringToNumber).parser;
|
|
48932
49057
|
const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
|
|
48933
49058
|
prefix: optional(string),
|
|
48934
49059
|
suffix: optional(string),
|
|
48935
|
-
answers: array(array(
|
|
49060
|
+
answers: array(array(numeric)),
|
|
48936
49061
|
cursorPosition: optional(array(number)),
|
|
48937
49062
|
matrixBoardSize: array(number),
|
|
48938
49063
|
static: optional(boolean)
|
|
@@ -48947,7 +49072,8 @@ const parseMeasurerWidget = parseWidget(constant("measurer"), object({
|
|
|
48947
49072
|
rulerPixels: number,
|
|
48948
49073
|
rulerLength: number,
|
|
48949
49074
|
box: pair(number, number),
|
|
48950
|
-
|
|
49075
|
+
// TODO(benchristel): static is not used. Remove it?
|
|
49076
|
+
static: defaulted(boolean, () => false)
|
|
48951
49077
|
}));
|
|
48952
49078
|
|
|
48953
49079
|
const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), object({
|
|
@@ -48956,9 +49082,10 @@ const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), o
|
|
|
48956
49082
|
smiles: optional(string)
|
|
48957
49083
|
}));
|
|
48958
49084
|
|
|
49085
|
+
const emptyStringToNull = pipeParsers(constant("")).then(convert(() => null)).parser;
|
|
48959
49086
|
const parseNumberLineWidget = parseWidget(constant("number-line"), object({
|
|
48960
49087
|
range: array(number),
|
|
48961
|
-
labelRange: array(nullable(number)),
|
|
49088
|
+
labelRange: array(nullable(union(number).or(emptyStringToNull).parser)),
|
|
48962
49089
|
labelStyle: string,
|
|
48963
49090
|
labelTicks: boolean,
|
|
48964
49091
|
isTickCtrl: optional(nullable(boolean)),
|
|
@@ -48970,26 +49097,32 @@ const parseNumberLineWidget = parseWidget(constant("number-line"), object({
|
|
|
48970
49097
|
snapDivisions: defaulted(number, () => 2),
|
|
48971
49098
|
tickStep: optional(nullable(number)),
|
|
48972
49099
|
correctRel: optional(nullable(string)),
|
|
48973
|
-
correctX: number,
|
|
49100
|
+
correctX: nullable(number),
|
|
48974
49101
|
initialX: optional(nullable(number)),
|
|
48975
49102
|
showTooltip: optional(boolean),
|
|
48976
|
-
static: boolean
|
|
49103
|
+
static: defaulted(boolean, () => false)
|
|
48977
49104
|
}));
|
|
48978
49105
|
|
|
48979
49106
|
const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "decimal", "percent", "pi");
|
|
48980
49107
|
const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
48981
49108
|
answers: array(object({
|
|
48982
49109
|
message: string,
|
|
48983
|
-
|
|
49110
|
+
// TODO(benchristel): value should never be null or undefined,
|
|
49111
|
+
// but we have some content where it is anyway. If we backfill
|
|
49112
|
+
// the data, simplify this.
|
|
49113
|
+
value: optional(nullable(number)),
|
|
48984
49114
|
status: string,
|
|
48985
49115
|
answerForms: optional(array(parseMathFormat)),
|
|
48986
49116
|
strict: boolean,
|
|
48987
49117
|
maxError: optional(nullable(number)),
|
|
48988
|
-
|
|
49118
|
+
// TODO(benchristel): simplify should never be a boolean, but we
|
|
49119
|
+
// have some content where it is anyway. If we ever backfill
|
|
49120
|
+
// the data, we should simplify `simplify`.
|
|
49121
|
+
simplify: optional(nullable(union(string).or(pipeParsers(boolean).then(convert(String)).parser).parser))
|
|
48989
49122
|
})),
|
|
48990
49123
|
labelText: optional(string),
|
|
48991
49124
|
size: string,
|
|
48992
|
-
coefficient: boolean,
|
|
49125
|
+
coefficient: defaulted(boolean, () => false),
|
|
48993
49126
|
rightAlign: optional(boolean),
|
|
48994
49127
|
static: defaulted(boolean, () => false),
|
|
48995
49128
|
answerForms: optional(array(object({
|
|
@@ -49021,13 +49154,13 @@ const parseOrdererWidget = parseWidget(constant("orderer"), object({
|
|
|
49021
49154
|
const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
49022
49155
|
passageNumber: number,
|
|
49023
49156
|
referenceNumber: number,
|
|
49024
|
-
summaryText: string
|
|
49157
|
+
summaryText: optional(string)
|
|
49025
49158
|
}));
|
|
49026
49159
|
|
|
49027
49160
|
const parsePassageWidget = parseWidget(constant("passage"), object({
|
|
49028
|
-
footnotes: string,
|
|
49161
|
+
footnotes: defaulted(string, () => ""),
|
|
49029
49162
|
passageText: string,
|
|
49030
|
-
passageTitle: string,
|
|
49163
|
+
passageTitle: defaulted(string, () => ""),
|
|
49031
49164
|
showLineNumbers: boolean,
|
|
49032
49165
|
static: defaulted(boolean, () => false)
|
|
49033
49166
|
}));
|
|
@@ -49210,6 +49343,12 @@ const parseWidgetsMapEntry = (_ref, widgetMap, ctx) => {
|
|
|
49210
49343
|
// sequence is a deprecated widget type, and the corresponding
|
|
49211
49344
|
// widget component no longer exists.
|
|
49212
49345
|
return parseAndAssign(`sequence ${id}`, parseDeprecatedWidget);
|
|
49346
|
+
case "lights-puzzle":
|
|
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);
|
|
49213
49352
|
default:
|
|
49214
49353
|
if (getWidget(type)) {
|
|
49215
49354
|
// @ts-expect-error - 'type' is not a valid widget type
|