@khanacademy/perseus 45.1.0 → 46.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.css +1 -1
- package/dist/es/index.js +166 -186
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +141 -160
- package/dist/index.js.map +1 -1
- package/dist/renderer.d.ts +0 -1
- package/dist/types.d.ts +0 -1
- package/dist/util/scoring.d.ts +20 -0
- package/dist/util.d.ts +0 -30
- package/dist/widgets/input-number/input-number.d.ts +1 -1
- package/dist/widgets/numeric-input/numeric-input.d.ts +5 -1
- package/package.json +1 -1
package/dist/es/index.css
CHANGED
package/dist/es/index.js
CHANGED
|
@@ -2793,12 +2793,6 @@ const rWidgetRule = /^\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/;
|
|
|
2793
2793
|
const rTypeFromWidgetId = /^([a-z-]+) ([0-9]+)$/;
|
|
2794
2794
|
const rWidgetParts = new RegExp(rWidgetRule.source + "$");
|
|
2795
2795
|
const snowman = "\u2603";
|
|
2796
|
-
const noScore = {
|
|
2797
|
-
type: "points",
|
|
2798
|
-
earned: 0,
|
|
2799
|
-
total: 0,
|
|
2800
|
-
message: null
|
|
2801
|
-
};
|
|
2802
2796
|
const seededRNG$1 = function seededRNG(seed) {
|
|
2803
2797
|
let randomSeed = seed;
|
|
2804
2798
|
return function () {
|
|
@@ -2875,97 +2869,6 @@ const split = "x".split(/(.)/g).length ? function (str, r) {
|
|
|
2875
2869
|
return output;
|
|
2876
2870
|
};
|
|
2877
2871
|
|
|
2878
|
-
/**
|
|
2879
|
-
* Combine two score objects.
|
|
2880
|
-
*
|
|
2881
|
-
* Given two score objects for two different widgets, combine them so that
|
|
2882
|
-
* if one is wrong, the total score is wrong, etc.
|
|
2883
|
-
*/
|
|
2884
|
-
function combineScores(scoreA, scoreB) {
|
|
2885
|
-
let message;
|
|
2886
|
-
if (scoreA.type === "points" && scoreB.type === "points") {
|
|
2887
|
-
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
2888
|
-
// TODO(alpert): Figure out how to combine messages usefully
|
|
2889
|
-
message = null;
|
|
2890
|
-
} else {
|
|
2891
|
-
message = scoreA.message || scoreB.message;
|
|
2892
|
-
}
|
|
2893
|
-
return {
|
|
2894
|
-
type: "points",
|
|
2895
|
-
earned: scoreA.earned + scoreB.earned,
|
|
2896
|
-
total: scoreA.total + scoreB.total,
|
|
2897
|
-
message: message
|
|
2898
|
-
};
|
|
2899
|
-
}
|
|
2900
|
-
if (scoreA.type === "points" && scoreB.type === "invalid") {
|
|
2901
|
-
return scoreB;
|
|
2902
|
-
}
|
|
2903
|
-
if (scoreA.type === "invalid" && scoreB.type === "points") {
|
|
2904
|
-
return scoreA;
|
|
2905
|
-
}
|
|
2906
|
-
if (scoreA.type === "invalid" && scoreB.type === "invalid") {
|
|
2907
|
-
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
2908
|
-
// TODO(alpert): Figure out how to combine messages usefully
|
|
2909
|
-
message = null;
|
|
2910
|
-
} else {
|
|
2911
|
-
message = scoreA.message || scoreB.message;
|
|
2912
|
-
}
|
|
2913
|
-
return {
|
|
2914
|
-
type: "invalid",
|
|
2915
|
-
message: message
|
|
2916
|
-
};
|
|
2917
|
-
}
|
|
2918
|
-
|
|
2919
|
-
/**
|
|
2920
|
-
* The above checks cover all combinations of score type, so if we get here
|
|
2921
|
-
* then something is amiss with our inputs.
|
|
2922
|
-
*/
|
|
2923
|
-
throw new PerseusError("PerseusScore with unknown type encountered", Errors.InvalidInput, {
|
|
2924
|
-
metadata: {
|
|
2925
|
-
scoreA: JSON.stringify(scoreA),
|
|
2926
|
-
scoreB: JSON.stringify(scoreB)
|
|
2927
|
-
}
|
|
2928
|
-
});
|
|
2929
|
-
}
|
|
2930
|
-
function flattenScores(widgetScoreMap) {
|
|
2931
|
-
return Object.values(widgetScoreMap).reduce(combineScores, noScore);
|
|
2932
|
-
}
|
|
2933
|
-
function isCorrect(score) {
|
|
2934
|
-
return score.type === "points" && score.earned >= score.total;
|
|
2935
|
-
}
|
|
2936
|
-
function keScoreFromPerseusScore(score,
|
|
2937
|
-
// It's weird, but this is what we're passing it
|
|
2938
|
-
guess, state) {
|
|
2939
|
-
if (score.type === "points") {
|
|
2940
|
-
return {
|
|
2941
|
-
empty: false,
|
|
2942
|
-
correct: isCorrect(score),
|
|
2943
|
-
message: score.message,
|
|
2944
|
-
guess: guess,
|
|
2945
|
-
state: state
|
|
2946
|
-
};
|
|
2947
|
-
}
|
|
2948
|
-
if (score.type === "invalid") {
|
|
2949
|
-
return {
|
|
2950
|
-
empty: true,
|
|
2951
|
-
correct: false,
|
|
2952
|
-
message: score.message,
|
|
2953
|
-
suppressAlmostThere: score.suppressAlmostThere,
|
|
2954
|
-
guess: guess,
|
|
2955
|
-
state: state
|
|
2956
|
-
};
|
|
2957
|
-
}
|
|
2958
|
-
throw new PerseusError(
|
|
2959
|
-
// @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
|
|
2960
|
-
"Invalid score type: " + score.type, Errors.InvalidInput, {
|
|
2961
|
-
metadata: {
|
|
2962
|
-
score: JSON.stringify(score),
|
|
2963
|
-
guess: JSON.stringify(guess),
|
|
2964
|
-
state: JSON.stringify(state)
|
|
2965
|
-
}
|
|
2966
|
-
});
|
|
2967
|
-
}
|
|
2968
|
-
|
|
2969
2872
|
/**
|
|
2970
2873
|
* Return the first valid interpretation of 'text' as a number, in the form
|
|
2971
2874
|
* {value: 2.3, exact: true}.
|
|
@@ -3263,28 +3166,6 @@ function strongEncodeURIComponent(str) {
|
|
|
3263
3166
|
.replace(/\*/g, "%2A");
|
|
3264
3167
|
}
|
|
3265
3168
|
|
|
3266
|
-
/**
|
|
3267
|
-
* If a widget says that it is empty once it is graded.
|
|
3268
|
-
* Trying to encapsulate references to the score format.
|
|
3269
|
-
*/
|
|
3270
|
-
function scoreIsEmpty(score) {
|
|
3271
|
-
// HACK(benkomalo): ugh. this isn't great; the Perseus score objects
|
|
3272
|
-
// overload the type "invalid" for what should probably be three
|
|
3273
|
-
// distinct cases:
|
|
3274
|
-
// - truly empty or not fully filled out
|
|
3275
|
-
// - invalid or malformed inputs
|
|
3276
|
-
// - "almost correct" like inputs where the widget wants to give
|
|
3277
|
-
// feedback (e.g. a fraction needs to be reduced, or `pi` should
|
|
3278
|
-
// be used instead of 3.14)
|
|
3279
|
-
//
|
|
3280
|
-
// Unfortunately the coercion happens all over the place, as these
|
|
3281
|
-
// Perseus style score objects are created *everywhere* (basically
|
|
3282
|
-
// in every widget), so it's hard to change now. We assume that
|
|
3283
|
-
// anything with a "message" is not truly empty, and one of the
|
|
3284
|
-
// latter two cases for now.
|
|
3285
|
-
return score.type === "invalid" && (!score.message || score.message.length === 0);
|
|
3286
|
-
}
|
|
3287
|
-
|
|
3288
3169
|
/*
|
|
3289
3170
|
* The touchHandlers are used to track the current state of the touch
|
|
3290
3171
|
* event, such as whether or not the user is currently pressed down (either
|
|
@@ -3459,13 +3340,9 @@ const Util = {
|
|
|
3459
3340
|
rTypeFromWidgetId,
|
|
3460
3341
|
rWidgetParts,
|
|
3461
3342
|
snowman,
|
|
3462
|
-
noScore,
|
|
3463
3343
|
seededRNG: seededRNG$1,
|
|
3464
3344
|
shuffle: shuffle$3,
|
|
3465
3345
|
split,
|
|
3466
|
-
combineScores,
|
|
3467
|
-
flattenScores,
|
|
3468
|
-
keScoreFromPerseusScore,
|
|
3469
3346
|
firstNumericalParse: firstNumericalParse$1,
|
|
3470
3347
|
stringArrayOfSize: stringArrayOfSize$1,
|
|
3471
3348
|
gridDimensionConfig,
|
|
@@ -3481,7 +3358,6 @@ const Util = {
|
|
|
3481
3358
|
parseQueryString,
|
|
3482
3359
|
updateQueryString: updateQueryString$2,
|
|
3483
3360
|
strongEncodeURIComponent,
|
|
3484
|
-
scoreIsEmpty,
|
|
3485
3361
|
touchHandlers,
|
|
3486
3362
|
resetTouchHandlers,
|
|
3487
3363
|
extractPointerLocation,
|
|
@@ -18702,6 +18578,126 @@ class QuestionParagraph extends React.Component {
|
|
|
18702
18578
|
}
|
|
18703
18579
|
}
|
|
18704
18580
|
|
|
18581
|
+
const noScore = {
|
|
18582
|
+
type: "points",
|
|
18583
|
+
earned: 0,
|
|
18584
|
+
total: 0,
|
|
18585
|
+
message: null
|
|
18586
|
+
};
|
|
18587
|
+
|
|
18588
|
+
/**
|
|
18589
|
+
* If a widget says that it is empty once it is graded.
|
|
18590
|
+
* Trying to encapsulate references to the score format.
|
|
18591
|
+
*/
|
|
18592
|
+
function scoreIsEmpty(score) {
|
|
18593
|
+
// HACK(benkomalo): ugh. this isn't great; the Perseus score objects
|
|
18594
|
+
// overload the type "invalid" for what should probably be three
|
|
18595
|
+
// distinct cases:
|
|
18596
|
+
// - truly empty or not fully filled out
|
|
18597
|
+
// - invalid or malformed inputs
|
|
18598
|
+
// - "almost correct" like inputs where the widget wants to give
|
|
18599
|
+
// feedback (e.g. a fraction needs to be reduced, or `pi` should
|
|
18600
|
+
// be used instead of 3.14)
|
|
18601
|
+
//
|
|
18602
|
+
// Unfortunately the coercion happens all over the place, as these
|
|
18603
|
+
// Perseus style score objects are created *everywhere* (basically
|
|
18604
|
+
// in every widget), so it's hard to change now. We assume that
|
|
18605
|
+
// anything with a "message" is not truly empty, and one of the
|
|
18606
|
+
// latter two cases for now.
|
|
18607
|
+
return score.type === "invalid" && (!score.message || score.message.length === 0);
|
|
18608
|
+
}
|
|
18609
|
+
|
|
18610
|
+
/**
|
|
18611
|
+
* Combine two score objects.
|
|
18612
|
+
*
|
|
18613
|
+
* Given two score objects for two different widgets, combine them so that
|
|
18614
|
+
* if one is wrong, the total score is wrong, etc.
|
|
18615
|
+
*/
|
|
18616
|
+
function combineScores(scoreA, scoreB) {
|
|
18617
|
+
let message;
|
|
18618
|
+
if (scoreA.type === "points" && scoreB.type === "points") {
|
|
18619
|
+
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
18620
|
+
// TODO(alpert): Figure out how to combine messages usefully
|
|
18621
|
+
message = null;
|
|
18622
|
+
} else {
|
|
18623
|
+
message = scoreA.message || scoreB.message;
|
|
18624
|
+
}
|
|
18625
|
+
return {
|
|
18626
|
+
type: "points",
|
|
18627
|
+
earned: scoreA.earned + scoreB.earned,
|
|
18628
|
+
total: scoreA.total + scoreB.total,
|
|
18629
|
+
message: message
|
|
18630
|
+
};
|
|
18631
|
+
}
|
|
18632
|
+
if (scoreA.type === "points" && scoreB.type === "invalid") {
|
|
18633
|
+
return scoreB;
|
|
18634
|
+
}
|
|
18635
|
+
if (scoreA.type === "invalid" && scoreB.type === "points") {
|
|
18636
|
+
return scoreA;
|
|
18637
|
+
}
|
|
18638
|
+
if (scoreA.type === "invalid" && scoreB.type === "invalid") {
|
|
18639
|
+
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
18640
|
+
// TODO(alpert): Figure out how to combine messages usefully
|
|
18641
|
+
message = null;
|
|
18642
|
+
} else {
|
|
18643
|
+
message = scoreA.message || scoreB.message;
|
|
18644
|
+
}
|
|
18645
|
+
return {
|
|
18646
|
+
type: "invalid",
|
|
18647
|
+
message: message
|
|
18648
|
+
};
|
|
18649
|
+
}
|
|
18650
|
+
|
|
18651
|
+
/**
|
|
18652
|
+
* The above checks cover all combinations of score type, so if we get here
|
|
18653
|
+
* then something is amiss with our inputs.
|
|
18654
|
+
*/
|
|
18655
|
+
throw new PerseusError("PerseusScore with unknown type encountered", Errors.InvalidInput, {
|
|
18656
|
+
metadata: {
|
|
18657
|
+
scoreA: JSON.stringify(scoreA),
|
|
18658
|
+
scoreB: JSON.stringify(scoreB)
|
|
18659
|
+
}
|
|
18660
|
+
});
|
|
18661
|
+
}
|
|
18662
|
+
function flattenScores(widgetScoreMap) {
|
|
18663
|
+
return Object.values(widgetScoreMap).reduce(combineScores, noScore);
|
|
18664
|
+
}
|
|
18665
|
+
function isCorrect(score) {
|
|
18666
|
+
return score.type === "points" && score.earned >= score.total;
|
|
18667
|
+
}
|
|
18668
|
+
function keScoreFromPerseusScore(score,
|
|
18669
|
+
// It's weird, but this is what we're passing it
|
|
18670
|
+
guess, state) {
|
|
18671
|
+
if (score.type === "points") {
|
|
18672
|
+
return {
|
|
18673
|
+
empty: false,
|
|
18674
|
+
correct: isCorrect(score),
|
|
18675
|
+
message: score.message,
|
|
18676
|
+
guess: guess,
|
|
18677
|
+
state: state
|
|
18678
|
+
};
|
|
18679
|
+
}
|
|
18680
|
+
if (score.type === "invalid") {
|
|
18681
|
+
return {
|
|
18682
|
+
empty: true,
|
|
18683
|
+
correct: false,
|
|
18684
|
+
message: score.message,
|
|
18685
|
+
suppressAlmostThere: score.suppressAlmostThere,
|
|
18686
|
+
guess: guess,
|
|
18687
|
+
state: state
|
|
18688
|
+
};
|
|
18689
|
+
}
|
|
18690
|
+
throw new PerseusError(
|
|
18691
|
+
// @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
|
|
18692
|
+
"Invalid score type: " + score.type, Errors.InvalidInput, {
|
|
18693
|
+
metadata: {
|
|
18694
|
+
score: JSON.stringify(score),
|
|
18695
|
+
guess: JSON.stringify(guess),
|
|
18696
|
+
state: JSON.stringify(state)
|
|
18697
|
+
}
|
|
18698
|
+
});
|
|
18699
|
+
}
|
|
18700
|
+
|
|
18705
18701
|
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
18706
18702
|
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
18707
18703
|
|
|
@@ -19300,7 +19296,7 @@ widgetIds, userInputMap, strings, locale) {
|
|
|
19300
19296
|
const scorer = getWidgetScorer(widget.type);
|
|
19301
19297
|
const score = scorer == null ? void 0 : scorer(userInputMap[id], widget.options, strings, locale);
|
|
19302
19298
|
if (score) {
|
|
19303
|
-
return
|
|
19299
|
+
return scoreIsEmpty(score);
|
|
19304
19300
|
}
|
|
19305
19301
|
});
|
|
19306
19302
|
}
|
|
@@ -19316,7 +19312,7 @@ strings, locale) {
|
|
|
19316
19312
|
// so this checks that the widgets are being used before scoring them
|
|
19317
19313
|
const usedWidgetIds = getWidgetIdsFromContent(perseusRenderData.content);
|
|
19318
19314
|
const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
|
|
19319
|
-
return
|
|
19315
|
+
return flattenScores(scores);
|
|
19320
19316
|
}
|
|
19321
19317
|
function scoreWidgetsFunctional(widgets,
|
|
19322
19318
|
// This is a port of old code, I'm not sure why
|
|
@@ -20596,28 +20592,6 @@ class Renderer extends React.Component {
|
|
|
20596
20592
|
const totalScore = this.score();
|
|
20597
20593
|
return [totalGuess, totalScore];
|
|
20598
20594
|
};
|
|
20599
|
-
this.examples = () => {
|
|
20600
|
-
const widgetIds = this.widgetIds;
|
|
20601
|
-
const examples = widgetIds.map(widgetId => {
|
|
20602
|
-
const widget = this.getWidgetInstance(widgetId);
|
|
20603
|
-
return widget != null && widget.examples ? widget.examples() : null;
|
|
20604
|
-
}).filter(Boolean);
|
|
20605
|
-
|
|
20606
|
-
// no widgets with examples
|
|
20607
|
-
if (!examples.length) {
|
|
20608
|
-
return null;
|
|
20609
|
-
}
|
|
20610
|
-
const allEqual = _.all(examples, function (example) {
|
|
20611
|
-
return _.isEqual(examples[0], example);
|
|
20612
|
-
});
|
|
20613
|
-
|
|
20614
|
-
// some widgets have different examples
|
|
20615
|
-
// TODO(alex): handle this better
|
|
20616
|
-
if (!allEqual) {
|
|
20617
|
-
return null;
|
|
20618
|
-
}
|
|
20619
|
-
return examples[0];
|
|
20620
|
-
};
|
|
20621
20595
|
// TranslationLinter callback
|
|
20622
20596
|
this.handletranslationLintErrors = lintErrors => {
|
|
20623
20597
|
if (!this._isMounted) {
|
|
@@ -20868,7 +20842,7 @@ class Renderer extends React.Component {
|
|
|
20868
20842
|
*/
|
|
20869
20843
|
score() {
|
|
20870
20844
|
const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
|
|
20871
|
-
const combinedScore =
|
|
20845
|
+
const combinedScore = flattenScores(scores);
|
|
20872
20846
|
return combinedScore;
|
|
20873
20847
|
}
|
|
20874
20848
|
render() {
|
|
@@ -21929,15 +21903,6 @@ class InputNumber extends React.Component {
|
|
|
21929
21903
|
currentValue: newValue
|
|
21930
21904
|
}, cb);
|
|
21931
21905
|
};
|
|
21932
|
-
this.examples = () => {
|
|
21933
|
-
const {
|
|
21934
|
-
strings
|
|
21935
|
-
} = this.context;
|
|
21936
|
-
const type = this.props.answerType;
|
|
21937
|
-
const forms = answerTypes[type].forms.split(/\s*,\s*/);
|
|
21938
|
-
const examples = _.map(forms, form => formExamples$1[form](this.props, strings));
|
|
21939
|
-
return [strings.yourAnswer].concat(examples);
|
|
21940
|
-
};
|
|
21941
21906
|
}
|
|
21942
21907
|
static getUserInputFromProps(props) {
|
|
21943
21908
|
return {
|
|
@@ -21950,6 +21915,15 @@ class InputNumber extends React.Component {
|
|
|
21950
21915
|
getPromptJSON() {
|
|
21951
21916
|
return getPromptJSON$s(this.props, this.getUserInput());
|
|
21952
21917
|
}
|
|
21918
|
+
examples() {
|
|
21919
|
+
const {
|
|
21920
|
+
strings
|
|
21921
|
+
} = this.context;
|
|
21922
|
+
const type = this.props.answerType;
|
|
21923
|
+
const forms = answerTypes[type].forms.split(/\s*,\s*/);
|
|
21924
|
+
const examples = _.map(forms, form => formExamples$1[form](this.props, strings));
|
|
21925
|
+
return [strings.yourAnswer].concat(examples);
|
|
21926
|
+
}
|
|
21953
21927
|
render() {
|
|
21954
21928
|
if (this.props.apiOptions.customKeypad) {
|
|
21955
21929
|
// TODO(charlie): Support "Review Mode".
|
|
@@ -22232,31 +22206,13 @@ class NumericInput extends React.Component {
|
|
|
22232
22206
|
previousValues: [""],
|
|
22233
22207
|
isFocused: false
|
|
22234
22208
|
};
|
|
22235
|
-
// TODO(Nicole, Jeremy): This is maybe never used and should be removed
|
|
22236
|
-
this.examples = () => {
|
|
22237
|
-
var _this$props$answerFor;
|
|
22238
|
-
// if the set of specified forms are empty, allow all forms
|
|
22239
|
-
const forms = ((_this$props$answerFor = this.props.answerForms) == null ? void 0 : _this$props$answerFor.length) !== 0 ? this.props.answerForms : Object.keys(formExamples).map(name => {
|
|
22240
|
-
return {
|
|
22241
|
-
name: name,
|
|
22242
|
-
simplify: "required"
|
|
22243
|
-
};
|
|
22244
|
-
});
|
|
22245
|
-
let examples = _.map(forms, form => {
|
|
22246
|
-
return formExamples[form.name](form, this.context.strings);
|
|
22247
|
-
});
|
|
22248
|
-
// Ensure no duplicate tooltip text from simplified and unsimplified
|
|
22249
|
-
// versions of the same format
|
|
22250
|
-
examples = _.uniq(examples);
|
|
22251
|
-
return [this.context.strings.yourAnswer].concat(examples);
|
|
22252
|
-
};
|
|
22253
22209
|
this.shouldShowExamples = () => {
|
|
22254
|
-
var _this$props$
|
|
22255
|
-
const noFormsAccepted = ((_this$props$
|
|
22210
|
+
var _this$props$answerFor, _this$props$answerFor2;
|
|
22211
|
+
const noFormsAccepted = ((_this$props$answerFor = this.props.answerForms) == null ? void 0 : _this$props$answerFor.length) === 0;
|
|
22256
22212
|
// To check if all answer forms are accepted, we must first
|
|
22257
22213
|
// find the *names* of all accepted forms, and see if they are
|
|
22258
22214
|
// all present, ignoring duplicates
|
|
22259
|
-
const answerFormNames = _.uniq((_this$props$
|
|
22215
|
+
const answerFormNames = _.uniq((_this$props$answerFor2 = this.props.answerForms) == null ? void 0 : _this$props$answerFor2.map(form => form.name));
|
|
22260
22216
|
const allFormsAccepted = answerFormNames.length >= Object.keys(formExamples).length;
|
|
22261
22217
|
return !noFormsAccepted && !allFormsAccepted;
|
|
22262
22218
|
};
|
|
@@ -22313,6 +22269,27 @@ class NumericInput extends React.Component {
|
|
|
22313
22269
|
currentValue: props.currentValue
|
|
22314
22270
|
};
|
|
22315
22271
|
}
|
|
22272
|
+
/**
|
|
22273
|
+
* Generates a string that demonstrates how to input the various supported
|
|
22274
|
+
* answer forms.
|
|
22275
|
+
*/
|
|
22276
|
+
examples() {
|
|
22277
|
+
var _this$props$answerFor3;
|
|
22278
|
+
// if the set of specified forms are empty, allow all forms
|
|
22279
|
+
const forms = ((_this$props$answerFor3 = this.props.answerForms) == null ? void 0 : _this$props$answerFor3.length) !== 0 ? this.props.answerForms : Object.keys(formExamples).map(name => {
|
|
22280
|
+
return {
|
|
22281
|
+
name: name,
|
|
22282
|
+
simplify: "required"
|
|
22283
|
+
};
|
|
22284
|
+
});
|
|
22285
|
+
let examples = _.map(forms, form => {
|
|
22286
|
+
return formExamples[form.name](form, this.context.strings);
|
|
22287
|
+
});
|
|
22288
|
+
// Ensure no duplicate tooltip text from simplified and unsimplified
|
|
22289
|
+
// versions of the same format
|
|
22290
|
+
examples = _.uniq(examples);
|
|
22291
|
+
return [this.context.strings.yourAnswer].concat(examples);
|
|
22292
|
+
}
|
|
22316
22293
|
getUserInput() {
|
|
22317
22294
|
return NumericInput.getUserInputFromProps(this.props);
|
|
22318
22295
|
}
|
|
@@ -29384,7 +29361,7 @@ const getPromptJSON$f = rendererJSON => {
|
|
|
29384
29361
|
// it. As such, scoring a group means scoring all widgets it contains.
|
|
29385
29362
|
function scoreGroup(userInput, options, strings, locale) {
|
|
29386
29363
|
const scores = scoreWidgetsFunctional(options.widgets, Object.keys(options.widgets), userInput, strings, locale);
|
|
29387
|
-
return
|
|
29364
|
+
return flattenScores(scores);
|
|
29388
29365
|
}
|
|
29389
29366
|
|
|
29390
29367
|
// exports has no 'transform'
|
|
@@ -45109,7 +45086,7 @@ const init = function init() {
|
|
|
45109
45086
|
|
|
45110
45087
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45111
45088
|
const libName = "@khanacademy/perseus";
|
|
45112
|
-
const libVersion = "
|
|
45089
|
+
const libVersion = "46.0.1";
|
|
45113
45090
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45114
45091
|
|
|
45115
45092
|
const apiVersion = {
|
|
@@ -45884,7 +45861,7 @@ class ServerItemRenderer extends React.Component {
|
|
|
45884
45861
|
// TODO(alex): Check whether we rely on the format here for
|
|
45885
45862
|
// analyzing ProblemLogs. If not, remove this layer.
|
|
45886
45863
|
const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
|
|
45887
|
-
const keScore =
|
|
45864
|
+
const keScore = keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
|
|
45888
45865
|
const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
|
|
45889
45866
|
this.setState({
|
|
45890
45867
|
questionCompleted: keScore.correct,
|
|
@@ -46963,7 +46940,7 @@ class MultiRenderer extends React.Component {
|
|
|
46963
46940
|
if (ref.getSerializedState) {
|
|
46964
46941
|
state = ref.getSerializedState();
|
|
46965
46942
|
}
|
|
46966
|
-
return
|
|
46943
|
+
return keScoreFromPerseusScore(score, guess, state);
|
|
46967
46944
|
}
|
|
46968
46945
|
|
|
46969
46946
|
/**
|
|
@@ -46993,8 +46970,8 @@ class MultiRenderer extends React.Component {
|
|
|
46993
46970
|
scores.push(data.ref.score());
|
|
46994
46971
|
return (_data$ref = data.ref) == null ? void 0 : _data$ref.getUserInput();
|
|
46995
46972
|
});
|
|
46996
|
-
const combinedScore = scores.reduce(
|
|
46997
|
-
return
|
|
46973
|
+
const combinedScore = scores.reduce(combineScores);
|
|
46974
|
+
return keScoreFromPerseusScore(combinedScore, guess, state);
|
|
46998
46975
|
}
|
|
46999
46976
|
|
|
47000
46977
|
/**
|
|
@@ -49837,6 +49814,9 @@ const GrapherUtil = {
|
|
|
49837
49814
|
getEquationString,
|
|
49838
49815
|
typeToButton
|
|
49839
49816
|
};
|
|
49817
|
+
const ScoringUtil = {
|
|
49818
|
+
keScoreFromPerseusScore
|
|
49819
|
+
};
|
|
49840
49820
|
|
|
49841
|
-
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, buildEmptyItemTreeForShape, buildMapper, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mathOnlyParser, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, scorePerseusItem, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
49821
|
+
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, buildEmptyItemTreeForShape, buildMapper, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mathOnlyParser, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, scorePerseusItem, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
49842
49822
|
//# sourceMappingURL=index.js.map
|