@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/es/index.js
CHANGED
|
@@ -326,11 +326,11 @@ const mockStrings = {
|
|
|
326
326
|
angleMeasure,
|
|
327
327
|
vertexX,
|
|
328
328
|
vertexY,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}) => `The angle measure is ${angleMeasure} degrees with a vertex at ${vertexX} comma ${vertexY}, a point on the
|
|
329
|
+
startingSideX,
|
|
330
|
+
startingSideY,
|
|
331
|
+
endingSideX,
|
|
332
|
+
endingSideY
|
|
333
|
+
}) => `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}.`
|
|
334
334
|
// The above strings are used for interactive graph SR descriptions.
|
|
335
335
|
};
|
|
336
336
|
|
|
@@ -741,6 +741,9 @@ const mapButtonSets = buttonSets => {
|
|
|
741
741
|
case "trig":
|
|
742
742
|
keypadButtonSets.trigonometry = true;
|
|
743
743
|
break;
|
|
744
|
+
case "scientific":
|
|
745
|
+
keypadButtonSets.scientific = true;
|
|
746
|
+
break;
|
|
744
747
|
}
|
|
745
748
|
});
|
|
746
749
|
return keypadButtonSets;
|
|
@@ -3821,7 +3824,9 @@ ZoomServiceClass.prototype._scrollHandler = function (e) {
|
|
|
3821
3824
|
}
|
|
3822
3825
|
};
|
|
3823
3826
|
ZoomServiceClass.prototype._keyHandler = function (e) {
|
|
3824
|
-
|
|
3827
|
+
// 27: Esc, 13: Enter, 32: Space
|
|
3828
|
+
const keyCodes = [27, 13, 32];
|
|
3829
|
+
if (keyCodes.includes(e.keyCode)) {
|
|
3825
3830
|
this._activeZoomClose();
|
|
3826
3831
|
}
|
|
3827
3832
|
};
|
|
@@ -3910,6 +3915,7 @@ Zoom.prototype.zoomImage = function () {
|
|
|
3910
3915
|
}.bind(this);
|
|
3911
3916
|
img.src = this._targetImage.src;
|
|
3912
3917
|
img.alt = this._targetImage.alt;
|
|
3918
|
+
img.tabIndex = 0;
|
|
3913
3919
|
this.$zoomedImage = $zoomedImage;
|
|
3914
3920
|
};
|
|
3915
3921
|
Zoom.prototype._zoomOriginal = function () {
|
|
@@ -17020,6 +17026,7 @@ class ImageLoader extends React.Component {
|
|
|
17020
17026
|
}
|
|
17021
17027
|
const staticUrl = getDependencies().staticUrl;
|
|
17022
17028
|
return /*#__PURE__*/React.createElement("img", _extends({
|
|
17029
|
+
tabIndex: 0,
|
|
17023
17030
|
src: staticUrl(src),
|
|
17024
17031
|
onKeyUp: onKeyUp,
|
|
17025
17032
|
onKeyDown: onKeyDown
|
|
@@ -22219,7 +22226,7 @@ function scoreNumericInput(userInput, rubric, strings) {
|
|
|
22219
22226
|
// We may have received TeX; try to parse it before grading.
|
|
22220
22227
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
22221
22228
|
const currentValue = ParseTex(userInput.currentValue);
|
|
22222
|
-
const normalizedAnswerExpected = rubric.answers.filter(answer => answer.status === "correct").every(answer => answer.value
|
|
22229
|
+
const normalizedAnswerExpected = rubric.answers.filter(answer => answer.status === "correct").every(answer => answer.value != null && Math.abs(answer.value) <= 1);
|
|
22223
22230
|
|
|
22224
22231
|
// The coefficient is an attribute of the widget
|
|
22225
22232
|
let localValue = currentValue;
|
|
@@ -27455,6 +27462,7 @@ class Dropdown extends React.Component {
|
|
|
27455
27462
|
}, this.props.visibleLabel), /*#__PURE__*/React.createElement(SingleSelect, {
|
|
27456
27463
|
id: dropdownId,
|
|
27457
27464
|
placeholder: "",
|
|
27465
|
+
className: "perseus-dropdown",
|
|
27458
27466
|
onChange: value => this._handleChange(parseInt(value)),
|
|
27459
27467
|
selectedValue: String(this.props.selected),
|
|
27460
27468
|
disabled: this.props.apiOptions.readOnly,
|
|
@@ -32362,7 +32370,7 @@ const LegacyGrid = ({
|
|
|
32362
32370
|
* "new-awesomeness": MyAwesomeNewWidget;
|
|
32363
32371
|
*
|
|
32364
32372
|
* // A deprecated widget
|
|
32365
|
-
* "super-old-widget":
|
|
32373
|
+
* "super-old-widget": DeprecatedStandinWidget;
|
|
32366
32374
|
* }
|
|
32367
32375
|
* }
|
|
32368
32376
|
*
|
|
@@ -32556,46 +32564,28 @@ function LockedLabel(props) {
|
|
|
32556
32564
|
|
|
32557
32565
|
function GraphLockedLabelsLayer(props) {
|
|
32558
32566
|
const {
|
|
32559
|
-
flags,
|
|
32560
32567
|
lockedFigures
|
|
32561
32568
|
} = props;
|
|
32562
32569
|
return lockedFigures.map((figure, i) => {
|
|
32563
|
-
var
|
|
32570
|
+
var _figure$labels, _figure$points$0$labe, _figure$points$1$labe;
|
|
32564
32571
|
if (figure.type === "label") {
|
|
32565
32572
|
return /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
32566
32573
|
key: `label-${i}`
|
|
32567
32574
|
}, figure));
|
|
32568
32575
|
}
|
|
32569
|
-
|
|
32570
|
-
|
|
32571
|
-
|
|
32572
|
-
|
|
32573
|
-
|
|
32574
|
-
|
|
32575
|
-
|
|
32576
|
-
|
|
32577
|
-
|
|
32578
|
-
// Polygon flag + polygon type
|
|
32579
|
-
flags != null && (_flags$mafs5 = flags["mafs"]) != null && _flags$mafs5["locked-polygon-labels"] && figure.type === "polygon" ||
|
|
32580
|
-
// Function flag + function type
|
|
32581
|
-
flags != null && (_flags$mafs6 = flags["mafs"]) != null && _flags$mafs6["locked-function-labels"] && figure.type === "function") {
|
|
32582
|
-
var _figure$labels, _figure$points$0$labe, _figure$points$1$labe;
|
|
32583
|
-
return /*#__PURE__*/React.createElement(React.Fragment, {
|
|
32584
|
-
key: i
|
|
32585
|
-
}, (_figure$labels = figure.labels) == null ? void 0 : _figure$labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
32586
|
-
key: `${i}-label-${j}`
|
|
32587
|
-
}, 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({
|
|
32588
|
-
key: `locked-figure-${i}-point-1-label-${k}`
|
|
32589
|
-
}, label))), (_figure$points$1$labe = figure.points[1].labels) == null ? void 0 : _figure$points$1$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
32590
|
-
key: `locked-figure-${i}-point-2-label-${k}`
|
|
32591
|
-
}, label)))));
|
|
32592
|
-
}
|
|
32593
|
-
return null;
|
|
32576
|
+
return /*#__PURE__*/React.createElement(React.Fragment, {
|
|
32577
|
+
key: i
|
|
32578
|
+
}, (_figure$labels = figure.labels) == null ? void 0 : _figure$labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
32579
|
+
key: `${i}-label-${j}`
|
|
32580
|
+
}, 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({
|
|
32581
|
+
key: `locked-figure-${i}-point-1-label-${k}`
|
|
32582
|
+
}, label))), (_figure$points$1$labe = figure.points[1].labels) == null ? void 0 : _figure$points$1$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
32583
|
+
key: `locked-figure-${i}-point-2-label-${k}`
|
|
32584
|
+
}, label)))));
|
|
32594
32585
|
});
|
|
32595
32586
|
}
|
|
32596
32587
|
|
|
32597
32588
|
const LockedEllipse = props => {
|
|
32598
|
-
var _flags$mafs;
|
|
32599
32589
|
const {
|
|
32600
32590
|
center,
|
|
32601
32591
|
radius,
|
|
@@ -32603,10 +32593,9 @@ const LockedEllipse = props => {
|
|
|
32603
32593
|
color: color$1,
|
|
32604
32594
|
fillStyle,
|
|
32605
32595
|
strokeStyle,
|
|
32606
|
-
ariaLabel
|
|
32607
|
-
flags
|
|
32596
|
+
ariaLabel
|
|
32608
32597
|
} = props;
|
|
32609
|
-
const hasAria = ariaLabel
|
|
32598
|
+
const hasAria = !!ariaLabel;
|
|
32610
32599
|
return /*#__PURE__*/React.createElement("g", {
|
|
32611
32600
|
className: "locked-ellipse",
|
|
32612
32601
|
"aria-label": hasAria ? ariaLabel : undefined,
|
|
@@ -32631,7 +32620,6 @@ const LockedEllipse = props => {
|
|
|
32631
32620
|
};
|
|
32632
32621
|
|
|
32633
32622
|
const LockedFunction = props => {
|
|
32634
|
-
var _props$flags;
|
|
32635
32623
|
const [equation, setEquation] = useState();
|
|
32636
32624
|
const {
|
|
32637
32625
|
color,
|
|
@@ -32644,7 +32632,7 @@ const LockedFunction = props => {
|
|
|
32644
32632
|
style: strokeStyle,
|
|
32645
32633
|
domain
|
|
32646
32634
|
};
|
|
32647
|
-
const hasAria = props.ariaLabel
|
|
32635
|
+
const hasAria = !!props.ariaLabel;
|
|
32648
32636
|
useEffect(() => {
|
|
32649
32637
|
// Parsing the equation in a "useEffect" hook saves about 2ms each frame
|
|
32650
32638
|
// when the learner is interacting with the graph (i.e. moving points).
|
|
@@ -32760,7 +32748,6 @@ function getArrayWithoutDuplicates(array) {
|
|
|
32760
32748
|
}
|
|
32761
32749
|
|
|
32762
32750
|
const LockedLine = props => {
|
|
32763
|
-
var _flags$mafs;
|
|
32764
32751
|
const {
|
|
32765
32752
|
color: color$1,
|
|
32766
32753
|
lineStyle,
|
|
@@ -32769,11 +32756,10 @@ const LockedLine = props => {
|
|
|
32769
32756
|
showPoint1,
|
|
32770
32757
|
showPoint2,
|
|
32771
32758
|
ariaLabel,
|
|
32772
|
-
flags,
|
|
32773
32759
|
range
|
|
32774
32760
|
} = props;
|
|
32775
32761
|
const [point1, point2] = points;
|
|
32776
|
-
const hasAria = ariaLabel
|
|
32762
|
+
const hasAria = !!ariaLabel;
|
|
32777
32763
|
let line;
|
|
32778
32764
|
if (kind === "ray") {
|
|
32779
32765
|
// Rays extend to the end of the graph in one direction.
|
|
@@ -32840,16 +32826,14 @@ const LockedLine = props => {
|
|
|
32840
32826
|
};
|
|
32841
32827
|
|
|
32842
32828
|
const LockedPoint = props => {
|
|
32843
|
-
var _flags$mafs;
|
|
32844
32829
|
const {
|
|
32845
|
-
flags,
|
|
32846
32830
|
color: color$1,
|
|
32847
32831
|
coord,
|
|
32848
32832
|
filled,
|
|
32849
32833
|
ariaLabel
|
|
32850
32834
|
} = props;
|
|
32851
32835
|
const [x, y] = coord;
|
|
32852
|
-
const hasAria = ariaLabel
|
|
32836
|
+
const hasAria = !!ariaLabel;
|
|
32853
32837
|
return /*#__PURE__*/React.createElement("g", {
|
|
32854
32838
|
className: "locked-point",
|
|
32855
32839
|
"aria-label": hasAria ? ariaLabel : undefined,
|
|
@@ -32868,7 +32852,6 @@ const LockedPoint = props => {
|
|
|
32868
32852
|
};
|
|
32869
32853
|
|
|
32870
32854
|
const LockedPolygon = props => {
|
|
32871
|
-
var _props$flags;
|
|
32872
32855
|
const {
|
|
32873
32856
|
points,
|
|
32874
32857
|
color: color$1,
|
|
@@ -32876,7 +32859,7 @@ const LockedPolygon = props => {
|
|
|
32876
32859
|
fillStyle,
|
|
32877
32860
|
strokeStyle
|
|
32878
32861
|
} = props;
|
|
32879
|
-
const hasAria = props.ariaLabel
|
|
32862
|
+
const hasAria = !!props.ariaLabel;
|
|
32880
32863
|
return /*#__PURE__*/React.createElement("g", {
|
|
32881
32864
|
className: "locked-polygon",
|
|
32882
32865
|
"aria-label": hasAria ? props.ariaLabel : undefined,
|
|
@@ -32904,15 +32887,13 @@ const LockedPolygon = props => {
|
|
|
32904
32887
|
};
|
|
32905
32888
|
|
|
32906
32889
|
const LockedVector = props => {
|
|
32907
|
-
var _flags$mafs;
|
|
32908
32890
|
const {
|
|
32909
32891
|
color,
|
|
32910
32892
|
points,
|
|
32911
|
-
ariaLabel
|
|
32912
|
-
flags
|
|
32893
|
+
ariaLabel
|
|
32913
32894
|
} = props;
|
|
32914
32895
|
const [tail, tip] = points;
|
|
32915
|
-
const hasAria = ariaLabel
|
|
32896
|
+
const hasAria = !!ariaLabel;
|
|
32916
32897
|
return /*#__PURE__*/React.createElement("g", {
|
|
32917
32898
|
className: "locked-vector",
|
|
32918
32899
|
"aria-label": hasAria ? ariaLabel : undefined,
|
|
@@ -32926,7 +32907,6 @@ const LockedVector = props => {
|
|
|
32926
32907
|
|
|
32927
32908
|
const GraphLockedLayer = props => {
|
|
32928
32909
|
const {
|
|
32929
|
-
flags,
|
|
32930
32910
|
lockedFigures
|
|
32931
32911
|
} = props;
|
|
32932
32912
|
return /*#__PURE__*/React.createElement(React.Fragment, null, lockedFigures.map((figure, index) => {
|
|
@@ -32934,40 +32914,28 @@ const GraphLockedLayer = props => {
|
|
|
32934
32914
|
case "point":
|
|
32935
32915
|
return /*#__PURE__*/React.createElement(LockedPoint, _extends({
|
|
32936
32916
|
key: `point-${index}`
|
|
32937
|
-
}, figure
|
|
32938
|
-
flags: flags
|
|
32939
|
-
}));
|
|
32917
|
+
}, figure));
|
|
32940
32918
|
case "line":
|
|
32941
32919
|
return /*#__PURE__*/React.createElement(LockedLine, _extends({
|
|
32942
32920
|
key: `line-${index}`,
|
|
32943
32921
|
range: props.range
|
|
32944
|
-
}, figure
|
|
32945
|
-
flags: flags
|
|
32946
|
-
}));
|
|
32922
|
+
}, figure));
|
|
32947
32923
|
case "vector":
|
|
32948
32924
|
return /*#__PURE__*/React.createElement(LockedVector, _extends({
|
|
32949
32925
|
key: `vector-${index}`
|
|
32950
|
-
}, figure
|
|
32951
|
-
flags: flags
|
|
32952
|
-
}));
|
|
32926
|
+
}, figure));
|
|
32953
32927
|
case "ellipse":
|
|
32954
32928
|
return /*#__PURE__*/React.createElement(LockedEllipse, _extends({
|
|
32955
32929
|
key: `ellipse-${index}`
|
|
32956
|
-
}, figure
|
|
32957
|
-
flags: flags
|
|
32958
|
-
}));
|
|
32930
|
+
}, figure));
|
|
32959
32931
|
case "polygon":
|
|
32960
32932
|
return /*#__PURE__*/React.createElement(LockedPolygon, _extends({
|
|
32961
32933
|
key: `polygon-${index}`
|
|
32962
|
-
}, figure
|
|
32963
|
-
flags: flags
|
|
32964
|
-
}));
|
|
32934
|
+
}, figure));
|
|
32965
32935
|
case "function":
|
|
32966
32936
|
return /*#__PURE__*/React.createElement(LockedFunction, _extends({
|
|
32967
32937
|
key: `function-${index}`
|
|
32968
|
-
}, figure
|
|
32969
|
-
flags: flags
|
|
32970
|
-
}));
|
|
32938
|
+
}, figure));
|
|
32971
32939
|
case "label":
|
|
32972
32940
|
// This is rendered outside the SVG element, since
|
|
32973
32941
|
// TeX cannot be rendered inside an SVG.
|
|
@@ -34131,10 +34099,10 @@ function AngleGraph(props) {
|
|
|
34131
34099
|
angleMeasure,
|
|
34132
34100
|
vertexX: srFormatNumber(coords[1][X], locale),
|
|
34133
34101
|
vertexY: srFormatNumber(coords[1][Y], locale),
|
|
34134
|
-
|
|
34135
|
-
|
|
34136
|
-
|
|
34137
|
-
|
|
34102
|
+
startingSideX: srFormatNumber(coords[2][X], locale),
|
|
34103
|
+
startingSideY: srFormatNumber(coords[2][Y], locale),
|
|
34104
|
+
endingSideX: srFormatNumber(coords[0][X], locale),
|
|
34105
|
+
endingSideY: srFormatNumber(coords[0][Y], locale)
|
|
34138
34106
|
});
|
|
34139
34107
|
const formatCoordinates = (x, y) => ({
|
|
34140
34108
|
x: srFormatNumber(x, locale),
|
|
@@ -35438,7 +35406,6 @@ function useDraggablePx(args) {
|
|
|
35438
35406
|
* - Interactive Graph Elements
|
|
35439
35407
|
*/
|
|
35440
35408
|
const MafsGraph = props => {
|
|
35441
|
-
var _props$flags;
|
|
35442
35409
|
const {
|
|
35443
35410
|
state,
|
|
35444
35411
|
dispatch,
|
|
@@ -35575,11 +35542,9 @@ const MafsGraph = props => {
|
|
|
35575
35542
|
})),
|
|
35576
35543
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
35577
35544
|
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, {
|
|
35578
|
-
flags: props.flags,
|
|
35579
35545
|
lockedFigures: props.lockedFigures,
|
|
35580
35546
|
range: state.range
|
|
35581
|
-
}))),
|
|
35582
|
-
flags: props.flags,
|
|
35547
|
+
}))), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
|
|
35583
35548
|
lockedFigures: props.lockedFigures
|
|
35584
35549
|
}), /*#__PURE__*/React.createElement(View, {
|
|
35585
35550
|
style: {
|
|
@@ -45552,7 +45517,7 @@ const init = function init() {
|
|
|
45552
45517
|
|
|
45553
45518
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45554
45519
|
const libName = "@khanacademy/perseus";
|
|
45555
|
-
const libVersion = "49.1
|
|
45520
|
+
const libVersion = "49.2.1";
|
|
45556
45521
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45557
45522
|
|
|
45558
45523
|
const apiVersion = {
|
|
@@ -47893,9 +47858,9 @@ function parseWidgetWithVersion(parseVersion, parseType, parseOptions) {
|
|
|
47893
47858
|
const parseCategorizerWidget = parseWidget(constant("categorizer"), object({
|
|
47894
47859
|
items: array(string),
|
|
47895
47860
|
categories: array(string),
|
|
47896
|
-
randomizeItems: boolean,
|
|
47861
|
+
randomizeItems: defaulted(boolean, () => false),
|
|
47897
47862
|
static: defaulted(boolean, () => false),
|
|
47898
|
-
values: array(number),
|
|
47863
|
+
values: array(defaulted(number, () => 0)),
|
|
47899
47864
|
highlightLint: optional(boolean),
|
|
47900
47865
|
linterContext: optional(object({
|
|
47901
47866
|
contentType: string,
|
|
@@ -47915,7 +47880,7 @@ const parseCSProgramWidget = parseWidget(constant("cs-program"), object({
|
|
|
47915
47880
|
showButtons: boolean,
|
|
47916
47881
|
width: number,
|
|
47917
47882
|
height: number,
|
|
47918
|
-
static: boolean
|
|
47883
|
+
static: defaulted(boolean, () => false)
|
|
47919
47884
|
}));
|
|
47920
47885
|
|
|
47921
47886
|
const parseDefinitionWidget = parseWidget(constant("definition"), object({
|
|
@@ -47925,7 +47890,7 @@ const parseDefinitionWidget = parseWidget(constant("definition"), object({
|
|
|
47925
47890
|
}));
|
|
47926
47891
|
|
|
47927
47892
|
const parseDropdownWidget = parseWidget(constant("dropdown"), object({
|
|
47928
|
-
placeholder: string,
|
|
47893
|
+
placeholder: defaulted(string, () => ""),
|
|
47929
47894
|
ariaLabel: optional(string),
|
|
47930
47895
|
visibleLabel: optional(string),
|
|
47931
47896
|
static: defaulted(boolean, () => false),
|
|
@@ -47942,33 +47907,127 @@ const parseExplanationWidget = parseWidget(constant("explanation"), object({
|
|
|
47942
47907
|
// We wrap parseWidgetsMap in a function here to make sure it is not
|
|
47943
47908
|
// referenced before it is defined. There is an import cycle between
|
|
47944
47909
|
// this file and widgets-map.ts that could cause it to be undefined.
|
|
47945
|
-
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
47946
|
-
static: boolean
|
|
47910
|
+
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
|
|
47911
|
+
static: defaulted(boolean, () => false)
|
|
47947
47912
|
}));
|
|
47948
47913
|
|
|
47949
|
-
|
|
47950
|
-
|
|
47951
|
-
|
|
47952
|
-
|
|
47914
|
+
// Given a function, creates a PartialParser that converts one type to another
|
|
47915
|
+
// using that function. The returned parser never fails.
|
|
47916
|
+
function convert(f) {
|
|
47917
|
+
return (rawValue, ctx) => ctx.success(f(rawValue));
|
|
47918
|
+
}
|
|
47919
|
+
|
|
47920
|
+
/**
|
|
47921
|
+
* Creates a parser for a widget options type with multiple major versions. Old
|
|
47922
|
+
* versions are migrated to the latest version. The parse fails if the input
|
|
47923
|
+
* data does not match any of the versions.
|
|
47924
|
+
*
|
|
47925
|
+
* @example
|
|
47926
|
+
* const parseOptions = versionedWidgetOptions(3, parseOptionsV3)
|
|
47927
|
+
* .withMigrationFrom(2, parseOptionsV2, migrateV2ToV3)
|
|
47928
|
+
* .withMigrationFrom(1, parseOptionsV1, migrateV1ToV2)
|
|
47929
|
+
* .withMigrationFrom(0, parseOptionsV0, migrateV0ToV1)
|
|
47930
|
+
* .parser;
|
|
47931
|
+
*
|
|
47932
|
+
* @param latestMajorVersion the latest major version of the widget options.
|
|
47933
|
+
* @param parseLatest a {@link Parser} for the latest version of the widget
|
|
47934
|
+
* options.
|
|
47935
|
+
* @returns a builder object, to which migrations from earlier versions can be
|
|
47936
|
+
* added. Migrations must be added in "reverse chronological" order as in the
|
|
47937
|
+
* example above.
|
|
47938
|
+
*/
|
|
47939
|
+
function versionedWidgetOptions(latestMajorVersion, parseLatest) {
|
|
47940
|
+
return new VersionedWidgetOptionsParserBuilder(latestMajorVersion, parseLatest, latest => latest, (raw, ctx) => ctx.failure("widget options with a known version number", raw));
|
|
47941
|
+
}
|
|
47942
|
+
class VersionedWidgetOptionsParserBuilder {
|
|
47943
|
+
constructor(majorVersion, parseThisVersion, migrateToLatest, parseOtherVersions) {
|
|
47944
|
+
this.parser = void 0;
|
|
47945
|
+
this.migrateToLatest = migrateToLatest;
|
|
47946
|
+
this.parseOtherVersions = parseOtherVersions;
|
|
47947
|
+
const parseThisVersionAndMigrateToLatest = pipeParsers(parseThisVersion).then(convert(this.migrateToLatest)).parser;
|
|
47948
|
+
this.parser = (raw, ctx) => {
|
|
47949
|
+
if (!isObject(raw)) {
|
|
47950
|
+
return ctx.failure("object", raw);
|
|
47951
|
+
}
|
|
47952
|
+
const versionParseResult = parseVersionedObject(raw, ctx);
|
|
47953
|
+
if (isFailure(versionParseResult)) {
|
|
47954
|
+
return versionParseResult;
|
|
47955
|
+
}
|
|
47956
|
+
if (versionParseResult.value.version.major !== majorVersion) {
|
|
47957
|
+
return this.parseOtherVersions(raw, ctx);
|
|
47958
|
+
}
|
|
47959
|
+
return parseThisVersionAndMigrateToLatest(raw, ctx);
|
|
47960
|
+
};
|
|
47961
|
+
}
|
|
47962
|
+
|
|
47963
|
+
/**
|
|
47964
|
+
* Add a migration from an old version of the widget options.
|
|
47965
|
+
*/
|
|
47966
|
+
withMigrationFrom(majorVersion, parseOldVersion, migrateToNextVersion) {
|
|
47967
|
+
const parseOtherVersions = this.parser;
|
|
47968
|
+
const migrateToLatest = old => this.migrateToLatest(migrateToNextVersion(old));
|
|
47969
|
+
return new VersionedWidgetOptionsParserBuilder(majorVersion, parseOldVersion, migrateToLatest, parseOtherVersions);
|
|
47970
|
+
}
|
|
47971
|
+
}
|
|
47972
|
+
const parseVersionedObject = object({
|
|
47973
|
+
version: defaulted(object({
|
|
47974
|
+
major: number,
|
|
47975
|
+
minor: number
|
|
47976
|
+
}), () => ({
|
|
47977
|
+
major: 0,
|
|
47978
|
+
minor: 0
|
|
47979
|
+
}))
|
|
47980
|
+
});
|
|
47981
|
+
|
|
47982
|
+
const stringOrNumberOrNullOrUndefined = union(string).or(number).or(constant(null)).or(constant(undefined)).parser;
|
|
47983
|
+
const parsePossiblyInvalidAnswerForm = object({
|
|
47984
|
+
// `value` is the possibly invalid part of this. It should always be a
|
|
47985
|
+
// string, but some answer forms don't have it. The Expression widget
|
|
47986
|
+
// ignores invalid values, so we can safely filter them out during parsing.
|
|
47987
|
+
value: optional(string),
|
|
47988
|
+
form: defaulted(boolean, () => false),
|
|
47989
|
+
simplify: defaulted(boolean, () => false),
|
|
47953
47990
|
considered: enumeration("correct", "wrong", "ungraded"),
|
|
47954
|
-
key: pipeParsers(
|
|
47991
|
+
key: pipeParsers(stringOrNumberOrNullOrUndefined).then(convert(String)).parser
|
|
47955
47992
|
});
|
|
47956
|
-
|
|
47993
|
+
function removeInvalidAnswerForms(possiblyInvalid) {
|
|
47994
|
+
const valid = [];
|
|
47995
|
+
for (const answerForm of possiblyInvalid) {
|
|
47996
|
+
const {
|
|
47997
|
+
value
|
|
47998
|
+
} = answerForm;
|
|
47999
|
+
if (value != null) {
|
|
48000
|
+
// Copying the object seems to be needed to make TypeScript happy
|
|
48001
|
+
valid.push(_extends({}, answerForm, {
|
|
48002
|
+
value
|
|
48003
|
+
}));
|
|
48004
|
+
}
|
|
48005
|
+
}
|
|
48006
|
+
return valid;
|
|
48007
|
+
}
|
|
48008
|
+
|
|
48009
|
+
// NOTE(benchristel): I copied the default buttonSets from
|
|
48010
|
+
// expression.tsx. See the parse-perseus-json/README.md for
|
|
48011
|
+
// an explanation of why we want to duplicate the default here.
|
|
48012
|
+
const parseButtonSets = defaulted(array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")), () => ["basic", "trig", "prealgebra", "logarithms"]);
|
|
48013
|
+
const version1 = object({
|
|
47957
48014
|
major: constant(1),
|
|
47958
48015
|
minor: number
|
|
47959
|
-
})
|
|
47960
|
-
|
|
48016
|
+
});
|
|
48017
|
+
const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expression"), object({
|
|
48018
|
+
answerForms: pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,
|
|
47961
48019
|
functions: array(string),
|
|
47962
48020
|
times: boolean,
|
|
47963
48021
|
visibleLabel: optional(string),
|
|
47964
48022
|
ariaLabel: optional(string),
|
|
47965
|
-
buttonSets:
|
|
48023
|
+
buttonSets: parseButtonSets,
|
|
47966
48024
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
47967
48025
|
}));
|
|
47968
|
-
const
|
|
48026
|
+
const version0 = optional(object({
|
|
47969
48027
|
major: constant(0),
|
|
47970
48028
|
minor: number
|
|
47971
|
-
}))
|
|
48029
|
+
}));
|
|
48030
|
+
const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expression"), object({
|
|
47972
48031
|
functions: array(string),
|
|
47973
48032
|
times: boolean,
|
|
47974
48033
|
visibleLabel: optional(string),
|
|
@@ -47976,14 +48035,14 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(optional(object({
|
|
|
47976
48035
|
form: boolean,
|
|
47977
48036
|
simplify: boolean,
|
|
47978
48037
|
value: string,
|
|
47979
|
-
buttonSets:
|
|
48038
|
+
buttonSets: parseButtonSets,
|
|
47980
48039
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
47981
48040
|
}));
|
|
47982
|
-
function migrateV0ToV1(widget
|
|
48041
|
+
function migrateV0ToV1(widget) {
|
|
47983
48042
|
const {
|
|
47984
48043
|
options
|
|
47985
48044
|
} = widget;
|
|
47986
|
-
return
|
|
48045
|
+
return _extends({}, widget, {
|
|
47987
48046
|
version: {
|
|
47988
48047
|
major: 1,
|
|
47989
48048
|
minor: 0
|
|
@@ -48002,9 +48061,9 @@ function migrateV0ToV1(widget, ctx) {
|
|
|
48002
48061
|
value: options.value
|
|
48003
48062
|
}]
|
|
48004
48063
|
}
|
|
48005
|
-
})
|
|
48064
|
+
});
|
|
48006
48065
|
}
|
|
48007
|
-
const parseExpressionWidget =
|
|
48066
|
+
const parseExpressionWidget = versionedWidgetOptions(1, parseExpressionWidgetV1).withMigrationFrom(0, parseExpressionWidgetV0, migrateV0ToV1).parser;
|
|
48008
48067
|
|
|
48009
48068
|
const parsePerseusRenderer = defaulted(object({
|
|
48010
48069
|
// TODO(benchristel): content is also defaulted to empty string in
|
|
@@ -48025,13 +48084,14 @@ const parsePerseusRenderer = defaulted(object({
|
|
|
48025
48084
|
images: {}
|
|
48026
48085
|
}));
|
|
48027
48086
|
|
|
48087
|
+
const falseToNull = pipeParsers(constant(false)).then(convert(() => null)).parser;
|
|
48028
48088
|
const parseGradedGroupWidgetOptions = object({
|
|
48029
48089
|
title: defaulted(string, () => ""),
|
|
48030
48090
|
hasHint: optional(nullable(boolean)),
|
|
48031
48091
|
// This module has an import cycle with parsePerseusRenderer.
|
|
48032
48092
|
// The anonymous function below ensures that we don't try to access
|
|
48033
48093
|
// parsePerseusRenderer before it's defined.
|
|
48034
|
-
hint:
|
|
48094
|
+
hint: union(falseToNull).or(constant(null)).or(constant(undefined)).or((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx)).parser,
|
|
48035
48095
|
content: string,
|
|
48036
48096
|
// This module has an import cycle with parseWidgetsMap.
|
|
48037
48097
|
// The anonymous function below ensures that we don't try to access
|
|
@@ -48050,31 +48110,69 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
|
|
|
48050
48110
|
gradedGroups: array(parseGradedGroupWidgetOptions)
|
|
48051
48111
|
}));
|
|
48052
48112
|
|
|
48113
|
+
/**
|
|
48114
|
+
* discriminatedUnion() should be preferred over union() when parsing a
|
|
48115
|
+
* discriminated union type, because discriminatedUnion() produces more
|
|
48116
|
+
* understandable failure messages. It takes the discriminant as the source of
|
|
48117
|
+
* truth for which variant is to be parsed, and expects the other data to match
|
|
48118
|
+
* that variant.
|
|
48119
|
+
*/
|
|
48120
|
+
function discriminatedUnionOn(discriminantKey) {
|
|
48121
|
+
const noMoreBranches = (raw, ctx) => {
|
|
48122
|
+
if (!isObject(raw)) {
|
|
48123
|
+
return ctx.failure("object", raw);
|
|
48124
|
+
}
|
|
48125
|
+
return ctx.forSubtree(discriminantKey).failure("a valid value", raw[discriminantKey]);
|
|
48126
|
+
};
|
|
48127
|
+
return new DiscriminatedUnionBuilder(discriminantKey, noMoreBranches);
|
|
48128
|
+
}
|
|
48129
|
+
class DiscriminatedUnionBuilder {
|
|
48130
|
+
constructor(discriminantKey, parser) {
|
|
48131
|
+
this.discriminantKey = discriminantKey;
|
|
48132
|
+
this.parser = parser;
|
|
48133
|
+
}
|
|
48134
|
+
withBranch(discriminantValue, parseNewVariant) {
|
|
48135
|
+
const parseNewBranch = discriminatedUnionBranch(this.discriminantKey, discriminantValue, parseNewVariant, this.parser);
|
|
48136
|
+
return new DiscriminatedUnionBuilder(this.discriminantKey, parseNewBranch);
|
|
48137
|
+
}
|
|
48138
|
+
}
|
|
48139
|
+
function discriminatedUnionBranch(discriminantKey, discriminantValue, parseVariant, parseOtherBranches) {
|
|
48140
|
+
return (raw, ctx) => {
|
|
48141
|
+
if (!isObject(raw)) {
|
|
48142
|
+
return ctx.failure("object", raw);
|
|
48143
|
+
}
|
|
48144
|
+
if (raw[discriminantKey] === discriminantValue) {
|
|
48145
|
+
return parseVariant(raw, ctx);
|
|
48146
|
+
}
|
|
48147
|
+
return parseOtherBranches(raw, ctx);
|
|
48148
|
+
};
|
|
48149
|
+
}
|
|
48150
|
+
|
|
48053
48151
|
const pairOfNumbers$3 = pair(number, number);
|
|
48054
48152
|
const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
|
|
48055
48153
|
const parseGrapherWidget = parseWidget(constant("grapher"), object({
|
|
48056
48154
|
availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
|
|
48057
|
-
correct:
|
|
48155
|
+
correct: discriminatedUnionOn("type").withBranch("absolute_value", object({
|
|
48058
48156
|
type: constant("absolute_value"),
|
|
48059
48157
|
coords: pairOfPoints
|
|
48060
|
-
})).
|
|
48158
|
+
})).withBranch("exponential", object({
|
|
48061
48159
|
type: constant("exponential"),
|
|
48062
48160
|
asymptote: pairOfPoints,
|
|
48063
48161
|
coords: pairOfPoints
|
|
48064
|
-
})).
|
|
48162
|
+
})).withBranch("linear", object({
|
|
48065
48163
|
type: constant("linear"),
|
|
48066
|
-
coords: pairOfPoints
|
|
48067
|
-
})).
|
|
48164
|
+
coords: defaulted(pairOfPoints, () => [[-5, 5], [5, 5]])
|
|
48165
|
+
})).withBranch("logarithm", object({
|
|
48068
48166
|
type: constant("logarithm"),
|
|
48069
48167
|
asymptote: pairOfPoints,
|
|
48070
48168
|
coords: pairOfPoints
|
|
48071
|
-
})).
|
|
48169
|
+
})).withBranch("quadratic", object({
|
|
48072
48170
|
type: constant("quadratic"),
|
|
48073
48171
|
coords: pairOfPoints
|
|
48074
|
-
})).
|
|
48172
|
+
})).withBranch("sinusoid", object({
|
|
48075
48173
|
type: constant("sinusoid"),
|
|
48076
48174
|
coords: pairOfPoints
|
|
48077
|
-
})).
|
|
48175
|
+
})).withBranch("tangent", object({
|
|
48078
48176
|
type: constant("tangent"),
|
|
48079
48177
|
coords: pairOfPoints
|
|
48080
48178
|
})).parser,
|
|
@@ -48134,15 +48232,22 @@ const stringToNumber = (rawValue, ctx) => {
|
|
|
48134
48232
|
return ctx.success(parsedNumber);
|
|
48135
48233
|
};
|
|
48136
48234
|
|
|
48137
|
-
|
|
48235
|
+
function emptyToZero(x) {
|
|
48236
|
+
return x === "" ? 0 : x;
|
|
48237
|
+
}
|
|
48238
|
+
const imageDimensionToNumber = pipeParsers(union(number).or(string).parser)
|
|
48239
|
+
// In this specific case, empty string is equivalent to zero. An empty
|
|
48240
|
+
// string parses to either NaN (using parseInt) or 0 (using unary +) and
|
|
48241
|
+
// CSS will treat NaN as invalid and default to 0 instead.
|
|
48242
|
+
.then(convert(emptyToZero)).then(stringToNumber).parser;
|
|
48138
48243
|
const parsePerseusImageBackground = object({
|
|
48139
48244
|
url: optional(nullable(string)),
|
|
48140
|
-
width: optional(
|
|
48141
|
-
height: optional(
|
|
48142
|
-
top: optional(
|
|
48143
|
-
left: optional(
|
|
48144
|
-
bottom: optional(
|
|
48145
|
-
scale: optional(
|
|
48245
|
+
width: optional(imageDimensionToNumber),
|
|
48246
|
+
height: optional(imageDimensionToNumber),
|
|
48247
|
+
top: optional(imageDimensionToNumber),
|
|
48248
|
+
left: optional(imageDimensionToNumber),
|
|
48249
|
+
bottom: optional(imageDimensionToNumber),
|
|
48250
|
+
scale: optional(imageDimensionToNumber)
|
|
48146
48251
|
});
|
|
48147
48252
|
|
|
48148
48253
|
const pairOfNumbers$2 = pair(number, number);
|
|
@@ -48183,8 +48288,10 @@ const parseInputNumberWidget = parseWidget(constant("input-number"), object({
|
|
|
48183
48288
|
}));
|
|
48184
48289
|
|
|
48185
48290
|
const pairOfNumbers$1 = pair(number, number);
|
|
48291
|
+
const stringOrEmpty = defaulted(string, () => "");
|
|
48292
|
+
const parseFunctionType = constant("function");
|
|
48186
48293
|
const parseFunctionElement = object({
|
|
48187
|
-
type:
|
|
48294
|
+
type: parseFunctionType,
|
|
48188
48295
|
key: string,
|
|
48189
48296
|
options: object({
|
|
48190
48297
|
value: string,
|
|
@@ -48196,8 +48303,9 @@ const parseFunctionElement = object({
|
|
|
48196
48303
|
strokeWidth: number
|
|
48197
48304
|
})
|
|
48198
48305
|
});
|
|
48306
|
+
const parseLabelType = constant("label");
|
|
48199
48307
|
const parseLabelElement = object({
|
|
48200
|
-
type:
|
|
48308
|
+
type: parseLabelType,
|
|
48201
48309
|
key: string,
|
|
48202
48310
|
options: object({
|
|
48203
48311
|
label: string,
|
|
@@ -48206,8 +48314,9 @@ const parseLabelElement = object({
|
|
|
48206
48314
|
coordY: string
|
|
48207
48315
|
})
|
|
48208
48316
|
});
|
|
48317
|
+
const parseLineType = constant("line");
|
|
48209
48318
|
const parseLineElement = object({
|
|
48210
|
-
type:
|
|
48319
|
+
type: parseLineType,
|
|
48211
48320
|
key: string,
|
|
48212
48321
|
options: object({
|
|
48213
48322
|
color: string,
|
|
@@ -48220,8 +48329,9 @@ const parseLineElement = object({
|
|
|
48220
48329
|
arrows: string
|
|
48221
48330
|
})
|
|
48222
48331
|
});
|
|
48332
|
+
const parseMovableLineType = constant("movable-line");
|
|
48223
48333
|
const parseMovableLineElement = object({
|
|
48224
|
-
type:
|
|
48334
|
+
type: parseMovableLineType,
|
|
48225
48335
|
key: string,
|
|
48226
48336
|
options: object({
|
|
48227
48337
|
startX: string,
|
|
@@ -48239,8 +48349,9 @@ const parseMovableLineElement = object({
|
|
|
48239
48349
|
constraintYMax: string
|
|
48240
48350
|
})
|
|
48241
48351
|
});
|
|
48352
|
+
const parseMovablePointType = constant("movable-point");
|
|
48242
48353
|
const parseMovablePointElement = object({
|
|
48243
|
-
type:
|
|
48354
|
+
type: parseMovablePointType,
|
|
48244
48355
|
key: string,
|
|
48245
48356
|
options: object({
|
|
48246
48357
|
startX: string,
|
|
@@ -48249,14 +48360,15 @@ const parseMovablePointElement = object({
|
|
|
48249
48360
|
constraint: string,
|
|
48250
48361
|
snap: number,
|
|
48251
48362
|
constraintFn: string,
|
|
48252
|
-
constraintXMin:
|
|
48253
|
-
constraintXMax:
|
|
48254
|
-
constraintYMin:
|
|
48255
|
-
constraintYMax:
|
|
48363
|
+
constraintXMin: stringOrEmpty,
|
|
48364
|
+
constraintXMax: stringOrEmpty,
|
|
48365
|
+
constraintYMin: stringOrEmpty,
|
|
48366
|
+
constraintYMax: stringOrEmpty
|
|
48256
48367
|
})
|
|
48257
48368
|
});
|
|
48369
|
+
const parseParametricType = constant("parametric");
|
|
48258
48370
|
const parseParametricElement = object({
|
|
48259
|
-
type:
|
|
48371
|
+
type: parseParametricType,
|
|
48260
48372
|
key: string,
|
|
48261
48373
|
options: object({
|
|
48262
48374
|
x: string,
|
|
@@ -48268,8 +48380,9 @@ const parseParametricElement = object({
|
|
|
48268
48380
|
strokeWidth: number
|
|
48269
48381
|
})
|
|
48270
48382
|
});
|
|
48383
|
+
const parsePointType = constant("point");
|
|
48271
48384
|
const parsePointElement = object({
|
|
48272
|
-
type:
|
|
48385
|
+
type: parsePointType,
|
|
48273
48386
|
key: string,
|
|
48274
48387
|
options: object({
|
|
48275
48388
|
color: string,
|
|
@@ -48277,8 +48390,9 @@ const parsePointElement = object({
|
|
|
48277
48390
|
coordY: string
|
|
48278
48391
|
})
|
|
48279
48392
|
});
|
|
48393
|
+
const parseRectangleType = constant("rectangle");
|
|
48280
48394
|
const parseRectangleElement = object({
|
|
48281
|
-
type:
|
|
48395
|
+
type: parseRectangleType,
|
|
48282
48396
|
key: string,
|
|
48283
48397
|
options: object({
|
|
48284
48398
|
color: string,
|
|
@@ -48289,7 +48403,7 @@ const parseRectangleElement = object({
|
|
|
48289
48403
|
})
|
|
48290
48404
|
});
|
|
48291
48405
|
const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
48292
|
-
static: boolean,
|
|
48406
|
+
static: defaulted(boolean, () => false),
|
|
48293
48407
|
graph: object({
|
|
48294
48408
|
editableSettings: optional(array(enumeration("canvas", "graph"))),
|
|
48295
48409
|
box: pairOfNumbers$1,
|
|
@@ -48306,7 +48420,7 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
|
48306
48420
|
rulerTicks: optional(number),
|
|
48307
48421
|
tickStep: pairOfNumbers$1
|
|
48308
48422
|
}),
|
|
48309
|
-
elements: array(
|
|
48423
|
+
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)
|
|
48310
48424
|
}));
|
|
48311
48425
|
|
|
48312
48426
|
// Used to represent 2-D points and ranges
|
|
@@ -48364,7 +48478,7 @@ const parsePerseusGraphTypePolygon = object({
|
|
|
48364
48478
|
showAngles: optional(boolean),
|
|
48365
48479
|
showSides: optional(boolean),
|
|
48366
48480
|
snapTo: optional(enumeration("grid", "angles", "sides")),
|
|
48367
|
-
match: optional(enumeration("similar", "congruent", "approx")),
|
|
48481
|
+
match: optional(enumeration("similar", "congruent", "approx", "exact")),
|
|
48368
48482
|
startCoords: optional(array(pairOfNumbers)),
|
|
48369
48483
|
// TODO: remove coord? it's legacy.
|
|
48370
48484
|
coord: optional(pairOfNumbers)
|
|
@@ -48399,7 +48513,7 @@ const parsePerseusGraphTypeSinusoid = object({
|
|
|
48399
48513
|
// TODO: remove coord? it's legacy.
|
|
48400
48514
|
coord: optional(pairOfNumbers)
|
|
48401
48515
|
});
|
|
48402
|
-
const parsePerseusGraphType =
|
|
48516
|
+
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;
|
|
48403
48517
|
const parseLockedFigureColor = enumeration(...lockedFigureColorNames);
|
|
48404
48518
|
const parseLockedFigureFillType = enumeration("none", "white", "translucent", "solid");
|
|
48405
48519
|
const parseLockedLineStyle = enumeration("solid", "dashed");
|
|
@@ -48518,7 +48632,7 @@ const parseLabelImageWidget = parseWidget(constant("label-image"), object({
|
|
|
48518
48632
|
})),
|
|
48519
48633
|
hideChoicesFromInstructions: boolean,
|
|
48520
48634
|
multipleAnswers: boolean,
|
|
48521
|
-
static: boolean
|
|
48635
|
+
static: defaulted(boolean, () => false)
|
|
48522
48636
|
}));
|
|
48523
48637
|
|
|
48524
48638
|
const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
@@ -48529,10 +48643,12 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
|
48529
48643
|
padding: boolean
|
|
48530
48644
|
}));
|
|
48531
48645
|
|
|
48646
|
+
const numberOrString = union(number).or(string).parser;
|
|
48647
|
+
const numeric = pipeParsers(defaulted(numberOrString, () => NaN)).then(stringToNumber).parser;
|
|
48532
48648
|
const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
|
|
48533
48649
|
prefix: optional(string),
|
|
48534
48650
|
suffix: optional(string),
|
|
48535
|
-
answers: array(array(
|
|
48651
|
+
answers: array(array(numeric)),
|
|
48536
48652
|
cursorPosition: optional(array(number)),
|
|
48537
48653
|
matrixBoardSize: array(number),
|
|
48538
48654
|
static: optional(boolean)
|
|
@@ -48547,7 +48663,8 @@ const parseMeasurerWidget = parseWidget(constant("measurer"), object({
|
|
|
48547
48663
|
rulerPixels: number,
|
|
48548
48664
|
rulerLength: number,
|
|
48549
48665
|
box: pair(number, number),
|
|
48550
|
-
|
|
48666
|
+
// TODO(benchristel): static is not used. Remove it?
|
|
48667
|
+
static: defaulted(boolean, () => false)
|
|
48551
48668
|
}));
|
|
48552
48669
|
|
|
48553
48670
|
const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), object({
|
|
@@ -48556,9 +48673,10 @@ const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), o
|
|
|
48556
48673
|
smiles: optional(string)
|
|
48557
48674
|
}));
|
|
48558
48675
|
|
|
48676
|
+
const emptyStringToNull = pipeParsers(constant("")).then(convert(() => null)).parser;
|
|
48559
48677
|
const parseNumberLineWidget = parseWidget(constant("number-line"), object({
|
|
48560
48678
|
range: array(number),
|
|
48561
|
-
labelRange: array(nullable(number)),
|
|
48679
|
+
labelRange: array(nullable(union(number).or(emptyStringToNull).parser)),
|
|
48562
48680
|
labelStyle: string,
|
|
48563
48681
|
labelTicks: boolean,
|
|
48564
48682
|
isTickCtrl: optional(nullable(boolean)),
|
|
@@ -48570,26 +48688,32 @@ const parseNumberLineWidget = parseWidget(constant("number-line"), object({
|
|
|
48570
48688
|
snapDivisions: defaulted(number, () => 2),
|
|
48571
48689
|
tickStep: optional(nullable(number)),
|
|
48572
48690
|
correctRel: optional(nullable(string)),
|
|
48573
|
-
correctX: number,
|
|
48691
|
+
correctX: nullable(number),
|
|
48574
48692
|
initialX: optional(nullable(number)),
|
|
48575
48693
|
showTooltip: optional(boolean),
|
|
48576
|
-
static: boolean
|
|
48694
|
+
static: defaulted(boolean, () => false)
|
|
48577
48695
|
}));
|
|
48578
48696
|
|
|
48579
48697
|
const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "decimal", "percent", "pi");
|
|
48580
48698
|
const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
48581
48699
|
answers: array(object({
|
|
48582
48700
|
message: string,
|
|
48583
|
-
|
|
48701
|
+
// TODO(benchristel): value should never be null or undefined,
|
|
48702
|
+
// but we have some content where it is anyway. If we backfill
|
|
48703
|
+
// the data, simplify this.
|
|
48704
|
+
value: optional(nullable(number)),
|
|
48584
48705
|
status: string,
|
|
48585
48706
|
answerForms: optional(array(parseMathFormat)),
|
|
48586
48707
|
strict: boolean,
|
|
48587
48708
|
maxError: optional(nullable(number)),
|
|
48588
|
-
|
|
48709
|
+
// TODO(benchristel): simplify should never be a boolean, but we
|
|
48710
|
+
// have some content where it is anyway. If we ever backfill
|
|
48711
|
+
// the data, we should simplify `simplify`.
|
|
48712
|
+
simplify: optional(nullable(union(string).or(pipeParsers(boolean).then(convert(String)).parser).parser))
|
|
48589
48713
|
})),
|
|
48590
48714
|
labelText: optional(string),
|
|
48591
48715
|
size: string,
|
|
48592
|
-
coefficient: boolean,
|
|
48716
|
+
coefficient: defaulted(boolean, () => false),
|
|
48593
48717
|
rightAlign: optional(boolean),
|
|
48594
48718
|
static: defaulted(boolean, () => false),
|
|
48595
48719
|
answerForms: optional(array(object({
|
|
@@ -48621,13 +48745,13 @@ const parseOrdererWidget = parseWidget(constant("orderer"), object({
|
|
|
48621
48745
|
const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
48622
48746
|
passageNumber: number,
|
|
48623
48747
|
referenceNumber: number,
|
|
48624
|
-
summaryText: string
|
|
48748
|
+
summaryText: optional(string)
|
|
48625
48749
|
}));
|
|
48626
48750
|
|
|
48627
48751
|
const parsePassageWidget = parseWidget(constant("passage"), object({
|
|
48628
|
-
footnotes: string,
|
|
48752
|
+
footnotes: defaulted(string, () => ""),
|
|
48629
48753
|
passageText: string,
|
|
48630
|
-
passageTitle: string,
|
|
48754
|
+
passageTitle: defaulted(string, () => ""),
|
|
48631
48755
|
showLineNumbers: boolean,
|
|
48632
48756
|
static: defaulted(boolean, () => false)
|
|
48633
48757
|
}));
|
|
@@ -48809,6 +48933,12 @@ const parseWidgetsMapEntry = ([key, widget], widgetMap, ctx) => {
|
|
|
48809
48933
|
// sequence is a deprecated widget type, and the corresponding
|
|
48810
48934
|
// widget component no longer exists.
|
|
48811
48935
|
return parseAndAssign(`sequence ${id}`, parseDeprecatedWidget);
|
|
48936
|
+
case "lights-puzzle":
|
|
48937
|
+
return parseAndAssign(`lights-puzzle ${id}`, parseDeprecatedWidget);
|
|
48938
|
+
case "simulator":
|
|
48939
|
+
return parseAndAssign(`simulator ${id}`, parseDeprecatedWidget);
|
|
48940
|
+
case "transformer":
|
|
48941
|
+
return parseAndAssign(`transformer ${id}`, parseDeprecatedWidget);
|
|
48812
48942
|
default:
|
|
48813
48943
|
if (getWidget(type)) {
|
|
48814
48944
|
// @ts-expect-error - 'type' is not a valid widget type
|