@khanacademy/perseus 43.0.0 → 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 +475 -306
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +7 -3
- package/dist/es/strings.js.map +1 -1
- package/dist/index.js +465 -296
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +14 -7
- package/dist/strings.js +7 -3
- package/dist/strings.js.map +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/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/interactive-graphs/utils.d.ts +6 -6
- 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/es/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { number as number$1, point, vector as vector$1, line } from '@khanacadem
|
|
|
23
23
|
import { entries, UnreachableCaseError, keys } from '@khanacademy/wonder-stuff-core';
|
|
24
24
|
import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
|
|
25
25
|
import SimpleMarkdown from '@khanacademy/simple-markdown';
|
|
26
|
-
import { pureMarkdownRules
|
|
26
|
+
import { pureMarkdownRules } from '@khanacademy/pure-markdown';
|
|
27
27
|
import { TextField } from '@khanacademy/wonder-blocks-form';
|
|
28
28
|
import Button from '@khanacademy/wonder-blocks-button';
|
|
29
29
|
import { Strut } from '@khanacademy/wonder-blocks-layout';
|
|
@@ -2760,8 +2760,6 @@ async function getImageSizeModern(url) {
|
|
|
2760
2760
|
});
|
|
2761
2761
|
}
|
|
2762
2762
|
|
|
2763
|
-
/* eslint-disable @babel/no-invalid-this, getter-return, one-var */
|
|
2764
|
-
|
|
2765
2763
|
// TODO: dedupe this with Coord in interactive2/types.js
|
|
2766
2764
|
|
|
2767
2765
|
let supportsPassive = false;
|
|
@@ -2837,8 +2835,8 @@ function shuffle$3(array, randomSeed, ensurePermuted = false) {
|
|
|
2837
2835
|
do {
|
|
2838
2836
|
// Fischer-Yates shuffle
|
|
2839
2837
|
for (let top = shuffled.length; top > 0; top--) {
|
|
2840
|
-
const newEnd = Math.floor(random() * top)
|
|
2841
|
-
|
|
2838
|
+
const newEnd = Math.floor(random() * top);
|
|
2839
|
+
const temp = shuffled[newEnd];
|
|
2842
2840
|
|
|
2843
2841
|
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
2844
2842
|
shuffled[newEnd] = shuffled[top - 1];
|
|
@@ -3170,13 +3168,13 @@ function eq$4(x, y) {
|
|
|
3170
3168
|
* Deep approximate equality on primitives, numbers, arrays, and objects.
|
|
3171
3169
|
* Recursive.
|
|
3172
3170
|
*/
|
|
3173
|
-
function deepEq$
|
|
3171
|
+
function deepEq$7(x, y) {
|
|
3174
3172
|
if (Array.isArray(x) && Array.isArray(y)) {
|
|
3175
3173
|
if (x.length !== y.length) {
|
|
3176
3174
|
return false;
|
|
3177
3175
|
}
|
|
3178
3176
|
for (let i = 0; i < x.length; i++) {
|
|
3179
|
-
if (!deepEq$
|
|
3177
|
+
if (!deepEq$7(x[i], y[i])) {
|
|
3180
3178
|
return false;
|
|
3181
3179
|
}
|
|
3182
3180
|
}
|
|
@@ -3194,10 +3192,10 @@ function deepEq$6(x, y) {
|
|
|
3194
3192
|
if (typeof x === "object" && typeof y === "object" && !!x && !!y) {
|
|
3195
3193
|
return x === y || _.all(x, function (v, k) {
|
|
3196
3194
|
// @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
|
|
3197
|
-
return deepEq$
|
|
3195
|
+
return deepEq$7(y[k], v);
|
|
3198
3196
|
}) && _.all(y, function (v, k) {
|
|
3199
3197
|
// @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
|
|
3200
|
-
return deepEq$
|
|
3198
|
+
return deepEq$7(x[k], v);
|
|
3201
3199
|
});
|
|
3202
3200
|
}
|
|
3203
3201
|
if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
|
|
@@ -3478,7 +3476,7 @@ const Util = {
|
|
|
3478
3476
|
tickStepFromNumTicks,
|
|
3479
3477
|
constrainedTickStepsFromTickSteps,
|
|
3480
3478
|
eq: eq$4,
|
|
3481
|
-
deepEq: deepEq$
|
|
3479
|
+
deepEq: deepEq$7,
|
|
3482
3480
|
parseQueryString,
|
|
3483
3481
|
updateQueryString: updateQueryString$2,
|
|
3484
3482
|
strongEncodeURIComponent,
|
|
@@ -10509,7 +10507,9 @@ const getClipPoint = function getClipPoint(graph, coord, angle) {
|
|
|
10509
10507
|
return clipPoint;
|
|
10510
10508
|
};
|
|
10511
10509
|
|
|
10512
|
-
|
|
10510
|
+
/**
|
|
10511
|
+
* Default methods for a wrapped movable.
|
|
10512
|
+
*/
|
|
10513
10513
|
|
|
10514
10514
|
/*
|
|
10515
10515
|
* attr() and animate() simply pass the
|
|
@@ -10767,7 +10767,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
10767
10767
|
*/
|
|
10768
10768
|
const {
|
|
10769
10769
|
eq: eq$3,
|
|
10770
|
-
deepEq: deepEq$
|
|
10770
|
+
deepEq: deepEq$6
|
|
10771
10771
|
} = Util;
|
|
10772
10772
|
|
|
10773
10773
|
// This should really be a readonly tuple of [number, number]
|
|
@@ -10927,7 +10927,7 @@ function similar(coords1, coords2, tolerance) {
|
|
|
10927
10927
|
angles = rotate(angles, i);
|
|
10928
10928
|
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
10929
10929
|
sides = rotate(sides, i);
|
|
10930
|
-
if (deepEq$
|
|
10930
|
+
if (deepEq$6(angles1, angles)) {
|
|
10931
10931
|
const sidePairs = _.zip(sides1, sides);
|
|
10932
10932
|
const factors = _.map(sidePairs, function (pair) {
|
|
10933
10933
|
return pair[0] / pair[1];
|
|
@@ -13865,7 +13865,7 @@ _.extend(MovableAngle.prototype, {
|
|
|
13865
13865
|
|
|
13866
13866
|
/* eslint-disable @babel/no-invalid-this */
|
|
13867
13867
|
const nestedMap$1 = Util.nestedMap;
|
|
13868
|
-
const deepEq$
|
|
13868
|
+
const deepEq$5 = Util.deepEq;
|
|
13869
13869
|
|
|
13870
13870
|
/**
|
|
13871
13871
|
* A base class for all Graphie Movables
|
|
@@ -13962,7 +13962,7 @@ const createSimpleClass = function createSimpleClass(addFunction) {
|
|
|
13962
13962
|
this._prevProps = this.props;
|
|
13963
13963
|
},
|
|
13964
13964
|
modify: function (graphie) {
|
|
13965
|
-
if (!deepEq$
|
|
13965
|
+
if (!deepEq$5(this.props, this._prevProps)) {
|
|
13966
13966
|
this.remove();
|
|
13967
13967
|
this.add(graphie);
|
|
13968
13968
|
this._prevProps = this.props;
|
|
@@ -16576,7 +16576,7 @@ var Movables = {
|
|
|
16576
16576
|
const GraphieMovable = GraphieClasses.GraphieMovable;
|
|
16577
16577
|
const createGraphie = GraphUtils.createGraphie;
|
|
16578
16578
|
const {
|
|
16579
|
-
deepEq: deepEq$
|
|
16579
|
+
deepEq: deepEq$4,
|
|
16580
16580
|
nestedMap
|
|
16581
16581
|
} = Util;
|
|
16582
16582
|
const {
|
|
@@ -16818,7 +16818,7 @@ class Graphie extends React.Component {
|
|
|
16818
16818
|
if (this.props.setup !== prevProps.setup) {
|
|
16819
16819
|
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.", Errors.Internal);
|
|
16820
16820
|
}
|
|
16821
|
-
if (!deepEq$
|
|
16821
|
+
if (!deepEq$4(this.props.options, prevProps.options) || !deepEq$4(this.props.box, prevProps.box) || !deepEq$4(this.props.range, prevProps.range)) {
|
|
16822
16822
|
this._setupGraphie();
|
|
16823
16823
|
}
|
|
16824
16824
|
this._updateMovables();
|
|
@@ -18536,7 +18536,7 @@ const rules$1 = _extends({}, pureMarkdownRules, {
|
|
|
18536
18536
|
// The href prop should always be here, but just in case somehow the article's
|
|
18537
18537
|
// link was not put together properly, let's make sure it's there so we
|
|
18538
18538
|
// don't break the entire page.
|
|
18539
|
-
const isKAUrl = href ? href.match(/https?:\/\/[
|
|
18539
|
+
const isKAUrl = href ? href.match(/https?:\/\/[^/]*khanacademy.org|^\//) : false;
|
|
18540
18540
|
if (!isKAUrl) {
|
|
18541
18541
|
// Prevents "reverse tabnabbing" phishing attacks
|
|
18542
18542
|
// @ts-expect-error - TS2322 - Type '"noopener noreferrer"' is not assignable to type 'null'.
|
|
@@ -20889,36 +20889,36 @@ function uniqueIdForInput(prefix = "input-") {
|
|
|
20889
20889
|
class TextInput extends React.Component {
|
|
20890
20890
|
constructor(props) {
|
|
20891
20891
|
super(props);
|
|
20892
|
+
this.inputRef = /*#__PURE__*/React.createRef();
|
|
20892
20893
|
this.id = void 0;
|
|
20894
|
+
this._getInput = () => {
|
|
20895
|
+
if (!this.inputRef.current) {
|
|
20896
|
+
throw new PerseusError("Input ref accessed before set", Errors.Internal);
|
|
20897
|
+
}
|
|
20898
|
+
return this.inputRef.current;
|
|
20899
|
+
};
|
|
20893
20900
|
this.focus = () => {
|
|
20894
|
-
|
|
20895
|
-
ReactDOM__default.findDOMNode(this).focus();
|
|
20901
|
+
this._getInput().focus();
|
|
20896
20902
|
};
|
|
20897
20903
|
this.blur = () => {
|
|
20898
|
-
|
|
20899
|
-
ReactDOM__default.findDOMNode(this).blur();
|
|
20904
|
+
this._getInput().blur();
|
|
20900
20905
|
};
|
|
20901
20906
|
this.getValue = () => {
|
|
20902
|
-
var
|
|
20903
|
-
|
|
20904
|
-
return (_ReactDOM$findDOMNode = ReactDOM__default.findDOMNode(this)) == null ? void 0 : _ReactDOM$findDOMNode.value;
|
|
20907
|
+
var _this$inputRef$curren;
|
|
20908
|
+
return (_this$inputRef$curren = this.inputRef.current) == null ? void 0 : _this$inputRef$curren.value;
|
|
20905
20909
|
};
|
|
20906
20910
|
this.getStringValue = () => {
|
|
20907
|
-
var
|
|
20908
|
-
|
|
20909
|
-
return (_ReactDOM$findDOMNode2 = ReactDOM__default.findDOMNode(this)) == null ? void 0 : _ReactDOM$findDOMNode2.value.toString();
|
|
20911
|
+
var _this$inputRef$curren2;
|
|
20912
|
+
return (_this$inputRef$curren2 = this.inputRef.current) == null ? void 0 : _this$inputRef$curren2.value.toString();
|
|
20910
20913
|
};
|
|
20911
20914
|
this.setSelectionRange = (selectionStart, selectionEnd) => {
|
|
20912
|
-
|
|
20913
|
-
ReactDOM__default.findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
|
|
20915
|
+
this._getInput().setSelectionRange(selectionStart, selectionEnd);
|
|
20914
20916
|
};
|
|
20915
20917
|
this.getSelectionStart = () => {
|
|
20916
|
-
|
|
20917
|
-
return ReactDOM__default.findDOMNode(this).selectionStart;
|
|
20918
|
+
return this._getInput().selectionStart;
|
|
20918
20919
|
};
|
|
20919
20920
|
this.getSelectionEnd = () => {
|
|
20920
|
-
|
|
20921
|
-
return ReactDOM__default.findDOMNode(this).selectionEnd;
|
|
20921
|
+
return this._getInput().selectionEnd;
|
|
20922
20922
|
};
|
|
20923
20923
|
if (props.id) {
|
|
20924
20924
|
this.id = props.id;
|
|
@@ -20939,6 +20939,7 @@ class TextInput extends React.Component {
|
|
|
20939
20939
|
} = this.props;
|
|
20940
20940
|
const formattedValue = value === null ? "" : value.toString();
|
|
20941
20941
|
return /*#__PURE__*/React.createElement(TextField, {
|
|
20942
|
+
ref: this.inputRef,
|
|
20942
20943
|
style: style,
|
|
20943
20944
|
disabled: disabled,
|
|
20944
20945
|
id: this.id,
|
|
@@ -20973,7 +20974,7 @@ function _objectWithoutPropertiesLoose(r, e) {
|
|
|
20973
20974
|
return t;
|
|
20974
20975
|
}
|
|
20975
20976
|
|
|
20976
|
-
const _excluded$
|
|
20977
|
+
const _excluded$8 = ["position", "visibility", "left", "top"];
|
|
20977
20978
|
const zIndex = 10;
|
|
20978
20979
|
let HorizontalDirection = /*#__PURE__*/function (HorizontalDirection) {
|
|
20979
20980
|
HorizontalDirection["Left"] = "left";
|
|
@@ -21024,7 +21025,7 @@ const TooltipArrow = _ref => {
|
|
|
21024
21025
|
left = 100,
|
|
21025
21026
|
top = 0
|
|
21026
21027
|
} = _ref,
|
|
21027
|
-
props = _objectWithoutPropertiesLoose(_ref, _excluded$
|
|
21028
|
+
props = _objectWithoutPropertiesLoose(_ref, _excluded$8);
|
|
21028
21029
|
// TODO(aria): Think about adding a box-shadow to the triangle here
|
|
21029
21030
|
// See http://css-tricks.com/triangle-with-shadow/
|
|
21030
21031
|
|
|
@@ -21384,7 +21385,7 @@ InputWithExamples.defaultProps = {
|
|
|
21384
21385
|
className: ""
|
|
21385
21386
|
};
|
|
21386
21387
|
|
|
21387
|
-
const _excluded$
|
|
21388
|
+
const _excluded$7 = ["keypadElement", "onFocus", "value"];
|
|
21388
21389
|
class SimpleKeypadInput extends React.Component {
|
|
21389
21390
|
constructor(...args) {
|
|
21390
21391
|
super(...args);
|
|
@@ -21429,7 +21430,7 @@ class SimpleKeypadInput extends React.Component {
|
|
|
21429
21430
|
onFocus,
|
|
21430
21431
|
value
|
|
21431
21432
|
} = _this$props,
|
|
21432
|
-
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$
|
|
21433
|
+
rest = _objectWithoutPropertiesLoose(_this$props, _excluded$7);
|
|
21433
21434
|
return (
|
|
21434
21435
|
/*#__PURE__*/
|
|
21435
21436
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
@@ -21991,7 +21992,7 @@ function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
|
|
|
21991
21992
|
return value / 100;
|
|
21992
21993
|
}
|
|
21993
21994
|
|
|
21994
|
-
// Otherwise, we return input
|
|
21995
|
+
// Otherwise, we return input value (number) stripped of the "%".
|
|
21995
21996
|
return value;
|
|
21996
21997
|
}
|
|
21997
21998
|
function scoreNumericInput(userInput, rubric, strings) {
|
|
@@ -25662,14 +25663,14 @@ var Choice$1 = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.
|
|
|
25662
25663
|
forwardedRef: ref
|
|
25663
25664
|
})));
|
|
25664
25665
|
|
|
25665
|
-
const _excluded$
|
|
25666
|
+
const _excluded$6 = ["content", "forwardedRef", "showContent"];
|
|
25666
25667
|
const ChoiceNoneAbove = function ChoiceNoneAbove(_ref) {
|
|
25667
25668
|
let {
|
|
25668
25669
|
content,
|
|
25669
25670
|
forwardedRef,
|
|
25670
25671
|
showContent = true
|
|
25671
25672
|
} = _ref,
|
|
25672
|
-
rest = _objectWithoutPropertiesLoose(_ref, _excluded$
|
|
25673
|
+
rest = _objectWithoutPropertiesLoose(_ref, _excluded$6);
|
|
25673
25674
|
const {
|
|
25674
25675
|
strings
|
|
25675
25676
|
} = usePerseusI18n();
|
|
@@ -26040,7 +26041,15 @@ const styles$p = StyleSheet.create({
|
|
|
26040
26041
|
}
|
|
26041
26042
|
});
|
|
26042
26043
|
|
|
26043
|
-
|
|
26044
|
+
/**
|
|
26045
|
+
* Checks if the user has selected at least one option. Additional validation
|
|
26046
|
+
* is done in scoreRadio to check if the number of selected options is correct
|
|
26047
|
+
* and if the user has selected both a correct option and the "none of the above"
|
|
26048
|
+
* option.
|
|
26049
|
+
* @param userInput
|
|
26050
|
+
* @see `scoreRadio` for the additional validation logic and the scoring logic.
|
|
26051
|
+
*/
|
|
26052
|
+
function validateRadio(userInput) {
|
|
26044
26053
|
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
26045
26054
|
return sum + (selected ? 1 : 0);
|
|
26046
26055
|
}, 0);
|
|
@@ -26050,6 +26059,17 @@ function scoreRadio(userInput, rubric, strings) {
|
|
|
26050
26059
|
message: null
|
|
26051
26060
|
};
|
|
26052
26061
|
}
|
|
26062
|
+
return null;
|
|
26063
|
+
}
|
|
26064
|
+
|
|
26065
|
+
function scoreRadio(userInput, rubric, strings) {
|
|
26066
|
+
const validationError = validateRadio(userInput);
|
|
26067
|
+
if (validationError) {
|
|
26068
|
+
return validationError;
|
|
26069
|
+
}
|
|
26070
|
+
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
26071
|
+
return sum + (selected ? 1 : 0);
|
|
26072
|
+
}, 0);
|
|
26053
26073
|
const numCorrect = rubric.choices.reduce((sum, currentChoice) => {
|
|
26054
26074
|
return currentChoice.correct ? sum + 1 : sum;
|
|
26055
26075
|
}, 0);
|
|
@@ -26426,6 +26446,7 @@ Radio$1.defaultProps = {
|
|
|
26426
26446
|
showSolutions: "none"
|
|
26427
26447
|
};
|
|
26428
26448
|
|
|
26449
|
+
const _excluded$5 = ["noneOfTheAbove"];
|
|
26429
26450
|
const {
|
|
26430
26451
|
shuffle: shuffle$2,
|
|
26431
26452
|
random
|
|
@@ -26514,17 +26535,15 @@ const transform = (widgetOptions, strings, problemNum) => {
|
|
|
26514
26535
|
};
|
|
26515
26536
|
const propUpgrades$1 = {
|
|
26516
26537
|
"1": v0props => {
|
|
26517
|
-
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
|
|
26521
|
-
|
|
26522
|
-
} else {
|
|
26538
|
+
const {
|
|
26539
|
+
noneOfTheAbove
|
|
26540
|
+
} = v0props,
|
|
26541
|
+
rest = _objectWithoutPropertiesLoose(v0props, _excluded$5);
|
|
26542
|
+
if (noneOfTheAbove) {
|
|
26523
26543
|
throw new Error("radio widget v0 no longer supports auto noneOfTheAbove");
|
|
26524
26544
|
}
|
|
26525
|
-
return
|
|
26526
|
-
|
|
26527
|
-
hasNoneOfTheAbove: hasNoneOfTheAbove
|
|
26545
|
+
return _extends({}, rest, {
|
|
26546
|
+
hasNoneOfTheAbove: false
|
|
26528
26547
|
});
|
|
26529
26548
|
}
|
|
26530
26549
|
};
|
|
@@ -26562,23 +26581,36 @@ const getPromptJSON$n = (renderProps, userInput) => {
|
|
|
26562
26581
|
};
|
|
26563
26582
|
};
|
|
26564
26583
|
|
|
26565
|
-
|
|
26566
|
-
|
|
26567
|
-
|
|
26568
|
-
|
|
26569
|
-
|
|
26570
|
-
|
|
26571
|
-
|
|
26572
|
-
|
|
26573
|
-
|
|
26574
|
-
|
|
26575
|
-
|
|
26576
|
-
if (!completed) {
|
|
26584
|
+
/**
|
|
26585
|
+
* Checks userInput from the categorizer widget to see if the user has selected
|
|
26586
|
+
* a category for each item.
|
|
26587
|
+
* @param userInput - The user's input corresponding to an array of indices that
|
|
26588
|
+
* represent the selected category for each row/item.
|
|
26589
|
+
* @param validationData - An array of strings corresponding to each row/item
|
|
26590
|
+
* @param strings - Used to provide a validation message
|
|
26591
|
+
*/
|
|
26592
|
+
function validateCategorizer(userInput, validationData, strings) {
|
|
26593
|
+
const incomplete = validationData.items.some((_, i) => userInput.values[i] == null);
|
|
26594
|
+
if (incomplete) {
|
|
26577
26595
|
return {
|
|
26578
26596
|
type: "invalid",
|
|
26579
26597
|
message: strings.invalidSelection
|
|
26580
26598
|
};
|
|
26581
26599
|
}
|
|
26600
|
+
return null;
|
|
26601
|
+
}
|
|
26602
|
+
|
|
26603
|
+
function scoreCategorizer(userInput, scoringData, strings) {
|
|
26604
|
+
const validationError = validateCategorizer(userInput, scoringData, strings);
|
|
26605
|
+
if (validationError) {
|
|
26606
|
+
return validationError;
|
|
26607
|
+
}
|
|
26608
|
+
let allCorrect = true;
|
|
26609
|
+
scoringData.values.forEach((value, i) => {
|
|
26610
|
+
if (userInput.values[i] !== value) {
|
|
26611
|
+
allCorrect = false;
|
|
26612
|
+
}
|
|
26613
|
+
});
|
|
26582
26614
|
return {
|
|
26583
26615
|
type: "points",
|
|
26584
26616
|
earned: allCorrect ? 1 : 0,
|
|
@@ -27124,15 +27156,26 @@ const getPromptJSON$k = (renderProps, userInput) => {
|
|
|
27124
27156
|
};
|
|
27125
27157
|
};
|
|
27126
27158
|
|
|
27127
|
-
|
|
27128
|
-
|
|
27129
|
-
|
|
27159
|
+
/**
|
|
27160
|
+
* Checks if the user has selected an item from the dropdown before scoring.
|
|
27161
|
+
* This is shown with a userInput value / index other than 0.
|
|
27162
|
+
*/
|
|
27163
|
+
function validateDropdown(userInput) {
|
|
27164
|
+
if (userInput.value === 0) {
|
|
27130
27165
|
return {
|
|
27131
27166
|
type: "invalid",
|
|
27132
27167
|
message: null
|
|
27133
27168
|
};
|
|
27134
27169
|
}
|
|
27135
|
-
|
|
27170
|
+
return null;
|
|
27171
|
+
}
|
|
27172
|
+
|
|
27173
|
+
function scoreDropdown(userInput, rubric) {
|
|
27174
|
+
const validationError = validateDropdown(userInput);
|
|
27175
|
+
if (validationError) {
|
|
27176
|
+
return validationError;
|
|
27177
|
+
}
|
|
27178
|
+
const correct = rubric.choices[userInput.value - 1].correct;
|
|
27136
27179
|
return {
|
|
27137
27180
|
type: "points",
|
|
27138
27181
|
earned: correct ? 1 : 0,
|
|
@@ -27519,8 +27562,6 @@ const getNextState = (currentState, answerable) => {
|
|
|
27519
27562
|
}
|
|
27520
27563
|
};
|
|
27521
27564
|
|
|
27522
|
-
// exports has no 'transform'
|
|
27523
|
-
|
|
27524
27565
|
// A Graded Group is more or less a Group widget that displays a check
|
|
27525
27566
|
// answer button below the rendered content. When clicked, the widget grades
|
|
27526
27567
|
// the stuff inside and displays feedback about whether the inputted answer was
|
|
@@ -28184,7 +28225,6 @@ const getPromptJSON$g = (renderProps, userInput) => {
|
|
|
28184
28225
|
};
|
|
28185
28226
|
};
|
|
28186
28227
|
|
|
28187
|
-
/* eslint-disable one-var */
|
|
28188
28228
|
// @ts-expect-error - TS2339 - Property 'Plot' does not exist on type 'typeof Graphie'.
|
|
28189
28229
|
const Plot$1 = Graphie.Plot;
|
|
28190
28230
|
const DEFAULT_BACKGROUND_IMAGE = {
|
|
@@ -28285,14 +28325,14 @@ const Linear = _.extend({}, PlotDefaults, {
|
|
|
28285
28325
|
return [m, b];
|
|
28286
28326
|
},
|
|
28287
28327
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28288
|
-
const m = coeffs[0]
|
|
28289
|
-
|
|
28328
|
+
const m = coeffs[0];
|
|
28329
|
+
const b = coeffs[1];
|
|
28290
28330
|
return m * x + b;
|
|
28291
28331
|
},
|
|
28292
28332
|
getEquationString: function (coords) {
|
|
28293
28333
|
const coeffs = this.getCoefficients(coords);
|
|
28294
|
-
const m = coeffs[0]
|
|
28295
|
-
|
|
28334
|
+
const m = coeffs[0];
|
|
28335
|
+
const b = coeffs[1];
|
|
28296
28336
|
return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
|
|
28297
28337
|
}
|
|
28298
28338
|
});
|
|
@@ -28316,9 +28356,9 @@ const Quadratic = _.extend({}, PlotDefaults, {
|
|
|
28316
28356
|
return [a, b, c];
|
|
28317
28357
|
},
|
|
28318
28358
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28319
|
-
const a = coeffs[0]
|
|
28320
|
-
|
|
28321
|
-
|
|
28359
|
+
const a = coeffs[0];
|
|
28360
|
+
const b = coeffs[1];
|
|
28361
|
+
const c = coeffs[2];
|
|
28322
28362
|
return (a * x + b) * x + c;
|
|
28323
28363
|
},
|
|
28324
28364
|
getPropsForCoeffs: function (coeffs) {
|
|
@@ -28330,9 +28370,9 @@ const Quadratic = _.extend({}, PlotDefaults, {
|
|
|
28330
28370
|
},
|
|
28331
28371
|
getEquationString: function (coords) {
|
|
28332
28372
|
const coeffs = this.getCoefficients(coords);
|
|
28333
|
-
const a = coeffs[0]
|
|
28334
|
-
|
|
28335
|
-
|
|
28373
|
+
const a = coeffs[0];
|
|
28374
|
+
const b = coeffs[1];
|
|
28375
|
+
const c = coeffs[2];
|
|
28336
28376
|
return "y = " + a.toFixed(3) + "x^2 + " + b.toFixed(3) + "x + " + c.toFixed(3);
|
|
28337
28377
|
}
|
|
28338
28378
|
});
|
|
@@ -28351,10 +28391,10 @@ const Sinusoid = _.extend({}, PlotDefaults, {
|
|
|
28351
28391
|
return [a, b, c, d];
|
|
28352
28392
|
},
|
|
28353
28393
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28354
|
-
const a = coeffs[0]
|
|
28355
|
-
|
|
28356
|
-
|
|
28357
|
-
|
|
28394
|
+
const a = coeffs[0];
|
|
28395
|
+
const b = coeffs[1];
|
|
28396
|
+
const c = coeffs[2];
|
|
28397
|
+
const d = coeffs[3];
|
|
28358
28398
|
return a * Math.sin(b * x - c) + d;
|
|
28359
28399
|
},
|
|
28360
28400
|
getPropsForCoeffs: function (coeffs) {
|
|
@@ -28367,10 +28407,10 @@ const Sinusoid = _.extend({}, PlotDefaults, {
|
|
|
28367
28407
|
},
|
|
28368
28408
|
getEquationString: function (coords) {
|
|
28369
28409
|
const coeffs = this.getCoefficients(coords);
|
|
28370
|
-
const a = coeffs[0]
|
|
28371
|
-
|
|
28372
|
-
|
|
28373
|
-
|
|
28410
|
+
const a = coeffs[0];
|
|
28411
|
+
const b = coeffs[1];
|
|
28412
|
+
const c = coeffs[2];
|
|
28413
|
+
const d = coeffs[3];
|
|
28374
28414
|
return "y = " + a.toFixed(3) + " sin(" + b.toFixed(3) + "x - " + c.toFixed(3) + ") + " + d.toFixed(3);
|
|
28375
28415
|
},
|
|
28376
28416
|
areEqual: function (coeffs1, coeffs2) {
|
|
@@ -28390,18 +28430,18 @@ const Tangent = _.extend({}, PlotDefaults, {
|
|
|
28390
28430
|
return [a, b, c, d];
|
|
28391
28431
|
},
|
|
28392
28432
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28393
|
-
const a = coeffs[0]
|
|
28394
|
-
|
|
28395
|
-
|
|
28396
|
-
|
|
28433
|
+
const a = coeffs[0];
|
|
28434
|
+
const b = coeffs[1];
|
|
28435
|
+
const c = coeffs[2];
|
|
28436
|
+
const d = coeffs[3];
|
|
28397
28437
|
return a * Math.tan(b * x - c) + d;
|
|
28398
28438
|
},
|
|
28399
28439
|
getEquationString: function (coords) {
|
|
28400
28440
|
const coeffs = this.getCoefficients(coords);
|
|
28401
|
-
const a = coeffs[0]
|
|
28402
|
-
|
|
28403
|
-
|
|
28404
|
-
|
|
28441
|
+
const a = coeffs[0];
|
|
28442
|
+
const b = coeffs[1];
|
|
28443
|
+
const c = coeffs[2];
|
|
28444
|
+
const d = coeffs[3];
|
|
28405
28445
|
return "y = " + a.toFixed(3) + " sin(" + b.toFixed(3) + "x - " + c.toFixed(3) + ") + " + d.toFixed(3);
|
|
28406
28446
|
},
|
|
28407
28447
|
areEqual: function (coeffs1, coeffs2) {
|
|
@@ -28460,9 +28500,9 @@ const Exponential = _.extend({}, PlotDefaults, {
|
|
|
28460
28500
|
return [a, b, c];
|
|
28461
28501
|
},
|
|
28462
28502
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28463
|
-
const a = coeffs[0]
|
|
28464
|
-
|
|
28465
|
-
|
|
28503
|
+
const a = coeffs[0];
|
|
28504
|
+
const b = coeffs[1];
|
|
28505
|
+
const c = coeffs[2];
|
|
28466
28506
|
return a * Math.exp(b * x) + c;
|
|
28467
28507
|
},
|
|
28468
28508
|
getEquationString: function (coords, asymptote) {
|
|
@@ -28470,9 +28510,9 @@ const Exponential = _.extend({}, PlotDefaults, {
|
|
|
28470
28510
|
return null;
|
|
28471
28511
|
}
|
|
28472
28512
|
const coeffs = this.getCoefficients(coords, asymptote);
|
|
28473
|
-
const a = coeffs[0]
|
|
28474
|
-
|
|
28475
|
-
|
|
28513
|
+
const a = coeffs[0];
|
|
28514
|
+
const b = coeffs[1];
|
|
28515
|
+
const c = coeffs[2];
|
|
28476
28516
|
return "y = " + a.toFixed(3) + "e^(" + b.toFixed(3) + "x) + " + c.toFixed(3);
|
|
28477
28517
|
}
|
|
28478
28518
|
});
|
|
@@ -28517,9 +28557,9 @@ const Logarithm = _.extend({}, PlotDefaults, {
|
|
|
28517
28557
|
}
|
|
28518
28558
|
},
|
|
28519
28559
|
getFunctionForCoeffs: function (coeffs, x, asymptote) {
|
|
28520
|
-
const a = coeffs[0]
|
|
28521
|
-
|
|
28522
|
-
|
|
28560
|
+
const a = coeffs[0];
|
|
28561
|
+
const b = coeffs[1];
|
|
28562
|
+
const c = coeffs[2];
|
|
28523
28563
|
return a * Math.log(b * x + c);
|
|
28524
28564
|
},
|
|
28525
28565
|
getEquationString: function (coords, asymptote) {
|
|
@@ -28527,9 +28567,9 @@ const Logarithm = _.extend({}, PlotDefaults, {
|
|
|
28527
28567
|
return null;
|
|
28528
28568
|
}
|
|
28529
28569
|
const coeffs = this.getCoefficients(coords, asymptote);
|
|
28530
|
-
const a = coeffs[0]
|
|
28531
|
-
|
|
28532
|
-
|
|
28570
|
+
const a = coeffs[0];
|
|
28571
|
+
const b = coeffs[1];
|
|
28572
|
+
const c = coeffs[2];
|
|
28533
28573
|
return "y = ln(" + a.toFixed(3) + "x + " + b.toFixed(3) + ") + " + c.toFixed(3);
|
|
28534
28574
|
}
|
|
28535
28575
|
});
|
|
@@ -28553,16 +28593,16 @@ const AbsoluteValue = _.extend({}, PlotDefaults, {
|
|
|
28553
28593
|
return [m, horizontalOffset, verticalOffset];
|
|
28554
28594
|
},
|
|
28555
28595
|
getFunctionForCoeffs: function (coeffs, x) {
|
|
28556
|
-
const m = coeffs[0]
|
|
28557
|
-
|
|
28558
|
-
|
|
28596
|
+
const m = coeffs[0];
|
|
28597
|
+
const horizontalOffset = coeffs[1];
|
|
28598
|
+
const verticalOffset = coeffs[2];
|
|
28559
28599
|
return m * Math.abs(x - horizontalOffset) + verticalOffset;
|
|
28560
28600
|
},
|
|
28561
28601
|
getEquationString: function (coords) {
|
|
28562
28602
|
const coeffs = this.getCoefficients(coords);
|
|
28563
|
-
const m = coeffs[0]
|
|
28564
|
-
|
|
28565
|
-
|
|
28603
|
+
const m = coeffs[0];
|
|
28604
|
+
const horizontalOffset = coeffs[1];
|
|
28605
|
+
const verticalOffset = coeffs[2];
|
|
28566
28606
|
return "y = " + m.toFixed(3) + "| x - " + horizontalOffset.toFixed(3) + "| + " + verticalOffset.toFixed(3);
|
|
28567
28607
|
}
|
|
28568
28608
|
});
|
|
@@ -31129,67 +31169,6 @@ function isUnlimitedGraphState(state) {
|
|
|
31129
31169
|
return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
|
|
31130
31170
|
}
|
|
31131
31171
|
|
|
31132
|
-
/**
|
|
31133
|
-
* Replace all text outside of the $ TeX blocks with `\\text{...}`
|
|
31134
|
-
* This way, the entire resulting string can be rendered within <TeX>
|
|
31135
|
-
* and the text outside of the $ blocks will be non-TeX text.
|
|
31136
|
-
*/
|
|
31137
|
-
function replaceOutsideTeX(mathString) {
|
|
31138
|
-
// All the information we need is in the first section,
|
|
31139
|
-
// whether it's typed as "blockmath" or "paragraph"
|
|
31140
|
-
const firstSection = parse$4(mathString)[0];
|
|
31141
|
-
|
|
31142
|
-
// If it's blockMath, the outer level has the full math content.
|
|
31143
|
-
if (firstSection.type === "blockMath") {
|
|
31144
|
-
return firstSection.content;
|
|
31145
|
-
}
|
|
31146
|
-
|
|
31147
|
-
// If it's a paragraph, we need to iterate through the sections
|
|
31148
|
-
// to look for individual math blocks.
|
|
31149
|
-
const condensedNodes = condenseTextNodes(firstSection.content);
|
|
31150
|
-
let result = "";
|
|
31151
|
-
for (const piece of condensedNodes) {
|
|
31152
|
-
piece.type === "math" ? result += piece.content : result += `\\text{${escapeSpecialChars(piece.content)}}`;
|
|
31153
|
-
}
|
|
31154
|
-
return result;
|
|
31155
|
-
}
|
|
31156
|
-
// Helper function for replaceOutsideTeX()
|
|
31157
|
-
// Condense adjacent text nodes into a single text node
|
|
31158
|
-
function condenseTextNodes(nodes) {
|
|
31159
|
-
const result = [];
|
|
31160
|
-
if (!nodes) {
|
|
31161
|
-
return result;
|
|
31162
|
-
}
|
|
31163
|
-
let currentText = "";
|
|
31164
|
-
for (const node of nodes) {
|
|
31165
|
-
if (node.type === "math") {
|
|
31166
|
-
if (currentText) {
|
|
31167
|
-
result.push({
|
|
31168
|
-
type: "text",
|
|
31169
|
-
content: currentText
|
|
31170
|
-
});
|
|
31171
|
-
currentText = "";
|
|
31172
|
-
}
|
|
31173
|
-
result.push(node);
|
|
31174
|
-
} else {
|
|
31175
|
-
currentText += node.content;
|
|
31176
|
-
}
|
|
31177
|
-
}
|
|
31178
|
-
if (currentText) {
|
|
31179
|
-
result.push({
|
|
31180
|
-
type: "text",
|
|
31181
|
-
content: currentText
|
|
31182
|
-
});
|
|
31183
|
-
}
|
|
31184
|
-
return result;
|
|
31185
|
-
}
|
|
31186
|
-
|
|
31187
|
-
// Helper function for replaceOutsideTeX()
|
|
31188
|
-
function escapeSpecialChars(str) {
|
|
31189
|
-
// Escape $, \, {, and } characters
|
|
31190
|
-
return str.replace(/([$\\{}])/g, "\\$1");
|
|
31191
|
-
}
|
|
31192
|
-
|
|
31193
31172
|
/**
|
|
31194
31173
|
* Parse a string of text and math into a list of objects with type and content
|
|
31195
31174
|
*
|
|
@@ -31221,6 +31200,29 @@ const mathOnlyParser = SimpleMarkdown.parserFor({
|
|
|
31221
31200
|
inline: true
|
|
31222
31201
|
});
|
|
31223
31202
|
|
|
31203
|
+
/**
|
|
31204
|
+
* Replace all text outside of the $ TeX blocks with `\\text{...}`
|
|
31205
|
+
* This way, the entire resulting string can be rendered within <TeX>
|
|
31206
|
+
* and the text outside of the $ blocks will be non-TeX text.
|
|
31207
|
+
*/
|
|
31208
|
+
function replaceOutsideTeX(mathString) {
|
|
31209
|
+
// All the information we need is in the first section,
|
|
31210
|
+
// whether it's typed as "blockmath" or "paragraph"
|
|
31211
|
+
const parsed = mathOnlyParser(mathString);
|
|
31212
|
+
let result = "";
|
|
31213
|
+
for (const piece of parsed) {
|
|
31214
|
+
piece.type === "math" ? result += "$" + piece.content + "$" : piece.type === "specialCharacter" ? result += escapeIfUnescaped(piece.content) : result += piece.content;
|
|
31215
|
+
}
|
|
31216
|
+
return `\\text{${result}}`;
|
|
31217
|
+
}
|
|
31218
|
+
function escapeIfUnescaped(character) {
|
|
31219
|
+
if (character.length === 1) {
|
|
31220
|
+
return "\\" + character;
|
|
31221
|
+
} else {
|
|
31222
|
+
return character;
|
|
31223
|
+
}
|
|
31224
|
+
}
|
|
31225
|
+
|
|
31224
31226
|
function AxisLabels() {
|
|
31225
31227
|
const {
|
|
31226
31228
|
range,
|
|
@@ -31764,7 +31766,7 @@ const styles$e = StyleSheet.create({
|
|
|
31764
31766
|
}
|
|
31765
31767
|
});
|
|
31766
31768
|
|
|
31767
|
-
const _excluded$
|
|
31769
|
+
const _excluded$4 = ["onFormatChange", "checkValidity", "useArrowKeys"];
|
|
31768
31770
|
const {
|
|
31769
31771
|
firstNumericalParse,
|
|
31770
31772
|
captureScratchpadTouchStart
|
|
@@ -31794,22 +31796,25 @@ const getNumericFormat = KhanMath.getNumericFormat;
|
|
|
31794
31796
|
class NumberInput extends React.Component {
|
|
31795
31797
|
constructor(...args) {
|
|
31796
31798
|
super(...args);
|
|
31799
|
+
this.inputRef = /*#__PURE__*/React.createRef();
|
|
31797
31800
|
this.state = {
|
|
31798
31801
|
format: this.props.format
|
|
31799
31802
|
};
|
|
31803
|
+
this._getInput = () => {
|
|
31804
|
+
if (!this.inputRef.current) {
|
|
31805
|
+
throw new PerseusError("Input ref accessed before set", Errors.Internal);
|
|
31806
|
+
}
|
|
31807
|
+
return this.inputRef.current;
|
|
31808
|
+
};
|
|
31800
31809
|
/* Return the current "value" of this input
|
|
31801
31810
|
* If empty, it returns the placeholder (if it is a number) or null
|
|
31802
31811
|
*/
|
|
31803
31812
|
this.getValue = () => {
|
|
31804
|
-
return this.parseInputValue(
|
|
31805
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'value' does not exist on type 'Element | Text'.
|
|
31806
|
-
ReactDOM__default.findDOMNode(this.refs.input).value // eslint-disable-line react/no-string-refs
|
|
31807
|
-
);
|
|
31813
|
+
return this.parseInputValue(this._getInput().value);
|
|
31808
31814
|
};
|
|
31809
31815
|
/* Return the current string value of this input */
|
|
31810
31816
|
this.getStringValue = () => {
|
|
31811
|
-
|
|
31812
|
-
return ReactDOM__default.findDOMNode(this.refs.input).value.toString(); // eslint-disable-line react/no-string-refs
|
|
31817
|
+
return this._getInput().toString();
|
|
31813
31818
|
};
|
|
31814
31819
|
this.parseInputValue = value => {
|
|
31815
31820
|
if (value === "") {
|
|
@@ -31821,26 +31826,21 @@ class NumberInput extends React.Component {
|
|
|
31821
31826
|
};
|
|
31822
31827
|
/* Set text input focus to this input */
|
|
31823
31828
|
this.focus = () => {
|
|
31824
|
-
|
|
31825
|
-
ReactDOM__default.findDOMNode(this.refs.input).focus(); // eslint-disable-line react/no-string-refs
|
|
31829
|
+
this._getInput().focus();
|
|
31826
31830
|
this._handleFocus();
|
|
31827
31831
|
};
|
|
31828
31832
|
this.blur = () => {
|
|
31829
|
-
|
|
31830
|
-
ReactDOM__default.findDOMNode(this.refs.input).blur(); // eslint-disable-line react/no-string-refs
|
|
31833
|
+
this._getInput().blur();
|
|
31831
31834
|
this._handleBlur();
|
|
31832
31835
|
};
|
|
31833
31836
|
this.setSelectionRange = (selectionStart, selectionEnd) => {
|
|
31834
|
-
|
|
31835
|
-
ReactDOM__default.findDOMNode(this).setSelectionRange(selectionStart, selectionEnd);
|
|
31837
|
+
this._getInput().setSelectionRange(selectionStart, selectionEnd);
|
|
31836
31838
|
};
|
|
31837
31839
|
this.getSelectionStart = () => {
|
|
31838
|
-
|
|
31839
|
-
return ReactDOM__default.findDOMNode(this).selectionStart;
|
|
31840
|
+
return this._getInput().selectionStart;
|
|
31840
31841
|
};
|
|
31841
31842
|
this.getSelectionEnd = () => {
|
|
31842
|
-
|
|
31843
|
-
return ReactDOM__default.findDOMNode(this).selectionEnd;
|
|
31843
|
+
return this._getInput().selectionEnd;
|
|
31844
31844
|
};
|
|
31845
31845
|
this._checkValidity = value => {
|
|
31846
31846
|
if (value == null) {
|
|
@@ -31900,9 +31900,7 @@ class NumberInput extends React.Component {
|
|
|
31900
31900
|
}
|
|
31901
31901
|
};
|
|
31902
31902
|
this._setValue = (val, format) => {
|
|
31903
|
-
|
|
31904
|
-
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'val' does not exist on type 'JQueryStatic'.
|
|
31905
|
-
$(ReactDOM__default.findDOMNode(this.refs.input)).val(toNumericString(val, format));
|
|
31903
|
+
this._getInput().value = toNumericString(val, format);
|
|
31906
31904
|
};
|
|
31907
31905
|
}
|
|
31908
31906
|
componentDidUpdate(prevProps) {
|
|
@@ -31922,13 +31920,11 @@ class NumberInput extends React.Component {
|
|
|
31922
31920
|
classes = classes + " " + this.props.className;
|
|
31923
31921
|
}
|
|
31924
31922
|
const _this$props = this.props,
|
|
31925
|
-
restProps = _objectWithoutPropertiesLoose(_this$props, _excluded$
|
|
31923
|
+
restProps = _objectWithoutPropertiesLoose(_this$props, _excluded$4);
|
|
31926
31924
|
return /*#__PURE__*/React.createElement("input", _extends({}, restProps, {
|
|
31927
31925
|
className: classes,
|
|
31928
|
-
type: "text"
|
|
31929
|
-
|
|
31930
|
-
,
|
|
31931
|
-
ref: "input",
|
|
31926
|
+
type: "text",
|
|
31927
|
+
ref: this.inputRef,
|
|
31932
31928
|
onChange: this._handleChange,
|
|
31933
31929
|
onFocus: this._handleFocus,
|
|
31934
31930
|
onBlur: this._handleBlur,
|
|
@@ -32849,12 +32845,12 @@ const MafsCssTransformWrapper = ({
|
|
|
32849
32845
|
}
|
|
32850
32846
|
}, children);
|
|
32851
32847
|
|
|
32852
|
-
const _excluded$
|
|
32848
|
+
const _excluded$3 = ["children"];
|
|
32853
32849
|
const TextLabel = _ref => {
|
|
32854
32850
|
let {
|
|
32855
32851
|
children
|
|
32856
32852
|
} = _ref,
|
|
32857
|
-
rest = _objectWithoutPropertiesLoose(_ref, _excluded$
|
|
32853
|
+
rest = _objectWithoutPropertiesLoose(_ref, _excluded$3);
|
|
32858
32854
|
return /*#__PURE__*/React__default.createElement(Text$1, _extends({
|
|
32859
32855
|
size: 16,
|
|
32860
32856
|
svgTextProps: {
|
|
@@ -33435,15 +33431,18 @@ function useControlPoint(params) {
|
|
|
33435
33431
|
} = useGraphConfig();
|
|
33436
33432
|
const {
|
|
33437
33433
|
point,
|
|
33438
|
-
|
|
33434
|
+
ariaDescribedBy,
|
|
33435
|
+
ariaLabel,
|
|
33436
|
+
ariaLive = "polite",
|
|
33439
33437
|
color,
|
|
33440
|
-
cursor,
|
|
33441
33438
|
constrain = p => snap(snapStep, p),
|
|
33439
|
+
cursor,
|
|
33440
|
+
forwardedRef = noop$1,
|
|
33441
|
+
sequenceNumber = 1,
|
|
33442
33442
|
onMove = noop$1,
|
|
33443
33443
|
onClick = noop$1,
|
|
33444
33444
|
onFocus = noop$1,
|
|
33445
|
-
onBlur = noop$1
|
|
33446
|
-
forwardedRef = noop$1
|
|
33445
|
+
onBlur = noop$1
|
|
33447
33446
|
} = params;
|
|
33448
33447
|
const {
|
|
33449
33448
|
strings,
|
|
@@ -33466,6 +33465,13 @@ function useControlPoint(params) {
|
|
|
33466
33465
|
onMove,
|
|
33467
33466
|
constrainKeyboardMovement: constrain
|
|
33468
33467
|
});
|
|
33468
|
+
|
|
33469
|
+
// if custom aria label is not provided, will use default of sequence number and point coordinates
|
|
33470
|
+
const pointAriaLabel = ariaLabel || strings.srPointAtCoordinates({
|
|
33471
|
+
num: sequenceNumber,
|
|
33472
|
+
x: srFormatNumber(point[X], locale),
|
|
33473
|
+
y: srFormatNumber(point[Y], locale)
|
|
33474
|
+
});
|
|
33469
33475
|
useLayoutEffect(() => {
|
|
33470
33476
|
setForwardedRef(forwardedRef, focusableHandleRef.current);
|
|
33471
33477
|
}, [forwardedRef]);
|
|
@@ -33475,15 +33481,9 @@ function useControlPoint(params) {
|
|
|
33475
33481
|
tabIndex: disableKeyboardInteraction ? -1 : 0,
|
|
33476
33482
|
ref: focusableHandleRef,
|
|
33477
33483
|
role: "button",
|
|
33478
|
-
"aria-
|
|
33479
|
-
|
|
33480
|
-
|
|
33481
|
-
y: srFormatNumber(point[Y], locale)
|
|
33482
|
-
})
|
|
33483
|
-
// aria-live="assertive" causes the new location of the point to be
|
|
33484
|
-
// announced immediately on move.
|
|
33485
|
-
,
|
|
33486
|
-
"aria-live": "assertive",
|
|
33484
|
+
"aria-describedby": ariaDescribedBy,
|
|
33485
|
+
"aria-label": pointAriaLabel,
|
|
33486
|
+
"aria-live": ariaLive,
|
|
33487
33487
|
onFocus: event => {
|
|
33488
33488
|
onFocus(event);
|
|
33489
33489
|
setFocused(true);
|
|
@@ -34769,6 +34769,7 @@ const MafsGraph = props => {
|
|
|
34769
34769
|
const uniqueId = React.useId();
|
|
34770
34770
|
const descriptionId = `interactive-graph-description-${uniqueId}`;
|
|
34771
34771
|
const interactiveElementsDescriptionId = `interactive-graph-interactive-elements-description-${uniqueId}`;
|
|
34772
|
+
const unlimitedGraphKeyboardPromptId = `unlimited-graph-keyboard-prompt-${uniqueId}`;
|
|
34772
34773
|
const graphRef = React.useRef(null);
|
|
34773
34774
|
const {
|
|
34774
34775
|
analytics
|
|
@@ -34841,7 +34842,7 @@ const MafsGraph = props => {
|
|
|
34841
34842
|
handleKeyboardEvent(event, state, dispatch);
|
|
34842
34843
|
},
|
|
34843
34844
|
"aria-label": fullGraphAriaLabel,
|
|
34844
|
-
"aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId),
|
|
34845
|
+
"aria-describedby": describedByIds(fullGraphAriaDescription && descriptionId, interactiveElementsDescription && interactiveElementsDescriptionId, isUnlimitedGraphState(state) && "unlimited-graph-keyboard-prompt"),
|
|
34845
34846
|
ref: graphRef,
|
|
34846
34847
|
tabIndex: 0,
|
|
34847
34848
|
onFocus: event => {
|
|
@@ -34911,6 +34912,7 @@ const MafsGraph = props => {
|
|
|
34911
34912
|
height: height
|
|
34912
34913
|
}, /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), graph)))), interactionPrompt && /*#__PURE__*/React.createElement(View, {
|
|
34913
34914
|
style: {
|
|
34915
|
+
display: interactionPrompt ? undefined : "hidden",
|
|
34914
34916
|
textAlign: "center",
|
|
34915
34917
|
backgroundColor: "white",
|
|
34916
34918
|
border: "1px solid #21242C52",
|
|
@@ -34922,7 +34924,9 @@ const MafsGraph = props => {
|
|
|
34922
34924
|
top: "50%",
|
|
34923
34925
|
transform: "translateY(-50%)"
|
|
34924
34926
|
}
|
|
34925
|
-
}, /*#__PURE__*/React.createElement(LabelMedium,
|
|
34927
|
+
}, /*#__PURE__*/React.createElement(LabelMedium, {
|
|
34928
|
+
id: unlimitedGraphKeyboardPromptId
|
|
34929
|
+
}, strings.graphKeyboardPrompt))), renderGraphControls({
|
|
34926
34930
|
state,
|
|
34927
34931
|
dispatch,
|
|
34928
34932
|
width,
|
|
@@ -34985,6 +34989,10 @@ const renderPolygonGraphControls = props => {
|
|
|
34985
34989
|
} = props;
|
|
34986
34990
|
const shouldShowRemoveButton = showRemovePointButton && focusedPointIndex !== null;
|
|
34987
34991
|
|
|
34992
|
+
// We want to disable the closePolygon button when
|
|
34993
|
+
// there are not enough coords to make a polygon
|
|
34994
|
+
const disableCloseButton = coords.length < 3;
|
|
34995
|
+
|
|
34988
34996
|
// If polygon is closed, show open button.
|
|
34989
34997
|
// If polygon is open, show close button.
|
|
34990
34998
|
const polygonButton = closedPolygon ? /*#__PURE__*/React.createElement(Button, {
|
|
@@ -35002,12 +35010,12 @@ const renderPolygonGraphControls = props => {
|
|
|
35002
35010
|
// Conditional disable when there are less than 3 points in
|
|
35003
35011
|
// the graph
|
|
35004
35012
|
,
|
|
35005
|
-
disabled:
|
|
35013
|
+
disabled: disableCloseButton,
|
|
35006
35014
|
style: {
|
|
35007
35015
|
width: "100%",
|
|
35008
35016
|
marginLeft: "20px"
|
|
35009
35017
|
},
|
|
35010
|
-
tabIndex: 0,
|
|
35018
|
+
tabIndex: disableCloseButton ? -1 : 0,
|
|
35011
35019
|
onClick: () => {
|
|
35012
35020
|
props.dispatch(actions.polygon.closePolygon());
|
|
35013
35021
|
}
|
|
@@ -35895,8 +35903,12 @@ function doMovePoint(state, action) {
|
|
|
35895
35903
|
newValue: newValue
|
|
35896
35904
|
});
|
|
35897
35905
|
|
|
35898
|
-
//
|
|
35899
|
-
if
|
|
35906
|
+
// Boolean value to track whether we can let the polygon sides interact.
|
|
35907
|
+
// They can interact if it's an unlimited polygon that is open.
|
|
35908
|
+
const polygonSidesCanIntersect = state.numSides === "unlimited" && !state.closedPolygon;
|
|
35909
|
+
|
|
35910
|
+
// Reject the move if it would cause the sides of the polygon to cross.
|
|
35911
|
+
if (!polygonSidesCanIntersect && polygonSidesIntersect(newCoords)) {
|
|
35900
35912
|
return state;
|
|
35901
35913
|
}
|
|
35902
35914
|
return _extends({}, state, {
|
|
@@ -36477,7 +36489,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
36477
36489
|
});
|
|
36478
36490
|
|
|
36479
36491
|
const eq$1 = Util.eq;
|
|
36480
|
-
const deepEq$
|
|
36492
|
+
const deepEq$3 = Util.deepEq;
|
|
36481
36493
|
function scoreInteractiveGraph(userInput, rubric) {
|
|
36482
36494
|
// None-type graphs are not graded
|
|
36483
36495
|
if (userInput.type === "none" && rubric.correct.type === "none") {
|
|
@@ -36527,7 +36539,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36527
36539
|
// If the parabola coefficients match, it's correct.
|
|
36528
36540
|
const guessCoeffs = getQuadraticCoefficients(userInput.coords);
|
|
36529
36541
|
const correctCoeffs = getQuadraticCoefficients(rubric.correct.coords);
|
|
36530
|
-
if (deepEq$
|
|
36542
|
+
if (deepEq$3(guessCoeffs, correctCoeffs)) {
|
|
36531
36543
|
return {
|
|
36532
36544
|
type: "points",
|
|
36533
36545
|
earned: 1,
|
|
@@ -36541,7 +36553,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36541
36553
|
const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
|
|
36542
36554
|
const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
|
|
36543
36555
|
// If the canonical coefficients match, it's correct.
|
|
36544
|
-
if (deepEq$
|
|
36556
|
+
if (deepEq$3(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
|
|
36545
36557
|
return {
|
|
36546
36558
|
type: "points",
|
|
36547
36559
|
earned: 1,
|
|
@@ -36550,7 +36562,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36550
36562
|
};
|
|
36551
36563
|
}
|
|
36552
36564
|
} else if (userInput.type === "circle" && rubric.correct.type === "circle") {
|
|
36553
|
-
if (deepEq$
|
|
36565
|
+
if (deepEq$3(userInput.center, rubric.correct.center) && eq$1(userInput.radius, rubric.correct.radius)) {
|
|
36554
36566
|
return {
|
|
36555
36567
|
type: "points",
|
|
36556
36568
|
earned: 1,
|
|
@@ -36572,7 +36584,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36572
36584
|
guess == null || guess.sort();
|
|
36573
36585
|
// @ts-expect-error - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
|
|
36574
36586
|
correct.sort();
|
|
36575
|
-
if (deepEq$
|
|
36587
|
+
if (deepEq$3(guess, correct)) {
|
|
36576
36588
|
return {
|
|
36577
36589
|
type: "points",
|
|
36578
36590
|
earned: 1,
|
|
@@ -36594,7 +36606,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36594
36606
|
/* exact */
|
|
36595
36607
|
guess.sort();
|
|
36596
36608
|
correct.sort();
|
|
36597
|
-
match = deepEq$
|
|
36609
|
+
match = deepEq$3(guess, correct);
|
|
36598
36610
|
}
|
|
36599
36611
|
if (match) {
|
|
36600
36612
|
return {
|
|
@@ -36609,7 +36621,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36609
36621
|
let correct = Util.deepClone(rubric.correct.coords);
|
|
36610
36622
|
guess = _.invoke(guess, "sort").sort();
|
|
36611
36623
|
correct = _.invoke(correct, "sort").sort();
|
|
36612
|
-
if (deepEq$
|
|
36624
|
+
if (deepEq$3(guess, correct)) {
|
|
36613
36625
|
return {
|
|
36614
36626
|
type: "points",
|
|
36615
36627
|
earned: 1,
|
|
@@ -36620,7 +36632,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36620
36632
|
} else if (userInput.type === "ray" && rubric.correct.type === "ray" && userInput.coords != null) {
|
|
36621
36633
|
const guess = userInput.coords;
|
|
36622
36634
|
const correct = rubric.correct.coords;
|
|
36623
|
-
if (deepEq$
|
|
36635
|
+
if (deepEq$3(guess[0], correct[0]) && collinear(correct[0], correct[1], guess[1])) {
|
|
36624
36636
|
return {
|
|
36625
36637
|
type: "points",
|
|
36626
36638
|
earned: 1,
|
|
@@ -36647,7 +36659,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
36647
36659
|
/* exact */
|
|
36648
36660
|
match =
|
|
36649
36661
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
36650
|
-
deepEq$
|
|
36662
|
+
deepEq$3(guess[1], correct[1]) &&
|
|
36651
36663
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
36652
36664
|
collinear(correct[1], correct[0], guess[0]) &&
|
|
36653
36665
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
@@ -40821,6 +40833,7 @@ const getPromptJSON$7 = () => {
|
|
|
40821
40833
|
return getUnsupportedPromptJSON("measurer");
|
|
40822
40834
|
};
|
|
40823
40835
|
|
|
40836
|
+
const _excluded$2 = ["imageUrl", "imageTop", "imageLeft"];
|
|
40824
40837
|
const defaultImage = {
|
|
40825
40838
|
url: null,
|
|
40826
40839
|
top: 0,
|
|
@@ -40927,14 +40940,19 @@ Measurer.defaultProps = {
|
|
|
40927
40940
|
};
|
|
40928
40941
|
const propUpgrades = {
|
|
40929
40942
|
"1": v0props => {
|
|
40930
|
-
const
|
|
40943
|
+
const {
|
|
40944
|
+
imageUrl,
|
|
40945
|
+
imageTop,
|
|
40946
|
+
imageLeft
|
|
40947
|
+
} = v0props,
|
|
40948
|
+
rest = _objectWithoutPropertiesLoose(v0props, _excluded$2);
|
|
40949
|
+
return _extends({}, rest, {
|
|
40931
40950
|
image: {
|
|
40932
|
-
url:
|
|
40933
|
-
top:
|
|
40934
|
-
left:
|
|
40951
|
+
url: imageUrl,
|
|
40952
|
+
top: imageTop,
|
|
40953
|
+
left: imageLeft
|
|
40935
40954
|
}
|
|
40936
|
-
})
|
|
40937
|
-
return v1props;
|
|
40955
|
+
});
|
|
40938
40956
|
}
|
|
40939
40957
|
};
|
|
40940
40958
|
var Measurer$1 = {
|
|
@@ -42462,14 +42480,28 @@ const getPromptJSON$5 = (renderProps, userInput) => {
|
|
|
42462
42480
|
};
|
|
42463
42481
|
};
|
|
42464
42482
|
|
|
42465
|
-
|
|
42483
|
+
/**
|
|
42484
|
+
* Checks user input from the orderer widget to see if the user has started
|
|
42485
|
+
* ordering the options, making the widget scorable.
|
|
42486
|
+
* @param userInput
|
|
42487
|
+
* @see `scoreOrderer` for more details.
|
|
42488
|
+
*/
|
|
42489
|
+
function validateOrderer(userInput) {
|
|
42466
42490
|
if (userInput.current.length === 0) {
|
|
42467
42491
|
return {
|
|
42468
42492
|
type: "invalid",
|
|
42469
42493
|
message: null
|
|
42470
42494
|
};
|
|
42471
42495
|
}
|
|
42472
|
-
|
|
42496
|
+
return null;
|
|
42497
|
+
}
|
|
42498
|
+
|
|
42499
|
+
function scoreOrderer(userInput, rubric) {
|
|
42500
|
+
const validateError = validateOrderer(userInput);
|
|
42501
|
+
if (validateError) {
|
|
42502
|
+
return validateError;
|
|
42503
|
+
}
|
|
42504
|
+
const correct = _.isEqual(userInput.current, rubric.correctOptions.map(option => option.content));
|
|
42473
42505
|
return {
|
|
42474
42506
|
type: "points",
|
|
42475
42507
|
earned: correct ? 1 : 0,
|
|
@@ -42649,8 +42681,6 @@ Card.defaultProps = {
|
|
|
42649
42681
|
animating: false,
|
|
42650
42682
|
linterContext: linterContextDefault
|
|
42651
42683
|
};
|
|
42652
|
-
const NORMAL = "normal";
|
|
42653
|
-
const HORIZONTAL = "horizontal";
|
|
42654
42684
|
class Orderer extends React.Component {
|
|
42655
42685
|
constructor(...args) {
|
|
42656
42686
|
super(...args);
|
|
@@ -42797,7 +42827,7 @@ class Orderer extends React.Component {
|
|
|
42797
42827
|
};
|
|
42798
42828
|
this.findCorrectIndex = (draggable, list) => {
|
|
42799
42829
|
// Find the correct index for a card given the current cards.
|
|
42800
|
-
const isHorizontal = this.props.layout ===
|
|
42830
|
+
const isHorizontal = this.props.layout === "horizontal";
|
|
42801
42831
|
// eslint-disable-next-line react/no-string-refs
|
|
42802
42832
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
42803
42833
|
const $dragList = $(ReactDOM__default.findDOMNode(this.refs.dragList));
|
|
@@ -42847,28 +42877,24 @@ class Orderer extends React.Component {
|
|
|
42847
42877
|
if (draggable == null) {
|
|
42848
42878
|
return false;
|
|
42849
42879
|
}
|
|
42850
|
-
const isHorizontal = this.props.layout ===
|
|
42851
|
-
|
|
42852
|
-
|
|
42853
|
-
|
|
42854
|
-
|
|
42855
|
-
|
|
42856
|
-
|
|
42857
|
-
|
|
42858
|
-
|
|
42859
|
-
|
|
42860
|
-
|
|
42861
|
-
|
|
42862
|
-
|
|
42863
|
-
|
|
42864
|
-
|
|
42865
|
-
|
|
42866
|
-
|
|
42867
|
-
|
|
42868
|
-
// @ts-expect-error - TS2769 - No overload matches this call. | TS2339 - Property 'width' does not exist on type 'JQueryStatic'.
|
|
42869
|
-
$(dragList).width();
|
|
42870
|
-
const // @ts-expect-error - TS2339 - Property 'outerWidth' does not exist on type 'JQueryStatic'.
|
|
42871
|
-
draggableWidth = $draggable.outerWidth(true);
|
|
42880
|
+
const isHorizontal = this.props.layout === "horizontal";
|
|
42881
|
+
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
42882
|
+
const $draggable = $(ReactDOM__default.findDOMNode(draggable));
|
|
42883
|
+
// eslint-disable-next-line react/no-string-refs
|
|
42884
|
+
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
42885
|
+
const $bank = $(ReactDOM__default.findDOMNode(this.refs.bank));
|
|
42886
|
+
// @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
|
|
42887
|
+
const draggableOffset = $draggable.offset();
|
|
42888
|
+
// @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
|
|
42889
|
+
const bankOffset = $bank.offset();
|
|
42890
|
+
// @ts-expect-error - TS2339 - Property 'outerHeight' does not exist on type 'JQueryStatic'.
|
|
42891
|
+
const draggableHeight = $draggable.outerHeight(true);
|
|
42892
|
+
// @ts-expect-error - TS2339 - Property 'outerHeight' does not exist on type 'JQueryStatic'.
|
|
42893
|
+
const bankHeight = $bank.outerHeight(true);
|
|
42894
|
+
// @ts-expect-error - TS2339 - Property 'outerWidth' does not exist on type 'JQueryStatic'.
|
|
42895
|
+
const bankWidth = $bank.outerWidth(true);
|
|
42896
|
+
// @ts-expect-error - TS2339 - Property 'outerWidth' does not exist on type 'JQueryStatic'.
|
|
42897
|
+
const draggableWidth = $draggable.outerWidth(true);
|
|
42872
42898
|
if (isHorizontal) {
|
|
42873
42899
|
return draggableOffset.top + draggableHeight / 2 < bankOffset.top + bankHeight;
|
|
42874
42900
|
}
|
|
@@ -43017,8 +43043,8 @@ Orderer.defaultProps = {
|
|
|
43017
43043
|
current: [],
|
|
43018
43044
|
options: [],
|
|
43019
43045
|
correctOptions: [],
|
|
43020
|
-
height:
|
|
43021
|
-
layout:
|
|
43046
|
+
height: "normal",
|
|
43047
|
+
layout: "horizontal",
|
|
43022
43048
|
linterContext: linterContextDefault
|
|
43023
43049
|
};
|
|
43024
43050
|
var Orderer$1 = {
|
|
@@ -43297,18 +43323,36 @@ const getPromptJSON$3 = () => {
|
|
|
43297
43323
|
};
|
|
43298
43324
|
|
|
43299
43325
|
const {
|
|
43300
|
-
deepEq: deepEq$
|
|
43326
|
+
deepEq: deepEq$2
|
|
43301
43327
|
} = Util;
|
|
43302
|
-
|
|
43303
|
-
|
|
43328
|
+
|
|
43329
|
+
/**
|
|
43330
|
+
* Checks user input to confirm it is not the same as the starting values for the graph.
|
|
43331
|
+
* This means the user has modified the graph, and the question can be scored.
|
|
43332
|
+
*
|
|
43333
|
+
* @see 'scorePlotter' for more details on scoring.
|
|
43334
|
+
*/
|
|
43335
|
+
function validatePlotter(userInput, validationData) {
|
|
43336
|
+
if (deepEq$2(userInput, validationData.starting)) {
|
|
43304
43337
|
return {
|
|
43305
43338
|
type: "invalid",
|
|
43306
43339
|
message: null
|
|
43307
43340
|
};
|
|
43308
43341
|
}
|
|
43342
|
+
return null;
|
|
43343
|
+
}
|
|
43344
|
+
|
|
43345
|
+
const {
|
|
43346
|
+
deepEq: deepEq$1
|
|
43347
|
+
} = Util;
|
|
43348
|
+
function scorePlotter(userInput, scoringData) {
|
|
43349
|
+
const validationError = validatePlotter(userInput, scoringData);
|
|
43350
|
+
if (validationError) {
|
|
43351
|
+
return validationError;
|
|
43352
|
+
}
|
|
43309
43353
|
return {
|
|
43310
43354
|
type: "points",
|
|
43311
|
-
earned: deepEq$1(userInput,
|
|
43355
|
+
earned: deepEq$1(userInput, scoringData.correct) ? 1 : 0,
|
|
43312
43356
|
total: 1,
|
|
43313
43357
|
message: null
|
|
43314
43358
|
};
|
|
@@ -44193,7 +44237,14 @@ const getPromptJSON$1 = userInput => {
|
|
|
44193
44237
|
};
|
|
44194
44238
|
};
|
|
44195
44239
|
|
|
44196
|
-
|
|
44240
|
+
/**
|
|
44241
|
+
* Checks user input for the sorter widget to ensure that the user has made
|
|
44242
|
+
* changes before attempting to score the widget.
|
|
44243
|
+
* @param userInput
|
|
44244
|
+
* @see 'scoreSorter' in 'packages/perseus/src/widgets/sorter/score-sorter.ts'
|
|
44245
|
+
* for more details on how the sorter widget is scored.
|
|
44246
|
+
*/
|
|
44247
|
+
function validateSorter(userInput) {
|
|
44197
44248
|
// If the sorter widget hasn't been changed yet, we treat it as "empty" which
|
|
44198
44249
|
// prevents the "Check" button from becoming active. We want the user
|
|
44199
44250
|
// to make a change before trying to move forward. This makes an
|
|
@@ -44206,6 +44257,14 @@ function scoreSorter(userInput, rubric) {
|
|
|
44206
44257
|
message: null
|
|
44207
44258
|
};
|
|
44208
44259
|
}
|
|
44260
|
+
return null;
|
|
44261
|
+
}
|
|
44262
|
+
|
|
44263
|
+
function scoreSorter(userInput, rubric) {
|
|
44264
|
+
const validationError = validateSorter(userInput);
|
|
44265
|
+
if (validationError) {
|
|
44266
|
+
return validationError;
|
|
44267
|
+
}
|
|
44209
44268
|
const correct = Util.deepEq(userInput.options, rubric.correct);
|
|
44210
44269
|
return {
|
|
44211
44270
|
type: "points",
|
|
@@ -44784,7 +44843,7 @@ const init = function init() {
|
|
|
44784
44843
|
|
|
44785
44844
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44786
44845
|
const libName = "@khanacademy/perseus";
|
|
44787
|
-
const libVersion = "43.
|
|
44846
|
+
const libVersion = "43.1.0";
|
|
44788
44847
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44789
44848
|
|
|
44790
44849
|
const apiVersion = {
|
|
@@ -46388,7 +46447,7 @@ const str = {
|
|
|
46388
46447
|
}
|
|
46389
46448
|
};
|
|
46390
46449
|
|
|
46391
|
-
const _excluded = ["item", "children", "shape", "serializedState", "onSerializedStateUpdated"];
|
|
46450
|
+
const _excluded$1 = ["item", "children", "shape", "serializedState", "onSerializedStateUpdated"];
|
|
46392
46451
|
|
|
46393
46452
|
// TODO(mdr)
|
|
46394
46453
|
// TODO(mdr)
|
|
@@ -46473,7 +46532,7 @@ class MultiRenderer extends React.Component {
|
|
|
46473
46532
|
// explicitly pulling them out of `this.props` so I don't pass them to
|
|
46474
46533
|
// `<Renderer>`. I'm not sure how else to do this.
|
|
46475
46534
|
const _this$props = this.props,
|
|
46476
|
-
otherProps = _objectWithoutPropertiesLoose(_this$props, _excluded);
|
|
46535
|
+
otherProps = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
|
|
46477
46536
|
return _extends({}, otherProps, {
|
|
46478
46537
|
strings: this.context.strings
|
|
46479
46538
|
});
|
|
@@ -48277,6 +48336,27 @@ function pair(parseA, parseB) {
|
|
|
48277
48336
|
};
|
|
48278
48337
|
}
|
|
48279
48338
|
|
|
48339
|
+
function pipeParsers(p) {
|
|
48340
|
+
return new ParserPipeline(p);
|
|
48341
|
+
}
|
|
48342
|
+
class ParserPipeline {
|
|
48343
|
+
constructor(parser) {
|
|
48344
|
+
this.parser = parser;
|
|
48345
|
+
}
|
|
48346
|
+
then(nextParser) {
|
|
48347
|
+
return new ParserPipeline(composeParsers(this.parser, nextParser));
|
|
48348
|
+
}
|
|
48349
|
+
}
|
|
48350
|
+
function composeParsers(parserA, parserB) {
|
|
48351
|
+
return (rawValue, ctx) => {
|
|
48352
|
+
const partialResult = parserA(rawValue, ctx);
|
|
48353
|
+
if (isFailure(partialResult)) {
|
|
48354
|
+
return partialResult;
|
|
48355
|
+
}
|
|
48356
|
+
return parserB(partialResult.value, ctx);
|
|
48357
|
+
};
|
|
48358
|
+
}
|
|
48359
|
+
|
|
48280
48360
|
function record(parseKey, parseValue) {
|
|
48281
48361
|
return (rawValue, ctx) => {
|
|
48282
48362
|
if (!isObject(rawValue)) {
|
|
@@ -48357,6 +48437,20 @@ function either(parseA, parseB) {
|
|
|
48357
48437
|
};
|
|
48358
48438
|
}
|
|
48359
48439
|
|
|
48440
|
+
function defaulted(parser, fallback) {
|
|
48441
|
+
return (rawValue, ctx) => {
|
|
48442
|
+
if (rawValue == null) {
|
|
48443
|
+
return success(fallback(rawValue));
|
|
48444
|
+
}
|
|
48445
|
+
return parser(rawValue, ctx);
|
|
48446
|
+
};
|
|
48447
|
+
}
|
|
48448
|
+
|
|
48449
|
+
const parseImages = defaulted(record(string, object({
|
|
48450
|
+
width: number,
|
|
48451
|
+
height: number
|
|
48452
|
+
})), () => ({}));
|
|
48453
|
+
|
|
48360
48454
|
function parseWidget(parseType, parseOptions) {
|
|
48361
48455
|
return object({
|
|
48362
48456
|
type: parseType,
|
|
@@ -48365,10 +48459,21 @@ function parseWidget(parseType, parseOptions) {
|
|
|
48365
48459
|
alignment: optional(string),
|
|
48366
48460
|
options: parseOptions,
|
|
48367
48461
|
key: optional(number),
|
|
48368
|
-
version: object({
|
|
48462
|
+
version: optional(object({
|
|
48369
48463
|
major: number,
|
|
48370
48464
|
minor: number
|
|
48371
|
-
})
|
|
48465
|
+
}))
|
|
48466
|
+
});
|
|
48467
|
+
}
|
|
48468
|
+
function parseWidgetWithVersion(parseVersion, parseType, parseOptions) {
|
|
48469
|
+
return object({
|
|
48470
|
+
type: parseType,
|
|
48471
|
+
static: optional(boolean),
|
|
48472
|
+
graded: optional(boolean),
|
|
48473
|
+
alignment: optional(string),
|
|
48474
|
+
options: parseOptions,
|
|
48475
|
+
key: optional(number),
|
|
48476
|
+
version: parseVersion
|
|
48372
48477
|
});
|
|
48373
48478
|
}
|
|
48374
48479
|
|
|
@@ -48376,7 +48481,7 @@ const parseCategorizerWidget = parseWidget(constant("categorizer"), object({
|
|
|
48376
48481
|
items: array(string),
|
|
48377
48482
|
categories: array(string),
|
|
48378
48483
|
randomizeItems: boolean,
|
|
48379
|
-
static: boolean,
|
|
48484
|
+
static: defaulted(boolean, () => false),
|
|
48380
48485
|
values: array(number),
|
|
48381
48486
|
highlightLint: optional(boolean),
|
|
48382
48487
|
linterContext: optional(object({
|
|
@@ -48408,7 +48513,7 @@ const parseDefinitionWidget = parseWidget(constant("definition"), object({
|
|
|
48408
48513
|
|
|
48409
48514
|
const parseDropdownWidget = parseWidget(constant("dropdown"), object({
|
|
48410
48515
|
placeholder: string,
|
|
48411
|
-
static: boolean,
|
|
48516
|
+
static: defaulted(boolean, () => false),
|
|
48412
48517
|
choices: array(object({
|
|
48413
48518
|
content: string,
|
|
48414
48519
|
correct: boolean
|
|
@@ -48431,9 +48536,12 @@ const parseAnswerForm = object({
|
|
|
48431
48536
|
form: boolean,
|
|
48432
48537
|
simplify: boolean,
|
|
48433
48538
|
considered: enumeration("correct", "wrong", "ungraded"),
|
|
48434
|
-
key: optional(string)
|
|
48539
|
+
key: pipeParsers(optional(union(string).or(number).parser)).then((key, ctx) => ctx.success(String(key))).parser
|
|
48435
48540
|
});
|
|
48436
|
-
const
|
|
48541
|
+
const parseExpressionWidgetV1 = parseWidgetWithVersion(object({
|
|
48542
|
+
major: constant(1),
|
|
48543
|
+
minor: number
|
|
48544
|
+
}), constant("expression"), object({
|
|
48437
48545
|
answerForms: array(parseAnswerForm),
|
|
48438
48546
|
functions: array(string),
|
|
48439
48547
|
times: boolean,
|
|
@@ -48442,23 +48550,68 @@ const parseExpressionWidget = parseWidget(constant("expression"), object({
|
|
|
48442
48550
|
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
48443
48551
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48444
48552
|
}));
|
|
48553
|
+
const parseExpressionWidgetV0 = parseWidgetWithVersion(optional(object({
|
|
48554
|
+
major: constant(0),
|
|
48555
|
+
minor: number
|
|
48556
|
+
})), constant("expression"), object({
|
|
48557
|
+
functions: array(string),
|
|
48558
|
+
times: boolean,
|
|
48559
|
+
visibleLabel: optional(string),
|
|
48560
|
+
ariaLabel: optional(string),
|
|
48561
|
+
form: boolean,
|
|
48562
|
+
simplify: boolean,
|
|
48563
|
+
value: string,
|
|
48564
|
+
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
48565
|
+
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48566
|
+
}));
|
|
48567
|
+
function migrateV0ToV1(widget, ctx) {
|
|
48568
|
+
const {
|
|
48569
|
+
options
|
|
48570
|
+
} = widget;
|
|
48571
|
+
return ctx.success(_extends({}, widget, {
|
|
48572
|
+
version: {
|
|
48573
|
+
major: 1,
|
|
48574
|
+
minor: 0
|
|
48575
|
+
},
|
|
48576
|
+
options: {
|
|
48577
|
+
times: options.times,
|
|
48578
|
+
buttonSets: options.buttonSets,
|
|
48579
|
+
functions: options.functions,
|
|
48580
|
+
buttonsVisible: options.buttonsVisible,
|
|
48581
|
+
visibleLabel: options.visibleLabel,
|
|
48582
|
+
ariaLabel: options.ariaLabel,
|
|
48583
|
+
answerForms: [{
|
|
48584
|
+
considered: "correct",
|
|
48585
|
+
form: options.form,
|
|
48586
|
+
simplify: options.simplify,
|
|
48587
|
+
value: options.value
|
|
48588
|
+
}]
|
|
48589
|
+
}
|
|
48590
|
+
}));
|
|
48591
|
+
}
|
|
48592
|
+
const parseExpressionWidget = union(parseExpressionWidgetV1).or(pipeParsers(parseExpressionWidgetV0).then(migrateV0ToV1).parser).parser;
|
|
48445
48593
|
|
|
48446
|
-
const parsePerseusRenderer = object({
|
|
48447
|
-
|
|
48594
|
+
const parsePerseusRenderer = defaulted(object({
|
|
48595
|
+
// TODO(benchristel): content is also defaulted to empty string in
|
|
48596
|
+
// renderer.tsx. See if we can remove one default or the other.
|
|
48597
|
+
content: defaulted(string, () => ""),
|
|
48448
48598
|
// This module has an import cycle with parseWidgetsMap, because the
|
|
48449
48599
|
// `group` widget can contain another renderer.
|
|
48450
48600
|
// The anonymous function below ensures that we don't try to access
|
|
48451
48601
|
// parseWidgetsMap before it's defined.
|
|
48452
|
-
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
48602
|
+
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
|
|
48453
48603
|
metadata: optional(array(string)),
|
|
48454
|
-
images:
|
|
48455
|
-
|
|
48456
|
-
|
|
48457
|
-
|
|
48458
|
-
|
|
48604
|
+
images: parseImages
|
|
48605
|
+
}),
|
|
48606
|
+
// Default value
|
|
48607
|
+
() => ({
|
|
48608
|
+
content: "",
|
|
48609
|
+
widgets: {},
|
|
48610
|
+
images: {}
|
|
48611
|
+
}));
|
|
48459
48612
|
|
|
48460
48613
|
const parseGradedGroupWidgetOptions = object({
|
|
48461
|
-
title: string,
|
|
48614
|
+
title: defaulted(string, () => ""),
|
|
48462
48615
|
hasHint: optional(nullable(boolean)),
|
|
48463
48616
|
// This module has an import cycle with parsePerseusRenderer.
|
|
48464
48617
|
// The anonymous function below ensures that we don't try to access
|
|
@@ -48552,7 +48705,7 @@ const parseIframeWidget = parseWidget(constant("iframe"), object({
|
|
|
48552
48705
|
height: union(number).or(string).parser,
|
|
48553
48706
|
allowFullScreen: boolean,
|
|
48554
48707
|
allowTopNavigation: optional(boolean),
|
|
48555
|
-
static: boolean
|
|
48708
|
+
static: defaulted(boolean, () => false)
|
|
48556
48709
|
}));
|
|
48557
48710
|
|
|
48558
48711
|
const parsePerseusImageBackground = object({
|
|
@@ -48931,13 +49084,13 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
|
48931
49084
|
padding: boolean
|
|
48932
49085
|
}));
|
|
48933
49086
|
|
|
48934
|
-
const parseMatrixWidget = parseWidget(constant("matrix"), object({
|
|
48935
|
-
prefix: string,
|
|
48936
|
-
suffix: string,
|
|
49087
|
+
const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
|
|
49088
|
+
prefix: optional(string),
|
|
49089
|
+
suffix: optional(string),
|
|
48937
49090
|
answers: array(array(number)),
|
|
48938
|
-
cursorPosition: array(number),
|
|
49091
|
+
cursorPosition: optional(array(number)),
|
|
48939
49092
|
matrixBoardSize: array(number),
|
|
48940
|
-
static: boolean
|
|
49093
|
+
static: optional(boolean)
|
|
48941
49094
|
}));
|
|
48942
49095
|
|
|
48943
49096
|
const parseMeasurerWidget = parseWidget(constant("measurer"), object({
|
|
@@ -49007,8 +49160,8 @@ const parseOrdererWidget = parseWidget(constant("orderer"), object({
|
|
|
49007
49160
|
options: array(parseRenderer),
|
|
49008
49161
|
correctOptions: array(parseRenderer),
|
|
49009
49162
|
otherOptions: array(parseRenderer),
|
|
49010
|
-
height:
|
|
49011
|
-
layout:
|
|
49163
|
+
height: enumeration("normal", "auto"),
|
|
49164
|
+
layout: enumeration("horizontal", "vertical")
|
|
49012
49165
|
}));
|
|
49013
49166
|
|
|
49014
49167
|
const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
@@ -49053,7 +49206,7 @@ const parsePythonProgramWidget = parseWidget(constant("python-program"), object(
|
|
|
49053
49206
|
|
|
49054
49207
|
const parseRadioWidget = parseWidget(constant("radio"), object({
|
|
49055
49208
|
choices: array(object({
|
|
49056
|
-
content: string,
|
|
49209
|
+
content: defaulted(string, () => ""),
|
|
49057
49210
|
clue: optional(string),
|
|
49058
49211
|
correct: optional(boolean),
|
|
49059
49212
|
isNoneOfTheAbove: optional(boolean),
|
|
@@ -49214,26 +49367,42 @@ const parseWidgetMapKeyComponents = pair(string, parseStringToPositiveInt);
|
|
|
49214
49367
|
const parseHint = object({
|
|
49215
49368
|
replace: optional(boolean),
|
|
49216
49369
|
content: string,
|
|
49217
|
-
widgets: parseWidgetsMap,
|
|
49370
|
+
widgets: defaulted(parseWidgetsMap, () => ({})),
|
|
49218
49371
|
metadata: optional(array(string)),
|
|
49219
|
-
images:
|
|
49220
|
-
width: number,
|
|
49221
|
-
height: number
|
|
49222
|
-
}))
|
|
49372
|
+
images: parseImages
|
|
49223
49373
|
});
|
|
49224
49374
|
|
|
49375
|
+
const _excluded = ["type", "options"];
|
|
49225
49376
|
object({
|
|
49226
49377
|
question: parsePerseusRenderer,
|
|
49227
|
-
hints: array(parseHint),
|
|
49228
|
-
answerArea: record(enumeration(...ItemExtras), boolean),
|
|
49229
|
-
itemDataVersion: object({
|
|
49378
|
+
hints: defaulted(array(parseHint), () => []),
|
|
49379
|
+
answerArea: pipeParsers(defaulted(object({}), () => ({}))).then(migrateAnswerArea).then(record(enumeration(...ItemExtras), boolean)).parser,
|
|
49380
|
+
itemDataVersion: optional(object({
|
|
49230
49381
|
major: number,
|
|
49231
49382
|
minor: number
|
|
49232
|
-
}),
|
|
49383
|
+
})),
|
|
49233
49384
|
// Deprecated field
|
|
49234
49385
|
answer: any
|
|
49235
49386
|
});
|
|
49236
49387
|
|
|
49388
|
+
// Some answerAreas have extra fields, like:
|
|
49389
|
+
//
|
|
49390
|
+
// "answerArea": {
|
|
49391
|
+
// "type": "multiple",
|
|
49392
|
+
// "options": {
|
|
49393
|
+
// "content": "",
|
|
49394
|
+
// "images": {},
|
|
49395
|
+
// "widgets": {}
|
|
49396
|
+
// }
|
|
49397
|
+
// }
|
|
49398
|
+
//
|
|
49399
|
+
// The "type" and "options" fields don't seem to be used anywhere. This
|
|
49400
|
+
// migration function removes them.
|
|
49401
|
+
function migrateAnswerArea(rawValue, ctx) {
|
|
49402
|
+
const rest = _objectWithoutPropertiesLoose(rawValue, _excluded);
|
|
49403
|
+
return ctx.success(rest);
|
|
49404
|
+
}
|
|
49405
|
+
|
|
49237
49406
|
/**
|
|
49238
49407
|
* Helper to parse PerseusItem JSON
|
|
49239
49408
|
* Why not just use JSON.parse? We want:
|