@khanacademy/perseus 43.0.1 → 44.0.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/all-widgets.d.ts +2 -23
- package/dist/basic-widgets.d.ts +2 -23
- package/dist/components/graph.d.ts +1 -0
- package/dist/components/icon.d.ts +0 -1
- package/dist/components/multi-button-group.d.ts +1 -0
- package/dist/components/number-input.d.ts +5 -3
- package/dist/components/text-input.d.ts +4 -2
- package/dist/components/tooltip.d.ts +1 -0
- package/dist/es/index.js +476 -261
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +4 -2
- package/dist/es/strings.js.map +1 -1
- package/dist/extra-widgets.d.ts +2 -23
- package/dist/index.d.ts +2 -2
- package/dist/index.js +465 -250
- package/dist/index.js.map +1 -1
- package/dist/interactive2/interactive-util.d.ts +1 -1
- package/dist/interactive2/movable-point-options.d.ts +2 -2
- package/dist/interactive2/movable-point.d.ts +7 -7
- package/dist/interactive2/objective_.d.ts +1 -1
- package/dist/jipt-paragraphs.d.ts +2 -2
- package/dist/perseus-api.d.ts +2 -77
- package/dist/perseus-types.d.ts +17 -17
- package/dist/server-item-renderer.d.ts +26 -49
- package/dist/strings.d.ts +1 -0
- package/dist/strings.js +4 -2
- package/dist/strings.js.map +1 -1
- package/dist/styles/global-styles.d.ts +0 -1
- package/dist/styles/shared.d.ts +0 -1
- package/dist/traversal.d.ts +1 -1
- package/dist/util/a11y.d.ts +0 -1
- package/dist/util/debounce.d.ts +1 -1
- 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/graded-group-widget.d.ts +2 -2
- 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/definition/definition.d.ts +1 -1
- package/dist/widgets/deprecated-standin/deprecated-standin.d.ts +1 -1
- package/dist/widgets/dropdown/dropdown.d.ts +9 -1
- package/dist/widgets/dropdown/validate-dropdown.d.ts +10 -0
- package/dist/widgets/explanation/explanation.d.ts +1 -1
- package/dist/widgets/expression/expression.d.ts +92 -217
- package/dist/widgets/image/image.d.ts +1 -1
- package/dist/widgets/interaction/interaction.d.ts +1 -1
- 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/graphs/quadratic.d.ts +1 -1
- package/dist/widgets/interactive-graphs/types.d.ts +1 -0
- package/dist/widgets/label-image/answer-pill.d.ts +0 -1
- package/dist/widgets/label-image/label-image.d.ts +5 -65
- package/dist/widgets/label-image/marker.d.ts +0 -1
- package/dist/widgets/matrix/matrix.d.ts +12 -5
- package/dist/widgets/measurer/measurer.d.ts +2 -2
- package/dist/widgets/molecule/molecule.d.ts +1 -1
- package/dist/widgets/orderer/validate-orderer.d.ts +12 -0
- package/dist/widgets/passage/passage-markdown.d.ts +2 -2
- package/dist/widgets/passage/passage.d.ts +1 -1
- package/dist/widgets/passage-ref/passage-ref.d.ts +1 -1
- package/dist/widgets/passage-ref-target/passage-ref-target.d.ts +1 -1
- 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/choice-icon/shared-styles.d.ts +0 -1
- package/dist/widgets/radio/choice-none-above.d.ts +1 -1
- package/dist/widgets/radio/radio.d.ts +3 -3
- package/dist/widgets/radio/validate-radio.d.ts +14 -0
- package/dist/widgets/sorter/validate-sorter.d.ts +13 -0
- package/dist/widgets/video/video.d.ts +1 -1
- package/package.json +5 -5
- package/dist/util/parse-perseus-json/general-purpose-parsers/compose-parsers.d.ts +0 -2
package/dist/index.js
CHANGED
|
@@ -384,7 +384,8 @@ const mockStrings = {
|
|
|
384
384
|
} = _ref28;
|
|
385
385
|
return `Interactive elements: ${elements}`;
|
|
386
386
|
},
|
|
387
|
-
srNoInteractiveElements: "No interactive elements"
|
|
387
|
+
srNoInteractiveElements: "No interactive elements",
|
|
388
|
+
selectAnAnswer: "Select an answer"
|
|
388
389
|
};
|
|
389
390
|
|
|
390
391
|
/**
|
|
@@ -2882,8 +2883,6 @@ async function getImageSizeModern(url) {
|
|
|
2882
2883
|
});
|
|
2883
2884
|
}
|
|
2884
2885
|
|
|
2885
|
-
/* eslint-disable @babel/no-invalid-this, getter-return, one-var */
|
|
2886
|
-
|
|
2887
2886
|
// TODO: dedupe this with Coord in interactive2/types.js
|
|
2888
2887
|
|
|
2889
2888
|
let supportsPassive = false;
|
|
@@ -2960,8 +2959,8 @@ function shuffle$3(array, randomSeed) {
|
|
|
2960
2959
|
do {
|
|
2961
2960
|
// Fischer-Yates shuffle
|
|
2962
2961
|
for (let top = shuffled.length; top > 0; top--) {
|
|
2963
|
-
const newEnd = Math.floor(random() * top)
|
|
2964
|
-
|
|
2962
|
+
const newEnd = Math.floor(random() * top);
|
|
2963
|
+
const temp = shuffled[newEnd];
|
|
2965
2964
|
|
|
2966
2965
|
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
2967
2966
|
shuffled[newEnd] = shuffled[top - 1];
|
|
@@ -3293,13 +3292,13 @@ function eq$4(x, y) {
|
|
|
3293
3292
|
* Deep approximate equality on primitives, numbers, arrays, and objects.
|
|
3294
3293
|
* Recursive.
|
|
3295
3294
|
*/
|
|
3296
|
-
function deepEq$
|
|
3295
|
+
function deepEq$7(x, y) {
|
|
3297
3296
|
if (Array.isArray(x) && Array.isArray(y)) {
|
|
3298
3297
|
if (x.length !== y.length) {
|
|
3299
3298
|
return false;
|
|
3300
3299
|
}
|
|
3301
3300
|
for (let i = 0; i < x.length; i++) {
|
|
3302
|
-
if (!deepEq$
|
|
3301
|
+
if (!deepEq$7(x[i], y[i])) {
|
|
3303
3302
|
return false;
|
|
3304
3303
|
}
|
|
3305
3304
|
}
|
|
@@ -3317,10 +3316,10 @@ function deepEq$6(x, y) {
|
|
|
3317
3316
|
if (typeof x === "object" && typeof y === "object" && !!x && !!y) {
|
|
3318
3317
|
return x === y || ___default["default"].all(x, function (v, k) {
|
|
3319
3318
|
// @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
|
|
3320
|
-
return deepEq$
|
|
3319
|
+
return deepEq$7(y[k], v);
|
|
3321
3320
|
}) && ___default["default"].all(y, function (v, k) {
|
|
3322
3321
|
// @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
|
|
3323
|
-
return deepEq$
|
|
3322
|
+
return deepEq$7(x[k], v);
|
|
3324
3323
|
});
|
|
3325
3324
|
}
|
|
3326
3325
|
if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
|
|
@@ -3599,7 +3598,7 @@ const Util = {
|
|
|
3599
3598
|
tickStepFromNumTicks,
|
|
3600
3599
|
constrainedTickStepsFromTickSteps,
|
|
3601
3600
|
eq: eq$4,
|
|
3602
|
-
deepEq: deepEq$
|
|
3601
|
+
deepEq: deepEq$7,
|
|
3603
3602
|
parseQueryString,
|
|
3604
3603
|
updateQueryString: updateQueryString$2,
|
|
3605
3604
|
strongEncodeURIComponent,
|
|
@@ -9002,7 +9001,6 @@ var Tex$1 = {
|
|
|
9002
9001
|
}
|
|
9003
9002
|
};
|
|
9004
9003
|
|
|
9005
|
-
/* eslint-disable @babel/no-invalid-this */
|
|
9006
9004
|
const {
|
|
9007
9005
|
processMath: processMath$1
|
|
9008
9006
|
} = Tex$1;
|
|
@@ -10632,7 +10630,9 @@ const getClipPoint = function (graph, coord, angle) {
|
|
|
10632
10630
|
return clipPoint;
|
|
10633
10631
|
};
|
|
10634
10632
|
|
|
10635
|
-
|
|
10633
|
+
/**
|
|
10634
|
+
* Default methods for a wrapped movable.
|
|
10635
|
+
*/
|
|
10636
10636
|
|
|
10637
10637
|
/*
|
|
10638
10638
|
* attr() and animate() simply pass the
|
|
@@ -10890,7 +10890,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
10890
10890
|
*/
|
|
10891
10891
|
const {
|
|
10892
10892
|
eq: eq$3,
|
|
10893
|
-
deepEq: deepEq$
|
|
10893
|
+
deepEq: deepEq$6
|
|
10894
10894
|
} = Util;
|
|
10895
10895
|
|
|
10896
10896
|
// This should really be a readonly tuple of [number, number]
|
|
@@ -11050,7 +11050,7 @@ function similar(coords1, coords2, tolerance) {
|
|
|
11050
11050
|
angles = rotate(angles, i);
|
|
11051
11051
|
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
11052
11052
|
sides = rotate(sides, i);
|
|
11053
|
-
if (deepEq$
|
|
11053
|
+
if (deepEq$6(angles1, angles)) {
|
|
11054
11054
|
const sidePairs = ___default["default"].zip(sides1, sides);
|
|
11055
11055
|
const factors = ___default["default"].map(sidePairs, function (pair) {
|
|
11056
11056
|
return pair[0] / pair[1];
|
|
@@ -13989,7 +13989,7 @@ ___default["default"].extend(MovableAngle.prototype, {
|
|
|
13989
13989
|
|
|
13990
13990
|
/* eslint-disable @babel/no-invalid-this */
|
|
13991
13991
|
const nestedMap$1 = Util.nestedMap;
|
|
13992
|
-
const deepEq$
|
|
13992
|
+
const deepEq$5 = Util.deepEq;
|
|
13993
13993
|
|
|
13994
13994
|
/**
|
|
13995
13995
|
* A base class for all Graphie Movables
|
|
@@ -14086,7 +14086,7 @@ const createSimpleClass = function (addFunction) {
|
|
|
14086
14086
|
this._prevProps = this.props;
|
|
14087
14087
|
},
|
|
14088
14088
|
modify: function (graphie) {
|
|
14089
|
-
if (!deepEq$
|
|
14089
|
+
if (!deepEq$5(this.props, this._prevProps)) {
|
|
14090
14090
|
this.remove();
|
|
14091
14091
|
this.add(graphie);
|
|
14092
14092
|
this._prevProps = this.props;
|
|
@@ -16744,7 +16744,7 @@ var Movables = {
|
|
|
16744
16744
|
const GraphieMovable = GraphieClasses.GraphieMovable;
|
|
16745
16745
|
const createGraphie = GraphUtils.createGraphie;
|
|
16746
16746
|
const {
|
|
16747
|
-
deepEq: deepEq$
|
|
16747
|
+
deepEq: deepEq$4,
|
|
16748
16748
|
nestedMap
|
|
16749
16749
|
} = Util;
|
|
16750
16750
|
const {
|
|
@@ -16782,7 +16782,7 @@ class Graphie extends React__namespace.Component {
|
|
|
16782
16782
|
if (this.props.setup !== prevProps.setup) {
|
|
16783
16783
|
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
16784
|
}
|
|
16785
|
-
if (!deepEq$
|
|
16785
|
+
if (!deepEq$4(this.props.options, prevProps.options) || !deepEq$4(this.props.box, prevProps.box) || !deepEq$4(this.props.range, prevProps.range)) {
|
|
16786
16786
|
this._setupGraphie();
|
|
16787
16787
|
}
|
|
16788
16788
|
this._updateMovables();
|
|
@@ -16830,7 +16830,7 @@ class Graphie extends React__namespace.Component {
|
|
|
16830
16830
|
};
|
|
16831
16831
|
_setupGraphie = () => {
|
|
16832
16832
|
this._removeMovables();
|
|
16833
|
-
const graphieDiv =
|
|
16833
|
+
const graphieDiv = this.graphieDivRef.current;
|
|
16834
16834
|
if (graphieDiv == null || graphieDiv instanceof Text) {
|
|
16835
16835
|
throw new Error("No graphie container div found");
|
|
16836
16836
|
}
|
|
@@ -18557,7 +18557,6 @@ const styles$C = aphrodite.StyleSheet.create({
|
|
|
18557
18557
|
}
|
|
18558
18558
|
});
|
|
18559
18559
|
|
|
18560
|
-
/* eslint-disable no-useless-escape, no-prototype-builtins */
|
|
18561
18560
|
const rules$1 = {
|
|
18562
18561
|
...pureMarkdown.pureMarkdownRules,
|
|
18563
18562
|
// NOTE: basically ignored by JIPT. wraps everything at the outer layer
|
|
@@ -18734,7 +18733,7 @@ const rules$1 = {
|
|
|
18734
18733
|
// The href prop should always be here, but just in case somehow the article's
|
|
18735
18734
|
// link was not put together properly, let's make sure it's there so we
|
|
18736
18735
|
// don't break the entire page.
|
|
18737
|
-
const isKAUrl = href ? href.match(/https?:\/\/[
|
|
18736
|
+
const isKAUrl = href ? href.match(/https?:\/\/[^/]*khanacademy.org|^\//) : false;
|
|
18738
18737
|
if (!isKAUrl) {
|
|
18739
18738
|
// Prevents "reverse tabnabbing" phishing attacks
|
|
18740
18739
|
// @ts-expect-error - TS2322 - Type '"noopener noreferrer"' is not assignable to type 'null'.
|
|
@@ -21144,6 +21143,7 @@ function uniqueIdForInput() {
|
|
|
21144
21143
|
return `${prefix}${lastId}`;
|
|
21145
21144
|
}
|
|
21146
21145
|
class TextInput extends React__namespace.Component {
|
|
21146
|
+
inputRef = /*#__PURE__*/React__namespace.createRef();
|
|
21147
21147
|
static defaultProps = {
|
|
21148
21148
|
value: "",
|
|
21149
21149
|
disabled: false
|
|
@@ -21157,33 +21157,32 @@ class TextInput extends React__namespace.Component {
|
|
|
21157
21157
|
this.id = uniqueIdForInput();
|
|
21158
21158
|
}
|
|
21159
21159
|
}
|
|
21160
|
+
_getInput = () => {
|
|
21161
|
+
if (!this.inputRef.current) {
|
|
21162
|
+
throw new perseusCore.PerseusError("Input ref accessed before set", perseusCore.Errors.Internal);
|
|
21163
|
+
}
|
|
21164
|
+
return this.inputRef.current;
|
|
21165
|
+
};
|
|
21160
21166
|
focus = () => {
|
|
21161
|
-
|
|
21162
|
-
ReactDOM__default["default"].findDOMNode(this).focus();
|
|
21167
|
+
this._getInput().focus();
|
|
21163
21168
|
};
|
|
21164
21169
|
blur = () => {
|
|
21165
|
-
|
|
21166
|
-
ReactDOM__default["default"].findDOMNode(this).blur();
|
|
21170
|
+
this._getInput().blur();
|
|
21167
21171
|
};
|
|
21168
21172
|
getValue = () => {
|
|
21169
|
-
|
|
21170
|
-
return ReactDOM__default["default"].findDOMNode(this)?.value;
|
|
21173
|
+
return this.inputRef.current?.value;
|
|
21171
21174
|
};
|
|
21172
21175
|
getStringValue = () => {
|
|
21173
|
-
|
|
21174
|
-
return ReactDOM__default["default"].findDOMNode(this)?.value.toString();
|
|
21176
|
+
return this.inputRef.current?.value.toString();
|
|
21175
21177
|
};
|
|
21176
21178
|
setSelectionRange = (selectionStart, selectionEnd) => {
|
|
21177
|
-
|
|
21178
|
-
ReactDOM__default["default"].findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
|
|
21179
|
+
this._getInput().setSelectionRange(selectionStart, selectionEnd);
|
|
21179
21180
|
};
|
|
21180
21181
|
getSelectionStart = () => {
|
|
21181
|
-
|
|
21182
|
-
return ReactDOM__default["default"].findDOMNode(this).selectionStart;
|
|
21182
|
+
return this._getInput().selectionStart;
|
|
21183
21183
|
};
|
|
21184
21184
|
getSelectionEnd = () => {
|
|
21185
|
-
|
|
21186
|
-
return ReactDOM__default["default"].findDOMNode(this).selectionEnd;
|
|
21185
|
+
return this._getInput().selectionEnd;
|
|
21187
21186
|
};
|
|
21188
21187
|
render() {
|
|
21189
21188
|
const {
|
|
@@ -21198,6 +21197,7 @@ class TextInput extends React__namespace.Component {
|
|
|
21198
21197
|
} = this.props;
|
|
21199
21198
|
const formattedValue = value === null ? "" : value.toString();
|
|
21200
21199
|
return /*#__PURE__*/React__namespace.createElement(wonderBlocksForm.TextField, {
|
|
21200
|
+
ref: this.inputRef,
|
|
21201
21201
|
style: style,
|
|
21202
21202
|
disabled: disabled,
|
|
21203
21203
|
id: this.id,
|
|
@@ -21377,6 +21377,7 @@ const HORIZONTAL_ALIGNMNENTS = {
|
|
|
21377
21377
|
* ```
|
|
21378
21378
|
*/
|
|
21379
21379
|
class Tooltip extends React__namespace.Component {
|
|
21380
|
+
tooltipContainerRef = /*#__PURE__*/React__namespace.createRef();
|
|
21380
21381
|
static defaultProps = {
|
|
21381
21382
|
className: "",
|
|
21382
21383
|
arrowSize: 10,
|
|
@@ -21459,8 +21460,7 @@ class Tooltip extends React__namespace.Component {
|
|
|
21459
21460
|
display: this.props.show ? "block" : "none"
|
|
21460
21461
|
}
|
|
21461
21462
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
21462
|
-
|
|
21463
|
-
ref: "tooltipContainer",
|
|
21463
|
+
ref: this.tooltipContainerRef,
|
|
21464
21464
|
className: "tooltipContainer",
|
|
21465
21465
|
style: {
|
|
21466
21466
|
position: "absolute",
|
|
@@ -21487,10 +21487,8 @@ class Tooltip extends React__namespace.Component {
|
|
|
21487
21487
|
}, this.props.children.slice(1)), arrowBelow));
|
|
21488
21488
|
}
|
|
21489
21489
|
_updateHeight() {
|
|
21490
|
-
const tooltipContainer =
|
|
21491
|
-
|
|
21492
|
-
this.refs.tooltipContainer);
|
|
21493
|
-
const height = tooltipContainer.offsetHeight;
|
|
21490
|
+
const tooltipContainer = this.tooltipContainerRef.current;
|
|
21491
|
+
const height = tooltipContainer?.offsetHeight || 0;
|
|
21494
21492
|
if (height !== this.state.height) {
|
|
21495
21493
|
this.setState({
|
|
21496
21494
|
height
|
|
@@ -22219,7 +22217,7 @@ function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
|
|
|
22219
22217
|
return value / 100;
|
|
22220
22218
|
}
|
|
22221
22219
|
|
|
22222
|
-
// Otherwise, we return input
|
|
22220
|
+
// Otherwise, we return input value (number) stripped of the "%".
|
|
22223
22221
|
return value;
|
|
22224
22222
|
}
|
|
22225
22223
|
function scoreNumericInput(userInput, rubric, strings) {
|
|
@@ -26277,7 +26275,15 @@ const styles$p = aphrodite.StyleSheet.create({
|
|
|
26277
26275
|
}
|
|
26278
26276
|
});
|
|
26279
26277
|
|
|
26280
|
-
|
|
26278
|
+
/**
|
|
26279
|
+
* Checks if the user has selected at least one option. Additional validation
|
|
26280
|
+
* is done in scoreRadio to check if the number of selected options is correct
|
|
26281
|
+
* and if the user has selected both a correct option and the "none of the above"
|
|
26282
|
+
* option.
|
|
26283
|
+
* @param userInput
|
|
26284
|
+
* @see `scoreRadio` for the additional validation logic and the scoring logic.
|
|
26285
|
+
*/
|
|
26286
|
+
function validateRadio(userInput) {
|
|
26281
26287
|
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
26282
26288
|
return sum + (selected ? 1 : 0);
|
|
26283
26289
|
}, 0);
|
|
@@ -26287,6 +26293,17 @@ function scoreRadio(userInput, rubric, strings) {
|
|
|
26287
26293
|
message: null
|
|
26288
26294
|
};
|
|
26289
26295
|
}
|
|
26296
|
+
return null;
|
|
26297
|
+
}
|
|
26298
|
+
|
|
26299
|
+
function scoreRadio(userInput, rubric, strings) {
|
|
26300
|
+
const validationError = validateRadio(userInput);
|
|
26301
|
+
if (validationError) {
|
|
26302
|
+
return validationError;
|
|
26303
|
+
}
|
|
26304
|
+
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
26305
|
+
return sum + (selected ? 1 : 0);
|
|
26306
|
+
}, 0);
|
|
26290
26307
|
const numCorrect = rubric.choices.reduce((sum, currentChoice) => {
|
|
26291
26308
|
return currentChoice.correct ? sum + 1 : sum;
|
|
26292
26309
|
}, 0);
|
|
@@ -26766,18 +26783,17 @@ const transform = (widgetOptions, strings, problemNum) => {
|
|
|
26766
26783
|
};
|
|
26767
26784
|
const propUpgrades$1 = {
|
|
26768
26785
|
"1": v0props => {
|
|
26769
|
-
|
|
26770
|
-
|
|
26771
|
-
|
|
26772
|
-
|
|
26773
|
-
|
|
26774
|
-
} else {
|
|
26786
|
+
const {
|
|
26787
|
+
noneOfTheAbove,
|
|
26788
|
+
...rest
|
|
26789
|
+
} = v0props;
|
|
26790
|
+
if (noneOfTheAbove) {
|
|
26775
26791
|
throw new Error("radio widget v0 no longer supports auto noneOfTheAbove");
|
|
26776
26792
|
}
|
|
26777
|
-
return
|
|
26778
|
-
|
|
26779
|
-
hasNoneOfTheAbove:
|
|
26780
|
-
}
|
|
26793
|
+
return {
|
|
26794
|
+
...rest,
|
|
26795
|
+
hasNoneOfTheAbove: false
|
|
26796
|
+
};
|
|
26781
26797
|
}
|
|
26782
26798
|
};
|
|
26783
26799
|
var Radio = {
|
|
@@ -26814,23 +26830,36 @@ const getPromptJSON$n = (renderProps, userInput) => {
|
|
|
26814
26830
|
};
|
|
26815
26831
|
};
|
|
26816
26832
|
|
|
26817
|
-
|
|
26818
|
-
|
|
26819
|
-
|
|
26820
|
-
|
|
26821
|
-
|
|
26822
|
-
|
|
26823
|
-
|
|
26824
|
-
|
|
26825
|
-
|
|
26826
|
-
|
|
26827
|
-
|
|
26828
|
-
if (!completed) {
|
|
26833
|
+
/**
|
|
26834
|
+
* Checks userInput from the categorizer widget to see if the user has selected
|
|
26835
|
+
* a category for each item.
|
|
26836
|
+
* @param userInput - The user's input corresponding to an array of indices that
|
|
26837
|
+
* represent the selected category for each row/item.
|
|
26838
|
+
* @param validationData - An array of strings corresponding to each row/item
|
|
26839
|
+
* @param strings - Used to provide a validation message
|
|
26840
|
+
*/
|
|
26841
|
+
function validateCategorizer(userInput, validationData, strings) {
|
|
26842
|
+
const incomplete = validationData.items.some((_, i) => userInput.values[i] == null);
|
|
26843
|
+
if (incomplete) {
|
|
26829
26844
|
return {
|
|
26830
26845
|
type: "invalid",
|
|
26831
26846
|
message: strings.invalidSelection
|
|
26832
26847
|
};
|
|
26833
26848
|
}
|
|
26849
|
+
return null;
|
|
26850
|
+
}
|
|
26851
|
+
|
|
26852
|
+
function scoreCategorizer(userInput, scoringData, strings) {
|
|
26853
|
+
const validationError = validateCategorizer(userInput, scoringData, strings);
|
|
26854
|
+
if (validationError) {
|
|
26855
|
+
return validationError;
|
|
26856
|
+
}
|
|
26857
|
+
let allCorrect = true;
|
|
26858
|
+
scoringData.values.forEach((value, i) => {
|
|
26859
|
+
if (userInput.values[i] !== value) {
|
|
26860
|
+
allCorrect = false;
|
|
26861
|
+
}
|
|
26862
|
+
});
|
|
26834
26863
|
return {
|
|
26835
26864
|
type: "points",
|
|
26836
26865
|
earned: allCorrect ? 1 : 0,
|
|
@@ -27384,15 +27413,26 @@ const getPromptJSON$k = (renderProps, userInput) => {
|
|
|
27384
27413
|
};
|
|
27385
27414
|
};
|
|
27386
27415
|
|
|
27387
|
-
|
|
27388
|
-
|
|
27389
|
-
|
|
27416
|
+
/**
|
|
27417
|
+
* Checks if the user has selected an item from the dropdown before scoring.
|
|
27418
|
+
* This is shown with a userInput value / index other than 0.
|
|
27419
|
+
*/
|
|
27420
|
+
function validateDropdown(userInput) {
|
|
27421
|
+
if (userInput.value === 0) {
|
|
27390
27422
|
return {
|
|
27391
27423
|
type: "invalid",
|
|
27392
27424
|
message: null
|
|
27393
27425
|
};
|
|
27394
27426
|
}
|
|
27395
|
-
|
|
27427
|
+
return null;
|
|
27428
|
+
}
|
|
27429
|
+
|
|
27430
|
+
function scoreDropdown(userInput, rubric) {
|
|
27431
|
+
const validationError = validateDropdown(userInput);
|
|
27432
|
+
if (validationError) {
|
|
27433
|
+
return validationError;
|
|
27434
|
+
}
|
|
27435
|
+
const correct = rubric.choices[userInput.value - 1].correct;
|
|
27396
27436
|
return {
|
|
27397
27437
|
type: "points",
|
|
27398
27438
|
earned: correct ? 1 : 0,
|
|
@@ -27402,6 +27442,7 @@ function scoreDropdown(userInput, rubric) {
|
|
|
27402
27442
|
}
|
|
27403
27443
|
|
|
27404
27444
|
class Dropdown extends React__namespace.Component {
|
|
27445
|
+
static contextType = PerseusI18nContext;
|
|
27405
27446
|
static defaultProps = {
|
|
27406
27447
|
choices: [],
|
|
27407
27448
|
selected: 0,
|
|
@@ -27444,26 +27485,44 @@ class Dropdown extends React__namespace.Component {
|
|
|
27444
27485
|
value: String(i + 1),
|
|
27445
27486
|
label: choice
|
|
27446
27487
|
}))];
|
|
27447
|
-
return /*#__PURE__*/React__namespace.createElement(
|
|
27448
|
-
|
|
27449
|
-
|
|
27488
|
+
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.UniqueIDProvider, {
|
|
27489
|
+
scope: "dropdown-widget",
|
|
27490
|
+
mockOnFirstRender: true
|
|
27491
|
+
}, ids => /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View
|
|
27492
|
+
// NOTE(jared): These are required to prevent weird behavior
|
|
27493
|
+
// When there's a dropdown in a zoomable table.
|
|
27494
|
+
, {
|
|
27450
27495
|
onClick: e => {
|
|
27451
27496
|
e.stopPropagation();
|
|
27452
27497
|
},
|
|
27453
27498
|
onTouchStart: e => {
|
|
27454
27499
|
e.stopPropagation();
|
|
27455
27500
|
}
|
|
27456
|
-
}, /*#__PURE__*/React__namespace.createElement(
|
|
27501
|
+
}, this.props.visibleLabel && /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelLarge, {
|
|
27502
|
+
tag: "label",
|
|
27503
|
+
id: ids.get("dropdown-label")
|
|
27504
|
+
}, this.props.visibleLabel), /*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.SingleSelect, {
|
|
27505
|
+
id: ids.get("dropdown"),
|
|
27457
27506
|
placeholder: "",
|
|
27458
27507
|
onChange: value => this._handleChange(parseInt(value)),
|
|
27459
27508
|
selectedValue: String(this.props.selected),
|
|
27460
|
-
disabled: this.props.apiOptions.readOnly
|
|
27461
|
-
|
|
27509
|
+
disabled: this.props.apiOptions.readOnly,
|
|
27510
|
+
"aria-label": this.props.ariaLabel || this.context.strings.selectAnAnswer,
|
|
27511
|
+
"aria-labelledby": ids.get("dropdown-label")
|
|
27512
|
+
// This is currently necessary for SRs to read the labels properly.
|
|
27513
|
+
// However, WB is working on a change to add the "combobox" role to
|
|
27514
|
+
// all dropdowns.
|
|
27515
|
+
// See https://khanacademy.atlassian.net/browse/WB-1671
|
|
27516
|
+
,
|
|
27517
|
+
role: "combobox"
|
|
27518
|
+
}, children)));
|
|
27462
27519
|
}
|
|
27463
27520
|
}
|
|
27464
27521
|
const optionsTransform = widgetOptions => {
|
|
27465
27522
|
return {
|
|
27466
27523
|
placeholder: widgetOptions.placeholder,
|
|
27524
|
+
visibleLabel: widgetOptions.visibleLabel,
|
|
27525
|
+
ariaLabel: widgetOptions.ariaLabel,
|
|
27467
27526
|
choices: widgetOptions.choices.map(choice => choice.content)
|
|
27468
27527
|
};
|
|
27469
27528
|
};
|
|
@@ -27782,8 +27841,6 @@ const getNextState = (currentState, answerable) => {
|
|
|
27782
27841
|
}
|
|
27783
27842
|
};
|
|
27784
27843
|
|
|
27785
|
-
// exports has no 'transform'
|
|
27786
|
-
|
|
27787
27844
|
// A Graded Group is more or less a Group widget that displays a check
|
|
27788
27845
|
// answer button below the rendered content. When clicked, the widget grades
|
|
27789
27846
|
// the stuff inside and displays feedback about whether the inputted answer was
|
|
@@ -28453,7 +28510,6 @@ const getPromptJSON$g = (renderProps, userInput) => {
|
|
|
28453
28510
|
};
|
|
28454
28511
|
};
|
|
28455
28512
|
|
|
28456
|
-
/* eslint-disable one-var */
|
|
28457
28513
|
// @ts-expect-error - TS2339 - Property 'Plot' does not exist on type 'typeof Graphie'.
|
|
28458
28514
|
const Plot$1 = Graphie.Plot;
|
|
28459
28515
|
const DEFAULT_BACKGROUND_IMAGE = {
|
|
@@ -28554,14 +28610,14 @@ const Linear = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28554
28610
|
return [m, b];
|
|
28555
28611
|
},
|
|
28556
28612
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28557
|
-
const m = coeffs[0]
|
|
28558
|
-
|
|
28613
|
+
const m = coeffs[0];
|
|
28614
|
+
const b = coeffs[1];
|
|
28559
28615
|
return m * x + b;
|
|
28560
28616
|
},
|
|
28561
28617
|
getEquationString: function (coords) {
|
|
28562
28618
|
const coeffs = this.getCoefficients(coords);
|
|
28563
|
-
const m = coeffs[0]
|
|
28564
|
-
|
|
28619
|
+
const m = coeffs[0];
|
|
28620
|
+
const b = coeffs[1];
|
|
28565
28621
|
return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
|
|
28566
28622
|
}
|
|
28567
28623
|
});
|
|
@@ -28585,9 +28641,9 @@ const Quadratic = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28585
28641
|
return [a, b, c];
|
|
28586
28642
|
},
|
|
28587
28643
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28588
|
-
const a = coeffs[0]
|
|
28589
|
-
|
|
28590
|
-
|
|
28644
|
+
const a = coeffs[0];
|
|
28645
|
+
const b = coeffs[1];
|
|
28646
|
+
const c = coeffs[2];
|
|
28591
28647
|
return (a * x + b) * x + c;
|
|
28592
28648
|
},
|
|
28593
28649
|
getPropsForCoeffs: function (coeffs) {
|
|
@@ -28599,9 +28655,9 @@ const Quadratic = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28599
28655
|
},
|
|
28600
28656
|
getEquationString: function (coords) {
|
|
28601
28657
|
const coeffs = this.getCoefficients(coords);
|
|
28602
|
-
const a = coeffs[0]
|
|
28603
|
-
|
|
28604
|
-
|
|
28658
|
+
const a = coeffs[0];
|
|
28659
|
+
const b = coeffs[1];
|
|
28660
|
+
const c = coeffs[2];
|
|
28605
28661
|
return "y = " + a.toFixed(3) + "x^2 + " + b.toFixed(3) + "x + " + c.toFixed(3);
|
|
28606
28662
|
}
|
|
28607
28663
|
});
|
|
@@ -28620,10 +28676,10 @@ const Sinusoid = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28620
28676
|
return [a, b, c, d];
|
|
28621
28677
|
},
|
|
28622
28678
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28623
|
-
const a = coeffs[0]
|
|
28624
|
-
|
|
28625
|
-
|
|
28626
|
-
|
|
28679
|
+
const a = coeffs[0];
|
|
28680
|
+
const b = coeffs[1];
|
|
28681
|
+
const c = coeffs[2];
|
|
28682
|
+
const d = coeffs[3];
|
|
28627
28683
|
return a * Math.sin(b * x - c) + d;
|
|
28628
28684
|
},
|
|
28629
28685
|
getPropsForCoeffs: function (coeffs) {
|
|
@@ -28636,10 +28692,10 @@ const Sinusoid = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28636
28692
|
},
|
|
28637
28693
|
getEquationString: function (coords) {
|
|
28638
28694
|
const coeffs = this.getCoefficients(coords);
|
|
28639
|
-
const a = coeffs[0]
|
|
28640
|
-
|
|
28641
|
-
|
|
28642
|
-
|
|
28695
|
+
const a = coeffs[0];
|
|
28696
|
+
const b = coeffs[1];
|
|
28697
|
+
const c = coeffs[2];
|
|
28698
|
+
const d = coeffs[3];
|
|
28643
28699
|
return "y = " + a.toFixed(3) + " sin(" + b.toFixed(3) + "x - " + c.toFixed(3) + ") + " + d.toFixed(3);
|
|
28644
28700
|
},
|
|
28645
28701
|
areEqual: function (coeffs1, coeffs2) {
|
|
@@ -28659,18 +28715,18 @@ const Tangent = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28659
28715
|
return [a, b, c, d];
|
|
28660
28716
|
},
|
|
28661
28717
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28662
|
-
const a = coeffs[0]
|
|
28663
|
-
|
|
28664
|
-
|
|
28665
|
-
|
|
28718
|
+
const a = coeffs[0];
|
|
28719
|
+
const b = coeffs[1];
|
|
28720
|
+
const c = coeffs[2];
|
|
28721
|
+
const d = coeffs[3];
|
|
28666
28722
|
return a * Math.tan(b * x - c) + d;
|
|
28667
28723
|
},
|
|
28668
28724
|
getEquationString: function (coords) {
|
|
28669
28725
|
const coeffs = this.getCoefficients(coords);
|
|
28670
|
-
const a = coeffs[0]
|
|
28671
|
-
|
|
28672
|
-
|
|
28673
|
-
|
|
28726
|
+
const a = coeffs[0];
|
|
28727
|
+
const b = coeffs[1];
|
|
28728
|
+
const c = coeffs[2];
|
|
28729
|
+
const d = coeffs[3];
|
|
28674
28730
|
return "y = " + a.toFixed(3) + " sin(" + b.toFixed(3) + "x - " + c.toFixed(3) + ") + " + d.toFixed(3);
|
|
28675
28731
|
},
|
|
28676
28732
|
areEqual: function (coeffs1, coeffs2) {
|
|
@@ -28729,9 +28785,9 @@ const Exponential = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28729
28785
|
return [a, b, c];
|
|
28730
28786
|
},
|
|
28731
28787
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28732
|
-
const a = coeffs[0]
|
|
28733
|
-
|
|
28734
|
-
|
|
28788
|
+
const a = coeffs[0];
|
|
28789
|
+
const b = coeffs[1];
|
|
28790
|
+
const c = coeffs[2];
|
|
28735
28791
|
return a * Math.exp(b * x) + c;
|
|
28736
28792
|
},
|
|
28737
28793
|
getEquationString: function (coords, asymptote) {
|
|
@@ -28739,9 +28795,9 @@ const Exponential = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28739
28795
|
return null;
|
|
28740
28796
|
}
|
|
28741
28797
|
const coeffs = this.getCoefficients(coords, asymptote);
|
|
28742
|
-
const a = coeffs[0]
|
|
28743
|
-
|
|
28744
|
-
|
|
28798
|
+
const a = coeffs[0];
|
|
28799
|
+
const b = coeffs[1];
|
|
28800
|
+
const c = coeffs[2];
|
|
28745
28801
|
return "y = " + a.toFixed(3) + "e^(" + b.toFixed(3) + "x) + " + c.toFixed(3);
|
|
28746
28802
|
}
|
|
28747
28803
|
});
|
|
@@ -28786,9 +28842,9 @@ const Logarithm = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28786
28842
|
}
|
|
28787
28843
|
},
|
|
28788
28844
|
getFunctionForCoeffs: function (coeffs, x, asymptote) {
|
|
28789
|
-
const a = coeffs[0]
|
|
28790
|
-
|
|
28791
|
-
|
|
28845
|
+
const a = coeffs[0];
|
|
28846
|
+
const b = coeffs[1];
|
|
28847
|
+
const c = coeffs[2];
|
|
28792
28848
|
return a * Math.log(b * x + c);
|
|
28793
28849
|
},
|
|
28794
28850
|
getEquationString: function (coords, asymptote) {
|
|
@@ -28796,9 +28852,9 @@ const Logarithm = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28796
28852
|
return null;
|
|
28797
28853
|
}
|
|
28798
28854
|
const coeffs = this.getCoefficients(coords, asymptote);
|
|
28799
|
-
const a = coeffs[0]
|
|
28800
|
-
|
|
28801
|
-
|
|
28855
|
+
const a = coeffs[0];
|
|
28856
|
+
const b = coeffs[1];
|
|
28857
|
+
const c = coeffs[2];
|
|
28802
28858
|
return "y = ln(" + a.toFixed(3) + "x + " + b.toFixed(3) + ") + " + c.toFixed(3);
|
|
28803
28859
|
}
|
|
28804
28860
|
});
|
|
@@ -28822,16 +28878,16 @@ const AbsoluteValue = ___default["default"].extend({}, PlotDefaults, {
|
|
|
28822
28878
|
return [m, horizontalOffset, verticalOffset];
|
|
28823
28879
|
},
|
|
28824
28880
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28825
|
-
const m = coeffs[0]
|
|
28826
|
-
|
|
28827
|
-
|
|
28881
|
+
const m = coeffs[0];
|
|
28882
|
+
const horizontalOffset = coeffs[1];
|
|
28883
|
+
const verticalOffset = coeffs[2];
|
|
28828
28884
|
return m * Math.abs(x - horizontalOffset) + verticalOffset;
|
|
28829
28885
|
},
|
|
28830
28886
|
getEquationString: function (coords) {
|
|
28831
28887
|
const coeffs = this.getCoefficients(coords);
|
|
28832
|
-
const m = coeffs[0]
|
|
28833
|
-
|
|
28834
|
-
|
|
28888
|
+
const m = coeffs[0];
|
|
28889
|
+
const horizontalOffset = coeffs[1];
|
|
28890
|
+
const verticalOffset = coeffs[2];
|
|
28835
28891
|
return "y = " + m.toFixed(3) + "| x - " + horizontalOffset.toFixed(3) + "| + " + verticalOffset.toFixed(3);
|
|
28836
28892
|
}
|
|
28837
28893
|
});
|
|
@@ -30592,6 +30648,7 @@ function numSteps$1(range, step) {
|
|
|
30592
30648
|
return Math.floor((range[1] - range[0]) / step);
|
|
30593
30649
|
}
|
|
30594
30650
|
class Graph extends React__namespace.Component {
|
|
30651
|
+
graphieDivRef = /*#__PURE__*/React__namespace.createRef();
|
|
30595
30652
|
protractor;
|
|
30596
30653
|
ruler;
|
|
30597
30654
|
_graphie;
|
|
@@ -30671,9 +30728,7 @@ class Graph extends React__namespace.Component {
|
|
|
30671
30728
|
if (this._hasSetupGraphieThisUpdate) {
|
|
30672
30729
|
return;
|
|
30673
30730
|
}
|
|
30674
|
-
|
|
30675
|
-
// eslint-disable-next-line react/no-string-refs
|
|
30676
|
-
const graphieDiv = ReactDOM__default["default"].findDOMNode(this.refs.graphieDiv);
|
|
30731
|
+
const graphieDiv = this.graphieDivRef.current;
|
|
30677
30732
|
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'empty' does not exist on type 'JQueryStatic'.
|
|
30678
30733
|
$__default["default"](graphieDiv).empty();
|
|
30679
30734
|
|
|
@@ -30862,7 +30917,7 @@ class Graph extends React__namespace.Component {
|
|
|
30862
30917
|
onClick: this.onClick
|
|
30863
30918
|
}, image, /*#__PURE__*/React__namespace.createElement("div", {
|
|
30864
30919
|
className: "graphie",
|
|
30865
|
-
ref:
|
|
30920
|
+
ref: this.graphieDivRef
|
|
30866
30921
|
}));
|
|
30867
30922
|
}
|
|
30868
30923
|
}
|
|
@@ -31948,13 +32003,13 @@ class InfoTip extends React__namespace.Component {
|
|
|
31948
32003
|
* this component allows multiple selection!
|
|
31949
32004
|
*/
|
|
31950
32005
|
class MultiButtonGroup extends React__namespace.Component {
|
|
32006
|
+
buttonContainerRef = /*#__PURE__*/React__namespace.createRef();
|
|
31951
32007
|
static defaultProps = {
|
|
31952
32008
|
values: [],
|
|
31953
32009
|
allowEmpty: true
|
|
31954
32010
|
};
|
|
31955
32011
|
focus() {
|
|
31956
|
-
|
|
31957
|
-
ReactDOM__namespace.findDOMNode(this)?.focus();
|
|
32012
|
+
this.buttonContainerRef.current?.focus();
|
|
31958
32013
|
return true;
|
|
31959
32014
|
}
|
|
31960
32015
|
toggleSelect = newValue => {
|
|
@@ -31989,7 +32044,8 @@ class MultiButtonGroup extends React__namespace.Component {
|
|
|
31989
32044
|
display: "inline-block"
|
|
31990
32045
|
};
|
|
31991
32046
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
31992
|
-
style: outerStyle
|
|
32047
|
+
style: outerStyle,
|
|
32048
|
+
ref: this.buttonContainerRef
|
|
31993
32049
|
}, buttons);
|
|
31994
32050
|
}
|
|
31995
32051
|
}
|
|
@@ -32054,6 +32110,7 @@ const getNumericFormat = KhanMath.getNumericFormat;
|
|
|
32054
32110
|
*/
|
|
32055
32111
|
class NumberInput extends React__namespace.Component {
|
|
32056
32112
|
static contextType = PerseusI18nContext;
|
|
32113
|
+
inputRef = /*#__PURE__*/React__namespace.createRef();
|
|
32057
32114
|
static propTypes = {
|
|
32058
32115
|
value: PropTypes__default["default"].number,
|
|
32059
32116
|
format: PropTypes__default["default"].string,
|
|
@@ -32080,23 +32137,24 @@ class NumberInput extends React__namespace.Component {
|
|
|
32080
32137
|
this._setValue(this.props.value, this.state.format);
|
|
32081
32138
|
}
|
|
32082
32139
|
}
|
|
32140
|
+
_getInput = () => {
|
|
32141
|
+
if (!this.inputRef.current) {
|
|
32142
|
+
throw new perseusCore.PerseusError("Input ref accessed before set", perseusCore.Errors.Internal);
|
|
32143
|
+
}
|
|
32144
|
+
return this.inputRef.current;
|
|
32145
|
+
};
|
|
32083
32146
|
|
|
32084
32147
|
/* Return the current "value" of this input
|
|
32085
32148
|
* If empty, it returns the placeholder (if it is a number) or null
|
|
32086
32149
|
*/
|
|
32087
32150
|
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
|
-
);
|
|
32151
|
+
return this.parseInputValue(this._getInput().value);
|
|
32092
32152
|
};
|
|
32093
32153
|
|
|
32094
32154
|
/* Return the current string value of this input */
|
|
32095
32155
|
getStringValue = () => {
|
|
32096
|
-
|
|
32097
|
-
return ReactDOM__default["default"].findDOMNode(this.refs.input).value.toString(); // eslint-disable-line react/no-string-refs
|
|
32156
|
+
return this._getInput().toString();
|
|
32098
32157
|
};
|
|
32099
|
-
|
|
32100
32158
|
parseInputValue = value => {
|
|
32101
32159
|
if (value === "") {
|
|
32102
32160
|
const placeholder = this.props.placeholder;
|
|
@@ -32108,26 +32166,21 @@ class NumberInput extends React__namespace.Component {
|
|
|
32108
32166
|
|
|
32109
32167
|
/* Set text input focus to this input */
|
|
32110
32168
|
focus = () => {
|
|
32111
|
-
|
|
32112
|
-
ReactDOM__default["default"].findDOMNode(this.refs.input).focus(); // eslint-disable-line react/no-string-refs
|
|
32169
|
+
this._getInput().focus();
|
|
32113
32170
|
this._handleFocus();
|
|
32114
32171
|
};
|
|
32115
32172
|
blur = () => {
|
|
32116
|
-
|
|
32117
|
-
ReactDOM__default["default"].findDOMNode(this.refs.input).blur(); // eslint-disable-line react/no-string-refs
|
|
32173
|
+
this._getInput().blur();
|
|
32118
32174
|
this._handleBlur();
|
|
32119
32175
|
};
|
|
32120
32176
|
setSelectionRange = (selectionStart, selectionEnd) => {
|
|
32121
|
-
|
|
32122
|
-
ReactDOM__default["default"].findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
|
|
32177
|
+
this._getInput().setSelectionRange(selectionStart, selectionEnd);
|
|
32123
32178
|
};
|
|
32124
32179
|
getSelectionStart = () => {
|
|
32125
|
-
|
|
32126
|
-
return ReactDOM__default["default"].findDOMNode(this).selectionStart;
|
|
32180
|
+
return this._getInput().selectionStart;
|
|
32127
32181
|
};
|
|
32128
32182
|
getSelectionEnd = () => {
|
|
32129
|
-
|
|
32130
|
-
return ReactDOM__default["default"].findDOMNode(this).selectionEnd;
|
|
32183
|
+
return this._getInput().selectionEnd;
|
|
32131
32184
|
};
|
|
32132
32185
|
_checkValidity = value => {
|
|
32133
32186
|
if (value == null) {
|
|
@@ -32187,9 +32240,7 @@ class NumberInput extends React__namespace.Component {
|
|
|
32187
32240
|
}
|
|
32188
32241
|
};
|
|
32189
32242
|
_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));
|
|
32243
|
+
this._getInput().value = toNumericString(val, format);
|
|
32193
32244
|
};
|
|
32194
32245
|
render() {
|
|
32195
32246
|
let classes = classNames__default["default"]({
|
|
@@ -32213,10 +32264,8 @@ class NumberInput extends React__namespace.Component {
|
|
|
32213
32264
|
} = this.props;
|
|
32214
32265
|
return /*#__PURE__*/React__namespace.createElement("input", _extends({}, restProps, {
|
|
32215
32266
|
className: classes,
|
|
32216
|
-
type: "text"
|
|
32217
|
-
|
|
32218
|
-
,
|
|
32219
|
-
ref: "input",
|
|
32267
|
+
type: "text",
|
|
32268
|
+
ref: this.inputRef,
|
|
32220
32269
|
onChange: this._handleChange,
|
|
32221
32270
|
onFocus: this._handleFocus,
|
|
32222
32271
|
onBlur: this._handleBlur,
|
|
@@ -33711,15 +33760,18 @@ function useControlPoint(params) {
|
|
|
33711
33760
|
} = useGraphConfig();
|
|
33712
33761
|
const {
|
|
33713
33762
|
point,
|
|
33714
|
-
|
|
33763
|
+
ariaDescribedBy,
|
|
33764
|
+
ariaLabel,
|
|
33765
|
+
ariaLive = "polite",
|
|
33715
33766
|
color,
|
|
33716
|
-
cursor,
|
|
33717
33767
|
constrain = p => snap(snapStep, p),
|
|
33768
|
+
cursor,
|
|
33769
|
+
forwardedRef = noop$1,
|
|
33770
|
+
sequenceNumber = 1,
|
|
33718
33771
|
onMove = noop$1,
|
|
33719
33772
|
onClick = noop$1,
|
|
33720
33773
|
onFocus = noop$1,
|
|
33721
|
-
onBlur = noop$1
|
|
33722
|
-
forwardedRef = noop$1
|
|
33774
|
+
onBlur = noop$1
|
|
33723
33775
|
} = params;
|
|
33724
33776
|
const {
|
|
33725
33777
|
strings,
|
|
@@ -33742,6 +33794,13 @@ function useControlPoint(params) {
|
|
|
33742
33794
|
onMove,
|
|
33743
33795
|
constrainKeyboardMovement: constrain
|
|
33744
33796
|
});
|
|
33797
|
+
|
|
33798
|
+
// if custom aria label is not provided, will use default of sequence number and point coordinates
|
|
33799
|
+
const pointAriaLabel = ariaLabel || strings.srPointAtCoordinates({
|
|
33800
|
+
num: sequenceNumber,
|
|
33801
|
+
x: srFormatNumber(point[X], locale),
|
|
33802
|
+
y: srFormatNumber(point[Y], locale)
|
|
33803
|
+
});
|
|
33745
33804
|
React.useLayoutEffect(() => {
|
|
33746
33805
|
setForwardedRef(forwardedRef, focusableHandleRef.current);
|
|
33747
33806
|
}, [forwardedRef]);
|
|
@@ -33751,15 +33810,9 @@ function useControlPoint(params) {
|
|
|
33751
33810
|
tabIndex: disableKeyboardInteraction ? -1 : 0,
|
|
33752
33811
|
ref: focusableHandleRef,
|
|
33753
33812
|
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",
|
|
33813
|
+
"aria-describedby": ariaDescribedBy,
|
|
33814
|
+
"aria-label": pointAriaLabel,
|
|
33815
|
+
"aria-live": ariaLive,
|
|
33763
33816
|
onFocus: event => {
|
|
33764
33817
|
onFocus(event);
|
|
33765
33818
|
setFocused(true);
|
|
@@ -36835,7 +36888,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
36835
36888
|
});
|
|
36836
36889
|
|
|
36837
36890
|
const eq$1 = Util.eq;
|
|
36838
|
-
const deepEq$
|
|
36891
|
+
const deepEq$3 = Util.deepEq;
|
|
36839
36892
|
function scoreInteractiveGraph(userInput, rubric) {
|
|
36840
36893
|
// None-type graphs are not graded
|
|
36841
36894
|
if (userInput.type === "none" && rubric.correct.type === "none") {
|
|
@@ -36885,7 +36938,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36885
36938
|
// If the parabola coefficients match, it's correct.
|
|
36886
36939
|
const guessCoeffs = getQuadraticCoefficients(userInput.coords);
|
|
36887
36940
|
const correctCoeffs = getQuadraticCoefficients(rubric.correct.coords);
|
|
36888
|
-
if (deepEq$
|
|
36941
|
+
if (deepEq$3(guessCoeffs, correctCoeffs)) {
|
|
36889
36942
|
return {
|
|
36890
36943
|
type: "points",
|
|
36891
36944
|
earned: 1,
|
|
@@ -36899,7 +36952,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36899
36952
|
const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
|
|
36900
36953
|
const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
|
|
36901
36954
|
// If the canonical coefficients match, it's correct.
|
|
36902
|
-
if (deepEq$
|
|
36955
|
+
if (deepEq$3(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
|
|
36903
36956
|
return {
|
|
36904
36957
|
type: "points",
|
|
36905
36958
|
earned: 1,
|
|
@@ -36908,7 +36961,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36908
36961
|
};
|
|
36909
36962
|
}
|
|
36910
36963
|
} else if (userInput.type === "circle" && rubric.correct.type === "circle") {
|
|
36911
|
-
if (deepEq$
|
|
36964
|
+
if (deepEq$3(userInput.center, rubric.correct.center) && eq$1(userInput.radius, rubric.correct.radius)) {
|
|
36912
36965
|
return {
|
|
36913
36966
|
type: "points",
|
|
36914
36967
|
earned: 1,
|
|
@@ -36930,7 +36983,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36930
36983
|
guess?.sort();
|
|
36931
36984
|
// @ts-expect-error - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
|
|
36932
36985
|
correct.sort();
|
|
36933
|
-
if (deepEq$
|
|
36986
|
+
if (deepEq$3(guess, correct)) {
|
|
36934
36987
|
return {
|
|
36935
36988
|
type: "points",
|
|
36936
36989
|
earned: 1,
|
|
@@ -36952,7 +37005,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36952
37005
|
/* exact */
|
|
36953
37006
|
guess.sort();
|
|
36954
37007
|
correct.sort();
|
|
36955
|
-
match = deepEq$
|
|
37008
|
+
match = deepEq$3(guess, correct);
|
|
36956
37009
|
}
|
|
36957
37010
|
if (match) {
|
|
36958
37011
|
return {
|
|
@@ -36967,7 +37020,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36967
37020
|
let correct = Util.deepClone(rubric.correct.coords);
|
|
36968
37021
|
guess = ___default["default"].invoke(guess, "sort").sort();
|
|
36969
37022
|
correct = ___default["default"].invoke(correct, "sort").sort();
|
|
36970
|
-
if (deepEq$
|
|
37023
|
+
if (deepEq$3(guess, correct)) {
|
|
36971
37024
|
return {
|
|
36972
37025
|
type: "points",
|
|
36973
37026
|
earned: 1,
|
|
@@ -36978,7 +37031,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36978
37031
|
} else if (userInput.type === "ray" && rubric.correct.type === "ray" && userInput.coords != null) {
|
|
36979
37032
|
const guess = userInput.coords;
|
|
36980
37033
|
const correct = rubric.correct.coords;
|
|
36981
|
-
if (deepEq$
|
|
37034
|
+
if (deepEq$3(guess[0], correct[0]) && collinear(correct[0], correct[1], guess[1])) {
|
|
36982
37035
|
return {
|
|
36983
37036
|
type: "points",
|
|
36984
37037
|
earned: 1,
|
|
@@ -37005,7 +37058,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
37005
37058
|
/* exact */
|
|
37006
37059
|
match =
|
|
37007
37060
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
37008
|
-
deepEq$
|
|
37061
|
+
deepEq$3(guess[1], correct[1]) &&
|
|
37009
37062
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
37010
37063
|
collinear(correct[1], correct[0], guess[0]) &&
|
|
37011
37064
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
@@ -41267,14 +41320,20 @@ class Measurer extends React__namespace.Component {
|
|
|
41267
41320
|
}
|
|
41268
41321
|
const propUpgrades = {
|
|
41269
41322
|
"1": v0props => {
|
|
41270
|
-
const
|
|
41323
|
+
const {
|
|
41324
|
+
imageUrl,
|
|
41325
|
+
imageTop,
|
|
41326
|
+
imageLeft,
|
|
41327
|
+
...rest
|
|
41328
|
+
} = v0props;
|
|
41329
|
+
return {
|
|
41330
|
+
...rest,
|
|
41271
41331
|
image: {
|
|
41272
|
-
url:
|
|
41273
|
-
top:
|
|
41274
|
-
left:
|
|
41332
|
+
url: imageUrl,
|
|
41333
|
+
top: imageTop,
|
|
41334
|
+
left: imageLeft
|
|
41275
41335
|
}
|
|
41276
|
-
}
|
|
41277
|
-
return v1props;
|
|
41336
|
+
};
|
|
41278
41337
|
}
|
|
41279
41338
|
};
|
|
41280
41339
|
var Measurer$1 = {
|
|
@@ -42808,14 +42867,28 @@ const getPromptJSON$5 = (renderProps, userInput) => {
|
|
|
42808
42867
|
};
|
|
42809
42868
|
};
|
|
42810
42869
|
|
|
42811
|
-
|
|
42870
|
+
/**
|
|
42871
|
+
* Checks user input from the orderer widget to see if the user has started
|
|
42872
|
+
* ordering the options, making the widget scorable.
|
|
42873
|
+
* @param userInput
|
|
42874
|
+
* @see `scoreOrderer` for more details.
|
|
42875
|
+
*/
|
|
42876
|
+
function validateOrderer(userInput) {
|
|
42812
42877
|
if (userInput.current.length === 0) {
|
|
42813
42878
|
return {
|
|
42814
42879
|
type: "invalid",
|
|
42815
42880
|
message: null
|
|
42816
42881
|
};
|
|
42817
42882
|
}
|
|
42818
|
-
|
|
42883
|
+
return null;
|
|
42884
|
+
}
|
|
42885
|
+
|
|
42886
|
+
function scoreOrderer(userInput, rubric) {
|
|
42887
|
+
const validateError = validateOrderer(userInput);
|
|
42888
|
+
if (validateError) {
|
|
42889
|
+
return validateError;
|
|
42890
|
+
}
|
|
42891
|
+
const correct = ___default["default"].isEqual(userInput.current, rubric.correctOptions.map(option => option.content));
|
|
42819
42892
|
return {
|
|
42820
42893
|
type: "points",
|
|
42821
42894
|
earned: correct ? 1 : 0,
|
|
@@ -42991,15 +43064,13 @@ class Card extends React__namespace.Component {
|
|
|
42991
43064
|
}))));
|
|
42992
43065
|
}
|
|
42993
43066
|
}
|
|
42994
|
-
const NORMAL = "normal";
|
|
42995
|
-
const HORIZONTAL = "horizontal";
|
|
42996
43067
|
class Orderer extends React__namespace.Component {
|
|
42997
43068
|
static defaultProps = {
|
|
42998
43069
|
current: [],
|
|
42999
43070
|
options: [],
|
|
43000
43071
|
correctOptions: [],
|
|
43001
|
-
height:
|
|
43002
|
-
layout:
|
|
43072
|
+
height: "normal",
|
|
43073
|
+
layout: "horizontal",
|
|
43003
43074
|
linterContext: PerseusLinter.linterContextDefault
|
|
43004
43075
|
};
|
|
43005
43076
|
state = {
|
|
@@ -43152,7 +43223,7 @@ class Orderer extends React__namespace.Component {
|
|
|
43152
43223
|
};
|
|
43153
43224
|
findCorrectIndex = (draggable, list) => {
|
|
43154
43225
|
// Find the correct index for a card given the current cards.
|
|
43155
|
-
const isHorizontal = this.props.layout ===
|
|
43226
|
+
const isHorizontal = this.props.layout === "horizontal";
|
|
43156
43227
|
// eslint-disable-next-line react/no-string-refs
|
|
43157
43228
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
43158
43229
|
const $dragList = $__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.dragList));
|
|
@@ -43202,28 +43273,24 @@ class Orderer extends React__namespace.Component {
|
|
|
43202
43273
|
if (draggable == null) {
|
|
43203
43274
|
return false;
|
|
43204
43275
|
}
|
|
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);
|
|
43276
|
+
const isHorizontal = this.props.layout === "horizontal";
|
|
43277
|
+
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
43278
|
+
const $draggable = $__default["default"](ReactDOM__default["default"].findDOMNode(draggable));
|
|
43279
|
+
// eslint-disable-next-line react/no-string-refs
|
|
43280
|
+
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
43281
|
+
const $bank = $__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.bank));
|
|
43282
|
+
// @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
|
|
43283
|
+
const draggableOffset = $draggable.offset();
|
|
43284
|
+
// @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
|
|
43285
|
+
const bankOffset = $bank.offset();
|
|
43286
|
+
// @ts-expect-error - TS2339 - Property 'outerHeight' does not exist on type 'JQueryStatic'.
|
|
43287
|
+
const draggableHeight = $draggable.outerHeight(true);
|
|
43288
|
+
// @ts-expect-error - TS2339 - Property 'outerHeight' does not exist on type 'JQueryStatic'.
|
|
43289
|
+
const bankHeight = $bank.outerHeight(true);
|
|
43290
|
+
// @ts-expect-error - TS2339 - Property 'outerWidth' does not exist on type 'JQueryStatic'.
|
|
43291
|
+
const bankWidth = $bank.outerWidth(true);
|
|
43292
|
+
// @ts-expect-error - TS2339 - Property 'outerWidth' does not exist on type 'JQueryStatic'.
|
|
43293
|
+
const draggableWidth = $draggable.outerWidth(true);
|
|
43227
43294
|
if (isHorizontal) {
|
|
43228
43295
|
return draggableOffset.top + draggableHeight / 2 < bankOffset.top + bankHeight;
|
|
43229
43296
|
}
|
|
@@ -43642,18 +43709,36 @@ const getPromptJSON$3 = () => {
|
|
|
43642
43709
|
};
|
|
43643
43710
|
|
|
43644
43711
|
const {
|
|
43645
|
-
deepEq: deepEq$
|
|
43712
|
+
deepEq: deepEq$2
|
|
43646
43713
|
} = Util;
|
|
43647
|
-
|
|
43648
|
-
|
|
43714
|
+
|
|
43715
|
+
/**
|
|
43716
|
+
* Checks user input to confirm it is not the same as the starting values for the graph.
|
|
43717
|
+
* This means the user has modified the graph, and the question can be scored.
|
|
43718
|
+
*
|
|
43719
|
+
* @see 'scorePlotter' for more details on scoring.
|
|
43720
|
+
*/
|
|
43721
|
+
function validatePlotter(userInput, validationData) {
|
|
43722
|
+
if (deepEq$2(userInput, validationData.starting)) {
|
|
43649
43723
|
return {
|
|
43650
43724
|
type: "invalid",
|
|
43651
43725
|
message: null
|
|
43652
43726
|
};
|
|
43653
43727
|
}
|
|
43728
|
+
return null;
|
|
43729
|
+
}
|
|
43730
|
+
|
|
43731
|
+
const {
|
|
43732
|
+
deepEq: deepEq$1
|
|
43733
|
+
} = Util;
|
|
43734
|
+
function scorePlotter(userInput, scoringData) {
|
|
43735
|
+
const validationError = validatePlotter(userInput, scoringData);
|
|
43736
|
+
if (validationError) {
|
|
43737
|
+
return validationError;
|
|
43738
|
+
}
|
|
43654
43739
|
return {
|
|
43655
43740
|
type: "points",
|
|
43656
|
-
earned: deepEq$1(userInput,
|
|
43741
|
+
earned: deepEq$1(userInput, scoringData.correct) ? 1 : 0,
|
|
43657
43742
|
total: 1,
|
|
43658
43743
|
message: null
|
|
43659
43744
|
};
|
|
@@ -44540,7 +44625,14 @@ const getPromptJSON$1 = userInput => {
|
|
|
44540
44625
|
};
|
|
44541
44626
|
};
|
|
44542
44627
|
|
|
44543
|
-
|
|
44628
|
+
/**
|
|
44629
|
+
* Checks user input for the sorter widget to ensure that the user has made
|
|
44630
|
+
* changes before attempting to score the widget.
|
|
44631
|
+
* @param userInput
|
|
44632
|
+
* @see 'scoreSorter' in 'packages/perseus/src/widgets/sorter/score-sorter.ts'
|
|
44633
|
+
* for more details on how the sorter widget is scored.
|
|
44634
|
+
*/
|
|
44635
|
+
function validateSorter(userInput) {
|
|
44544
44636
|
// If the sorter widget hasn't been changed yet, we treat it as "empty" which
|
|
44545
44637
|
// prevents the "Check" button from becoming active. We want the user
|
|
44546
44638
|
// to make a change before trying to move forward. This makes an
|
|
@@ -44553,6 +44645,14 @@ function scoreSorter(userInput, rubric) {
|
|
|
44553
44645
|
message: null
|
|
44554
44646
|
};
|
|
44555
44647
|
}
|
|
44648
|
+
return null;
|
|
44649
|
+
}
|
|
44650
|
+
|
|
44651
|
+
function scoreSorter(userInput, rubric) {
|
|
44652
|
+
const validationError = validateSorter(userInput);
|
|
44653
|
+
if (validationError) {
|
|
44654
|
+
return validationError;
|
|
44655
|
+
}
|
|
44556
44656
|
const correct = Util.deepEq(userInput.options, rubric.correct);
|
|
44557
44657
|
return {
|
|
44558
44658
|
type: "points",
|
|
@@ -45127,7 +45227,7 @@ const init = function () {
|
|
|
45127
45227
|
|
|
45128
45228
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45129
45229
|
const libName = "@khanacademy/perseus";
|
|
45130
|
-
const libVersion = "
|
|
45230
|
+
const libVersion = "44.0.0";
|
|
45131
45231
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45132
45232
|
|
|
45133
45233
|
const apiVersion = {
|
|
@@ -45412,7 +45512,6 @@ const styles$3 = aphrodite.StyleSheet.create({
|
|
|
45412
45512
|
}
|
|
45413
45513
|
});
|
|
45414
45514
|
|
|
45415
|
-
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
45416
45515
|
// Use of UNSAFE_componentWillReceiveProps:
|
|
45417
45516
|
// eslint-disable-next-line react/no-unsafe
|
|
45418
45517
|
class HintsRenderer extends React__namespace.Component {
|
|
@@ -48638,6 +48737,27 @@ function pair(parseA, parseB) {
|
|
|
48638
48737
|
};
|
|
48639
48738
|
}
|
|
48640
48739
|
|
|
48740
|
+
function pipeParsers(p) {
|
|
48741
|
+
return new ParserPipeline(p);
|
|
48742
|
+
}
|
|
48743
|
+
class ParserPipeline {
|
|
48744
|
+
constructor(parser) {
|
|
48745
|
+
this.parser = parser;
|
|
48746
|
+
}
|
|
48747
|
+
then(nextParser) {
|
|
48748
|
+
return new ParserPipeline(composeParsers(this.parser, nextParser));
|
|
48749
|
+
}
|
|
48750
|
+
}
|
|
48751
|
+
function composeParsers(parserA, parserB) {
|
|
48752
|
+
return (rawValue, ctx) => {
|
|
48753
|
+
const partialResult = parserA(rawValue, ctx);
|
|
48754
|
+
if (isFailure(partialResult)) {
|
|
48755
|
+
return partialResult;
|
|
48756
|
+
}
|
|
48757
|
+
return parserB(partialResult.value, ctx);
|
|
48758
|
+
};
|
|
48759
|
+
}
|
|
48760
|
+
|
|
48641
48761
|
function record(parseKey, parseValue) {
|
|
48642
48762
|
return (rawValue, ctx) => {
|
|
48643
48763
|
if (!isObject(rawValue)) {
|
|
@@ -48718,6 +48838,20 @@ function either(parseA, parseB) {
|
|
|
48718
48838
|
};
|
|
48719
48839
|
}
|
|
48720
48840
|
|
|
48841
|
+
function defaulted(parser, fallback) {
|
|
48842
|
+
return (rawValue, ctx) => {
|
|
48843
|
+
if (rawValue == null) {
|
|
48844
|
+
return success(fallback(rawValue));
|
|
48845
|
+
}
|
|
48846
|
+
return parser(rawValue, ctx);
|
|
48847
|
+
};
|
|
48848
|
+
}
|
|
48849
|
+
|
|
48850
|
+
const parseImages = defaulted(record(string, object({
|
|
48851
|
+
width: number,
|
|
48852
|
+
height: number
|
|
48853
|
+
})), () => ({}));
|
|
48854
|
+
|
|
48721
48855
|
function parseWidget(parseType, parseOptions) {
|
|
48722
48856
|
return object({
|
|
48723
48857
|
type: parseType,
|
|
@@ -48726,10 +48860,21 @@ function parseWidget(parseType, parseOptions) {
|
|
|
48726
48860
|
alignment: optional(string),
|
|
48727
48861
|
options: parseOptions,
|
|
48728
48862
|
key: optional(number),
|
|
48729
|
-
version: object({
|
|
48863
|
+
version: optional(object({
|
|
48730
48864
|
major: number,
|
|
48731
48865
|
minor: number
|
|
48732
|
-
})
|
|
48866
|
+
}))
|
|
48867
|
+
});
|
|
48868
|
+
}
|
|
48869
|
+
function parseWidgetWithVersion(parseVersion, parseType, parseOptions) {
|
|
48870
|
+
return object({
|
|
48871
|
+
type: parseType,
|
|
48872
|
+
static: optional(boolean),
|
|
48873
|
+
graded: optional(boolean),
|
|
48874
|
+
alignment: optional(string),
|
|
48875
|
+
options: parseOptions,
|
|
48876
|
+
key: optional(number),
|
|
48877
|
+
version: parseVersion
|
|
48733
48878
|
});
|
|
48734
48879
|
}
|
|
48735
48880
|
|
|
@@ -48737,7 +48882,7 @@ const parseCategorizerWidget = parseWidget(constant("categorizer"), object({
|
|
|
48737
48882
|
items: array(string),
|
|
48738
48883
|
categories: array(string),
|
|
48739
48884
|
randomizeItems: boolean,
|
|
48740
|
-
static: boolean,
|
|
48885
|
+
static: defaulted(boolean, () => false),
|
|
48741
48886
|
values: array(number),
|
|
48742
48887
|
highlightLint: optional(boolean),
|
|
48743
48888
|
linterContext: optional(object({
|
|
@@ -48769,7 +48914,9 @@ const parseDefinitionWidget = parseWidget(constant("definition"), object({
|
|
|
48769
48914
|
|
|
48770
48915
|
const parseDropdownWidget = parseWidget(constant("dropdown"), object({
|
|
48771
48916
|
placeholder: string,
|
|
48772
|
-
|
|
48917
|
+
ariaLabel: optional(string),
|
|
48918
|
+
visibleLabel: optional(string),
|
|
48919
|
+
static: defaulted(boolean, () => false),
|
|
48773
48920
|
choices: array(object({
|
|
48774
48921
|
content: string,
|
|
48775
48922
|
correct: boolean
|
|
@@ -48792,9 +48939,12 @@ const parseAnswerForm = object({
|
|
|
48792
48939
|
form: boolean,
|
|
48793
48940
|
simplify: boolean,
|
|
48794
48941
|
considered: enumeration("correct", "wrong", "ungraded"),
|
|
48795
|
-
key: optional(string)
|
|
48942
|
+
key: pipeParsers(optional(union(string).or(number).parser)).then((key, ctx) => ctx.success(String(key))).parser
|
|
48796
48943
|
});
|
|
48797
|
-
const
|
|
48944
|
+
const parseExpressionWidgetV1 = parseWidgetWithVersion(object({
|
|
48945
|
+
major: constant(1),
|
|
48946
|
+
minor: number
|
|
48947
|
+
}), constant("expression"), object({
|
|
48798
48948
|
answerForms: array(parseAnswerForm),
|
|
48799
48949
|
functions: array(string),
|
|
48800
48950
|
times: boolean,
|
|
@@ -48803,23 +48953,69 @@ const parseExpressionWidget = parseWidget(constant("expression"), object({
|
|
|
48803
48953
|
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
48804
48954
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48805
48955
|
}));
|
|
48956
|
+
const parseExpressionWidgetV0 = parseWidgetWithVersion(optional(object({
|
|
48957
|
+
major: constant(0),
|
|
48958
|
+
minor: number
|
|
48959
|
+
})), constant("expression"), object({
|
|
48960
|
+
functions: array(string),
|
|
48961
|
+
times: boolean,
|
|
48962
|
+
visibleLabel: optional(string),
|
|
48963
|
+
ariaLabel: optional(string),
|
|
48964
|
+
form: boolean,
|
|
48965
|
+
simplify: boolean,
|
|
48966
|
+
value: string,
|
|
48967
|
+
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
48968
|
+
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48969
|
+
}));
|
|
48970
|
+
function migrateV0ToV1(widget, ctx) {
|
|
48971
|
+
const {
|
|
48972
|
+
options
|
|
48973
|
+
} = widget;
|
|
48974
|
+
return ctx.success({
|
|
48975
|
+
...widget,
|
|
48976
|
+
version: {
|
|
48977
|
+
major: 1,
|
|
48978
|
+
minor: 0
|
|
48979
|
+
},
|
|
48980
|
+
options: {
|
|
48981
|
+
times: options.times,
|
|
48982
|
+
buttonSets: options.buttonSets,
|
|
48983
|
+
functions: options.functions,
|
|
48984
|
+
buttonsVisible: options.buttonsVisible,
|
|
48985
|
+
visibleLabel: options.visibleLabel,
|
|
48986
|
+
ariaLabel: options.ariaLabel,
|
|
48987
|
+
answerForms: [{
|
|
48988
|
+
considered: "correct",
|
|
48989
|
+
form: options.form,
|
|
48990
|
+
simplify: options.simplify,
|
|
48991
|
+
value: options.value
|
|
48992
|
+
}]
|
|
48993
|
+
}
|
|
48994
|
+
});
|
|
48995
|
+
}
|
|
48996
|
+
const parseExpressionWidget = union(parseExpressionWidgetV1).or(pipeParsers(parseExpressionWidgetV0).then(migrateV0ToV1).parser).parser;
|
|
48806
48997
|
|
|
48807
|
-
const parsePerseusRenderer = object({
|
|
48808
|
-
|
|
48998
|
+
const parsePerseusRenderer = defaulted(object({
|
|
48999
|
+
// TODO(benchristel): content is also defaulted to empty string in
|
|
49000
|
+
// renderer.tsx. See if we can remove one default or the other.
|
|
49001
|
+
content: defaulted(string, () => ""),
|
|
48809
49002
|
// This module has an import cycle with parseWidgetsMap, because the
|
|
48810
49003
|
// `group` widget can contain another renderer.
|
|
48811
49004
|
// The anonymous function below ensures that we don't try to access
|
|
48812
49005
|
// parseWidgetsMap before it's defined.
|
|
48813
|
-
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
49006
|
+
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
|
|
48814
49007
|
metadata: optional(array(string)),
|
|
48815
|
-
images:
|
|
48816
|
-
|
|
48817
|
-
|
|
48818
|
-
|
|
48819
|
-
|
|
49008
|
+
images: parseImages
|
|
49009
|
+
}),
|
|
49010
|
+
// Default value
|
|
49011
|
+
() => ({
|
|
49012
|
+
content: "",
|
|
49013
|
+
widgets: {},
|
|
49014
|
+
images: {}
|
|
49015
|
+
}));
|
|
48820
49016
|
|
|
48821
49017
|
const parseGradedGroupWidgetOptions = object({
|
|
48822
|
-
title: string,
|
|
49018
|
+
title: defaulted(string, () => ""),
|
|
48823
49019
|
hasHint: optional(nullable(boolean)),
|
|
48824
49020
|
// This module has an import cycle with parsePerseusRenderer.
|
|
48825
49021
|
// The anonymous function below ensures that we don't try to access
|
|
@@ -48913,7 +49109,7 @@ const parseIframeWidget = parseWidget(constant("iframe"), object({
|
|
|
48913
49109
|
height: union(number).or(string).parser,
|
|
48914
49110
|
allowFullScreen: boolean,
|
|
48915
49111
|
allowTopNavigation: optional(boolean),
|
|
48916
|
-
static: boolean
|
|
49112
|
+
static: defaulted(boolean, () => false)
|
|
48917
49113
|
}));
|
|
48918
49114
|
|
|
48919
49115
|
const parsePerseusImageBackground = object({
|
|
@@ -49292,13 +49488,13 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
|
49292
49488
|
padding: boolean
|
|
49293
49489
|
}));
|
|
49294
49490
|
|
|
49295
|
-
const parseMatrixWidget = parseWidget(constant("matrix"), object({
|
|
49296
|
-
prefix: string,
|
|
49297
|
-
suffix: string,
|
|
49491
|
+
const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
|
|
49492
|
+
prefix: optional(string),
|
|
49493
|
+
suffix: optional(string),
|
|
49298
49494
|
answers: array(array(number)),
|
|
49299
|
-
cursorPosition: array(number),
|
|
49495
|
+
cursorPosition: optional(array(number)),
|
|
49300
49496
|
matrixBoardSize: array(number),
|
|
49301
|
-
static: boolean
|
|
49497
|
+
static: optional(boolean)
|
|
49302
49498
|
}));
|
|
49303
49499
|
|
|
49304
49500
|
const parseMeasurerWidget = parseWidget(constant("measurer"), object({
|
|
@@ -49368,8 +49564,8 @@ const parseOrdererWidget = parseWidget(constant("orderer"), object({
|
|
|
49368
49564
|
options: array(parseRenderer),
|
|
49369
49565
|
correctOptions: array(parseRenderer),
|
|
49370
49566
|
otherOptions: array(parseRenderer),
|
|
49371
|
-
height:
|
|
49372
|
-
layout:
|
|
49567
|
+
height: enumeration("normal", "auto"),
|
|
49568
|
+
layout: enumeration("horizontal", "vertical")
|
|
49373
49569
|
}));
|
|
49374
49570
|
|
|
49375
49571
|
const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
@@ -49414,7 +49610,7 @@ const parsePythonProgramWidget = parseWidget(constant("python-program"), object(
|
|
|
49414
49610
|
|
|
49415
49611
|
const parseRadioWidget = parseWidget(constant("radio"), object({
|
|
49416
49612
|
choices: array(object({
|
|
49417
|
-
content: string,
|
|
49613
|
+
content: defaulted(string, () => ""),
|
|
49418
49614
|
clue: optional(string),
|
|
49419
49615
|
correct: optional(boolean),
|
|
49420
49616
|
isNoneOfTheAbove: optional(boolean),
|
|
@@ -49576,26 +49772,45 @@ const parseWidgetMapKeyComponents = pair(string, parseStringToPositiveInt);
|
|
|
49576
49772
|
const parseHint = object({
|
|
49577
49773
|
replace: optional(boolean),
|
|
49578
49774
|
content: string,
|
|
49579
|
-
widgets: parseWidgetsMap,
|
|
49775
|
+
widgets: defaulted(parseWidgetsMap, () => ({})),
|
|
49580
49776
|
metadata: optional(array(string)),
|
|
49581
|
-
images:
|
|
49582
|
-
width: number,
|
|
49583
|
-
height: number
|
|
49584
|
-
}))
|
|
49777
|
+
images: parseImages
|
|
49585
49778
|
});
|
|
49586
49779
|
|
|
49587
49780
|
object({
|
|
49588
49781
|
question: parsePerseusRenderer,
|
|
49589
|
-
hints: array(parseHint),
|
|
49590
|
-
answerArea: record(enumeration(...ItemExtras), boolean),
|
|
49591
|
-
itemDataVersion: object({
|
|
49782
|
+
hints: defaulted(array(parseHint), () => []),
|
|
49783
|
+
answerArea: pipeParsers(defaulted(object({}), () => ({}))).then(migrateAnswerArea).then(record(enumeration(...ItemExtras), boolean)).parser,
|
|
49784
|
+
itemDataVersion: optional(object({
|
|
49592
49785
|
major: number,
|
|
49593
49786
|
minor: number
|
|
49594
|
-
}),
|
|
49787
|
+
})),
|
|
49595
49788
|
// Deprecated field
|
|
49596
49789
|
answer: any
|
|
49597
49790
|
});
|
|
49598
49791
|
|
|
49792
|
+
// Some answerAreas have extra fields, like:
|
|
49793
|
+
//
|
|
49794
|
+
// "answerArea": {
|
|
49795
|
+
// "type": "multiple",
|
|
49796
|
+
// "options": {
|
|
49797
|
+
// "content": "",
|
|
49798
|
+
// "images": {},
|
|
49799
|
+
// "widgets": {}
|
|
49800
|
+
// }
|
|
49801
|
+
// }
|
|
49802
|
+
//
|
|
49803
|
+
// The "type" and "options" fields don't seem to be used anywhere. This
|
|
49804
|
+
// migration function removes them.
|
|
49805
|
+
function migrateAnswerArea(rawValue, ctx) {
|
|
49806
|
+
const {
|
|
49807
|
+
type: _,
|
|
49808
|
+
options: __,
|
|
49809
|
+
...rest
|
|
49810
|
+
} = rawValue;
|
|
49811
|
+
return ctx.success(rest);
|
|
49812
|
+
}
|
|
49813
|
+
|
|
49599
49814
|
/**
|
|
49600
49815
|
* Helper to parse PerseusItem JSON
|
|
49601
49816
|
* Why not just use JSON.parse? We want:
|