@khanacademy/perseus 43.0.1 → 43.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/number-input.d.ts +5 -3
- package/dist/components/text-input.d.ts +4 -2
- package/dist/es/index.js +433 -238
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +424 -229
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +14 -7
- package/dist/util/parse-perseus-json/general-purpose-parsers/defaulted.d.ts +1 -1
- package/dist/util/parse-perseus-json/general-purpose-parsers/index.d.ts +2 -0
- package/dist/util/parse-perseus-json/general-purpose-parsers/pipe-parsers.d.ts +7 -0
- package/dist/util/parse-perseus-json/general-purpose-parsers/pipe-parsers.typetest.d.ts +1 -0
- package/dist/util/parse-perseus-json/general-purpose-parsers/unknown.d.ts +2 -0
- package/dist/util/parse-perseus-json/perseus-parsers/images-map.d.ts +5 -0
- package/dist/util/parse-perseus-json/perseus-parsers/widget.d.ts +4 -0
- package/dist/validation.types.d.ts +14 -6
- package/dist/widgets/categorizer/categorizer.d.ts +2 -2
- package/dist/widgets/categorizer/score-categorizer.d.ts +2 -2
- package/dist/widgets/categorizer/validate-categorizer.d.ts +15 -0
- package/dist/widgets/dropdown/validate-dropdown.d.ts +10 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-point.d.ts +11 -7
- package/dist/widgets/interactive-graphs/graphs/components/use-control-point.d.ts +7 -3
- package/dist/widgets/interactive-graphs/types.d.ts +1 -0
- package/dist/widgets/matrix/matrix.d.ts +12 -5
- package/dist/widgets/measurer/measurer.d.ts +1 -1
- package/dist/widgets/orderer/validate-orderer.d.ts +12 -0
- package/dist/widgets/plotter/plotter.d.ts +2 -2
- package/dist/widgets/plotter/score-plotter.d.ts +2 -2
- package/dist/widgets/plotter/validate-plotter.d.ts +12 -0
- package/dist/widgets/radio/radio.d.ts +1 -1
- package/dist/widgets/radio/validate-radio.d.ts +14 -0
- package/dist/widgets/sorter/validate-sorter.d.ts +13 -0
- package/package.json +4 -4
- package/dist/util/parse-perseus-json/general-purpose-parsers/compose-parsers.d.ts +0 -2
package/dist/index.js
CHANGED
|
@@ -2882,8 +2882,6 @@ async function getImageSizeModern(url) {
|
|
|
2882
2882
|
});
|
|
2883
2883
|
}
|
|
2884
2884
|
|
|
2885
|
-
/* eslint-disable @babel/no-invalid-this, getter-return, one-var */
|
|
2886
|
-
|
|
2887
2885
|
// TODO: dedupe this with Coord in interactive2/types.js
|
|
2888
2886
|
|
|
2889
2887
|
let supportsPassive = false;
|
|
@@ -2960,8 +2958,8 @@ function shuffle$3(array, randomSeed) {
|
|
|
2960
2958
|
do {
|
|
2961
2959
|
// Fischer-Yates shuffle
|
|
2962
2960
|
for (let top = shuffled.length; top > 0; top--) {
|
|
2963
|
-
const newEnd = Math.floor(random() * top)
|
|
2964
|
-
|
|
2961
|
+
const newEnd = Math.floor(random() * top);
|
|
2962
|
+
const temp = shuffled[newEnd];
|
|
2965
2963
|
|
|
2966
2964
|
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
2967
2965
|
shuffled[newEnd] = shuffled[top - 1];
|
|
@@ -3293,13 +3291,13 @@ function eq$4(x, y) {
|
|
|
3293
3291
|
* Deep approximate equality on primitives, numbers, arrays, and objects.
|
|
3294
3292
|
* Recursive.
|
|
3295
3293
|
*/
|
|
3296
|
-
function deepEq$
|
|
3294
|
+
function deepEq$7(x, y) {
|
|
3297
3295
|
if (Array.isArray(x) && Array.isArray(y)) {
|
|
3298
3296
|
if (x.length !== y.length) {
|
|
3299
3297
|
return false;
|
|
3300
3298
|
}
|
|
3301
3299
|
for (let i = 0; i < x.length; i++) {
|
|
3302
|
-
if (!deepEq$
|
|
3300
|
+
if (!deepEq$7(x[i], y[i])) {
|
|
3303
3301
|
return false;
|
|
3304
3302
|
}
|
|
3305
3303
|
}
|
|
@@ -3317,10 +3315,10 @@ function deepEq$6(x, y) {
|
|
|
3317
3315
|
if (typeof x === "object" && typeof y === "object" && !!x && !!y) {
|
|
3318
3316
|
return x === y || ___default["default"].all(x, function (v, k) {
|
|
3319
3317
|
// @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
|
|
3320
|
-
return deepEq$
|
|
3318
|
+
return deepEq$7(y[k], v);
|
|
3321
3319
|
}) && ___default["default"].all(y, function (v, k) {
|
|
3322
3320
|
// @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
|
|
3323
|
-
return deepEq$
|
|
3321
|
+
return deepEq$7(x[k], v);
|
|
3324
3322
|
});
|
|
3325
3323
|
}
|
|
3326
3324
|
if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
|
|
@@ -3599,7 +3597,7 @@ const Util = {
|
|
|
3599
3597
|
tickStepFromNumTicks,
|
|
3600
3598
|
constrainedTickStepsFromTickSteps,
|
|
3601
3599
|
eq: eq$4,
|
|
3602
|
-
deepEq: deepEq$
|
|
3600
|
+
deepEq: deepEq$7,
|
|
3603
3601
|
parseQueryString,
|
|
3604
3602
|
updateQueryString: updateQueryString$2,
|
|
3605
3603
|
strongEncodeURIComponent,
|
|
@@ -9002,7 +9000,6 @@ var Tex$1 = {
|
|
|
9002
9000
|
}
|
|
9003
9001
|
};
|
|
9004
9002
|
|
|
9005
|
-
/* eslint-disable @babel/no-invalid-this */
|
|
9006
9003
|
const {
|
|
9007
9004
|
processMath: processMath$1
|
|
9008
9005
|
} = Tex$1;
|
|
@@ -10632,7 +10629,9 @@ const getClipPoint = function (graph, coord, angle) {
|
|
|
10632
10629
|
return clipPoint;
|
|
10633
10630
|
};
|
|
10634
10631
|
|
|
10635
|
-
|
|
10632
|
+
/**
|
|
10633
|
+
* Default methods for a wrapped movable.
|
|
10634
|
+
*/
|
|
10636
10635
|
|
|
10637
10636
|
/*
|
|
10638
10637
|
* attr() and animate() simply pass the
|
|
@@ -10890,7 +10889,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
10890
10889
|
*/
|
|
10891
10890
|
const {
|
|
10892
10891
|
eq: eq$3,
|
|
10893
|
-
deepEq: deepEq$
|
|
10892
|
+
deepEq: deepEq$6
|
|
10894
10893
|
} = Util;
|
|
10895
10894
|
|
|
10896
10895
|
// This should really be a readonly tuple of [number, number]
|
|
@@ -11050,7 +11049,7 @@ function similar(coords1, coords2, tolerance) {
|
|
|
11050
11049
|
angles = rotate(angles, i);
|
|
11051
11050
|
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
11052
11051
|
sides = rotate(sides, i);
|
|
11053
|
-
if (deepEq$
|
|
11052
|
+
if (deepEq$6(angles1, angles)) {
|
|
11054
11053
|
const sidePairs = ___default["default"].zip(sides1, sides);
|
|
11055
11054
|
const factors = ___default["default"].map(sidePairs, function (pair) {
|
|
11056
11055
|
return pair[0] / pair[1];
|
|
@@ -13989,7 +13988,7 @@ ___default["default"].extend(MovableAngle.prototype, {
|
|
|
13989
13988
|
|
|
13990
13989
|
/* eslint-disable @babel/no-invalid-this */
|
|
13991
13990
|
const nestedMap$1 = Util.nestedMap;
|
|
13992
|
-
const deepEq$
|
|
13991
|
+
const deepEq$5 = Util.deepEq;
|
|
13993
13992
|
|
|
13994
13993
|
/**
|
|
13995
13994
|
* A base class for all Graphie Movables
|
|
@@ -14086,7 +14085,7 @@ const createSimpleClass = function (addFunction) {
|
|
|
14086
14085
|
this._prevProps = this.props;
|
|
14087
14086
|
},
|
|
14088
14087
|
modify: function (graphie) {
|
|
14089
|
-
if (!deepEq$
|
|
14088
|
+
if (!deepEq$5(this.props, this._prevProps)) {
|
|
14090
14089
|
this.remove();
|
|
14091
14090
|
this.add(graphie);
|
|
14092
14091
|
this._prevProps = this.props;
|
|
@@ -16744,7 +16743,7 @@ var Movables = {
|
|
|
16744
16743
|
const GraphieMovable = GraphieClasses.GraphieMovable;
|
|
16745
16744
|
const createGraphie = GraphUtils.createGraphie;
|
|
16746
16745
|
const {
|
|
16747
|
-
deepEq: deepEq$
|
|
16746
|
+
deepEq: deepEq$4,
|
|
16748
16747
|
nestedMap
|
|
16749
16748
|
} = Util;
|
|
16750
16749
|
const {
|
|
@@ -16782,7 +16781,7 @@ class Graphie extends React__namespace.Component {
|
|
|
16782
16781
|
if (this.props.setup !== prevProps.setup) {
|
|
16783
16782
|
Log.error("<Graphie> was given a new setup function. " + "This is a bad idea; please refactor your code to give " + "the same setup function reference to <Graphie> on " + "every render.", perseusCore.Errors.Internal);
|
|
16784
16783
|
}
|
|
16785
|
-
if (!deepEq$
|
|
16784
|
+
if (!deepEq$4(this.props.options, prevProps.options) || !deepEq$4(this.props.box, prevProps.box) || !deepEq$4(this.props.range, prevProps.range)) {
|
|
16786
16785
|
this._setupGraphie();
|
|
16787
16786
|
}
|
|
16788
16787
|
this._updateMovables();
|
|
@@ -18557,7 +18556,6 @@ const styles$C = aphrodite.StyleSheet.create({
|
|
|
18557
18556
|
}
|
|
18558
18557
|
});
|
|
18559
18558
|
|
|
18560
|
-
/* eslint-disable no-useless-escape, no-prototype-builtins */
|
|
18561
18559
|
const rules$1 = {
|
|
18562
18560
|
...pureMarkdown.pureMarkdownRules,
|
|
18563
18561
|
// NOTE: basically ignored by JIPT. wraps everything at the outer layer
|
|
@@ -18734,7 +18732,7 @@ const rules$1 = {
|
|
|
18734
18732
|
// The href prop should always be here, but just in case somehow the article's
|
|
18735
18733
|
// link was not put together properly, let's make sure it's there so we
|
|
18736
18734
|
// don't break the entire page.
|
|
18737
|
-
const isKAUrl = href ? href.match(/https?:\/\/[
|
|
18735
|
+
const isKAUrl = href ? href.match(/https?:\/\/[^/]*khanacademy.org|^\//) : false;
|
|
18738
18736
|
if (!isKAUrl) {
|
|
18739
18737
|
// Prevents "reverse tabnabbing" phishing attacks
|
|
18740
18738
|
// @ts-expect-error - TS2322 - Type '"noopener noreferrer"' is not assignable to type 'null'.
|
|
@@ -21144,6 +21142,7 @@ function uniqueIdForInput() {
|
|
|
21144
21142
|
return `${prefix}${lastId}`;
|
|
21145
21143
|
}
|
|
21146
21144
|
class TextInput extends React__namespace.Component {
|
|
21145
|
+
inputRef = /*#__PURE__*/React__namespace.createRef();
|
|
21147
21146
|
static defaultProps = {
|
|
21148
21147
|
value: "",
|
|
21149
21148
|
disabled: false
|
|
@@ -21157,33 +21156,32 @@ class TextInput extends React__namespace.Component {
|
|
|
21157
21156
|
this.id = uniqueIdForInput();
|
|
21158
21157
|
}
|
|
21159
21158
|
}
|
|
21159
|
+
_getInput = () => {
|
|
21160
|
+
if (!this.inputRef.current) {
|
|
21161
|
+
throw new perseusCore.PerseusError("Input ref accessed before set", perseusCore.Errors.Internal);
|
|
21162
|
+
}
|
|
21163
|
+
return this.inputRef.current;
|
|
21164
|
+
};
|
|
21160
21165
|
focus = () => {
|
|
21161
|
-
|
|
21162
|
-
ReactDOM__default["default"].findDOMNode(this).focus();
|
|
21166
|
+
this._getInput().focus();
|
|
21163
21167
|
};
|
|
21164
21168
|
blur = () => {
|
|
21165
|
-
|
|
21166
|
-
ReactDOM__default["default"].findDOMNode(this).blur();
|
|
21169
|
+
this._getInput().blur();
|
|
21167
21170
|
};
|
|
21168
21171
|
getValue = () => {
|
|
21169
|
-
|
|
21170
|
-
return ReactDOM__default["default"].findDOMNode(this)?.value;
|
|
21172
|
+
return this.inputRef.current?.value;
|
|
21171
21173
|
};
|
|
21172
21174
|
getStringValue = () => {
|
|
21173
|
-
|
|
21174
|
-
return ReactDOM__default["default"].findDOMNode(this)?.value.toString();
|
|
21175
|
+
return this.inputRef.current?.value.toString();
|
|
21175
21176
|
};
|
|
21176
21177
|
setSelectionRange = (selectionStart, selectionEnd) => {
|
|
21177
|
-
|
|
21178
|
-
ReactDOM__default["default"].findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
|
|
21178
|
+
this._getInput().setSelectionRange(selectionStart, selectionEnd);
|
|
21179
21179
|
};
|
|
21180
21180
|
getSelectionStart = () => {
|
|
21181
|
-
|
|
21182
|
-
return ReactDOM__default["default"].findDOMNode(this).selectionStart;
|
|
21181
|
+
return this._getInput().selectionStart;
|
|
21183
21182
|
};
|
|
21184
21183
|
getSelectionEnd = () => {
|
|
21185
|
-
|
|
21186
|
-
return ReactDOM__default["default"].findDOMNode(this).selectionEnd;
|
|
21184
|
+
return this._getInput().selectionEnd;
|
|
21187
21185
|
};
|
|
21188
21186
|
render() {
|
|
21189
21187
|
const {
|
|
@@ -21198,6 +21196,7 @@ class TextInput extends React__namespace.Component {
|
|
|
21198
21196
|
} = this.props;
|
|
21199
21197
|
const formattedValue = value === null ? "" : value.toString();
|
|
21200
21198
|
return /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
|
|
21199
|
+
ref: this.inputRef,
|
|
21201
21200
|
style: style,
|
|
21202
21201
|
disabled: disabled,
|
|
21203
21202
|
id: this.id,
|
|
@@ -22219,7 +22218,7 @@ function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
|
|
|
22219
22218
|
return value / 100;
|
|
22220
22219
|
}
|
|
22221
22220
|
|
|
22222
|
-
// Otherwise, we return input
|
|
22221
|
+
// Otherwise, we return input value (number) stripped of the "%".
|
|
22223
22222
|
return value;
|
|
22224
22223
|
}
|
|
22225
22224
|
function scoreNumericInput(userInput, rubric, strings) {
|
|
@@ -26277,7 +26276,15 @@ const styles$p = aphrodite.StyleSheet.create({
|
|
|
26277
26276
|
}
|
|
26278
26277
|
});
|
|
26279
26278
|
|
|
26280
|
-
|
|
26279
|
+
/**
|
|
26280
|
+
* Checks if the user has selected at least one option. Additional validation
|
|
26281
|
+
* is done in scoreRadio to check if the number of selected options is correct
|
|
26282
|
+
* and if the user has selected both a correct option and the "none of the above"
|
|
26283
|
+
* option.
|
|
26284
|
+
* @param userInput
|
|
26285
|
+
* @see `scoreRadio` for the additional validation logic and the scoring logic.
|
|
26286
|
+
*/
|
|
26287
|
+
function validateRadio(userInput) {
|
|
26281
26288
|
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
26282
26289
|
return sum + (selected ? 1 : 0);
|
|
26283
26290
|
}, 0);
|
|
@@ -26287,6 +26294,17 @@ function scoreRadio(userInput, rubric, strings) {
|
|
|
26287
26294
|
message: null
|
|
26288
26295
|
};
|
|
26289
26296
|
}
|
|
26297
|
+
return null;
|
|
26298
|
+
}
|
|
26299
|
+
|
|
26300
|
+
function scoreRadio(userInput, rubric, strings) {
|
|
26301
|
+
const validationError = validateRadio(userInput);
|
|
26302
|
+
if (validationError) {
|
|
26303
|
+
return validationError;
|
|
26304
|
+
}
|
|
26305
|
+
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
26306
|
+
return sum + (selected ? 1 : 0);
|
|
26307
|
+
}, 0);
|
|
26290
26308
|
const numCorrect = rubric.choices.reduce((sum, currentChoice) => {
|
|
26291
26309
|
return currentChoice.correct ? sum + 1 : sum;
|
|
26292
26310
|
}, 0);
|
|
@@ -26766,18 +26784,17 @@ const transform = (widgetOptions, strings, problemNum) => {
|
|
|
26766
26784
|
};
|
|
26767
26785
|
const propUpgrades$1 = {
|
|
26768
26786
|
"1": v0props => {
|
|
26769
|
-
|
|
26770
|
-
|
|
26771
|
-
|
|
26772
|
-
|
|
26773
|
-
|
|
26774
|
-
} else {
|
|
26787
|
+
const {
|
|
26788
|
+
noneOfTheAbove,
|
|
26789
|
+
...rest
|
|
26790
|
+
} = v0props;
|
|
26791
|
+
if (noneOfTheAbove) {
|
|
26775
26792
|
throw new Error("radio widget v0 no longer supports auto noneOfTheAbove");
|
|
26776
26793
|
}
|
|
26777
|
-
return
|
|
26778
|
-
|
|
26779
|
-
hasNoneOfTheAbove:
|
|
26780
|
-
}
|
|
26794
|
+
return {
|
|
26795
|
+
...rest,
|
|
26796
|
+
hasNoneOfTheAbove: false
|
|
26797
|
+
};
|
|
26781
26798
|
}
|
|
26782
26799
|
};
|
|
26783
26800
|
var Radio = {
|
|
@@ -26814,23 +26831,36 @@ const getPromptJSON$n = (renderProps, userInput) => {
|
|
|
26814
26831
|
};
|
|
26815
26832
|
};
|
|
26816
26833
|
|
|
26817
|
-
|
|
26818
|
-
|
|
26819
|
-
|
|
26820
|
-
|
|
26821
|
-
|
|
26822
|
-
|
|
26823
|
-
|
|
26824
|
-
|
|
26825
|
-
|
|
26826
|
-
|
|
26827
|
-
|
|
26828
|
-
if (!completed) {
|
|
26834
|
+
/**
|
|
26835
|
+
* Checks userInput from the categorizer widget to see if the user has selected
|
|
26836
|
+
* a category for each item.
|
|
26837
|
+
* @param userInput - The user's input corresponding to an array of indices that
|
|
26838
|
+
* represent the selected category for each row/item.
|
|
26839
|
+
* @param validationData - An array of strings corresponding to each row/item
|
|
26840
|
+
* @param strings - Used to provide a validation message
|
|
26841
|
+
*/
|
|
26842
|
+
function validateCategorizer(userInput, validationData, strings) {
|
|
26843
|
+
const incomplete = validationData.items.some((_, i) => userInput.values[i] == null);
|
|
26844
|
+
if (incomplete) {
|
|
26829
26845
|
return {
|
|
26830
26846
|
type: "invalid",
|
|
26831
26847
|
message: strings.invalidSelection
|
|
26832
26848
|
};
|
|
26833
26849
|
}
|
|
26850
|
+
return null;
|
|
26851
|
+
}
|
|
26852
|
+
|
|
26853
|
+
function scoreCategorizer(userInput, scoringData, strings) {
|
|
26854
|
+
const validationError = validateCategorizer(userInput, scoringData, strings);
|
|
26855
|
+
if (validationError) {
|
|
26856
|
+
return validationError;
|
|
26857
|
+
}
|
|
26858
|
+
let allCorrect = true;
|
|
26859
|
+
scoringData.values.forEach((value, i) => {
|
|
26860
|
+
if (userInput.values[i] !== value) {
|
|
26861
|
+
allCorrect = false;
|
|
26862
|
+
}
|
|
26863
|
+
});
|
|
26834
26864
|
return {
|
|
26835
26865
|
type: "points",
|
|
26836
26866
|
earned: allCorrect ? 1 : 0,
|
|
@@ -27384,15 +27414,26 @@ const getPromptJSON$k = (renderProps, userInput) => {
|
|
|
27384
27414
|
};
|
|
27385
27415
|
};
|
|
27386
27416
|
|
|
27387
|
-
|
|
27388
|
-
|
|
27389
|
-
|
|
27417
|
+
/**
|
|
27418
|
+
* Checks if the user has selected an item from the dropdown before scoring.
|
|
27419
|
+
* This is shown with a userInput value / index other than 0.
|
|
27420
|
+
*/
|
|
27421
|
+
function validateDropdown(userInput) {
|
|
27422
|
+
if (userInput.value === 0) {
|
|
27390
27423
|
return {
|
|
27391
27424
|
type: "invalid",
|
|
27392
27425
|
message: null
|
|
27393
27426
|
};
|
|
27394
27427
|
}
|
|
27395
|
-
|
|
27428
|
+
return null;
|
|
27429
|
+
}
|
|
27430
|
+
|
|
27431
|
+
function scoreDropdown(userInput, rubric) {
|
|
27432
|
+
const validationError = validateDropdown(userInput);
|
|
27433
|
+
if (validationError) {
|
|
27434
|
+
return validationError;
|
|
27435
|
+
}
|
|
27436
|
+
const correct = rubric.choices[userInput.value - 1].correct;
|
|
27396
27437
|
return {
|
|
27397
27438
|
type: "points",
|
|
27398
27439
|
earned: correct ? 1 : 0,
|
|
@@ -27782,8 +27823,6 @@ const getNextState = (currentState, answerable) => {
|
|
|
27782
27823
|
}
|
|
27783
27824
|
};
|
|
27784
27825
|
|
|
27785
|
-
// exports has no 'transform'
|
|
27786
|
-
|
|
27787
27826
|
// A Graded Group is more or less a Group widget that displays a check
|
|
27788
27827
|
// answer button below the rendered content. When clicked, the widget grades
|
|
27789
27828
|
// the stuff inside and displays feedback about whether the inputted answer was
|
|
@@ -28453,7 +28492,6 @@ const getPromptJSON$g = (renderProps, userInput) => {
|
|
|
28453
28492
|
};
|
|
28454
28493
|
};
|
|
28455
28494
|
|
|
28456
|
-
/* eslint-disable one-var */
|
|
28457
28495
|
// @ts-expect-error - TS2339 - Property 'Plot' does not exist on type 'typeof Graphie'.
|
|
28458
28496
|
const Plot$1 = Graphie.Plot;
|
|
28459
28497
|
const DEFAULT_BACKGROUND_IMAGE = {
|
|
@@ -28554,14 +28592,14 @@ const Linear = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28554
28592
|
return [m, b];
|
|
28555
28593
|
},
|
|
28556
28594
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28557
|
-
const m = coeffs[0]
|
|
28558
|
-
|
|
28595
|
+
const m = coeffs[0];
|
|
28596
|
+
const b = coeffs[1];
|
|
28559
28597
|
return m * x + b;
|
|
28560
28598
|
},
|
|
28561
28599
|
getEquationString: function (coords) {
|
|
28562
28600
|
const coeffs = this.getCoefficients(coords);
|
|
28563
|
-
const m = coeffs[0]
|
|
28564
|
-
|
|
28601
|
+
const m = coeffs[0];
|
|
28602
|
+
const b = coeffs[1];
|
|
28565
28603
|
return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
|
|
28566
28604
|
}
|
|
28567
28605
|
});
|
|
@@ -28585,9 +28623,9 @@ const Quadratic = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28585
28623
|
return [a, b, c];
|
|
28586
28624
|
},
|
|
28587
28625
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28588
|
-
const a = coeffs[0]
|
|
28589
|
-
|
|
28590
|
-
|
|
28626
|
+
const a = coeffs[0];
|
|
28627
|
+
const b = coeffs[1];
|
|
28628
|
+
const c = coeffs[2];
|
|
28591
28629
|
return (a * x + b) * x + c;
|
|
28592
28630
|
},
|
|
28593
28631
|
getPropsForCoeffs: function (coeffs) {
|
|
@@ -28599,9 +28637,9 @@ const Quadratic = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28599
28637
|
},
|
|
28600
28638
|
getEquationString: function (coords) {
|
|
28601
28639
|
const coeffs = this.getCoefficients(coords);
|
|
28602
|
-
const a = coeffs[0]
|
|
28603
|
-
|
|
28604
|
-
|
|
28640
|
+
const a = coeffs[0];
|
|
28641
|
+
const b = coeffs[1];
|
|
28642
|
+
const c = coeffs[2];
|
|
28605
28643
|
return "y = " + a.toFixed(3) + "x^2 + " + b.toFixed(3) + "x + " + c.toFixed(3);
|
|
28606
28644
|
}
|
|
28607
28645
|
});
|
|
@@ -28620,10 +28658,10 @@ const Sinusoid = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28620
28658
|
return [a, b, c, d];
|
|
28621
28659
|
},
|
|
28622
28660
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28623
|
-
const a = coeffs[0]
|
|
28624
|
-
|
|
28625
|
-
|
|
28626
|
-
|
|
28661
|
+
const a = coeffs[0];
|
|
28662
|
+
const b = coeffs[1];
|
|
28663
|
+
const c = coeffs[2];
|
|
28664
|
+
const d = coeffs[3];
|
|
28627
28665
|
return a * Math.sin(b * x - c) + d;
|
|
28628
28666
|
},
|
|
28629
28667
|
getPropsForCoeffs: function (coeffs) {
|
|
@@ -28636,10 +28674,10 @@ const Sinusoid = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28636
28674
|
},
|
|
28637
28675
|
getEquationString: function (coords) {
|
|
28638
28676
|
const coeffs = this.getCoefficients(coords);
|
|
28639
|
-
const a = coeffs[0]
|
|
28640
|
-
|
|
28641
|
-
|
|
28642
|
-
|
|
28677
|
+
const a = coeffs[0];
|
|
28678
|
+
const b = coeffs[1];
|
|
28679
|
+
const c = coeffs[2];
|
|
28680
|
+
const d = coeffs[3];
|
|
28643
28681
|
return "y = " + a.toFixed(3) + " sin(" + b.toFixed(3) + "x - " + c.toFixed(3) + ") + " + d.toFixed(3);
|
|
28644
28682
|
},
|
|
28645
28683
|
areEqual: function (coeffs1, coeffs2) {
|
|
@@ -28659,18 +28697,18 @@ const Tangent = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28659
28697
|
return [a, b, c, d];
|
|
28660
28698
|
},
|
|
28661
28699
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28662
|
-
const a = coeffs[0]
|
|
28663
|
-
|
|
28664
|
-
|
|
28665
|
-
|
|
28700
|
+
const a = coeffs[0];
|
|
28701
|
+
const b = coeffs[1];
|
|
28702
|
+
const c = coeffs[2];
|
|
28703
|
+
const d = coeffs[3];
|
|
28666
28704
|
return a * Math.tan(b * x - c) + d;
|
|
28667
28705
|
},
|
|
28668
28706
|
getEquationString: function (coords) {
|
|
28669
28707
|
const coeffs = this.getCoefficients(coords);
|
|
28670
|
-
const a = coeffs[0]
|
|
28671
|
-
|
|
28672
|
-
|
|
28673
|
-
|
|
28708
|
+
const a = coeffs[0];
|
|
28709
|
+
const b = coeffs[1];
|
|
28710
|
+
const c = coeffs[2];
|
|
28711
|
+
const d = coeffs[3];
|
|
28674
28712
|
return "y = " + a.toFixed(3) + " sin(" + b.toFixed(3) + "x - " + c.toFixed(3) + ") + " + d.toFixed(3);
|
|
28675
28713
|
},
|
|
28676
28714
|
areEqual: function (coeffs1, coeffs2) {
|
|
@@ -28729,9 +28767,9 @@ const Exponential = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28729
28767
|
return [a, b, c];
|
|
28730
28768
|
},
|
|
28731
28769
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28732
|
-
const a = coeffs[0]
|
|
28733
|
-
|
|
28734
|
-
|
|
28770
|
+
const a = coeffs[0];
|
|
28771
|
+
const b = coeffs[1];
|
|
28772
|
+
const c = coeffs[2];
|
|
28735
28773
|
return a * Math.exp(b * x) + c;
|
|
28736
28774
|
},
|
|
28737
28775
|
getEquationString: function (coords, asymptote) {
|
|
@@ -28739,9 +28777,9 @@ const Exponential = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28739
28777
|
return null;
|
|
28740
28778
|
}
|
|
28741
28779
|
const coeffs = this.getCoefficients(coords, asymptote);
|
|
28742
|
-
const a = coeffs[0]
|
|
28743
|
-
|
|
28744
|
-
|
|
28780
|
+
const a = coeffs[0];
|
|
28781
|
+
const b = coeffs[1];
|
|
28782
|
+
const c = coeffs[2];
|
|
28745
28783
|
return "y = " + a.toFixed(3) + "e^(" + b.toFixed(3) + "x) + " + c.toFixed(3);
|
|
28746
28784
|
}
|
|
28747
28785
|
});
|
|
@@ -28786,9 +28824,9 @@ const Logarithm = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28786
28824
|
}
|
|
28787
28825
|
},
|
|
28788
28826
|
getFunctionForCoeffs: function (coeffs, x, asymptote) {
|
|
28789
|
-
const a = coeffs[0]
|
|
28790
|
-
|
|
28791
|
-
|
|
28827
|
+
const a = coeffs[0];
|
|
28828
|
+
const b = coeffs[1];
|
|
28829
|
+
const c = coeffs[2];
|
|
28792
28830
|
return a * Math.log(b * x + c);
|
|
28793
28831
|
},
|
|
28794
28832
|
getEquationString: function (coords, asymptote) {
|
|
@@ -28796,9 +28834,9 @@ const Logarithm = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28796
28834
|
return null;
|
|
28797
28835
|
}
|
|
28798
28836
|
const coeffs = this.getCoefficients(coords, asymptote);
|
|
28799
|
-
const a = coeffs[0]
|
|
28800
|
-
|
|
28801
|
-
|
|
28837
|
+
const a = coeffs[0];
|
|
28838
|
+
const b = coeffs[1];
|
|
28839
|
+
const c = coeffs[2];
|
|
28802
28840
|
return "y = ln(" + a.toFixed(3) + "x + " + b.toFixed(3) + ") + " + c.toFixed(3);
|
|
28803
28841
|
}
|
|
28804
28842
|
});
|
|
@@ -28822,16 +28860,16 @@ const AbsoluteValue = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28822
28860
|
return [m, horizontalOffset, verticalOffset];
|
|
28823
28861
|
},
|
|
28824
28862
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28825
|
-
const m = coeffs[0]
|
|
28826
|
-
|
|
28827
|
-
|
|
28863
|
+
const m = coeffs[0];
|
|
28864
|
+
const horizontalOffset = coeffs[1];
|
|
28865
|
+
const verticalOffset = coeffs[2];
|
|
28828
28866
|
return m * Math.abs(x - horizontalOffset) + verticalOffset;
|
|
28829
28867
|
},
|
|
28830
28868
|
getEquationString: function (coords) {
|
|
28831
28869
|
const coeffs = this.getCoefficients(coords);
|
|
28832
|
-
const m = coeffs[0]
|
|
28833
|
-
|
|
28834
|
-
|
|
28870
|
+
const m = coeffs[0];
|
|
28871
|
+
const horizontalOffset = coeffs[1];
|
|
28872
|
+
const verticalOffset = coeffs[2];
|
|
28835
28873
|
return "y = " + m.toFixed(3) + "| x - " + horizontalOffset.toFixed(3) + "| + " + verticalOffset.toFixed(3);
|
|
28836
28874
|
}
|
|
28837
28875
|
});
|
|
@@ -32054,6 +32092,7 @@ const getNumericFormat = KhanMath.getNumericFormat;
|
|
|
32054
32092
|
*/
|
|
32055
32093
|
class NumberInput extends React__namespace.Component {
|
|
32056
32094
|
static contextType = PerseusI18nContext;
|
|
32095
|
+
inputRef = /*#__PURE__*/React__namespace.createRef();
|
|
32057
32096
|
static propTypes = {
|
|
32058
32097
|
value: PropTypes__default["default"].number,
|
|
32059
32098
|
format: PropTypes__default["default"].string,
|
|
@@ -32080,23 +32119,24 @@ class NumberInput extends React__namespace.Component {
|
|
|
32080
32119
|
this._setValue(this.props.value, this.state.format);
|
|
32081
32120
|
}
|
|
32082
32121
|
}
|
|
32122
|
+
_getInput = () => {
|
|
32123
|
+
if (!this.inputRef.current) {
|
|
32124
|
+
throw new perseusCore.PerseusError("Input ref accessed before set", perseusCore.Errors.Internal);
|
|
32125
|
+
}
|
|
32126
|
+
return this.inputRef.current;
|
|
32127
|
+
};
|
|
32083
32128
|
|
|
32084
32129
|
/* Return the current "value" of this input
|
|
32085
32130
|
* If empty, it returns the placeholder (if it is a number) or null
|
|
32086
32131
|
*/
|
|
32087
32132
|
getValue = () => {
|
|
32088
|
-
return this.parseInputValue(
|
|
32089
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
|
|
32090
|
-
ReactDOM__default["default"].findDOMNode(this.refs.input).value // eslint-disable-line react/no-string-refs
|
|
32091
|
-
);
|
|
32133
|
+
return this.parseInputValue(this._getInput().value);
|
|
32092
32134
|
};
|
|
32093
32135
|
|
|
32094
32136
|
/* Return the current string value of this input */
|
|
32095
32137
|
getStringValue = () => {
|
|
32096
|
-
|
|
32097
|
-
return ReactDOM__default["default"].findDOMNode(this.refs.input).value.toString(); // eslint-disable-line react/no-string-refs
|
|
32138
|
+
return this._getInput().toString();
|
|
32098
32139
|
};
|
|
32099
|
-
|
|
32100
32140
|
parseInputValue = value => {
|
|
32101
32141
|
if (value === "") {
|
|
32102
32142
|
const placeholder = this.props.placeholder;
|
|
@@ -32108,26 +32148,21 @@ class NumberInput extends React__namespace.Component {
|
|
|
32108
32148
|
|
|
32109
32149
|
/* Set text input focus to this input */
|
|
32110
32150
|
focus = () => {
|
|
32111
|
-
|
|
32112
|
-
ReactDOM__default["default"].findDOMNode(this.refs.input).focus(); // eslint-disable-line react/no-string-refs
|
|
32151
|
+
this._getInput().focus();
|
|
32113
32152
|
this._handleFocus();
|
|
32114
32153
|
};
|
|
32115
32154
|
blur = () => {
|
|
32116
|
-
|
|
32117
|
-
ReactDOM__default["default"].findDOMNode(this.refs.input).blur(); // eslint-disable-line react/no-string-refs
|
|
32155
|
+
this._getInput().blur();
|
|
32118
32156
|
this._handleBlur();
|
|
32119
32157
|
};
|
|
32120
32158
|
setSelectionRange = (selectionStart, selectionEnd) => {
|
|
32121
|
-
|
|
32122
|
-
ReactDOM__default["default"].findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
|
|
32159
|
+
this._getInput().setSelectionRange(selectionStart, selectionEnd);
|
|
32123
32160
|
};
|
|
32124
32161
|
getSelectionStart = () => {
|
|
32125
|
-
|
|
32126
|
-
return ReactDOM__default["default"].findDOMNode(this).selectionStart;
|
|
32162
|
+
return this._getInput().selectionStart;
|
|
32127
32163
|
};
|
|
32128
32164
|
getSelectionEnd = () => {
|
|
32129
|
-
|
|
32130
|
-
return ReactDOM__default["default"].findDOMNode(this).selectionEnd;
|
|
32165
|
+
return this._getInput().selectionEnd;
|
|
32131
32166
|
};
|
|
32132
32167
|
_checkValidity = value => {
|
|
32133
32168
|
if (value == null) {
|
|
@@ -32187,9 +32222,7 @@ class NumberInput extends React__namespace.Component {
|
|
|
32187
32222
|
}
|
|
32188
32223
|
};
|
|
32189
32224
|
_setValue = (val, format) => {
|
|
32190
|
-
|
|
32191
|
-
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'val' does not exist on type 'JQueryStatic'.
|
|
32192
|
-
$__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.input)).val(toNumericString(val, format));
|
|
32225
|
+
this._getInput().value = toNumericString(val, format);
|
|
32193
32226
|
};
|
|
32194
32227
|
render() {
|
|
32195
32228
|
let classes = classNames__default["default"]({
|
|
@@ -32213,10 +32246,8 @@ class NumberInput extends React__namespace.Component {
|
|
|
32213
32246
|
} = this.props;
|
|
32214
32247
|
return /*#__PURE__*/React__namespace.createElement("input", _extends({}, restProps, {
|
|
32215
32248
|
className: classes,
|
|
32216
|
-
type: "text"
|
|
32217
|
-
|
|
32218
|
-
,
|
|
32219
|
-
ref: "input",
|
|
32249
|
+
type: "text",
|
|
32250
|
+
ref: this.inputRef,
|
|
32220
32251
|
onChange: this._handleChange,
|
|
32221
32252
|
onFocus: this._handleFocus,
|
|
32222
32253
|
onBlur: this._handleBlur,
|
|
@@ -33711,15 +33742,18 @@ function useControlPoint(params) {
|
|
|
33711
33742
|
} = useGraphConfig();
|
|
33712
33743
|
const {
|
|
33713
33744
|
point,
|
|
33714
|
-
|
|
33745
|
+
ariaDescribedBy,
|
|
33746
|
+
ariaLabel,
|
|
33747
|
+
ariaLive = "polite",
|
|
33715
33748
|
color,
|
|
33716
|
-
cursor,
|
|
33717
33749
|
constrain = p => snap(snapStep, p),
|
|
33750
|
+
cursor,
|
|
33751
|
+
forwardedRef = noop$1,
|
|
33752
|
+
sequenceNumber = 1,
|
|
33718
33753
|
onMove = noop$1,
|
|
33719
33754
|
onClick = noop$1,
|
|
33720
33755
|
onFocus = noop$1,
|
|
33721
|
-
onBlur = noop$1
|
|
33722
|
-
forwardedRef = noop$1
|
|
33756
|
+
onBlur = noop$1
|
|
33723
33757
|
} = params;
|
|
33724
33758
|
const {
|
|
33725
33759
|
strings,
|
|
@@ -33742,6 +33776,13 @@ function useControlPoint(params) {
|
|
|
33742
33776
|
onMove,
|
|
33743
33777
|
constrainKeyboardMovement: constrain
|
|
33744
33778
|
});
|
|
33779
|
+
|
|
33780
|
+
// if custom aria label is not provided, will use default of sequence number and point coordinates
|
|
33781
|
+
const pointAriaLabel = ariaLabel || strings.srPointAtCoordinates({
|
|
33782
|
+
num: sequenceNumber,
|
|
33783
|
+
x: srFormatNumber(point[X], locale),
|
|
33784
|
+
y: srFormatNumber(point[Y], locale)
|
|
33785
|
+
});
|
|
33745
33786
|
React.useLayoutEffect(() => {
|
|
33746
33787
|
setForwardedRef(forwardedRef, focusableHandleRef.current);
|
|
33747
33788
|
}, [forwardedRef]);
|
|
@@ -33751,15 +33792,9 @@ function useControlPoint(params) {
|
|
|
33751
33792
|
tabIndex: disableKeyboardInteraction ? -1 : 0,
|
|
33752
33793
|
ref: focusableHandleRef,
|
|
33753
33794
|
role: "button",
|
|
33754
|
-
"aria-
|
|
33755
|
-
|
|
33756
|
-
|
|
33757
|
-
y: srFormatNumber(point[Y], locale)
|
|
33758
|
-
})
|
|
33759
|
-
// aria-live="assertive" causes the new location of the point to be
|
|
33760
|
-
// announced immediately on move.
|
|
33761
|
-
,
|
|
33762
|
-
"aria-live": "assertive",
|
|
33795
|
+
"aria-describedby": ariaDescribedBy,
|
|
33796
|
+
"aria-label": pointAriaLabel,
|
|
33797
|
+
"aria-live": ariaLive,
|
|
33763
33798
|
onFocus: event => {
|
|
33764
33799
|
onFocus(event);
|
|
33765
33800
|
setFocused(true);
|
|
@@ -36835,7 +36870,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
36835
36870
|
});
|
|
36836
36871
|
|
|
36837
36872
|
const eq$1 = Util.eq;
|
|
36838
|
-
const deepEq$
|
|
36873
|
+
const deepEq$3 = Util.deepEq;
|
|
36839
36874
|
function scoreInteractiveGraph(userInput, rubric) {
|
|
36840
36875
|
// None-type graphs are not graded
|
|
36841
36876
|
if (userInput.type === "none" && rubric.correct.type === "none") {
|
|
@@ -36885,7 +36920,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36885
36920
|
// If the parabola coefficients match, it's correct.
|
|
36886
36921
|
const guessCoeffs = getQuadraticCoefficients(userInput.coords);
|
|
36887
36922
|
const correctCoeffs = getQuadraticCoefficients(rubric.correct.coords);
|
|
36888
|
-
if (deepEq$
|
|
36923
|
+
if (deepEq$3(guessCoeffs, correctCoeffs)) {
|
|
36889
36924
|
return {
|
|
36890
36925
|
type: "points",
|
|
36891
36926
|
earned: 1,
|
|
@@ -36899,7 +36934,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36899
36934
|
const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
|
|
36900
36935
|
const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
|
|
36901
36936
|
// If the canonical coefficients match, it's correct.
|
|
36902
|
-
if (deepEq$
|
|
36937
|
+
if (deepEq$3(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
|
|
36903
36938
|
return {
|
|
36904
36939
|
type: "points",
|
|
36905
36940
|
earned: 1,
|
|
@@ -36908,7 +36943,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36908
36943
|
};
|
|
36909
36944
|
}
|
|
36910
36945
|
} else if (userInput.type === "circle" && rubric.correct.type === "circle") {
|
|
36911
|
-
if (deepEq$
|
|
36946
|
+
if (deepEq$3(userInput.center, rubric.correct.center) && eq$1(userInput.radius, rubric.correct.radius)) {
|
|
36912
36947
|
return {
|
|
36913
36948
|
type: "points",
|
|
36914
36949
|
earned: 1,
|
|
@@ -36930,7 +36965,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36930
36965
|
guess?.sort();
|
|
36931
36966
|
// @ts-expect-error - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
|
|
36932
36967
|
correct.sort();
|
|
36933
|
-
if (deepEq$
|
|
36968
|
+
if (deepEq$3(guess, correct)) {
|
|
36934
36969
|
return {
|
|
36935
36970
|
type: "points",
|
|
36936
36971
|
earned: 1,
|
|
@@ -36952,7 +36987,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36952
36987
|
/* exact */
|
|
36953
36988
|
guess.sort();
|
|
36954
36989
|
correct.sort();
|
|
36955
|
-
match = deepEq$
|
|
36990
|
+
match = deepEq$3(guess, correct);
|
|
36956
36991
|
}
|
|
36957
36992
|
if (match) {
|
|
36958
36993
|
return {
|
|
@@ -36967,7 +37002,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36967
37002
|
let correct = Util.deepClone(rubric.correct.coords);
|
|
36968
37003
|
guess = ___default["default"].invoke(guess, "sort").sort();
|
|
36969
37004
|
correct = ___default["default"].invoke(correct, "sort").sort();
|
|
36970
|
-
if (deepEq$
|
|
37005
|
+
if (deepEq$3(guess, correct)) {
|
|
36971
37006
|
return {
|
|
36972
37007
|
type: "points",
|
|
36973
37008
|
earned: 1,
|
|
@@ -36978,7 +37013,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36978
37013
|
} else if (userInput.type === "ray" && rubric.correct.type === "ray" && userInput.coords != null) {
|
|
36979
37014
|
const guess = userInput.coords;
|
|
36980
37015
|
const correct = rubric.correct.coords;
|
|
36981
|
-
if (deepEq$
|
|
37016
|
+
if (deepEq$3(guess[0], correct[0]) && collinear(correct[0], correct[1], guess[1])) {
|
|
36982
37017
|
return {
|
|
36983
37018
|
type: "points",
|
|
36984
37019
|
earned: 1,
|
|
@@ -37005,7 +37040,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
37005
37040
|
/* exact */
|
|
37006
37041
|
match =
|
|
37007
37042
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
37008
|
-
deepEq$
|
|
37043
|
+
deepEq$3(guess[1], correct[1]) &&
|
|
37009
37044
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
37010
37045
|
collinear(correct[1], correct[0], guess[0]) &&
|
|
37011
37046
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
@@ -41267,14 +41302,20 @@ class Measurer extends React__namespace.Component {
|
|
|
41267
41302
|
}
|
|
41268
41303
|
const propUpgrades = {
|
|
41269
41304
|
"1": v0props => {
|
|
41270
|
-
const
|
|
41305
|
+
const {
|
|
41306
|
+
imageUrl,
|
|
41307
|
+
imageTop,
|
|
41308
|
+
imageLeft,
|
|
41309
|
+
...rest
|
|
41310
|
+
} = v0props;
|
|
41311
|
+
return {
|
|
41312
|
+
...rest,
|
|
41271
41313
|
image: {
|
|
41272
|
-
url:
|
|
41273
|
-
top:
|
|
41274
|
-
left:
|
|
41314
|
+
url: imageUrl,
|
|
41315
|
+
top: imageTop,
|
|
41316
|
+
left: imageLeft
|
|
41275
41317
|
}
|
|
41276
|
-
}
|
|
41277
|
-
return v1props;
|
|
41318
|
+
};
|
|
41278
41319
|
}
|
|
41279
41320
|
};
|
|
41280
41321
|
var Measurer$1 = {
|
|
@@ -42808,14 +42849,28 @@ const getPromptJSON$5 = (renderProps, userInput) => {
|
|
|
42808
42849
|
};
|
|
42809
42850
|
};
|
|
42810
42851
|
|
|
42811
|
-
|
|
42852
|
+
/**
|
|
42853
|
+
* Checks user input from the orderer widget to see if the user has started
|
|
42854
|
+
* ordering the options, making the widget scorable.
|
|
42855
|
+
* @param userInput
|
|
42856
|
+
* @see `scoreOrderer` for more details.
|
|
42857
|
+
*/
|
|
42858
|
+
function validateOrderer(userInput) {
|
|
42812
42859
|
if (userInput.current.length === 0) {
|
|
42813
42860
|
return {
|
|
42814
42861
|
type: "invalid",
|
|
42815
42862
|
message: null
|
|
42816
42863
|
};
|
|
42817
42864
|
}
|
|
42818
|
-
|
|
42865
|
+
return null;
|
|
42866
|
+
}
|
|
42867
|
+
|
|
42868
|
+
function scoreOrderer(userInput, rubric) {
|
|
42869
|
+
const validateError = validateOrderer(userInput);
|
|
42870
|
+
if (validateError) {
|
|
42871
|
+
return validateError;
|
|
42872
|
+
}
|
|
42873
|
+
const correct = ___default["default"].isEqual(userInput.current, rubric.correctOptions.map(option => option.content));
|
|
42819
42874
|
return {
|
|
42820
42875
|
type: "points",
|
|
42821
42876
|
earned: correct ? 1 : 0,
|
|
@@ -42991,15 +43046,13 @@ class Card extends React__namespace.Component {
|
|
|
42991
43046
|
}))));
|
|
42992
43047
|
}
|
|
42993
43048
|
}
|
|
42994
|
-
const NORMAL = "normal";
|
|
42995
|
-
const HORIZONTAL = "horizontal";
|
|
42996
43049
|
class Orderer extends React__namespace.Component {
|
|
42997
43050
|
static defaultProps = {
|
|
42998
43051
|
current: [],
|
|
42999
43052
|
options: [],
|
|
43000
43053
|
correctOptions: [],
|
|
43001
|
-
height:
|
|
43002
|
-
layout:
|
|
43054
|
+
height: "normal",
|
|
43055
|
+
layout: "horizontal",
|
|
43003
43056
|
linterContext: PerseusLinter.linterContextDefault
|
|
43004
43057
|
};
|
|
43005
43058
|
state = {
|
|
@@ -43152,7 +43205,7 @@ class Orderer extends React__namespace.Component {
|
|
|
43152
43205
|
};
|
|
43153
43206
|
findCorrectIndex = (draggable, list) => {
|
|
43154
43207
|
// Find the correct index for a card given the current cards.
|
|
43155
|
-
const isHorizontal = this.props.layout ===
|
|
43208
|
+
const isHorizontal = this.props.layout === "horizontal";
|
|
43156
43209
|
// eslint-disable-next-line react/no-string-refs
|
|
43157
43210
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
43158
43211
|
const $dragList = $__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.dragList));
|
|
@@ -43202,28 +43255,24 @@ class Orderer extends React__namespace.Component {
|
|
|
43202
43255
|
if (draggable == null) {
|
|
43203
43256
|
return false;
|
|
43204
43257
|
}
|
|
43205
|
-
const isHorizontal = this.props.layout ===
|
|
43206
|
-
|
|
43207
|
-
|
|
43208
|
-
|
|
43209
|
-
|
|
43210
|
-
|
|
43211
|
-
|
|
43212
|
-
|
|
43213
|
-
|
|
43214
|
-
|
|
43215
|
-
|
|
43216
|
-
|
|
43217
|
-
|
|
43218
|
-
|
|
43219
|
-
|
|
43220
|
-
|
|
43221
|
-
|
|
43222
|
-
|
|
43223
|
-
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'width' does not exist on type 'JQueryStatic'.
|
|
43224
|
-
$__default["default"](dragList).width();
|
|
43225
|
-
const // @ts-expect-error - TS2339 - Property 'outerWidth' does not exist on type 'JQueryStatic'.
|
|
43226
|
-
draggableWidth = $draggable.outerWidth(true);
|
|
43258
|
+
const isHorizontal = this.props.layout === "horizontal";
|
|
43259
|
+
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
43260
|
+
const $draggable = $__default["default"](ReactDOM__default["default"].findDOMNode(draggable));
|
|
43261
|
+
// eslint-disable-next-line react/no-string-refs
|
|
43262
|
+
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
43263
|
+
const $bank = $__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.bank));
|
|
43264
|
+
// @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
|
|
43265
|
+
const draggableOffset = $draggable.offset();
|
|
43266
|
+
// @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
|
|
43267
|
+
const bankOffset = $bank.offset();
|
|
43268
|
+
// @ts-expect-error - TS2339 - Property 'outerHeight' does not exist on type 'JQueryStatic'.
|
|
43269
|
+
const draggableHeight = $draggable.outerHeight(true);
|
|
43270
|
+
// @ts-expect-error - TS2339 - Property 'outerHeight' does not exist on type 'JQueryStatic'.
|
|
43271
|
+
const bankHeight = $bank.outerHeight(true);
|
|
43272
|
+
// @ts-expect-error - TS2339 - Property 'outerWidth' does not exist on type 'JQueryStatic'.
|
|
43273
|
+
const bankWidth = $bank.outerWidth(true);
|
|
43274
|
+
// @ts-expect-error - TS2339 - Property 'outerWidth' does not exist on type 'JQueryStatic'.
|
|
43275
|
+
const draggableWidth = $draggable.outerWidth(true);
|
|
43227
43276
|
if (isHorizontal) {
|
|
43228
43277
|
return draggableOffset.top + draggableHeight / 2 < bankOffset.top + bankHeight;
|
|
43229
43278
|
}
|
|
@@ -43642,18 +43691,36 @@ const getPromptJSON$3 = () => {
|
|
|
43642
43691
|
};
|
|
43643
43692
|
|
|
43644
43693
|
const {
|
|
43645
|
-
deepEq: deepEq$
|
|
43694
|
+
deepEq: deepEq$2
|
|
43646
43695
|
} = Util;
|
|
43647
|
-
|
|
43648
|
-
|
|
43696
|
+
|
|
43697
|
+
/**
|
|
43698
|
+
* Checks user input to confirm it is not the same as the starting values for the graph.
|
|
43699
|
+
* This means the user has modified the graph, and the question can be scored.
|
|
43700
|
+
*
|
|
43701
|
+
* @see 'scorePlotter' for more details on scoring.
|
|
43702
|
+
*/
|
|
43703
|
+
function validatePlotter(userInput, validationData) {
|
|
43704
|
+
if (deepEq$2(userInput, validationData.starting)) {
|
|
43649
43705
|
return {
|
|
43650
43706
|
type: "invalid",
|
|
43651
43707
|
message: null
|
|
43652
43708
|
};
|
|
43653
43709
|
}
|
|
43710
|
+
return null;
|
|
43711
|
+
}
|
|
43712
|
+
|
|
43713
|
+
const {
|
|
43714
|
+
deepEq: deepEq$1
|
|
43715
|
+
} = Util;
|
|
43716
|
+
function scorePlotter(userInput, scoringData) {
|
|
43717
|
+
const validationError = validatePlotter(userInput, scoringData);
|
|
43718
|
+
if (validationError) {
|
|
43719
|
+
return validationError;
|
|
43720
|
+
}
|
|
43654
43721
|
return {
|
|
43655
43722
|
type: "points",
|
|
43656
|
-
earned: deepEq$1(userInput,
|
|
43723
|
+
earned: deepEq$1(userInput, scoringData.correct) ? 1 : 0,
|
|
43657
43724
|
total: 1,
|
|
43658
43725
|
message: null
|
|
43659
43726
|
};
|
|
@@ -44540,7 +44607,14 @@ const getPromptJSON$1 = userInput => {
|
|
|
44540
44607
|
};
|
|
44541
44608
|
};
|
|
44542
44609
|
|
|
44543
|
-
|
|
44610
|
+
/**
|
|
44611
|
+
* Checks user input for the sorter widget to ensure that the user has made
|
|
44612
|
+
* changes before attempting to score the widget.
|
|
44613
|
+
* @param userInput
|
|
44614
|
+
* @see 'scoreSorter' in 'packages/perseus/src/widgets/sorter/score-sorter.ts'
|
|
44615
|
+
* for more details on how the sorter widget is scored.
|
|
44616
|
+
*/
|
|
44617
|
+
function validateSorter(userInput) {
|
|
44544
44618
|
// If the sorter widget hasn't been changed yet, we treat it as "empty" which
|
|
44545
44619
|
// prevents the "Check" button from becoming active. We want the user
|
|
44546
44620
|
// to make a change before trying to move forward. This makes an
|
|
@@ -44553,6 +44627,14 @@ function scoreSorter(userInput, rubric) {
|
|
|
44553
44627
|
message: null
|
|
44554
44628
|
};
|
|
44555
44629
|
}
|
|
44630
|
+
return null;
|
|
44631
|
+
}
|
|
44632
|
+
|
|
44633
|
+
function scoreSorter(userInput, rubric) {
|
|
44634
|
+
const validationError = validateSorter(userInput);
|
|
44635
|
+
if (validationError) {
|
|
44636
|
+
return validationError;
|
|
44637
|
+
}
|
|
44556
44638
|
const correct = Util.deepEq(userInput.options, rubric.correct);
|
|
44557
44639
|
return {
|
|
44558
44640
|
type: "points",
|
|
@@ -45127,7 +45209,7 @@ const init = function () {
|
|
|
45127
45209
|
|
|
45128
45210
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45129
45211
|
const libName = "@khanacademy/perseus";
|
|
45130
|
-
const libVersion = "43.0
|
|
45212
|
+
const libVersion = "43.1.0";
|
|
45131
45213
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45132
45214
|
|
|
45133
45215
|
const apiVersion = {
|
|
@@ -45412,7 +45494,6 @@ const styles$3 = aphrodite.StyleSheet.create({
|
|
|
45412
45494
|
}
|
|
45413
45495
|
});
|
|
45414
45496
|
|
|
45415
|
-
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
45416
45497
|
// Use of UNSAFE_componentWillReceiveProps:
|
|
45417
45498
|
// eslint-disable-next-line react/no-unsafe
|
|
45418
45499
|
class HintsRenderer extends React__namespace.Component {
|
|
@@ -48638,6 +48719,27 @@ function pair(parseA, parseB) {
|
|
|
48638
48719
|
};
|
|
48639
48720
|
}
|
|
48640
48721
|
|
|
48722
|
+
function pipeParsers(p) {
|
|
48723
|
+
return new ParserPipeline(p);
|
|
48724
|
+
}
|
|
48725
|
+
class ParserPipeline {
|
|
48726
|
+
constructor(parser) {
|
|
48727
|
+
this.parser = parser;
|
|
48728
|
+
}
|
|
48729
|
+
then(nextParser) {
|
|
48730
|
+
return new ParserPipeline(composeParsers(this.parser, nextParser));
|
|
48731
|
+
}
|
|
48732
|
+
}
|
|
48733
|
+
function composeParsers(parserA, parserB) {
|
|
48734
|
+
return (rawValue, ctx) => {
|
|
48735
|
+
const partialResult = parserA(rawValue, ctx);
|
|
48736
|
+
if (isFailure(partialResult)) {
|
|
48737
|
+
return partialResult;
|
|
48738
|
+
}
|
|
48739
|
+
return parserB(partialResult.value, ctx);
|
|
48740
|
+
};
|
|
48741
|
+
}
|
|
48742
|
+
|
|
48641
48743
|
function record(parseKey, parseValue) {
|
|
48642
48744
|
return (rawValue, ctx) => {
|
|
48643
48745
|
if (!isObject(rawValue)) {
|
|
@@ -48718,6 +48820,20 @@ function either(parseA, parseB) {
|
|
|
48718
48820
|
};
|
|
48719
48821
|
}
|
|
48720
48822
|
|
|
48823
|
+
function defaulted(parser, fallback) {
|
|
48824
|
+
return (rawValue, ctx) => {
|
|
48825
|
+
if (rawValue == null) {
|
|
48826
|
+
return success(fallback(rawValue));
|
|
48827
|
+
}
|
|
48828
|
+
return parser(rawValue, ctx);
|
|
48829
|
+
};
|
|
48830
|
+
}
|
|
48831
|
+
|
|
48832
|
+
const parseImages = defaulted(record(string, object({
|
|
48833
|
+
width: number,
|
|
48834
|
+
height: number
|
|
48835
|
+
})), () => ({}));
|
|
48836
|
+
|
|
48721
48837
|
function parseWidget(parseType, parseOptions) {
|
|
48722
48838
|
return object({
|
|
48723
48839
|
type: parseType,
|
|
@@ -48726,10 +48842,21 @@ function parseWidget(parseType, parseOptions) {
|
|
|
48726
48842
|
alignment: optional(string),
|
|
48727
48843
|
options: parseOptions,
|
|
48728
48844
|
key: optional(number),
|
|
48729
|
-
version: object({
|
|
48845
|
+
version: optional(object({
|
|
48730
48846
|
major: number,
|
|
48731
48847
|
minor: number
|
|
48732
|
-
})
|
|
48848
|
+
}))
|
|
48849
|
+
});
|
|
48850
|
+
}
|
|
48851
|
+
function parseWidgetWithVersion(parseVersion, parseType, parseOptions) {
|
|
48852
|
+
return object({
|
|
48853
|
+
type: parseType,
|
|
48854
|
+
static: optional(boolean),
|
|
48855
|
+
graded: optional(boolean),
|
|
48856
|
+
alignment: optional(string),
|
|
48857
|
+
options: parseOptions,
|
|
48858
|
+
key: optional(number),
|
|
48859
|
+
version: parseVersion
|
|
48733
48860
|
});
|
|
48734
48861
|
}
|
|
48735
48862
|
|
|
@@ -48737,7 +48864,7 @@ const parseCategorizerWidget = parseWidget(constant("categorizer"), object({
|
|
|
48737
48864
|
items: array(string),
|
|
48738
48865
|
categories: array(string),
|
|
48739
48866
|
randomizeItems: boolean,
|
|
48740
|
-
static: boolean,
|
|
48867
|
+
static: defaulted(boolean, () => false),
|
|
48741
48868
|
values: array(number),
|
|
48742
48869
|
highlightLint: optional(boolean),
|
|
48743
48870
|
linterContext: optional(object({
|
|
@@ -48769,7 +48896,7 @@ const parseDefinitionWidget = parseWidget(constant("definition"), object({
|
|
|
48769
48896
|
|
|
48770
48897
|
const parseDropdownWidget = parseWidget(constant("dropdown"), object({
|
|
48771
48898
|
placeholder: string,
|
|
48772
|
-
static: boolean,
|
|
48899
|
+
static: defaulted(boolean, () => false),
|
|
48773
48900
|
choices: array(object({
|
|
48774
48901
|
content: string,
|
|
48775
48902
|
correct: boolean
|
|
@@ -48792,9 +48919,12 @@ const parseAnswerForm = object({
|
|
|
48792
48919
|
form: boolean,
|
|
48793
48920
|
simplify: boolean,
|
|
48794
48921
|
considered: enumeration("correct", "wrong", "ungraded"),
|
|
48795
|
-
key: optional(string)
|
|
48922
|
+
key: pipeParsers(optional(union(string).or(number).parser)).then((key, ctx) => ctx.success(String(key))).parser
|
|
48796
48923
|
});
|
|
48797
|
-
const
|
|
48924
|
+
const parseExpressionWidgetV1 = parseWidgetWithVersion(object({
|
|
48925
|
+
major: constant(1),
|
|
48926
|
+
minor: number
|
|
48927
|
+
}), constant("expression"), object({
|
|
48798
48928
|
answerForms: array(parseAnswerForm),
|
|
48799
48929
|
functions: array(string),
|
|
48800
48930
|
times: boolean,
|
|
@@ -48803,23 +48933,69 @@ const parseExpressionWidget = parseWidget(constant("expression"), object({
|
|
|
48803
48933
|
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
48804
48934
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48805
48935
|
}));
|
|
48936
|
+
const parseExpressionWidgetV0 = parseWidgetWithVersion(optional(object({
|
|
48937
|
+
major: constant(0),
|
|
48938
|
+
minor: number
|
|
48939
|
+
})), constant("expression"), object({
|
|
48940
|
+
functions: array(string),
|
|
48941
|
+
times: boolean,
|
|
48942
|
+
visibleLabel: optional(string),
|
|
48943
|
+
ariaLabel: optional(string),
|
|
48944
|
+
form: boolean,
|
|
48945
|
+
simplify: boolean,
|
|
48946
|
+
value: string,
|
|
48947
|
+
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
48948
|
+
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48949
|
+
}));
|
|
48950
|
+
function migrateV0ToV1(widget, ctx) {
|
|
48951
|
+
const {
|
|
48952
|
+
options
|
|
48953
|
+
} = widget;
|
|
48954
|
+
return ctx.success({
|
|
48955
|
+
...widget,
|
|
48956
|
+
version: {
|
|
48957
|
+
major: 1,
|
|
48958
|
+
minor: 0
|
|
48959
|
+
},
|
|
48960
|
+
options: {
|
|
48961
|
+
times: options.times,
|
|
48962
|
+
buttonSets: options.buttonSets,
|
|
48963
|
+
functions: options.functions,
|
|
48964
|
+
buttonsVisible: options.buttonsVisible,
|
|
48965
|
+
visibleLabel: options.visibleLabel,
|
|
48966
|
+
ariaLabel: options.ariaLabel,
|
|
48967
|
+
answerForms: [{
|
|
48968
|
+
considered: "correct",
|
|
48969
|
+
form: options.form,
|
|
48970
|
+
simplify: options.simplify,
|
|
48971
|
+
value: options.value
|
|
48972
|
+
}]
|
|
48973
|
+
}
|
|
48974
|
+
});
|
|
48975
|
+
}
|
|
48976
|
+
const parseExpressionWidget = union(parseExpressionWidgetV1).or(pipeParsers(parseExpressionWidgetV0).then(migrateV0ToV1).parser).parser;
|
|
48806
48977
|
|
|
48807
|
-
const parsePerseusRenderer = object({
|
|
48808
|
-
|
|
48978
|
+
const parsePerseusRenderer = defaulted(object({
|
|
48979
|
+
// TODO(benchristel): content is also defaulted to empty string in
|
|
48980
|
+
// renderer.tsx. See if we can remove one default or the other.
|
|
48981
|
+
content: defaulted(string, () => ""),
|
|
48809
48982
|
// This module has an import cycle with parseWidgetsMap, because the
|
|
48810
48983
|
// `group` widget can contain another renderer.
|
|
48811
48984
|
// The anonymous function below ensures that we don't try to access
|
|
48812
48985
|
// parseWidgetsMap before it's defined.
|
|
48813
|
-
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
48986
|
+
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
|
|
48814
48987
|
metadata: optional(array(string)),
|
|
48815
|
-
images:
|
|
48816
|
-
|
|
48817
|
-
|
|
48818
|
-
|
|
48819
|
-
|
|
48988
|
+
images: parseImages
|
|
48989
|
+
}),
|
|
48990
|
+
// Default value
|
|
48991
|
+
() => ({
|
|
48992
|
+
content: "",
|
|
48993
|
+
widgets: {},
|
|
48994
|
+
images: {}
|
|
48995
|
+
}));
|
|
48820
48996
|
|
|
48821
48997
|
const parseGradedGroupWidgetOptions = object({
|
|
48822
|
-
title: string,
|
|
48998
|
+
title: defaulted(string, () => ""),
|
|
48823
48999
|
hasHint: optional(nullable(boolean)),
|
|
48824
49000
|
// This module has an import cycle with parsePerseusRenderer.
|
|
48825
49001
|
// The anonymous function below ensures that we don't try to access
|
|
@@ -48913,7 +49089,7 @@ const parseIframeWidget = parseWidget(constant("iframe"), object({
|
|
|
48913
49089
|
height: union(number).or(string).parser,
|
|
48914
49090
|
allowFullScreen: boolean,
|
|
48915
49091
|
allowTopNavigation: optional(boolean),
|
|
48916
|
-
static: boolean
|
|
49092
|
+
static: defaulted(boolean, () => false)
|
|
48917
49093
|
}));
|
|
48918
49094
|
|
|
48919
49095
|
const parsePerseusImageBackground = object({
|
|
@@ -49292,13 +49468,13 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
|
49292
49468
|
padding: boolean
|
|
49293
49469
|
}));
|
|
49294
49470
|
|
|
49295
|
-
const parseMatrixWidget = parseWidget(constant("matrix"), object({
|
|
49296
|
-
prefix: string,
|
|
49297
|
-
suffix: string,
|
|
49471
|
+
const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
|
|
49472
|
+
prefix: optional(string),
|
|
49473
|
+
suffix: optional(string),
|
|
49298
49474
|
answers: array(array(number)),
|
|
49299
|
-
cursorPosition: array(number),
|
|
49475
|
+
cursorPosition: optional(array(number)),
|
|
49300
49476
|
matrixBoardSize: array(number),
|
|
49301
|
-
static: boolean
|
|
49477
|
+
static: optional(boolean)
|
|
49302
49478
|
}));
|
|
49303
49479
|
|
|
49304
49480
|
const parseMeasurerWidget = parseWidget(constant("measurer"), object({
|
|
@@ -49368,8 +49544,8 @@ const parseOrdererWidget = parseWidget(constant("orderer"), object({
|
|
|
49368
49544
|
options: array(parseRenderer),
|
|
49369
49545
|
correctOptions: array(parseRenderer),
|
|
49370
49546
|
otherOptions: array(parseRenderer),
|
|
49371
|
-
height:
|
|
49372
|
-
layout:
|
|
49547
|
+
height: enumeration("normal", "auto"),
|
|
49548
|
+
layout: enumeration("horizontal", "vertical")
|
|
49373
49549
|
}));
|
|
49374
49550
|
|
|
49375
49551
|
const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
@@ -49414,7 +49590,7 @@ const parsePythonProgramWidget = parseWidget(constant("python-program"), object(
|
|
|
49414
49590
|
|
|
49415
49591
|
const parseRadioWidget = parseWidget(constant("radio"), object({
|
|
49416
49592
|
choices: array(object({
|
|
49417
|
-
content: string,
|
|
49593
|
+
content: defaulted(string, () => ""),
|
|
49418
49594
|
clue: optional(string),
|
|
49419
49595
|
correct: optional(boolean),
|
|
49420
49596
|
isNoneOfTheAbove: optional(boolean),
|
|
@@ -49576,26 +49752,45 @@ const parseWidgetMapKeyComponents = pair(string, parseStringToPositiveInt);
|
|
|
49576
49752
|
const parseHint = object({
|
|
49577
49753
|
replace: optional(boolean),
|
|
49578
49754
|
content: string,
|
|
49579
|
-
widgets: parseWidgetsMap,
|
|
49755
|
+
widgets: defaulted(parseWidgetsMap, () => ({})),
|
|
49580
49756
|
metadata: optional(array(string)),
|
|
49581
|
-
images:
|
|
49582
|
-
width: number,
|
|
49583
|
-
height: number
|
|
49584
|
-
}))
|
|
49757
|
+
images: parseImages
|
|
49585
49758
|
});
|
|
49586
49759
|
|
|
49587
49760
|
object({
|
|
49588
49761
|
question: parsePerseusRenderer,
|
|
49589
|
-
hints: array(parseHint),
|
|
49590
|
-
answerArea: record(enumeration(...ItemExtras), boolean),
|
|
49591
|
-
itemDataVersion: object({
|
|
49762
|
+
hints: defaulted(array(parseHint), () => []),
|
|
49763
|
+
answerArea: pipeParsers(defaulted(object({}), () => ({}))).then(migrateAnswerArea).then(record(enumeration(...ItemExtras), boolean)).parser,
|
|
49764
|
+
itemDataVersion: optional(object({
|
|
49592
49765
|
major: number,
|
|
49593
49766
|
minor: number
|
|
49594
|
-
}),
|
|
49767
|
+
})),
|
|
49595
49768
|
// Deprecated field
|
|
49596
49769
|
answer: any
|
|
49597
49770
|
});
|
|
49598
49771
|
|
|
49772
|
+
// Some answerAreas have extra fields, like:
|
|
49773
|
+
//
|
|
49774
|
+
// "answerArea": {
|
|
49775
|
+
// "type": "multiple",
|
|
49776
|
+
// "options": {
|
|
49777
|
+
// "content": "",
|
|
49778
|
+
// "images": {},
|
|
49779
|
+
// "widgets": {}
|
|
49780
|
+
// }
|
|
49781
|
+
// }
|
|
49782
|
+
//
|
|
49783
|
+
// The "type" and "options" fields don't seem to be used anywhere. This
|
|
49784
|
+
// migration function removes them.
|
|
49785
|
+
function migrateAnswerArea(rawValue, ctx) {
|
|
49786
|
+
const {
|
|
49787
|
+
type: _,
|
|
49788
|
+
options: __,
|
|
49789
|
+
...rest
|
|
49790
|
+
} = rawValue;
|
|
49791
|
+
return ctx.success(rest);
|
|
49792
|
+
}
|
|
49793
|
+
|
|
49599
49794
|
/**
|
|
49600
49795
|
* Helper to parse PerseusItem JSON
|
|
49601
49796
|
* Why not just use JSON.parse? We want:
|