@khanacademy/perseus 52.0.1 → 53.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/number-input.d.ts +2 -0
- package/dist/components/range-input.d.ts +1 -0
- package/dist/components/svg-image.d.ts +2 -2
- package/dist/es/index.css +0 -30
- package/dist/es/index.js +385 -865
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +0 -30
- package/dist/index.d.ts +1 -2
- package/dist/index.js +383 -868
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +0 -1
- package/dist/renderer.d.ts +1 -1
- package/dist/styles/constants.d.ts +0 -2
- package/dist/types.d.ts +3 -20
- package/dist/util/math-utils.d.ts +1 -0
- package/dist/util/scoring.d.ts +0 -15
- package/dist/util/test-utils.d.ts +1 -1
- package/dist/widget-ai-utils/mock-widget/prompt-utils.d.ts +1 -1
- package/dist/widget-container.d.ts +1 -1
- package/dist/widgets/categorizer/categorizer.d.ts +0 -3
- package/dist/widgets/cs-program/cs-program.d.ts +2 -3
- package/dist/widgets/definition/definition.d.ts +0 -2
- package/dist/widgets/deprecated-standin/deprecated-standin.d.ts +0 -1
- package/dist/widgets/dropdown/dropdown.d.ts +2 -4
- package/dist/widgets/explanation/explanation.d.ts +0 -2
- package/dist/widgets/expression/expression.d.ts +1 -5
- package/dist/widgets/grapher/grapher.d.ts +0 -2
- package/dist/widgets/group/group.d.ts +0 -4
- package/dist/widgets/iframe/iframe.d.ts +0 -2
- package/dist/widgets/image/image.d.ts +0 -3
- package/dist/widgets/input-number/input-number.d.ts +0 -3
- package/dist/widgets/interaction/interaction.d.ts +0 -1
- package/dist/widgets/interactive-graph.d.ts +0 -2
- package/dist/widgets/interactive-graphs/backgrounds/axis-ticks.d.ts +1 -0
- package/dist/widgets/label-image/label-image.d.ts +2 -2
- package/dist/widgets/matcher/matcher.d.ts +0 -2
- package/dist/widgets/matrix/matrix.d.ts +0 -3
- package/dist/widgets/measurer/measurer.d.ts +0 -1
- package/dist/widgets/mock-widgets/mock-widget-types.d.ts +0 -6
- package/dist/widgets/mock-widgets/mock-widget.d.ts +2 -5
- package/dist/widgets/molecule/molecule.d.ts +0 -1
- package/dist/widgets/number-line/number-line.d.ts +2 -3
- package/dist/widgets/numeric-input/numeric-input.d.ts +2 -3
- package/dist/widgets/orderer/orderer.d.ts +0 -3
- package/dist/widgets/passage/passage.d.ts +0 -1
- package/dist/widgets/passage-ref/passage-ref.d.ts +0 -2
- package/dist/widgets/passage-ref-target/passage-ref-target.d.ts +0 -2
- package/dist/widgets/plotter/plotter.d.ts +0 -3
- package/dist/widgets/radio/radio.d.ts +0 -3
- package/dist/widgets/sorter/sorter.d.ts +2 -4
- package/dist/widgets/table/table.d.ts +0 -3
- package/dist/widgets/video/video.d.ts +0 -3
- package/dist/widgets.d.ts +1 -32
- package/package.json +10 -10
- package/dist/renderer-util.d.ts +0 -14
- package/dist/widgets/__shared__/score-noop.d.ts +0 -11
- package/dist/widgets/group/score-group.d.ts +0 -4
- package/dist/widgets/group/validate-group.d.ts +0 -4
- package/dist/widgets/mock-widgets/score-mock-widget.d.ts +0 -5
- package/dist/widgets/mock-widgets/validate-mock-widget.d.ts +0 -4
package/dist/index.js
CHANGED
|
@@ -6,7 +6,6 @@ var KAS = require('@khanacademy/kas');
|
|
|
6
6
|
var mathInput = require('@khanacademy/math-input');
|
|
7
7
|
var perseusCore = require('@khanacademy/perseus-core');
|
|
8
8
|
var PerseusLinter = require('@khanacademy/perseus-linter');
|
|
9
|
-
var perseusScore = require('@khanacademy/perseus-score');
|
|
10
9
|
var wonderBlocksCore = require('@khanacademy/wonder-blocks-core');
|
|
11
10
|
var Tooltip$1 = require('@khanacademy/wonder-blocks-tooltip');
|
|
12
11
|
var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
|
|
@@ -21,6 +20,7 @@ var wonderBlocksTokens = require('@khanacademy/wonder-blocks-tokens');
|
|
|
21
20
|
var $ = require('jquery');
|
|
22
21
|
var uuid = require('uuid');
|
|
23
22
|
var PropTypes = require('prop-types');
|
|
23
|
+
var perseusScore = require('@khanacademy/perseus-score');
|
|
24
24
|
var wonderStuffCore = require('@khanacademy/wonder-stuff-core');
|
|
25
25
|
var wonderBlocksProgressSpinner = require('@khanacademy/wonder-blocks-progress-spinner');
|
|
26
26
|
var kmath = require('@khanacademy/kmath');
|
|
@@ -1612,7 +1612,6 @@ const ClassNames = {
|
|
|
1612
1612
|
SELECTED: "perseus-radio-selected",
|
|
1613
1613
|
OPTION_CONTENT: "perseus-radio-option-content"
|
|
1614
1614
|
},
|
|
1615
|
-
INTERACTIVE: "perseus-interactive",
|
|
1616
1615
|
CORRECT: "perseus-correct",
|
|
1617
1616
|
INCORRECT: "perseus-incorrect",
|
|
1618
1617
|
UNANSWERED: "perseus-unanswered",
|
|
@@ -1868,7 +1867,6 @@ class Expression extends React__namespace.Component {
|
|
|
1868
1867
|
// eslint-disable-next-line react/no-string-refs
|
|
1869
1868
|
, {
|
|
1870
1869
|
ref: "input",
|
|
1871
|
-
className: ClassNames.INTERACTIVE,
|
|
1872
1870
|
value: this.props.value,
|
|
1873
1871
|
onChange: this.changeAndTrack,
|
|
1874
1872
|
convertDotToTimes: this.props.times,
|
|
@@ -1970,7 +1968,6 @@ var Expression$1 = {
|
|
|
1970
1968
|
name: "expression",
|
|
1971
1969
|
displayName: "Expression / Equation",
|
|
1972
1970
|
accessible: true,
|
|
1973
|
-
defaultAlignment: "inline-block",
|
|
1974
1971
|
widget: ExpressionWithDependencies,
|
|
1975
1972
|
transform: widgetOptions => {
|
|
1976
1973
|
const {
|
|
@@ -1995,12 +1992,6 @@ var Expression$1 = {
|
|
|
1995
1992
|
propUpgrades: perseusCore.expressionLogic.widgetOptionsUpgrades,
|
|
1996
1993
|
// For use by the editor
|
|
1997
1994
|
isLintable: true,
|
|
1998
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
1999
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
|
|
2000
|
-
scorer: perseusScore.scoreExpression,
|
|
2001
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
2002
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
|
|
2003
|
-
validator: perseusScore.validateExpression,
|
|
2004
1995
|
getPublicWidgetOptions: perseusCore.getExpressionPublicWidgetOptions,
|
|
2005
1996
|
// TODO(LEMS-2656): remove TS suppression
|
|
2006
1997
|
// @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusExpressionRubric'.
|
|
@@ -3280,10 +3271,6 @@ const pureMdMax$1 = "1023px";
|
|
|
3280
3271
|
const pureLgMax$1 = "1279px";
|
|
3281
3272
|
// @tableBackgroundAccent: #f9f9f9; // for striping
|
|
3282
3273
|
const tableBackgroundAccent = "#F9F9F9";
|
|
3283
|
-
// @zIndexAboveScratchpad: @zIndexScratchPad + 1;
|
|
3284
|
-
const zIndexAboveScratchpad$1 = 2;
|
|
3285
|
-
// @zIndexInteractiveComponent: @zIndexAboveScratchpad + 1;
|
|
3286
|
-
const zIndexInteractiveComponent$1 = 3;
|
|
3287
3274
|
// @phoneMargin: 16px;
|
|
3288
3275
|
const phoneMargin = 16;
|
|
3289
3276
|
const negativePhoneMargin = -16;
|
|
@@ -3327,8 +3314,6 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
3327
3314
|
pureMdMax: pureMdMax$1,
|
|
3328
3315
|
pureLgMax: pureLgMax$1,
|
|
3329
3316
|
tableBackgroundAccent: tableBackgroundAccent,
|
|
3330
|
-
zIndexAboveScratchpad: zIndexAboveScratchpad$1,
|
|
3331
|
-
zIndexInteractiveComponent: zIndexInteractiveComponent$1,
|
|
3332
3317
|
phoneMargin: phoneMargin,
|
|
3333
3318
|
negativePhoneMargin: negativePhoneMargin,
|
|
3334
3319
|
hintBorderWidth: hintBorderWidth,
|
|
@@ -17623,130 +17608,162 @@ class QuestionParagraph extends React__namespace.Component {
|
|
|
17623
17608
|
}
|
|
17624
17609
|
}
|
|
17625
17610
|
|
|
17626
|
-
const noScore = {
|
|
17627
|
-
type: "points",
|
|
17628
|
-
earned: 0,
|
|
17629
|
-
total: 0,
|
|
17630
|
-
message: null
|
|
17631
|
-
};
|
|
17632
|
-
|
|
17633
|
-
/**
|
|
17634
|
-
* If a widget says that it is empty once it is graded.
|
|
17635
|
-
* Trying to encapsulate references to the score format.
|
|
17636
|
-
*/
|
|
17637
|
-
function scoreIsEmpty(score) {
|
|
17638
|
-
// HACK(benkomalo): ugh. this isn't great; the Perseus score objects
|
|
17639
|
-
// overload the type "invalid" for what should probably be three
|
|
17640
|
-
// distinct cases:
|
|
17641
|
-
// - truly empty or not fully filled out
|
|
17642
|
-
// - invalid or malformed inputs
|
|
17643
|
-
// - "almost correct" like inputs where the widget wants to give
|
|
17644
|
-
// feedback (e.g. a fraction needs to be reduced, or `pi` should
|
|
17645
|
-
// be used instead of 3.14)
|
|
17646
|
-
//
|
|
17647
|
-
// Unfortunately the coercion happens all over the place, as these
|
|
17648
|
-
// Perseus style score objects are created *everywhere* (basically
|
|
17649
|
-
// in every widget), so it's hard to change now. We assume that
|
|
17650
|
-
// anything with a "message" is not truly empty, and one of the
|
|
17651
|
-
// latter two cases for now.
|
|
17652
|
-
return score.type === "invalid" && (!score.message || score.message.length === 0);
|
|
17653
|
-
}
|
|
17654
|
-
|
|
17655
17611
|
/**
|
|
17656
|
-
*
|
|
17612
|
+
* TranslationLinter asynchronously calls Khan Academy's poentry linter.
|
|
17657
17613
|
*
|
|
17658
|
-
*
|
|
17659
|
-
*
|
|
17614
|
+
* The poentry linter checks for issues that prevent content from being
|
|
17615
|
+
* translated. This linter is KA-specific and so TranslationLinter does not do
|
|
17616
|
+
* anything in non-KA environments.
|
|
17660
17617
|
*/
|
|
17661
|
-
|
|
17662
|
-
|
|
17663
|
-
|
|
17664
|
-
|
|
17665
|
-
|
|
17666
|
-
|
|
17667
|
-
|
|
17668
|
-
|
|
17618
|
+
|
|
17619
|
+
// A LintCB is called once the linter has been run.
|
|
17620
|
+
|
|
17621
|
+
// msec to wait before we actually call the linter after receiving the
|
|
17622
|
+
// last call. The timer is reset if the lint is called during the wait.
|
|
17623
|
+
const DEBOUNCE_TIMEOUT = 1000;
|
|
17624
|
+
class TranslationLinter {
|
|
17625
|
+
// To avoid linting when nothing has changed.
|
|
17626
|
+
previousContent = null;
|
|
17627
|
+
|
|
17628
|
+
/**
|
|
17629
|
+
* Runs the poentry linter on the given perseus string asynchronously.
|
|
17630
|
+
*
|
|
17631
|
+
* It doesn't do anything unless we're in a KA environment.
|
|
17632
|
+
*
|
|
17633
|
+
* onLintErrorsGenerated is called even if there are no errors. It is not
|
|
17634
|
+
* called if perseusStr is unchanged from the last call.
|
|
17635
|
+
*
|
|
17636
|
+
* Linting is relatively expensive -- so we debounce linting each instance
|
|
17637
|
+
* for some amount of time.
|
|
17638
|
+
*/
|
|
17639
|
+
runLinter = _.debounce((perseusStr, onLintErrorsGenerated) => {
|
|
17640
|
+
// TODO(FEI-5003): Do we still need to check for 'KA'?
|
|
17641
|
+
if (typeof KA === "undefined") {
|
|
17642
|
+
return;
|
|
17669
17643
|
}
|
|
17670
|
-
|
|
17671
|
-
|
|
17672
|
-
earned: scoreA.earned + scoreB.earned,
|
|
17673
|
-
total: scoreA.total + scoreB.total,
|
|
17674
|
-
message: message
|
|
17675
|
-
};
|
|
17676
|
-
}
|
|
17677
|
-
if (scoreA.type === "points" && scoreB.type === "invalid") {
|
|
17678
|
-
return scoreB;
|
|
17679
|
-
}
|
|
17680
|
-
if (scoreA.type === "invalid" && scoreB.type === "points") {
|
|
17681
|
-
return scoreA;
|
|
17682
|
-
}
|
|
17683
|
-
if (scoreA.type === "invalid" && scoreB.type === "invalid") {
|
|
17684
|
-
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
17685
|
-
// TODO(alpert): Figure out how to combine messages usefully
|
|
17686
|
-
message = null;
|
|
17687
|
-
} else {
|
|
17688
|
-
message = scoreA.message || scoreB.message;
|
|
17644
|
+
if (perseusStr === this.previousContent) {
|
|
17645
|
+
return;
|
|
17689
17646
|
}
|
|
17690
|
-
|
|
17691
|
-
|
|
17692
|
-
|
|
17693
|
-
|
|
17694
|
-
|
|
17647
|
+
this.previousContent = perseusStr;
|
|
17648
|
+
if (perseusStr === "") {
|
|
17649
|
+
onLintErrorsGenerated([]);
|
|
17650
|
+
return;
|
|
17651
|
+
}
|
|
17652
|
+
fetch("/api/internal/translations/lint_poentry?preview=1&lang=en", {
|
|
17653
|
+
headers: {
|
|
17654
|
+
"Content-Type": "application/json"
|
|
17655
|
+
},
|
|
17656
|
+
body: JSON.stringify({
|
|
17657
|
+
// The poentry linter verifies that the translation from
|
|
17658
|
+
// the original text to the translated text is sane. We
|
|
17659
|
+
// want to ensure that the translation from English to
|
|
17660
|
+
// English is sane, so msgid === mstr.
|
|
17661
|
+
msgid: perseusStr,
|
|
17662
|
+
// msgid is the original text
|
|
17663
|
+
msgstr: perseusStr,
|
|
17664
|
+
// msgstr is the translated text
|
|
17665
|
+
format: "perseus_text",
|
|
17666
|
+
filename: ""
|
|
17667
|
+
}),
|
|
17668
|
+
method: "POST"
|
|
17669
|
+
}).then(response => {
|
|
17670
|
+
if (response.status >= 400) {
|
|
17671
|
+
return {
|
|
17672
|
+
status: "error",
|
|
17673
|
+
message: "Could not run i18n linter."
|
|
17674
|
+
};
|
|
17675
|
+
}
|
|
17676
|
+
return response.json();
|
|
17677
|
+
}, rejection => {
|
|
17678
|
+
return {
|
|
17679
|
+
status: "error",
|
|
17680
|
+
message: "Could not run i18n linter."
|
|
17681
|
+
};
|
|
17682
|
+
}).then(json => {
|
|
17683
|
+
if (json.status === "error") {
|
|
17684
|
+
onLintErrorsGenerated(["Some part of this text makes it untranslatable. " + "The specific message from the i18n linter was: " + json.message.replace(/\n/g, " ")]);
|
|
17685
|
+
} else {
|
|
17686
|
+
onLintErrorsGenerated([]);
|
|
17687
|
+
}
|
|
17688
|
+
});
|
|
17689
|
+
}, DEBOUNCE_TIMEOUT);
|
|
17695
17690
|
|
|
17696
17691
|
/**
|
|
17697
|
-
*
|
|
17698
|
-
*
|
|
17692
|
+
* Applies an array of errors generated by linters without position
|
|
17693
|
+
* information (TranslationLinter and the legacy getSaveWarnings()) to the
|
|
17694
|
+
* top of a Perseus tree.
|
|
17699
17695
|
*/
|
|
17700
|
-
|
|
17701
|
-
|
|
17702
|
-
|
|
17703
|
-
|
|
17696
|
+
applyLintErrors(parsedMarkdown, translationLintErrors) {
|
|
17697
|
+
// These lint errors do not have position data associated with
|
|
17698
|
+
// them, so we just plop them at the top.
|
|
17699
|
+
if (translationLintErrors.length) {
|
|
17700
|
+
const errorText = translationLintErrors.join("\n\n");
|
|
17701
|
+
parsedMarkdown.unshift({
|
|
17702
|
+
content: {
|
|
17703
|
+
type: "text",
|
|
17704
|
+
content: ""
|
|
17705
|
+
},
|
|
17706
|
+
insideTable: false,
|
|
17707
|
+
message: errorText,
|
|
17708
|
+
ruleName: "legacy-error",
|
|
17709
|
+
severity: PerseusLinter.Rule.Severity.ERROR,
|
|
17710
|
+
type: "lint"
|
|
17711
|
+
});
|
|
17704
17712
|
}
|
|
17705
|
-
}
|
|
17706
|
-
}
|
|
17707
|
-
function flattenScores(widgetScoreMap) {
|
|
17708
|
-
return Object.values(widgetScoreMap).reduce(combineScores, noScore);
|
|
17709
|
-
}
|
|
17710
|
-
function isCorrect(score) {
|
|
17711
|
-
return score.type === "points" && score.earned >= score.total;
|
|
17713
|
+
}
|
|
17712
17714
|
}
|
|
17713
|
-
|
|
17714
|
-
|
|
17715
|
-
|
|
17716
|
-
|
|
17717
|
-
|
|
17718
|
-
|
|
17719
|
-
|
|
17720
|
-
|
|
17721
|
-
|
|
17722
|
-
|
|
17723
|
-
|
|
17715
|
+
|
|
17716
|
+
/**
|
|
17717
|
+
* Preprocess TeX code to convert things that MathJax doesn't know how to handle
|
|
17718
|
+
* to things is does.
|
|
17719
|
+
*/
|
|
17720
|
+
var preprocessTex = (texCode => texCode
|
|
17721
|
+
// Replace uses of \begin{align}...\end{align} which KaTeX doesn't
|
|
17722
|
+
// support (yet) with \begin{aligned}...\end{aligned} which renders
|
|
17723
|
+
// the same is supported by KaTeX. It does the same for align*.
|
|
17724
|
+
// TODO(kevinb) update content to use aligned instead of align.
|
|
17725
|
+
// TODO(LEMS-1608) Remove this replacement as MathJax supports the
|
|
17726
|
+
// "align" macro correctly (and, in fact, it is not synonymous with
|
|
17727
|
+
// "aligned").
|
|
17728
|
+
.replace(/\{align[*]?\}/g, "{aligned}")
|
|
17729
|
+
// Replace non-breaking spaces with regular spaces.
|
|
17730
|
+
.replace(/[\u00a0]/g, " "));
|
|
17731
|
+
|
|
17732
|
+
const {
|
|
17733
|
+
interactiveSizes
|
|
17734
|
+
} = constants;
|
|
17735
|
+
|
|
17736
|
+
// Note: these size cutoffs represent content-width cutoffs as specified in
|
|
17737
|
+
// http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
|
|
17738
|
+
// TODO(benkomalo): these values aren't used in JS outside of this file, but
|
|
17739
|
+
// are coupled to the values in styles/styles.less - DRY it up at some point
|
|
17740
|
+
const smMax = articleMaxWidthTableInPx;
|
|
17741
|
+
const mdMax = articleMaxWidthInPx;
|
|
17742
|
+
const containerSizeClass = {
|
|
17743
|
+
SMALL: "small",
|
|
17744
|
+
MEDIUM: "medium",
|
|
17745
|
+
LARGE: "large",
|
|
17746
|
+
XLARGE: "xlarge"
|
|
17747
|
+
};
|
|
17748
|
+
const getClassFromWidth = width => {
|
|
17749
|
+
if (!width) {
|
|
17750
|
+
return containerSizeClass.MEDIUM;
|
|
17724
17751
|
}
|
|
17725
|
-
if (
|
|
17726
|
-
return
|
|
17727
|
-
empty: true,
|
|
17728
|
-
correct: false,
|
|
17729
|
-
message: score.message,
|
|
17730
|
-
suppressAlmostThere: score.suppressAlmostThere,
|
|
17731
|
-
guess: guess,
|
|
17732
|
-
state: state
|
|
17733
|
-
};
|
|
17752
|
+
if (width <= smMax) {
|
|
17753
|
+
return containerSizeClass.SMALL;
|
|
17734
17754
|
}
|
|
17735
|
-
|
|
17736
|
-
|
|
17737
|
-
|
|
17738
|
-
|
|
17739
|
-
|
|
17740
|
-
|
|
17741
|
-
|
|
17742
|
-
|
|
17743
|
-
}
|
|
17744
|
-
|
|
17755
|
+
if (width <= mdMax) {
|
|
17756
|
+
return containerSizeClass.MEDIUM;
|
|
17757
|
+
}
|
|
17758
|
+
return containerSizeClass.LARGE;
|
|
17759
|
+
};
|
|
17760
|
+
const getInteractiveBoxFromSizeClass = sizeClass => {
|
|
17761
|
+
if (sizeClass === containerSizeClass.SMALL) {
|
|
17762
|
+
return [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall];
|
|
17763
|
+
}
|
|
17764
|
+
return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
|
|
17765
|
+
};
|
|
17745
17766
|
|
|
17746
|
-
const DEFAULT_ALIGNMENT$1 = "block";
|
|
17747
|
-
// NOTE(kevinb): "default" is not one in `validAlignments`.
|
|
17748
|
-
const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
|
|
17749
|
-
const DEFAULT_STATIC = false;
|
|
17750
17767
|
const DEFAULT_TRACKING = "";
|
|
17751
17768
|
const DEFAULT_LINTABLE = false;
|
|
17752
17769
|
const widgets = {};
|
|
@@ -17763,7 +17780,6 @@ const registerWidgets = widgets => {
|
|
|
17763
17780
|
widgets.forEach(widget => {
|
|
17764
17781
|
registerWidget(widget.name, widget);
|
|
17765
17782
|
});
|
|
17766
|
-
validateAlignments();
|
|
17767
17783
|
};
|
|
17768
17784
|
|
|
17769
17785
|
/**
|
|
@@ -17844,12 +17860,6 @@ const getWidget = name => {
|
|
|
17844
17860
|
const getWidgetExport = name => {
|
|
17845
17861
|
return widgets[name] ?? null;
|
|
17846
17862
|
};
|
|
17847
|
-
const getWidgetValidator = name => {
|
|
17848
|
-
return widgets[name]?.validator ?? null;
|
|
17849
|
-
};
|
|
17850
|
-
const getWidgetScorer = name => {
|
|
17851
|
-
return widgets[name]?.scorer ?? null;
|
|
17852
|
-
};
|
|
17853
17863
|
const getPublicWidgetOptionsFunction = name => {
|
|
17854
17864
|
return widgets[name]?.getPublicWidgetOptions ?? (i => i);
|
|
17855
17865
|
};
|
|
@@ -17895,112 +17905,6 @@ const isAccessible = widgetInfo => {
|
|
|
17895
17905
|
const getAllWidgetTypes = () => {
|
|
17896
17906
|
return ___default["default"].keys(widgets);
|
|
17897
17907
|
};
|
|
17898
|
-
const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
|
|
17899
|
-
const type = oldWidgetInfo.type;
|
|
17900
|
-
// NOTE(jeremy): This looks like it could be replaced by fixing types so
|
|
17901
|
-
// that `type` is non-optional. But we're seeing this in Sentry today so I
|
|
17902
|
-
// suspect we have legacy data (potentially unpublished) and we should
|
|
17903
|
-
// figure that out before depending solely on types.
|
|
17904
|
-
if (!___default["default"].isString(type)) {
|
|
17905
|
-
throw new perseusCore.PerseusError("widget type must be a string, but was: " + type, perseusCore.Errors.Internal);
|
|
17906
|
-
}
|
|
17907
|
-
const widgetExports = widgets[type];
|
|
17908
|
-
if (widgetExports == null) {
|
|
17909
|
-
// If we have a widget that isn't registered, we can't upgrade it
|
|
17910
|
-
// TODO(aria): Figure out what the best thing to do here would be
|
|
17911
|
-
return oldWidgetInfo;
|
|
17912
|
-
}
|
|
17913
|
-
|
|
17914
|
-
// Unversioned widgets (pre-July 2014) are all implicitly 0.0
|
|
17915
|
-
const initialVersion = oldWidgetInfo.version || {
|
|
17916
|
-
major: 0,
|
|
17917
|
-
minor: 0
|
|
17918
|
-
};
|
|
17919
|
-
const latestVersion = widgetExports.version || {
|
|
17920
|
-
major: 0,
|
|
17921
|
-
minor: 0
|
|
17922
|
-
};
|
|
17923
|
-
|
|
17924
|
-
// If the widget version is later than what we understand (major
|
|
17925
|
-
// version is higher than latest, or major versions are equal and minor
|
|
17926
|
-
// version is higher than latest), don't perform any upgrades.
|
|
17927
|
-
if (initialVersion.major > latestVersion.major || initialVersion.major === latestVersion.major && initialVersion.minor > latestVersion.minor) {
|
|
17928
|
-
return oldWidgetInfo;
|
|
17929
|
-
}
|
|
17930
|
-
|
|
17931
|
-
// We do a clone here so that it's safe to mutate the input parameter
|
|
17932
|
-
// in propUpgrades functions (which I will probably accidentally do at
|
|
17933
|
-
// some point, and we would like to not break when that happens).
|
|
17934
|
-
let newEditorProps = ___default["default"].clone(oldWidgetInfo.options) || {};
|
|
17935
|
-
const upgradePropsMap = widgetExports.propUpgrades || {};
|
|
17936
|
-
|
|
17937
|
-
// Empty props usually mean a newly created widget by the editor,
|
|
17938
|
-
// and are always considerered up-to-date.
|
|
17939
|
-
// Mostly, we'd rather not run upgrade functions on props that are
|
|
17940
|
-
// not complete.
|
|
17941
|
-
if (___default["default"].keys(newEditorProps).length !== 0) {
|
|
17942
|
-
// We loop through all the versions after the current version of
|
|
17943
|
-
// the loaded widget, up to and including the latest version of the
|
|
17944
|
-
// loaded widget, and run the upgrade function to bring our loaded
|
|
17945
|
-
// widget's props up to that version.
|
|
17946
|
-
// There is a little subtlety here in that we call
|
|
17947
|
-
// upgradePropsMap[1] to upgrade *to* version 1,
|
|
17948
|
-
// (not from version 1).
|
|
17949
|
-
for (let nextVersion = initialVersion.major + 1; nextVersion <= latestVersion.major; nextVersion++) {
|
|
17950
|
-
if (upgradePropsMap[String(nextVersion)]) {
|
|
17951
|
-
newEditorProps = upgradePropsMap[String(nextVersion)](newEditorProps);
|
|
17952
|
-
} else {
|
|
17953
|
-
// This is a Log.error because it is unlikely to be hit in
|
|
17954
|
-
// local testing, and a Log.error is slightly less scary in
|
|
17955
|
-
// prod than a `throw new Error`
|
|
17956
|
-
Log.error("No upgrade found for widget. Cannot render.", perseusCore.Errors.Internal, {
|
|
17957
|
-
loggedMetadata: {
|
|
17958
|
-
type,
|
|
17959
|
-
fromMajorVersion: nextVersion - 1,
|
|
17960
|
-
toMajorVersion: nextVersion
|
|
17961
|
-
}
|
|
17962
|
-
});
|
|
17963
|
-
// But try to keep going anyways (yolo!)
|
|
17964
|
-
// (Throwing an error here would just break the page
|
|
17965
|
-
// silently anyways, so that doesn't seem much better
|
|
17966
|
-
// than a halfhearted attempt to continue, however
|
|
17967
|
-
// shallow...)
|
|
17968
|
-
}
|
|
17969
|
-
}
|
|
17970
|
-
}
|
|
17971
|
-
|
|
17972
|
-
// Minor version upgrades (eg. new optional props) don't have
|
|
17973
|
-
// transform functions. Instead, we fill in the new props with their
|
|
17974
|
-
// defaults.
|
|
17975
|
-
const defaultProps = type in editors ? editors[type].defaultProps : {};
|
|
17976
|
-
newEditorProps = {
|
|
17977
|
-
...defaultProps,
|
|
17978
|
-
...newEditorProps
|
|
17979
|
-
};
|
|
17980
|
-
let alignment = oldWidgetInfo.alignment;
|
|
17981
|
-
|
|
17982
|
-
// Widgets that support multiple alignments will "lock in" the
|
|
17983
|
-
// alignment to the alignment that would be listed first in the
|
|
17984
|
-
// select box. If the widget only supports one alignment, the
|
|
17985
|
-
// alignment value will likely just end up as "default".
|
|
17986
|
-
if (alignment == null || alignment === "default") {
|
|
17987
|
-
alignment = getSupportedAlignments(type)[0];
|
|
17988
|
-
}
|
|
17989
|
-
let widgetStatic = oldWidgetInfo.static;
|
|
17990
|
-
if (widgetStatic == null) {
|
|
17991
|
-
widgetStatic = DEFAULT_STATIC;
|
|
17992
|
-
}
|
|
17993
|
-
return ___default["default"].extend({}, oldWidgetInfo, {
|
|
17994
|
-
// maintain other info, like type
|
|
17995
|
-
// After upgrading we guarantee that the version is up-to-date
|
|
17996
|
-
version: latestVersion,
|
|
17997
|
-
// Default graded to true (so null/undefined becomes true):
|
|
17998
|
-
graded: oldWidgetInfo.graded != null ? oldWidgetInfo.graded : true,
|
|
17999
|
-
alignment: alignment,
|
|
18000
|
-
static: widgetStatic,
|
|
18001
|
-
options: newEditorProps
|
|
18002
|
-
});
|
|
18003
|
-
};
|
|
18004
17908
|
const getRendererPropsForWidgetInfo = (widgetInfo, strings, problemNum) => {
|
|
18005
17909
|
const type = widgetInfo.type;
|
|
18006
17910
|
const widgetExports = widgets[type];
|
|
@@ -18042,70 +17946,6 @@ const traverseChildWidgets$3 = (widgetInfo, traverseRenderer) => {
|
|
|
18042
17946
|
return widgetInfo;
|
|
18043
17947
|
};
|
|
18044
17948
|
|
|
18045
|
-
/**
|
|
18046
|
-
* Handling for the optional alignments for widgets
|
|
18047
|
-
* See widget-container.jsx for details on how alignments are implemented.
|
|
18048
|
-
*/
|
|
18049
|
-
|
|
18050
|
-
/**
|
|
18051
|
-
* Returns the list of supported alignments for the given (string) widget
|
|
18052
|
-
* type. This is used primarily at editing time to display the choices
|
|
18053
|
-
* for the user.
|
|
18054
|
-
*
|
|
18055
|
-
* Supported alignments are given as an array of strings in the exports of
|
|
18056
|
-
* a widget's module.
|
|
18057
|
-
*/
|
|
18058
|
-
const getSupportedAlignments = type => {
|
|
18059
|
-
const widgetExport = widgets[type];
|
|
18060
|
-
// @ts-expect-error - TS2322 - Type 'string[] | readonly Alignment[]' is not assignable to type 'readonly Alignment[]'.
|
|
18061
|
-
return widgetExport && widgetExport.supportedAlignments || DEFAULT_SUPPORTED_ALIGNMENTS;
|
|
18062
|
-
};
|
|
18063
|
-
|
|
18064
|
-
/**
|
|
18065
|
-
* For the given (string) widget type, determine the default alignment for
|
|
18066
|
-
* the widget. This is used at rendering time to go from "default" alignment
|
|
18067
|
-
* to the actual alignment displayed on the screen.
|
|
18068
|
-
*
|
|
18069
|
-
* The default alignment is given either as a string (called
|
|
18070
|
-
* `defaultAlignment`) or a function (called `getDefaultAlignment`) on
|
|
18071
|
-
* the exports of a widget's module.
|
|
18072
|
-
*/
|
|
18073
|
-
const getDefaultAlignment = type => {
|
|
18074
|
-
const widgetExports = widgets[type];
|
|
18075
|
-
let alignment;
|
|
18076
|
-
if (!widgetExports) {
|
|
18077
|
-
return DEFAULT_ALIGNMENT$1;
|
|
18078
|
-
}
|
|
18079
|
-
if (widgetExports.getDefaultAlignment) {
|
|
18080
|
-
alignment = widgetExports.getDefaultAlignment();
|
|
18081
|
-
} else {
|
|
18082
|
-
alignment = widgetExports.defaultAlignment;
|
|
18083
|
-
}
|
|
18084
|
-
return alignment || DEFAULT_ALIGNMENT$1;
|
|
18085
|
-
};
|
|
18086
|
-
const validAlignments = ["block", "inline-block", "inline", "float-left", "float-right", "full-width"];
|
|
18087
|
-
|
|
18088
|
-
/**
|
|
18089
|
-
* Used at startup to fail fast if an alignment given by a widget is
|
|
18090
|
-
* invalid.
|
|
18091
|
-
*/
|
|
18092
|
-
// TODO(alex): Change this to run as a testcase (vs. being run at runtime)
|
|
18093
|
-
// TODO(LP-10707): I think this can be completely removed because our TypeScript types
|
|
18094
|
-
// enforce this!
|
|
18095
|
-
const validateAlignments = () => {
|
|
18096
|
-
___default["default"].each(widgets, function (widgetInfo) {
|
|
18097
|
-
if (widgetInfo.defaultAlignment && !___default["default"].contains(validAlignments, widgetInfo.defaultAlignment)) {
|
|
18098
|
-
throw new perseusCore.PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid defaultAlignment value: " + widgetInfo.defaultAlignment, perseusCore.Errors.InvalidInput);
|
|
18099
|
-
}
|
|
18100
|
-
if (widgetInfo.supportedAlignments) {
|
|
18101
|
-
const unknownAlignments = ___default["default"].difference(widgetInfo.supportedAlignments, validAlignments);
|
|
18102
|
-
if (unknownAlignments.length) {
|
|
18103
|
-
throw new perseusCore.PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid value for supportedAlignments: " + unknownAlignments.join(" "), perseusCore.Errors.InvalidInput);
|
|
18104
|
-
}
|
|
18105
|
-
}
|
|
18106
|
-
});
|
|
18107
|
-
};
|
|
18108
|
-
|
|
18109
17949
|
/**
|
|
18110
17950
|
* Handling for static mode for widgets that support it.
|
|
18111
17951
|
*/
|
|
@@ -18159,8 +17999,6 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
18159
17999
|
replaceDeprecatedEditors: replaceDeprecatedEditors,
|
|
18160
18000
|
getWidget: getWidget,
|
|
18161
18001
|
getWidgetExport: getWidgetExport,
|
|
18162
|
-
getWidgetValidator: getWidgetValidator,
|
|
18163
|
-
getWidgetScorer: getWidgetScorer,
|
|
18164
18002
|
getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
|
|
18165
18003
|
getEditor: getEditor,
|
|
18166
18004
|
getTransform: getTransform,
|
|
@@ -18169,267 +18007,14 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
18169
18007
|
getPublicWidgets: getPublicWidgets,
|
|
18170
18008
|
isAccessible: isAccessible,
|
|
18171
18009
|
getAllWidgetTypes: getAllWidgetTypes,
|
|
18172
|
-
upgradeWidgetInfoToLatestVersion: upgradeWidgetInfoToLatestVersion,
|
|
18173
18010
|
getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
|
|
18174
18011
|
traverseChildWidgets: traverseChildWidgets$3,
|
|
18175
|
-
getSupportedAlignments: getSupportedAlignments,
|
|
18176
|
-
getDefaultAlignment: getDefaultAlignment,
|
|
18177
|
-
validateAlignments: validateAlignments,
|
|
18178
18012
|
supportsStaticMode: supportsStaticMode,
|
|
18179
18013
|
getStaticTransform: getStaticTransform,
|
|
18180
18014
|
getTracking: getTracking,
|
|
18181
18015
|
isLintable: isLintable
|
|
18182
18016
|
});
|
|
18183
18017
|
|
|
18184
|
-
function getUpgradedWidgetOptions(oldWidgetOptions) {
|
|
18185
|
-
return perseusCore.mapObject(oldWidgetOptions, (widgetInfo, widgetId) => {
|
|
18186
|
-
if (!widgetInfo.type || !widgetInfo.alignment) {
|
|
18187
|
-
const newValues = {};
|
|
18188
|
-
if (!widgetInfo.type) {
|
|
18189
|
-
// TODO: why does widget have no type?
|
|
18190
|
-
// We don't want to derive type from widget ID
|
|
18191
|
-
// see: LEMS-1845
|
|
18192
|
-
newValues.type = widgetId.split(" ")[0];
|
|
18193
|
-
}
|
|
18194
|
-
if (!widgetInfo.alignment) {
|
|
18195
|
-
newValues.alignment = "default";
|
|
18196
|
-
}
|
|
18197
|
-
widgetInfo = {
|
|
18198
|
-
...widgetInfo,
|
|
18199
|
-
...newValues
|
|
18200
|
-
};
|
|
18201
|
-
}
|
|
18202
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
18203
|
-
return upgradeWidgetInfoToLatestVersion(widgetInfo);
|
|
18204
|
-
});
|
|
18205
|
-
}
|
|
18206
|
-
|
|
18207
|
-
/**
|
|
18208
|
-
* Checks the given user input to see if any answerable widgets have not been
|
|
18209
|
-
* "filled in" (ie. if they're empty). Another way to think about this
|
|
18210
|
-
* function is that its a check to see if we can score the provided input.
|
|
18211
|
-
*/
|
|
18212
|
-
function emptyWidgetsFunctional(widgets,
|
|
18213
|
-
// This is a port of old code, I'm not sure why
|
|
18214
|
-
// we need widgetIds vs the keys of the widgets object
|
|
18215
|
-
widgetIds, userInputMap, strings, locale) {
|
|
18216
|
-
return widgetIds.filter(id => {
|
|
18217
|
-
const widget = widgets[id];
|
|
18218
|
-
if (!widget || widget.static === true) {
|
|
18219
|
-
// Static widgets shouldn't count as empty
|
|
18220
|
-
return false;
|
|
18221
|
-
}
|
|
18222
|
-
const validator = getWidgetValidator(widget.type);
|
|
18223
|
-
const userInput = userInputMap[id];
|
|
18224
|
-
const validationData = widget.options;
|
|
18225
|
-
const score = validator?.(userInput, validationData, strings, locale);
|
|
18226
|
-
if (score) {
|
|
18227
|
-
return scoreIsEmpty(score);
|
|
18228
|
-
}
|
|
18229
|
-
});
|
|
18230
|
-
}
|
|
18231
|
-
|
|
18232
|
-
// TODO: combine scorePerseusItem with scoreWidgetsFunctional
|
|
18233
|
-
// once scorePerseusItem is the only one calling scoreWidgetsFunctional
|
|
18234
|
-
function scorePerseusItem(perseusRenderData, userInputMap,
|
|
18235
|
-
// TODO(LEMS-2461,LEMS-2391): these probably
|
|
18236
|
-
// need to be removed before we move this to the server
|
|
18237
|
-
strings, locale) {
|
|
18238
|
-
// There seems to be a chance that PerseusRenderer.widgets might include
|
|
18239
|
-
// widget data for widgets that are not in PerseusRenderer.content,
|
|
18240
|
-
// so this checks that the widgets are being used before scoring them
|
|
18241
|
-
const usedWidgetIds = perseusCore.getWidgetIdsFromContent(perseusRenderData.content);
|
|
18242
|
-
const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
|
|
18243
|
-
return flattenScores(scores);
|
|
18244
|
-
}
|
|
18245
|
-
function scoreWidgetsFunctional(widgets,
|
|
18246
|
-
// This is a port of old code, I'm not sure why
|
|
18247
|
-
// we need widgetIds vs the keys of the widgets object
|
|
18248
|
-
widgetIds, userInputMap, strings, locale) {
|
|
18249
|
-
const upgradedWidgets = getUpgradedWidgetOptions(widgets);
|
|
18250
|
-
const gradedWidgetIds = widgetIds.filter(id => {
|
|
18251
|
-
const props = upgradedWidgets[id];
|
|
18252
|
-
const widgetIsGraded = props?.graded == null || props.graded;
|
|
18253
|
-
const widgetIsStatic = !!props?.static;
|
|
18254
|
-
// Ungraded widgets or widgets set to static shouldn't be graded.
|
|
18255
|
-
return widgetIsGraded && !widgetIsStatic;
|
|
18256
|
-
});
|
|
18257
|
-
const widgetScores = {};
|
|
18258
|
-
gradedWidgetIds.forEach(id => {
|
|
18259
|
-
const widget = upgradedWidgets[id];
|
|
18260
|
-
if (!widget) {
|
|
18261
|
-
return;
|
|
18262
|
-
}
|
|
18263
|
-
const userInput = userInputMap[id];
|
|
18264
|
-
const validator = getWidgetValidator(widget.type);
|
|
18265
|
-
const scorer = getWidgetScorer(widget.type);
|
|
18266
|
-
|
|
18267
|
-
// We do validation (empty checks) first and then scoring. If
|
|
18268
|
-
// validation fails, it's result is itself a PerseusScore.
|
|
18269
|
-
const score = validator?.(userInput, widget.options, strings, locale) ?? scorer?.(userInput, widget.options, strings, locale);
|
|
18270
|
-
if (score != null) {
|
|
18271
|
-
widgetScores[id] = score;
|
|
18272
|
-
}
|
|
18273
|
-
});
|
|
18274
|
-
return widgetScores;
|
|
18275
|
-
}
|
|
18276
|
-
|
|
18277
|
-
/**
|
|
18278
|
-
* TranslationLinter asynchronously calls Khan Academy's poentry linter.
|
|
18279
|
-
*
|
|
18280
|
-
* The poentry linter checks for issues that prevent content from being
|
|
18281
|
-
* translated. This linter is KA-specific and so TranslationLinter does not do
|
|
18282
|
-
* anything in non-KA environments.
|
|
18283
|
-
*/
|
|
18284
|
-
|
|
18285
|
-
// A LintCB is called once the linter has been run.
|
|
18286
|
-
|
|
18287
|
-
// msec to wait before we actually call the linter after receiving the
|
|
18288
|
-
// last call. The timer is reset if the lint is called during the wait.
|
|
18289
|
-
const DEBOUNCE_TIMEOUT = 1000;
|
|
18290
|
-
class TranslationLinter {
|
|
18291
|
-
// To avoid linting when nothing has changed.
|
|
18292
|
-
previousContent = null;
|
|
18293
|
-
|
|
18294
|
-
/**
|
|
18295
|
-
* Runs the poentry linter on the given perseus string asynchronously.
|
|
18296
|
-
*
|
|
18297
|
-
* It doesn't do anything unless we're in a KA environment.
|
|
18298
|
-
*
|
|
18299
|
-
* onLintErrorsGenerated is called even if there are no errors. It is not
|
|
18300
|
-
* called if perseusStr is unchanged from the last call.
|
|
18301
|
-
*
|
|
18302
|
-
* Linting is relatively expensive -- so we debounce linting each instance
|
|
18303
|
-
* for some amount of time.
|
|
18304
|
-
*/
|
|
18305
|
-
runLinter = _.debounce((perseusStr, onLintErrorsGenerated) => {
|
|
18306
|
-
// TODO(FEI-5003): Do we still need to check for 'KA'?
|
|
18307
|
-
if (typeof KA === "undefined") {
|
|
18308
|
-
return;
|
|
18309
|
-
}
|
|
18310
|
-
if (perseusStr === this.previousContent) {
|
|
18311
|
-
return;
|
|
18312
|
-
}
|
|
18313
|
-
this.previousContent = perseusStr;
|
|
18314
|
-
if (perseusStr === "") {
|
|
18315
|
-
onLintErrorsGenerated([]);
|
|
18316
|
-
return;
|
|
18317
|
-
}
|
|
18318
|
-
fetch("/api/internal/translations/lint_poentry?preview=1&lang=en", {
|
|
18319
|
-
headers: {
|
|
18320
|
-
"Content-Type": "application/json"
|
|
18321
|
-
},
|
|
18322
|
-
body: JSON.stringify({
|
|
18323
|
-
// The poentry linter verifies that the translation from
|
|
18324
|
-
// the original text to the translated text is sane. We
|
|
18325
|
-
// want to ensure that the translation from English to
|
|
18326
|
-
// English is sane, so msgid === mstr.
|
|
18327
|
-
msgid: perseusStr,
|
|
18328
|
-
// msgid is the original text
|
|
18329
|
-
msgstr: perseusStr,
|
|
18330
|
-
// msgstr is the translated text
|
|
18331
|
-
format: "perseus_text",
|
|
18332
|
-
filename: ""
|
|
18333
|
-
}),
|
|
18334
|
-
method: "POST"
|
|
18335
|
-
}).then(response => {
|
|
18336
|
-
if (response.status >= 400) {
|
|
18337
|
-
return {
|
|
18338
|
-
status: "error",
|
|
18339
|
-
message: "Could not run i18n linter."
|
|
18340
|
-
};
|
|
18341
|
-
}
|
|
18342
|
-
return response.json();
|
|
18343
|
-
}, rejection => {
|
|
18344
|
-
return {
|
|
18345
|
-
status: "error",
|
|
18346
|
-
message: "Could not run i18n linter."
|
|
18347
|
-
};
|
|
18348
|
-
}).then(json => {
|
|
18349
|
-
if (json.status === "error") {
|
|
18350
|
-
onLintErrorsGenerated(["Some part of this text makes it untranslatable. " + "The specific message from the i18n linter was: " + json.message.replace(/\n/g, " ")]);
|
|
18351
|
-
} else {
|
|
18352
|
-
onLintErrorsGenerated([]);
|
|
18353
|
-
}
|
|
18354
|
-
});
|
|
18355
|
-
}, DEBOUNCE_TIMEOUT);
|
|
18356
|
-
|
|
18357
|
-
/**
|
|
18358
|
-
* Applies an array of errors generated by linters without position
|
|
18359
|
-
* information (TranslationLinter and the legacy getSaveWarnings()) to the
|
|
18360
|
-
* top of a Perseus tree.
|
|
18361
|
-
*/
|
|
18362
|
-
applyLintErrors(parsedMarkdown, translationLintErrors) {
|
|
18363
|
-
// These lint errors do not have position data associated with
|
|
18364
|
-
// them, so we just plop them at the top.
|
|
18365
|
-
if (translationLintErrors.length) {
|
|
18366
|
-
const errorText = translationLintErrors.join("\n\n");
|
|
18367
|
-
parsedMarkdown.unshift({
|
|
18368
|
-
content: {
|
|
18369
|
-
type: "text",
|
|
18370
|
-
content: ""
|
|
18371
|
-
},
|
|
18372
|
-
insideTable: false,
|
|
18373
|
-
message: errorText,
|
|
18374
|
-
ruleName: "legacy-error",
|
|
18375
|
-
severity: PerseusLinter.Rule.Severity.ERROR,
|
|
18376
|
-
type: "lint"
|
|
18377
|
-
});
|
|
18378
|
-
}
|
|
18379
|
-
}
|
|
18380
|
-
}
|
|
18381
|
-
|
|
18382
|
-
/**
|
|
18383
|
-
* Preprocess TeX code to convert things that MathJax doesn't know how to handle
|
|
18384
|
-
* to things is does.
|
|
18385
|
-
*/
|
|
18386
|
-
var preprocessTex = (texCode => texCode
|
|
18387
|
-
// Replace uses of \begin{align}...\end{align} which KaTeX doesn't
|
|
18388
|
-
// support (yet) with \begin{aligned}...\end{aligned} which renders
|
|
18389
|
-
// the same is supported by KaTeX. It does the same for align*.
|
|
18390
|
-
// TODO(kevinb) update content to use aligned instead of align.
|
|
18391
|
-
// TODO(LEMS-1608) Remove this replacement as MathJax supports the
|
|
18392
|
-
// "align" macro correctly (and, in fact, it is not synonymous with
|
|
18393
|
-
// "aligned").
|
|
18394
|
-
.replace(/\{align[*]?\}/g, "{aligned}")
|
|
18395
|
-
// Replace non-breaking spaces with regular spaces.
|
|
18396
|
-
.replace(/[\u00a0]/g, " "));
|
|
18397
|
-
|
|
18398
|
-
const {
|
|
18399
|
-
interactiveSizes
|
|
18400
|
-
} = constants;
|
|
18401
|
-
|
|
18402
|
-
// Note: these size cutoffs represent content-width cutoffs as specified in
|
|
18403
|
-
// http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
|
|
18404
|
-
// TODO(benkomalo): these values aren't used in JS outside of this file, but
|
|
18405
|
-
// are coupled to the values in styles/styles.less - DRY it up at some point
|
|
18406
|
-
const smMax = articleMaxWidthTableInPx;
|
|
18407
|
-
const mdMax = articleMaxWidthInPx;
|
|
18408
|
-
const containerSizeClass = {
|
|
18409
|
-
SMALL: "small",
|
|
18410
|
-
MEDIUM: "medium",
|
|
18411
|
-
LARGE: "large",
|
|
18412
|
-
XLARGE: "xlarge"
|
|
18413
|
-
};
|
|
18414
|
-
const getClassFromWidth = width => {
|
|
18415
|
-
if (!width) {
|
|
18416
|
-
return containerSizeClass.MEDIUM;
|
|
18417
|
-
}
|
|
18418
|
-
if (width <= smMax) {
|
|
18419
|
-
return containerSizeClass.SMALL;
|
|
18420
|
-
}
|
|
18421
|
-
if (width <= mdMax) {
|
|
18422
|
-
return containerSizeClass.MEDIUM;
|
|
18423
|
-
}
|
|
18424
|
-
return containerSizeClass.LARGE;
|
|
18425
|
-
};
|
|
18426
|
-
const getInteractiveBoxFromSizeClass = sizeClass => {
|
|
18427
|
-
if (sizeClass === containerSizeClass.SMALL) {
|
|
18428
|
-
return [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall];
|
|
18429
|
-
}
|
|
18430
|
-
return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
|
|
18431
|
-
};
|
|
18432
|
-
|
|
18433
18018
|
class WidgetContainer extends React__namespace.Component {
|
|
18434
18019
|
widgetRef = /*#__PURE__*/React__namespace.createRef();
|
|
18435
18020
|
static defaultProps = {
|
|
@@ -18502,7 +18087,7 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
18502
18087
|
}
|
|
18503
18088
|
let alignment = this.state.widgetProps.alignment;
|
|
18504
18089
|
if (alignment === "default") {
|
|
18505
|
-
alignment = getDefaultAlignment(type);
|
|
18090
|
+
alignment = perseusCore.CoreWidgetRegistry.getDefaultAlignment(type);
|
|
18506
18091
|
}
|
|
18507
18092
|
className += " widget-" + alignment;
|
|
18508
18093
|
const apiOptions = this.state.widgetProps.apiOptions;
|
|
@@ -18510,6 +18095,7 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
18510
18095
|
// Hack to prevent interaction with static widgets: we overlay a big
|
|
18511
18096
|
// div on top of the widget and overflow: hidden the container.
|
|
18512
18097
|
// Ideally widgets themselves should know how to prevent interaction.
|
|
18098
|
+
// UPDATE HTML5: `inert` on the underlying div would be better
|
|
18513
18099
|
const isStatic = this.state.widgetProps.static || apiOptions.readOnly;
|
|
18514
18100
|
const staticContainerStyles = {
|
|
18515
18101
|
position: "relative",
|
|
@@ -18521,7 +18107,7 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
18521
18107
|
position: "absolute",
|
|
18522
18108
|
top: 0,
|
|
18523
18109
|
left: 0,
|
|
18524
|
-
zIndex:
|
|
18110
|
+
zIndex: 3
|
|
18525
18111
|
};
|
|
18526
18112
|
|
|
18527
18113
|
// Some widgets may include strings of markdown that we may
|
|
@@ -18787,7 +18373,7 @@ class Renderer extends React__namespace.Component {
|
|
|
18787
18373
|
};
|
|
18788
18374
|
};
|
|
18789
18375
|
_getInitialWidgetState = props => {
|
|
18790
|
-
const allWidgetInfo = getUpgradedWidgetOptions(props.widgets);
|
|
18376
|
+
const allWidgetInfo = perseusCore.getUpgradedWidgetOptions(props.widgets);
|
|
18791
18377
|
return {
|
|
18792
18378
|
widgetInfo: allWidgetInfo,
|
|
18793
18379
|
widgetProps: this._getAllWidgetsStartProps(allWidgetInfo, props)
|
|
@@ -19699,7 +19285,7 @@ class Renderer extends React__namespace.Component {
|
|
|
19699
19285
|
* empty if the graph is in the starting state.
|
|
19700
19286
|
*/
|
|
19701
19287
|
emptyWidgets() {
|
|
19702
|
-
return emptyWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.
|
|
19288
|
+
return perseusScore.emptyWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.context.locale);
|
|
19703
19289
|
}
|
|
19704
19290
|
_setWidgetProps = (id, newProps, cb, silent) => {
|
|
19705
19291
|
this.setState(prevState => {
|
|
@@ -19829,8 +19415,8 @@ class Renderer extends React__namespace.Component {
|
|
|
19829
19415
|
* @deprecated use scorePerseusItem
|
|
19830
19416
|
*/
|
|
19831
19417
|
score() {
|
|
19832
|
-
const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.
|
|
19833
|
-
const combinedScore = flattenScores(scores);
|
|
19418
|
+
const scores = perseusScore.scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.context.locale);
|
|
19419
|
+
const combinedScore = perseusScore.flattenScores(scores);
|
|
19834
19420
|
return combinedScore;
|
|
19835
19421
|
}
|
|
19836
19422
|
|
|
@@ -20351,7 +19937,7 @@ class InputWithExamples extends React__namespace.Component {
|
|
|
20351
19937
|
};
|
|
20352
19938
|
_getInputClassName = () => {
|
|
20353
19939
|
// Otherwise, we need to add these INPUT and FOCUSED tags here.
|
|
20354
|
-
let className = ClassNames.INPUT
|
|
19940
|
+
let className = ClassNames.INPUT;
|
|
20355
19941
|
if (this.state.focused) {
|
|
20356
19942
|
className += " " + ClassNames.FOCUSED;
|
|
20357
19943
|
}
|
|
@@ -20744,14 +20330,10 @@ const propTransform$3 = widgetOptions => {
|
|
|
20744
20330
|
var InputNumber$1 = {
|
|
20745
20331
|
name: "input-number",
|
|
20746
20332
|
displayName: "Input number (deprecated - use numeric input instead)",
|
|
20747
|
-
defaultAlignment: "inline-block",
|
|
20748
20333
|
hidden: true,
|
|
20749
20334
|
widget: InputNumber,
|
|
20750
20335
|
transform: propTransform$3,
|
|
20751
20336
|
isLintable: true,
|
|
20752
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
20753
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInputNumberUserInput'.
|
|
20754
|
-
scorer: perseusScore.scoreInputNumber,
|
|
20755
20337
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
20756
20338
|
if (rubric.value == null) {
|
|
20757
20339
|
return;
|
|
@@ -21003,14 +20585,11 @@ const propsTransform = function (widgetOptions) {
|
|
|
21003
20585
|
var NumericInput$1 = {
|
|
21004
20586
|
name: "numeric-input",
|
|
21005
20587
|
displayName: "Numeric input",
|
|
21006
|
-
defaultAlignment: "inline-block",
|
|
21007
20588
|
accessible: true,
|
|
21008
20589
|
widget: NumericInput,
|
|
21009
20590
|
transform: propsTransform,
|
|
21010
20591
|
isLintable: true,
|
|
21011
|
-
|
|
21012
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumericInputUserInput'.
|
|
21013
|
-
scorer: perseusScore.scoreNumericInput,
|
|
20592
|
+
getPublicWidgetOptions: perseusCore.getNumericInputPublicWidgetOptions,
|
|
21014
20593
|
// TODO(LEMS-2656): remove TS suppression
|
|
21015
20594
|
// @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusNumericInputRubric'
|
|
21016
20595
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
@@ -21071,24 +20650,6 @@ const getPromptJSON$p = renderProps => {
|
|
|
21071
20650
|
};
|
|
21072
20651
|
};
|
|
21073
20652
|
|
|
21074
|
-
/**
|
|
21075
|
-
* Several widgets don't have "right"/"wrong" scoring logic,
|
|
21076
|
-
* so this just says to move on past those widgets
|
|
21077
|
-
*
|
|
21078
|
-
* TODO(LEMS-2543) widgets that use this probably shouldn't have any
|
|
21079
|
-
* scoring logic and the thing scoring an exercise
|
|
21080
|
-
* should just know to skip these
|
|
21081
|
-
*/
|
|
21082
|
-
function scoreNoop() {
|
|
21083
|
-
let points = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
21084
|
-
return {
|
|
21085
|
-
type: "points",
|
|
21086
|
-
earned: points,
|
|
21087
|
-
total: points,
|
|
21088
|
-
message: null
|
|
21089
|
-
};
|
|
21090
|
-
}
|
|
21091
|
-
|
|
21092
20653
|
/**
|
|
21093
20654
|
* Utility functions for manipulating ranges of highlightable content.
|
|
21094
20655
|
*/
|
|
@@ -23266,8 +22827,7 @@ var Passage$1 = {
|
|
|
23266
22827
|
transform: editorProps => {
|
|
23267
22828
|
return ___default["default"].pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
|
|
23268
22829
|
},
|
|
23269
|
-
isLintable: true
|
|
23270
|
-
scorer: () => scoreNoop()
|
|
22830
|
+
isLintable: true
|
|
23271
22831
|
};
|
|
23272
22832
|
|
|
23273
22833
|
/**
|
|
@@ -23393,16 +22953,13 @@ var PassageRef$1 = {
|
|
|
23393
22953
|
name: "passage-ref",
|
|
23394
22954
|
displayName: "PassageRef (SAT only)",
|
|
23395
22955
|
hidden: true,
|
|
23396
|
-
defaultAlignment: "inline",
|
|
23397
22956
|
widget: PassageRef,
|
|
23398
22957
|
transform: widgetOptions => ({
|
|
23399
22958
|
passageNumber: widgetOptions.passageNumber,
|
|
23400
22959
|
referenceNumber: widgetOptions.referenceNumber,
|
|
23401
22960
|
summaryText: widgetOptions.summaryText
|
|
23402
22961
|
}),
|
|
23403
|
-
version: perseusCore.passageRefLogic.version
|
|
23404
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
23405
|
-
scorer: () => scoreNoop()
|
|
22962
|
+
version: perseusCore.passageRefLogic.version
|
|
23406
22963
|
};
|
|
23407
22964
|
|
|
23408
22965
|
/**
|
|
@@ -23444,109 +23001,6 @@ var mediaQueries = {
|
|
|
23444
23001
|
xlOrLarger: `@media screen and (min-width: ${pureXlMin})`
|
|
23445
23002
|
};
|
|
23446
23003
|
|
|
23447
|
-
const {
|
|
23448
|
-
zIndexAboveScratchpad,
|
|
23449
|
-
zIndexInteractiveComponent,
|
|
23450
|
-
radioBorderColor,
|
|
23451
|
-
checkedColor,
|
|
23452
|
-
circleSize,
|
|
23453
|
-
radioMarginWidth
|
|
23454
|
-
} = constants;
|
|
23455
|
-
var sharedStyles = aphrodite.StyleSheet.create({
|
|
23456
|
-
perseusInteractive: {
|
|
23457
|
-
zIndex: zIndexInteractiveComponent,
|
|
23458
|
-
position: "relative"
|
|
23459
|
-
},
|
|
23460
|
-
aboveScratchpad: {
|
|
23461
|
-
position: "relative",
|
|
23462
|
-
zIndex: zIndexAboveScratchpad
|
|
23463
|
-
},
|
|
23464
|
-
blankBackground: {
|
|
23465
|
-
// TODO(emily): Use WB colors?
|
|
23466
|
-
backgroundColor: "#FDFDFD"
|
|
23467
|
-
},
|
|
23468
|
-
perseusSrOnly: {
|
|
23469
|
-
border: 0,
|
|
23470
|
-
clip: "rect(0,0,0,0)",
|
|
23471
|
-
height: 1,
|
|
23472
|
-
margin: -1,
|
|
23473
|
-
overflow: "hidden",
|
|
23474
|
-
padding: 0,
|
|
23475
|
-
position: "absolute",
|
|
23476
|
-
width: 1
|
|
23477
|
-
},
|
|
23478
|
-
responsiveLabel: {
|
|
23479
|
-
// NOTE(charlie): The values used here should be kept in sync with the
|
|
23480
|
-
// caption text sizing in Perseus' styles.less.
|
|
23481
|
-
// TODO(charlie): Migrate the captions over to using this style.
|
|
23482
|
-
[mediaQueries.smOrSmaller]: {
|
|
23483
|
-
fontSize: 14,
|
|
23484
|
-
lineHeight: 1.3
|
|
23485
|
-
},
|
|
23486
|
-
[mediaQueries.md]: {
|
|
23487
|
-
fontSize: 17,
|
|
23488
|
-
lineHeight: 1.4
|
|
23489
|
-
},
|
|
23490
|
-
[mediaQueries.lgOrLarger]: {
|
|
23491
|
-
fontSize: 20,
|
|
23492
|
-
lineHeight: 1.4
|
|
23493
|
-
}
|
|
23494
|
-
},
|
|
23495
|
-
responsiveInput: {
|
|
23496
|
-
display: "inline-block",
|
|
23497
|
-
WebkitAppearance: "none",
|
|
23498
|
-
appearance: "none",
|
|
23499
|
-
"::-ms-check": {
|
|
23500
|
-
display: "none"
|
|
23501
|
-
},
|
|
23502
|
-
backgroundColor: "#fff",
|
|
23503
|
-
border: "2px solid #fff",
|
|
23504
|
-
boxShadow: `0 0px 0px 1px ${radioBorderColor}`,
|
|
23505
|
-
outline: "none",
|
|
23506
|
-
boxSizing: "border-box",
|
|
23507
|
-
flexShrink: 0,
|
|
23508
|
-
marginBottom: 1,
|
|
23509
|
-
marginLeft: 1,
|
|
23510
|
-
marginRight: 1,
|
|
23511
|
-
marginTop: 1,
|
|
23512
|
-
height: circleSize - 2,
|
|
23513
|
-
width: circleSize - 2
|
|
23514
|
-
},
|
|
23515
|
-
responsiveRadioInput: {
|
|
23516
|
-
borderRadius: "50%",
|
|
23517
|
-
":checked": {
|
|
23518
|
-
backgroundColor: checkedColor,
|
|
23519
|
-
border: "none",
|
|
23520
|
-
borderRadius: "50%",
|
|
23521
|
-
boxShadow: `inset 0px 0px 0px 2px white, ` + `0 0px 0px 2px ${checkedColor}`,
|
|
23522
|
-
marginTop: radioMarginWidth,
|
|
23523
|
-
marginBottom: radioMarginWidth,
|
|
23524
|
-
marginLeft: radioMarginWidth,
|
|
23525
|
-
marginRight: radioMarginWidth,
|
|
23526
|
-
height: circleSize - 2 * radioMarginWidth,
|
|
23527
|
-
width: circleSize - 2 * radioMarginWidth
|
|
23528
|
-
}
|
|
23529
|
-
},
|
|
23530
|
-
responsiveRadioInputActive: {
|
|
23531
|
-
backgroundColor: "#fff",
|
|
23532
|
-
border: "2px solid #fff",
|
|
23533
|
-
borderRadius: "50%",
|
|
23534
|
-
boxShadow: `0 0px 0px 2px ${checkedColor}`,
|
|
23535
|
-
marginTop: radioMarginWidth,
|
|
23536
|
-
marginBottom: radioMarginWidth,
|
|
23537
|
-
marginLeft: radioMarginWidth,
|
|
23538
|
-
marginRight: radioMarginWidth,
|
|
23539
|
-
height: circleSize - 2 * radioMarginWidth,
|
|
23540
|
-
width: circleSize - 2 * radioMarginWidth,
|
|
23541
|
-
":checked": {
|
|
23542
|
-
backgroundColor: "#fff"
|
|
23543
|
-
}
|
|
23544
|
-
},
|
|
23545
|
-
disableTextSelection: {
|
|
23546
|
-
userSelect: "none"
|
|
23547
|
-
}
|
|
23548
|
-
});
|
|
23549
|
-
|
|
23550
23004
|
/**
|
|
23551
23005
|
* A small collection of useful scrolling utility functions.
|
|
23552
23006
|
*/
|
|
@@ -24615,7 +24069,7 @@ const BaseRadio = function (_ref) {
|
|
|
24615
24069
|
const aphroditeClassName = checked => {
|
|
24616
24070
|
// Whether or not to show correctness borders
|
|
24617
24071
|
// for this choice and the next choice.
|
|
24618
|
-
return aphrodite.css(
|
|
24072
|
+
return aphrodite.css(styles$o.item, styles$o.responsiveItem, checked && styles$o.selectedItem, checked && choice.highlighted && styles$o.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$o.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$o.nextHighlighted);
|
|
24619
24073
|
};
|
|
24620
24074
|
|
|
24621
24075
|
// HACK(abdulrahman): Preloads the selection-state
|
|
@@ -25219,18 +24673,105 @@ var Radio = {
|
|
|
25219
24673
|
staticTransform: transform,
|
|
25220
24674
|
version: perseusCore.radioLogic.version,
|
|
25221
24675
|
propUpgrades: perseusCore.radioLogic.widgetOptionsUpgrades,
|
|
25222
|
-
isLintable: true
|
|
25223
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
25224
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
|
|
25225
|
-
scorer: perseusScore.scoreRadio,
|
|
25226
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
25227
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
|
|
25228
|
-
validator: perseusScore.validateRadio
|
|
24676
|
+
isLintable: true
|
|
25229
24677
|
};
|
|
25230
24678
|
|
|
25231
24679
|
/* eslint-disable import/no-named-as-default */
|
|
25232
24680
|
var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
|
|
25233
24681
|
|
|
24682
|
+
const {
|
|
24683
|
+
radioBorderColor,
|
|
24684
|
+
checkedColor,
|
|
24685
|
+
circleSize,
|
|
24686
|
+
radioMarginWidth
|
|
24687
|
+
} = constants;
|
|
24688
|
+
var sharedStyles = aphrodite.StyleSheet.create({
|
|
24689
|
+
blankBackground: {
|
|
24690
|
+
// TODO(emily): Use WB colors?
|
|
24691
|
+
backgroundColor: "#FDFDFD"
|
|
24692
|
+
},
|
|
24693
|
+
perseusSrOnly: {
|
|
24694
|
+
border: 0,
|
|
24695
|
+
clip: "rect(0,0,0,0)",
|
|
24696
|
+
height: 1,
|
|
24697
|
+
margin: -1,
|
|
24698
|
+
overflow: "hidden",
|
|
24699
|
+
padding: 0,
|
|
24700
|
+
position: "absolute",
|
|
24701
|
+
width: 1
|
|
24702
|
+
},
|
|
24703
|
+
responsiveLabel: {
|
|
24704
|
+
// NOTE(charlie): The values used here should be kept in sync with the
|
|
24705
|
+
// caption text sizing in Perseus' styles.less.
|
|
24706
|
+
// TODO(charlie): Migrate the captions over to using this style.
|
|
24707
|
+
[mediaQueries.smOrSmaller]: {
|
|
24708
|
+
fontSize: 14,
|
|
24709
|
+
lineHeight: 1.3
|
|
24710
|
+
},
|
|
24711
|
+
[mediaQueries.md]: {
|
|
24712
|
+
fontSize: 17,
|
|
24713
|
+
lineHeight: 1.4
|
|
24714
|
+
},
|
|
24715
|
+
[mediaQueries.lgOrLarger]: {
|
|
24716
|
+
fontSize: 20,
|
|
24717
|
+
lineHeight: 1.4
|
|
24718
|
+
}
|
|
24719
|
+
},
|
|
24720
|
+
responsiveInput: {
|
|
24721
|
+
display: "inline-block",
|
|
24722
|
+
WebkitAppearance: "none",
|
|
24723
|
+
appearance: "none",
|
|
24724
|
+
"::-ms-check": {
|
|
24725
|
+
display: "none"
|
|
24726
|
+
},
|
|
24727
|
+
backgroundColor: "#fff",
|
|
24728
|
+
border: "2px solid #fff",
|
|
24729
|
+
boxShadow: `0 0px 0px 1px ${radioBorderColor}`,
|
|
24730
|
+
outline: "none",
|
|
24731
|
+
boxSizing: "border-box",
|
|
24732
|
+
flexShrink: 0,
|
|
24733
|
+
marginBottom: 1,
|
|
24734
|
+
marginLeft: 1,
|
|
24735
|
+
marginRight: 1,
|
|
24736
|
+
marginTop: 1,
|
|
24737
|
+
height: circleSize - 2,
|
|
24738
|
+
width: circleSize - 2
|
|
24739
|
+
},
|
|
24740
|
+
responsiveRadioInput: {
|
|
24741
|
+
borderRadius: "50%",
|
|
24742
|
+
":checked": {
|
|
24743
|
+
backgroundColor: checkedColor,
|
|
24744
|
+
border: "none",
|
|
24745
|
+
borderRadius: "50%",
|
|
24746
|
+
boxShadow: `inset 0px 0px 0px 2px white, ` + `0 0px 0px 2px ${checkedColor}`,
|
|
24747
|
+
marginTop: radioMarginWidth,
|
|
24748
|
+
marginBottom: radioMarginWidth,
|
|
24749
|
+
marginLeft: radioMarginWidth,
|
|
24750
|
+
marginRight: radioMarginWidth,
|
|
24751
|
+
height: circleSize - 2 * radioMarginWidth,
|
|
24752
|
+
width: circleSize - 2 * radioMarginWidth
|
|
24753
|
+
}
|
|
24754
|
+
},
|
|
24755
|
+
responsiveRadioInputActive: {
|
|
24756
|
+
backgroundColor: "#fff",
|
|
24757
|
+
border: "2px solid #fff",
|
|
24758
|
+
borderRadius: "50%",
|
|
24759
|
+
boxShadow: `0 0px 0px 2px ${checkedColor}`,
|
|
24760
|
+
marginTop: radioMarginWidth,
|
|
24761
|
+
marginBottom: radioMarginWidth,
|
|
24762
|
+
marginLeft: radioMarginWidth,
|
|
24763
|
+
marginRight: radioMarginWidth,
|
|
24764
|
+
height: circleSize - 2 * radioMarginWidth,
|
|
24765
|
+
width: circleSize - 2 * radioMarginWidth,
|
|
24766
|
+
":checked": {
|
|
24767
|
+
backgroundColor: "#fff"
|
|
24768
|
+
}
|
|
24769
|
+
},
|
|
24770
|
+
disableTextSelection: {
|
|
24771
|
+
userSelect: "none"
|
|
24772
|
+
}
|
|
24773
|
+
});
|
|
24774
|
+
|
|
25234
24775
|
const getPromptJSON$n = (renderProps, userInput) => {
|
|
25235
24776
|
return {
|
|
25236
24777
|
type: "categorizer",
|
|
@@ -25331,7 +24872,6 @@ class Categorizer extends React__namespace.Component {
|
|
|
25331
24872
|
className: "category " + aphrodite.css(styles$n.cell),
|
|
25332
24873
|
key: catNum
|
|
25333
24874
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
25334
|
-
className: ClassNames.INTERACTIVE,
|
|
25335
24875
|
role: "button",
|
|
25336
24876
|
"aria-label": catName,
|
|
25337
24877
|
onClick: () => this.onChange(itemNum, catNum)
|
|
@@ -25423,12 +24963,6 @@ var Categorizer$1 = {
|
|
|
25423
24963
|
return ___default["default"].pick(editorProps, "items", "categories", "values", "randomizeItems");
|
|
25424
24964
|
},
|
|
25425
24965
|
isLintable: true,
|
|
25426
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
25427
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
|
|
25428
|
-
scorer: perseusScore.scoreCategorizer,
|
|
25429
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
25430
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
|
|
25431
|
-
validator: perseusScore.validateCategorizer,
|
|
25432
24966
|
getPublicWidgetOptions: perseusCore.getCategorizerPublicWidgetOptions
|
|
25433
24967
|
};
|
|
25434
24968
|
|
|
@@ -25638,12 +25172,9 @@ const styles$m = aphrodite.StyleSheet.create({
|
|
|
25638
25172
|
var CSProgram$1 = {
|
|
25639
25173
|
name: "cs-program",
|
|
25640
25174
|
displayName: "CS Program",
|
|
25641
|
-
supportedAlignments: ["block", "full-width"],
|
|
25642
25175
|
widget: CSProgram,
|
|
25643
25176
|
hidden: true,
|
|
25644
|
-
|
|
25645
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
|
|
25646
|
-
scorer: perseusScore.scoreCSProgram
|
|
25177
|
+
getPublicWidgetOptions: perseusCore.getCSProgramPublicWidgetOptions
|
|
25647
25178
|
};
|
|
25648
25179
|
|
|
25649
25180
|
const getPromptJSON$l = renderProps => {
|
|
@@ -25720,11 +25251,8 @@ var Definition$1 = {
|
|
|
25720
25251
|
name: "definition",
|
|
25721
25252
|
displayName: "Definition",
|
|
25722
25253
|
accessible: true,
|
|
25723
|
-
defaultAlignment: "inline",
|
|
25724
25254
|
widget: Definition,
|
|
25725
|
-
transform: x => x
|
|
25726
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
25727
|
-
scorer: () => scoreNoop()
|
|
25255
|
+
transform: x => x
|
|
25728
25256
|
};
|
|
25729
25257
|
|
|
25730
25258
|
// There are no required props, but this component can receive props from any
|
|
@@ -25751,9 +25279,7 @@ var DeprecatedStandin$1 = {
|
|
|
25751
25279
|
name: "deprecated-standin",
|
|
25752
25280
|
displayName: "Deprecated Standin",
|
|
25753
25281
|
widget: DeprecatedStandin,
|
|
25754
|
-
hidden: true
|
|
25755
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
25756
|
-
scorer: () => scoreNoop(1)
|
|
25282
|
+
hidden: true
|
|
25757
25283
|
};
|
|
25758
25284
|
|
|
25759
25285
|
const getPromptJSON$k = (renderProps, userInput) => {
|
|
@@ -25862,16 +25388,10 @@ const optionsTransform = widgetOptions => {
|
|
|
25862
25388
|
var Dropdown$1 = {
|
|
25863
25389
|
name: "dropdown",
|
|
25864
25390
|
displayName: "Drop down",
|
|
25865
|
-
defaultAlignment: "inline-block",
|
|
25866
25391
|
accessible: true,
|
|
25867
25392
|
widget: Dropdown,
|
|
25868
25393
|
transform: optionsTransform,
|
|
25869
|
-
|
|
25870
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
|
|
25871
|
-
scorer: perseusScore.scoreDropdown,
|
|
25872
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
25873
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
|
|
25874
|
-
validator: perseusScore.validateDropdown
|
|
25394
|
+
getPublicWidgetOptions: perseusCore.getDropdownPublicWidgetOptions
|
|
25875
25395
|
};
|
|
25876
25396
|
|
|
25877
25397
|
const getPromptJSON$j = renderProps => {
|
|
@@ -26030,12 +25550,9 @@ var Explanation$1 = {
|
|
|
26030
25550
|
name: "explanation",
|
|
26031
25551
|
displayName: "Explanation",
|
|
26032
25552
|
accessible: true,
|
|
26033
|
-
defaultAlignment: "inline",
|
|
26034
25553
|
widget: Explanation,
|
|
26035
25554
|
transform: ___default["default"].identity,
|
|
26036
|
-
isLintable: true
|
|
26037
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
26038
|
-
scorer: () => scoreNoop()
|
|
25555
|
+
isLintable: true
|
|
26039
25556
|
};
|
|
26040
25557
|
|
|
26041
25558
|
const getPromptJSON$i = (title, rendererJSON, hintRendererJSON) => {
|
|
@@ -27156,7 +26673,7 @@ class FunctionGrapher extends React__namespace.Component {
|
|
|
27156
26673
|
boxSizing: "initial"
|
|
27157
26674
|
}
|
|
27158
26675
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
27159
|
-
className: "graphie-container
|
|
26676
|
+
className: "graphie-container blank-background",
|
|
27160
26677
|
style: {
|
|
27161
26678
|
width: box[0],
|
|
27162
26679
|
height: box[1]
|
|
@@ -27284,8 +26801,7 @@ class Grapher extends React__namespace.Component {
|
|
|
27284
26801
|
const coords = this.props.plot.coords;
|
|
27285
26802
|
const asymptote = this.props.plot.asymptote;
|
|
27286
26803
|
const typeSelector = /*#__PURE__*/React__namespace.createElement("div", {
|
|
27287
|
-
style: typeSelectorStyle
|
|
27288
|
-
className: "above-scratchpad"
|
|
26804
|
+
style: typeSelectorStyle
|
|
27289
26805
|
}, /*#__PURE__*/React__namespace.createElement(ButtonGroup, {
|
|
27290
26806
|
value: type,
|
|
27291
26807
|
allowEmpty: true,
|
|
@@ -27367,10 +26883,7 @@ var Grapher$1 = {
|
|
|
27367
26883
|
hidden: true,
|
|
27368
26884
|
widget: Grapher,
|
|
27369
26885
|
transform: propTransform$2,
|
|
27370
|
-
staticTransform: staticTransform$4
|
|
27371
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
27372
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGrapherUserInput'.
|
|
27373
|
-
scorer: perseusScore.scoreGrapher
|
|
26886
|
+
staticTransform: staticTransform$4
|
|
27374
26887
|
};
|
|
27375
26888
|
|
|
27376
26889
|
const getPromptJSON$f = rendererJSON => {
|
|
@@ -27387,24 +26900,6 @@ const getPromptJSON$f = rendererJSON => {
|
|
|
27387
26900
|
};
|
|
27388
26901
|
};
|
|
27389
26902
|
|
|
27390
|
-
// The `group` widget is basically a widget hosting a full Perseus system in
|
|
27391
|
-
// it. As such, scoring a group means scoring all widgets it contains.
|
|
27392
|
-
function scoreGroup(userInput, rubric, strings, locale) {
|
|
27393
|
-
const scores = scoreWidgetsFunctional(rubric.widgets, Object.keys(rubric.widgets), userInput, strings, locale);
|
|
27394
|
-
return flattenScores(scores);
|
|
27395
|
-
}
|
|
27396
|
-
|
|
27397
|
-
function validateGroup(userInput, validationData, strings, locale) {
|
|
27398
|
-
const emptyWidgets = emptyWidgetsFunctional(validationData.widgets, Object.keys(validationData.widgets), userInput, strings, locale);
|
|
27399
|
-
if (emptyWidgets.length === 0) {
|
|
27400
|
-
return null;
|
|
27401
|
-
}
|
|
27402
|
-
return {
|
|
27403
|
-
type: "invalid",
|
|
27404
|
-
message: null
|
|
27405
|
-
};
|
|
27406
|
-
}
|
|
27407
|
-
|
|
27408
26903
|
// exports has no 'transform'
|
|
27409
26904
|
|
|
27410
26905
|
class Group extends React__namespace.Component {
|
|
@@ -27556,12 +27051,6 @@ var Group$1 = {
|
|
|
27556
27051
|
displayName: "Group (SAT only)",
|
|
27557
27052
|
widget: Group,
|
|
27558
27053
|
traverseChildWidgets: traverseChildWidgets,
|
|
27559
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
27560
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
|
|
27561
|
-
scorer: scoreGroup,
|
|
27562
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
27563
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
|
|
27564
|
-
validator: validateGroup,
|
|
27565
27054
|
hidden: true,
|
|
27566
27055
|
isLintable: true
|
|
27567
27056
|
};
|
|
@@ -27701,10 +27190,7 @@ var Iframe$1 = {
|
|
|
27701
27190
|
displayName: "Iframe (deprecated)",
|
|
27702
27191
|
widget: Iframe,
|
|
27703
27192
|
// Let's not expose it to all content creators yet
|
|
27704
|
-
hidden: true
|
|
27705
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
27706
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusIframeUserInput'.
|
|
27707
|
-
scorer: perseusScore.scoreIframe
|
|
27193
|
+
hidden: true
|
|
27708
27194
|
};
|
|
27709
27195
|
|
|
27710
27196
|
const getPromptJSON$d = renderProps => {
|
|
@@ -27726,15 +27212,13 @@ const defaultBackgroundImage$2 = {
|
|
|
27726
27212
|
width: 0,
|
|
27727
27213
|
height: 0
|
|
27728
27214
|
};
|
|
27729
|
-
const editorAlignments = ["block", "full-width"];
|
|
27730
|
-
const DEFAULT_ALIGNMENT = "block";
|
|
27731
27215
|
|
|
27732
27216
|
// there is no transform as part of exports
|
|
27733
27217
|
|
|
27734
27218
|
class ImageWidget extends React__namespace.Component {
|
|
27735
27219
|
static contextType = PerseusI18nContext;
|
|
27736
27220
|
static defaultProps = {
|
|
27737
|
-
alignment:
|
|
27221
|
+
alignment: "block",
|
|
27738
27222
|
title: "",
|
|
27739
27223
|
range: [defaultRange, defaultRange],
|
|
27740
27224
|
box: [defaultBoxSize, defaultBoxSize],
|
|
@@ -27901,13 +27385,9 @@ var Image$1 = {
|
|
|
27901
27385
|
const bgImage = widgetOptions.backgroundImage;
|
|
27902
27386
|
return !(bgImage && bgImage.url && !widgetOptions.alt);
|
|
27903
27387
|
},
|
|
27904
|
-
defaultAlignment: DEFAULT_ALIGNMENT,
|
|
27905
|
-
supportedAlignments: editorAlignments,
|
|
27906
27388
|
displayName: "Image",
|
|
27907
27389
|
widget: ImageWidget,
|
|
27908
|
-
isLintable: true
|
|
27909
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
27910
|
-
scorer: () => scoreNoop()
|
|
27390
|
+
isLintable: true
|
|
27911
27391
|
};
|
|
27912
27392
|
|
|
27913
27393
|
const getPromptJSON$c = () => {
|
|
@@ -28510,9 +27990,7 @@ var Interactive = {
|
|
|
28510
27990
|
displayName: "Interaction",
|
|
28511
27991
|
widget: Interaction,
|
|
28512
27992
|
transform: ___default["default"].identity,
|
|
28513
|
-
hidden: true
|
|
28514
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
28515
|
-
scorer: () => scoreNoop()
|
|
27993
|
+
hidden: true
|
|
28516
27994
|
};
|
|
28517
27995
|
|
|
28518
27996
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -28794,7 +28272,7 @@ class Graph extends React__namespace.Component {
|
|
|
28794
28272
|
image = null;
|
|
28795
28273
|
}
|
|
28796
28274
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
28797
|
-
className: "graphie-container
|
|
28275
|
+
className: "graphie-container blank-background",
|
|
28798
28276
|
style: {
|
|
28799
28277
|
width: this.props.box[0],
|
|
28800
28278
|
height: this.props.box[1]
|
|
@@ -29428,7 +28906,8 @@ const YGridTick = _ref => {
|
|
|
29428
28906
|
let {
|
|
29429
28907
|
y,
|
|
29430
28908
|
range,
|
|
29431
|
-
tickStep
|
|
28909
|
+
tickStep,
|
|
28910
|
+
showPi
|
|
29432
28911
|
} = _ref;
|
|
29433
28912
|
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
29434
28913
|
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
@@ -29464,6 +28943,7 @@ const YGridTick = _ref => {
|
|
|
29464
28943
|
// If the graph displays both the y and x axis lines within the graph, we want
|
|
29465
28944
|
// to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
|
|
29466
28945
|
const showLabel = shouldShowLabel(y, range, tickStep);
|
|
28946
|
+
const yLabel = showPi ? divideByAndShowPi(y) : y.toString();
|
|
29467
28947
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29468
28948
|
className: "tick",
|
|
29469
28949
|
"aria-hidden": true
|
|
@@ -29481,12 +28961,13 @@ const YGridTick = _ref => {
|
|
|
29481
28961
|
textAnchor: "end",
|
|
29482
28962
|
x: xPositionText,
|
|
29483
28963
|
y: yPositionText
|
|
29484
|
-
},
|
|
28964
|
+
}, yLabel));
|
|
29485
28965
|
};
|
|
29486
28966
|
const XGridTick = _ref2 => {
|
|
29487
28967
|
let {
|
|
29488
28968
|
x,
|
|
29489
|
-
range
|
|
28969
|
+
range,
|
|
28970
|
+
showPi
|
|
29490
28971
|
} = _ref2;
|
|
29491
28972
|
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
29492
28973
|
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
@@ -29525,6 +29006,7 @@ const XGridTick = _ref2 => {
|
|
|
29525
29006
|
// Apply the adjustments to the x and y positions for the text
|
|
29526
29007
|
const xPositionText = xPosition + xAdjustment;
|
|
29527
29008
|
const yPositionText = yPosition + yAdjustment;
|
|
29009
|
+
const xLabel = showPi ? divideByAndShowPi(x) : x.toString();
|
|
29528
29010
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29529
29011
|
className: "tick",
|
|
29530
29012
|
"aria-hidden": true
|
|
@@ -29542,7 +29024,7 @@ const XGridTick = _ref2 => {
|
|
|
29542
29024
|
textAnchor: "middle",
|
|
29543
29025
|
x: xPositionText,
|
|
29544
29026
|
y: yPositionText
|
|
29545
|
-
},
|
|
29027
|
+
}, xLabel));
|
|
29546
29028
|
};
|
|
29547
29029
|
|
|
29548
29030
|
// Determines whether to show the label for the given tick
|
|
@@ -29589,6 +29071,22 @@ const countSignificantDecimals = number => {
|
|
|
29589
29071
|
}
|
|
29590
29072
|
return numStr.split(".")[1].length;
|
|
29591
29073
|
};
|
|
29074
|
+
|
|
29075
|
+
// Show the given value as a multiple of pi (already assumed to be
|
|
29076
|
+
// a multiple of pi). Exported for testing
|
|
29077
|
+
function divideByAndShowPi(value) {
|
|
29078
|
+
const dividedValue = value / Math.PI;
|
|
29079
|
+
switch (dividedValue) {
|
|
29080
|
+
case 1:
|
|
29081
|
+
return "π";
|
|
29082
|
+
case -1:
|
|
29083
|
+
return "-π";
|
|
29084
|
+
case 0:
|
|
29085
|
+
return "0";
|
|
29086
|
+
default:
|
|
29087
|
+
return dividedValue + "π";
|
|
29088
|
+
}
|
|
29089
|
+
}
|
|
29592
29090
|
const AxisTicks = () => {
|
|
29593
29091
|
const {
|
|
29594
29092
|
tickStep,
|
|
@@ -29611,6 +29109,10 @@ const AxisTicks = () => {
|
|
|
29611
29109
|
key: `y-grid-tick-${y}`,
|
|
29612
29110
|
range: range,
|
|
29613
29111
|
tickStep: tickStep[Y]
|
|
29112
|
+
// Show the tick labels as multiples of pi
|
|
29113
|
+
// if the tick step is a multiple of pi.
|
|
29114
|
+
,
|
|
29115
|
+
showPi: tickStep[Y] % Math.PI === 0
|
|
29614
29116
|
});
|
|
29615
29117
|
})), /*#__PURE__*/React__namespace.createElement("g", {
|
|
29616
29118
|
className: "x-axis-ticks"
|
|
@@ -29619,6 +29121,10 @@ const AxisTicks = () => {
|
|
|
29619
29121
|
x: x,
|
|
29620
29122
|
key: `x-grid-tick-${x}`,
|
|
29621
29123
|
range: range
|
|
29124
|
+
// Show the tick labels as multiples of pi
|
|
29125
|
+
// if the tick step is a multiple of pi.
|
|
29126
|
+
,
|
|
29127
|
+
showPi: tickStep[X] % Math.PI === 0
|
|
29622
29128
|
});
|
|
29623
29129
|
})));
|
|
29624
29130
|
};
|
|
@@ -29920,6 +29426,15 @@ const styles$d = aphrodite.StyleSheet.create({
|
|
|
29920
29426
|
}
|
|
29921
29427
|
});
|
|
29922
29428
|
|
|
29429
|
+
function isPiMultiple(value) {
|
|
29430
|
+
// Early return for integers that clearly aren't multiples
|
|
29431
|
+
// of pi to save some computation.
|
|
29432
|
+
if (Number.isInteger(value)) {
|
|
29433
|
+
return false;
|
|
29434
|
+
}
|
|
29435
|
+
return value % Math.PI === 0 || value % (Math.PI / 2) === 0 || value % (Math.PI / 3) === 0 || value % (Math.PI / 4) === 0 || value % (Math.PI / 6) === 0;
|
|
29436
|
+
}
|
|
29437
|
+
|
|
29923
29438
|
const {
|
|
29924
29439
|
firstNumericalParse,
|
|
29925
29440
|
captureScratchpadTouchStart
|
|
@@ -29957,7 +29472,8 @@ class NumberInput extends React__namespace.Component {
|
|
|
29957
29472
|
onFormatChange: PropTypes__default["default"].func,
|
|
29958
29473
|
checkValidity: PropTypes__default["default"].func,
|
|
29959
29474
|
size: PropTypes__default["default"].oneOf(["mini", "small", "normal"]),
|
|
29960
|
-
label: PropTypes__default["default"].oneOf(["put your labels outside your inputs!"])
|
|
29475
|
+
label: PropTypes__default["default"].oneOf(["put your labels outside your inputs!"]),
|
|
29476
|
+
allowPiTruncation: PropTypes__default["default"].bool
|
|
29961
29477
|
};
|
|
29962
29478
|
static defaultProps = {
|
|
29963
29479
|
value: null,
|
|
@@ -29970,6 +29486,19 @@ class NumberInput extends React__namespace.Component {
|
|
|
29970
29486
|
state = {
|
|
29971
29487
|
format: this.props.format
|
|
29972
29488
|
};
|
|
29489
|
+
componentDidMount() {
|
|
29490
|
+
// If the value is a multiple of pi, but it is not in the pi format,
|
|
29491
|
+
// then convert it to the pi format and show it as a multiple of pi.
|
|
29492
|
+
const value = this.getValue();
|
|
29493
|
+
if (this.props.allowPiTruncation && value !== null && value !== 0) {
|
|
29494
|
+
if (this.state.format !== "pi" && isPiMultiple(value)) {
|
|
29495
|
+
this._setValue(value / Math.PI, "pi");
|
|
29496
|
+
this.setState({
|
|
29497
|
+
format: "pi"
|
|
29498
|
+
});
|
|
29499
|
+
}
|
|
29500
|
+
}
|
|
29501
|
+
}
|
|
29973
29502
|
componentDidUpdate(prevProps) {
|
|
29974
29503
|
if (!kmath.number.equal(this.getValue(), this.props.value)) {
|
|
29975
29504
|
this._setValue(this.props.value, this.state.format);
|
|
@@ -30126,7 +29655,8 @@ class RangeInput extends React__namespace.Component {
|
|
|
30126
29655
|
value: PropTypes__default["default"].array.isRequired,
|
|
30127
29656
|
onChange: PropTypes__default["default"].func.isRequired,
|
|
30128
29657
|
placeholder: PropTypes__default["default"].array,
|
|
30129
|
-
checkValidity: PropTypes__default["default"].func
|
|
29658
|
+
checkValidity: PropTypes__default["default"].func,
|
|
29659
|
+
allowPiTruncation: PropTypes__default["default"].bool
|
|
30130
29660
|
};
|
|
30131
29661
|
static defaultProps = {
|
|
30132
29662
|
placeholder: [null, null]
|
|
@@ -30150,14 +29680,16 @@ class RangeInput extends React__namespace.Component {
|
|
|
30150
29680
|
// eslint-disable-next-line react/jsx-no-bind
|
|
30151
29681
|
,
|
|
30152
29682
|
onChange: this.onChange.bind(this, 0),
|
|
30153
|
-
placeholder: this.props.placeholder[0]
|
|
29683
|
+
placeholder: this.props.placeholder[0],
|
|
29684
|
+
allowPiTruncation: this.props.allowPiTruncation
|
|
30154
29685
|
})), /*#__PURE__*/React__namespace.createElement(NumberInput, _extends({}, this.props, {
|
|
30155
29686
|
value: value[1],
|
|
30156
29687
|
checkValidity: val => checkValidity([value[0], val])
|
|
30157
29688
|
// eslint-disable-next-line react/jsx-no-bind
|
|
30158
29689
|
,
|
|
30159
29690
|
onChange: this.onChange.bind(this, 1),
|
|
30160
|
-
placeholder: this.props.placeholder[1]
|
|
29691
|
+
placeholder: this.props.placeholder[1],
|
|
29692
|
+
allowPiTruncation: this.props.allowPiTruncation
|
|
30161
29693
|
})));
|
|
30162
29694
|
}
|
|
30163
29695
|
}
|
|
@@ -37226,10 +36758,7 @@ var InteractiveGraph$1 = {
|
|
|
37226
36758
|
name: "interactive-graph",
|
|
37227
36759
|
displayName: "Interactive graph (Assessments only)",
|
|
37228
36760
|
widget: InteractiveGraph,
|
|
37229
|
-
staticTransform: staticTransform$3
|
|
37230
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
37231
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInteractiveGraphUserInput'.
|
|
37232
|
-
scorer: perseusScore.scoreInteractiveGraph
|
|
36761
|
+
staticTransform: staticTransform$3
|
|
37233
36762
|
};
|
|
37234
36763
|
|
|
37235
36764
|
// TODO: can we use WB typography?
|
|
@@ -38187,9 +37716,7 @@ var LabelImage$1 = {
|
|
|
38187
37716
|
widget: LabelImageWithDependencies,
|
|
38188
37717
|
accessible: true,
|
|
38189
37718
|
isLintable: true,
|
|
38190
|
-
|
|
38191
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusLabelImageUserInput'.
|
|
38192
|
-
scorer: perseusScore.scoreLabelImage
|
|
37719
|
+
getPublicWidgetOptions: perseusCore.getLabelImagePublicWidgetOptions
|
|
38193
37720
|
};
|
|
38194
37721
|
|
|
38195
37722
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -38426,7 +37953,7 @@ class Draggable extends React__namespace.Component {
|
|
|
38426
37953
|
// We need to keep backwards compatbility with rules specified directly
|
|
38427
37954
|
// in CSS. Hence the hacky tacking on of manual classNames.
|
|
38428
37955
|
// See sortable.less for details.
|
|
38429
|
-
let className = aphrodite.css(styles$8.card, styles$8.draggable, layout === "horizontal" && styles$8.horizontalCard, layout === "vertical" && styles$8.verticalCard, type === ItemState.DRAGGING && styles$8.dragging, type === ItemState.DISABLED && styles$8.disabled, !includePadding && styles$8.unpaddedCard) + " " +
|
|
37956
|
+
let className = aphrodite.css(styles$8.card, styles$8.draggable, layout === "horizontal" && styles$8.horizontalCard, layout === "vertical" && styles$8.verticalCard, type === ItemState.DRAGGING && styles$8.dragging, type === ItemState.DISABLED && styles$8.disabled, !includePadding && styles$8.unpaddedCard) + " " + "perseus-sortable-draggable";
|
|
38430
37957
|
if (!includePadding) {
|
|
38431
37958
|
className += " perseus-sortable-draggable-unpadded";
|
|
38432
37959
|
}
|
|
@@ -39117,10 +38644,7 @@ var Matcher$1 = {
|
|
|
39117
38644
|
name: "matcher",
|
|
39118
38645
|
displayName: "Matcher (two column)",
|
|
39119
38646
|
widget: Matcher,
|
|
39120
|
-
isLintable: true
|
|
39121
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
39122
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatcherUserInput'.
|
|
39123
|
-
scorer: perseusScore.scoreMatcher
|
|
38647
|
+
isLintable: true
|
|
39124
38648
|
};
|
|
39125
38649
|
|
|
39126
38650
|
const getPromptJSON$8 = (renderProps, userInput) => {
|
|
@@ -39523,13 +39047,7 @@ var Matrix$1 = {
|
|
|
39523
39047
|
widget: Matrix,
|
|
39524
39048
|
transform: propTransform$1,
|
|
39525
39049
|
staticTransform: staticTransform$2,
|
|
39526
|
-
isLintable: true
|
|
39527
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
39528
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
|
|
39529
|
-
scorer: perseusScore.scoreMatrix,
|
|
39530
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
39531
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
|
|
39532
|
-
validator: perseusScore.validateMatrix
|
|
39050
|
+
isLintable: true
|
|
39533
39051
|
};
|
|
39534
39052
|
|
|
39535
39053
|
const getPromptJSON$7 = () => {
|
|
@@ -39619,7 +39137,7 @@ class Measurer extends React__namespace.Component {
|
|
|
39619
39137
|
// TODO(scottgrant): This isn't a11y-friendly! We should insist on
|
|
39620
39138
|
// finding some valid alt text when this widget is used.
|
|
39621
39139
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
39622
|
-
className: "perseus-widget perseus-widget-measurer " + "graphie-container
|
|
39140
|
+
className: "perseus-widget perseus-widget-measurer " + "graphie-container blank-background",
|
|
39623
39141
|
style: {
|
|
39624
39142
|
width: this.props.box[0],
|
|
39625
39143
|
height: this.props.box[1]
|
|
@@ -39644,9 +39162,7 @@ var Measurer$1 = {
|
|
|
39644
39162
|
hidden: true,
|
|
39645
39163
|
widget: Measurer,
|
|
39646
39164
|
version: perseusCore.measurerLogic.version,
|
|
39647
|
-
propUpgrades: perseusCore.measurerLogic.widgetOptionsUpgrades
|
|
39648
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
39649
|
-
scorer: () => scoreNoop(1)
|
|
39165
|
+
propUpgrades: perseusCore.measurerLogic.widgetOptionsUpgrades
|
|
39650
39166
|
};
|
|
39651
39167
|
|
|
39652
39168
|
/**
|
|
@@ -40534,8 +40050,7 @@ var Molecule$1 = {
|
|
|
40534
40050
|
name: "molecule-renderer",
|
|
40535
40051
|
displayName: "Molecule renderer",
|
|
40536
40052
|
hidden: true,
|
|
40537
|
-
widget: MoleculeWidget
|
|
40538
|
-
scorer: () => scoreNoop()
|
|
40053
|
+
widget: MoleculeWidget
|
|
40539
40054
|
};
|
|
40540
40055
|
|
|
40541
40056
|
const getPromptJSON$6 = (renderProps, userInput) => {
|
|
@@ -41110,12 +40625,7 @@ var NumberLine$1 = {
|
|
|
41110
40625
|
widget: NumberLine,
|
|
41111
40626
|
transform: numberLineTransform,
|
|
41112
40627
|
staticTransform: staticTransform$1,
|
|
41113
|
-
|
|
41114
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
|
|
41115
|
-
scorer: perseusScore.scoreNumberLine,
|
|
41116
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
41117
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
|
|
41118
|
-
validator: perseusScore.validateNumberLine
|
|
40628
|
+
getPublicWidgetOptions: perseusCore.getNumberLinePublicWidgetOptions
|
|
41119
40629
|
};
|
|
41120
40630
|
|
|
41121
40631
|
const getPromptJSON$5 = (renderProps, userInput) => {
|
|
@@ -41133,7 +40643,7 @@ const getPromptJSON$5 = (renderProps, userInput) => {
|
|
|
41133
40643
|
class PlaceholderCard extends React__namespace.Component {
|
|
41134
40644
|
render() {
|
|
41135
40645
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
41136
|
-
className: "card-wrap
|
|
40646
|
+
className: "card-wrap",
|
|
41137
40647
|
style: {
|
|
41138
40648
|
width: this.props.width
|
|
41139
40649
|
}
|
|
@@ -41148,7 +40658,7 @@ class PlaceholderCard extends React__namespace.Component {
|
|
|
41148
40658
|
class DragHintCard extends React__namespace.Component {
|
|
41149
40659
|
render() {
|
|
41150
40660
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
41151
|
-
className: "card-wrap
|
|
40661
|
+
className: "card-wrap"
|
|
41152
40662
|
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
41153
40663
|
className: "card drag-hint"
|
|
41154
40664
|
}));
|
|
@@ -41283,7 +40793,7 @@ class Card extends React__namespace.Component {
|
|
|
41283
40793
|
const rendererProps = ___default["default"].pick(this.props, "content");
|
|
41284
40794
|
const onMouseDown = this.props.animating ? $__default["default"].noop : this.onMouseDown;
|
|
41285
40795
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
41286
|
-
className: "card-wrap
|
|
40796
|
+
className: "card-wrap",
|
|
41287
40797
|
style: style,
|
|
41288
40798
|
onMouseDown: onMouseDown,
|
|
41289
40799
|
onTouchStart: onMouseDown,
|
|
@@ -41654,7 +41164,7 @@ class Orderer extends React__namespace.Component {
|
|
|
41654
41164
|
});
|
|
41655
41165
|
}, this));
|
|
41656
41166
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
41657
|
-
className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "
|
|
41167
|
+
className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "blank-background " + "perseus-clearfix "
|
|
41658
41168
|
// eslint-disable-next-line react/no-string-refs
|
|
41659
41169
|
,
|
|
41660
41170
|
ref: "orderer"
|
|
@@ -41667,12 +41177,6 @@ var Orderer$1 = {
|
|
|
41667
41177
|
hidden: true,
|
|
41668
41178
|
widget: Orderer,
|
|
41669
41179
|
isLintable: true,
|
|
41670
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
41671
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
|
|
41672
|
-
scorer: perseusScore.scoreOrderer,
|
|
41673
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
41674
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
|
|
41675
|
-
validator: perseusScore.validateOrderer,
|
|
41676
41180
|
getPublicWidgetOptions: perseusCore.getOrdererPublicWidgetOptions
|
|
41677
41181
|
};
|
|
41678
41182
|
|
|
@@ -41716,15 +41220,12 @@ class PassageRefTarget extends React__namespace.Component {
|
|
|
41716
41220
|
var PassageRefTarget$1 = {
|
|
41717
41221
|
name: "passage-ref-target",
|
|
41718
41222
|
displayName: "PassageRefTarget",
|
|
41719
|
-
defaultAlignment: "inline",
|
|
41720
41223
|
widget: PassageRefTarget,
|
|
41721
41224
|
hidden: true,
|
|
41722
41225
|
transform: editorProps => {
|
|
41723
41226
|
return ___default["default"].pick(editorProps, "content");
|
|
41724
41227
|
},
|
|
41725
|
-
isLintable: true
|
|
41726
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
41727
|
-
scorer: () => scoreNoop()
|
|
41228
|
+
isLintable: true
|
|
41728
41229
|
};
|
|
41729
41230
|
|
|
41730
41231
|
const getPromptJSON$4 = () => {
|
|
@@ -42726,7 +42227,7 @@ class Plotter extends React__namespace.Component {
|
|
|
42726
42227
|
marginBottom: this.props.labels[0] ? paddingForBottomLabel : 0
|
|
42727
42228
|
};
|
|
42728
42229
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
42729
|
-
className: "perseus-widget-plotter graphie
|
|
42230
|
+
className: "perseus-widget-plotter graphie"
|
|
42730
42231
|
// eslint-disable-next-line react/no-string-refs
|
|
42731
42232
|
,
|
|
42732
42233
|
ref: "graphieDiv",
|
|
@@ -42742,13 +42243,7 @@ var Plotter$1 = {
|
|
|
42742
42243
|
displayName: "Plotter",
|
|
42743
42244
|
hidden: true,
|
|
42744
42245
|
widget: Plotter,
|
|
42745
|
-
staticTransform: staticTransform
|
|
42746
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
42747
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
|
|
42748
|
-
scorer: perseusScore.scorePlotter,
|
|
42749
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
42750
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
|
|
42751
|
-
validator: perseusScore.validatePlotter
|
|
42246
|
+
staticTransform: staticTransform
|
|
42752
42247
|
};
|
|
42753
42248
|
|
|
42754
42249
|
const getPromptJSON$2 = () => {
|
|
@@ -42909,12 +42404,7 @@ var Sorter$1 = {
|
|
|
42909
42404
|
displayName: "Sorter",
|
|
42910
42405
|
widget: Sorter,
|
|
42911
42406
|
isLintable: true,
|
|
42912
|
-
|
|
42913
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
|
|
42914
|
-
scorer: perseusScore.scoreSorter,
|
|
42915
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
42916
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
|
|
42917
|
-
validator: perseusScore.validateSorter
|
|
42407
|
+
getPublicWidgetOptions: perseusCore.getSorterPublicWidgetOptions
|
|
42918
42408
|
};
|
|
42919
42409
|
|
|
42920
42410
|
const {
|
|
@@ -43130,13 +42620,7 @@ var Table$1 = {
|
|
|
43130
42620
|
widget: Table,
|
|
43131
42621
|
transform: propTransform,
|
|
43132
42622
|
hidden: true,
|
|
43133
|
-
isLintable: true
|
|
43134
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
43135
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
|
|
43136
|
-
scorer: perseusScore.scoreTable,
|
|
43137
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
43138
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
|
|
43139
|
-
validator: perseusScore.validateTable
|
|
42623
|
+
isLintable: true
|
|
43140
42624
|
};
|
|
43141
42625
|
|
|
43142
42626
|
const getPromptJSON = () => {
|
|
@@ -43298,11 +42782,7 @@ class Video extends React__namespace.Component {
|
|
|
43298
42782
|
var Video$1 = {
|
|
43299
42783
|
name: "video",
|
|
43300
42784
|
displayName: "Video",
|
|
43301
|
-
|
|
43302
|
-
supportedAlignments: ["block", "float-left", "float-right", "full-width"],
|
|
43303
|
-
widget: Video,
|
|
43304
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
43305
|
-
scorer: () => scoreNoop()
|
|
42785
|
+
widget: Video
|
|
43306
42786
|
};
|
|
43307
42787
|
|
|
43308
42788
|
/* eslint-disable import/no-named-as-default */
|
|
@@ -43321,7 +42801,7 @@ const init = function () {
|
|
|
43321
42801
|
|
|
43322
42802
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43323
42803
|
const libName = "@khanacademy/perseus";
|
|
43324
|
-
const libVersion = "
|
|
42804
|
+
const libVersion = "53.0.0";
|
|
43325
42805
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43326
42806
|
|
|
43327
42807
|
const apiVersion = {
|
|
@@ -44197,6 +43677,42 @@ const ref = /*#__PURE__*/React__namespace.forwardRef((props, ref) => /*#__PURE__
|
|
|
44197
43677
|
}));
|
|
44198
43678
|
}));
|
|
44199
43679
|
|
|
43680
|
+
function isCorrect(score) {
|
|
43681
|
+
return score.type === "points" && score.earned >= score.total;
|
|
43682
|
+
}
|
|
43683
|
+
function keScoreFromPerseusScore(score,
|
|
43684
|
+
// It's weird, but this is what we're passing it
|
|
43685
|
+
guess, state) {
|
|
43686
|
+
if (score.type === "points") {
|
|
43687
|
+
return {
|
|
43688
|
+
empty: false,
|
|
43689
|
+
correct: isCorrect(score),
|
|
43690
|
+
message: score.message,
|
|
43691
|
+
guess: guess,
|
|
43692
|
+
state: state
|
|
43693
|
+
};
|
|
43694
|
+
}
|
|
43695
|
+
if (score.type === "invalid") {
|
|
43696
|
+
return {
|
|
43697
|
+
empty: true,
|
|
43698
|
+
correct: false,
|
|
43699
|
+
message: score.message,
|
|
43700
|
+
suppressAlmostThere: score.suppressAlmostThere,
|
|
43701
|
+
guess: guess,
|
|
43702
|
+
state: state
|
|
43703
|
+
};
|
|
43704
|
+
}
|
|
43705
|
+
throw new perseusCore.PerseusError(
|
|
43706
|
+
// @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
|
|
43707
|
+
"Invalid score type: " + score.type, perseusCore.Errors.InvalidInput, {
|
|
43708
|
+
metadata: {
|
|
43709
|
+
score: JSON.stringify(score),
|
|
43710
|
+
guess: JSON.stringify(guess),
|
|
43711
|
+
state: JSON.stringify(state)
|
|
43712
|
+
}
|
|
43713
|
+
});
|
|
43714
|
+
}
|
|
43715
|
+
|
|
44200
43716
|
/**
|
|
44201
43717
|
* Traverses a {content, widgets, images} renderer props object,
|
|
44202
43718
|
* such as `itemData.question`
|
|
@@ -44217,7 +43733,7 @@ const deepCallbackFor = function (contentCallback, widgetCallback, optionsCallba
|
|
|
44217
43733
|
// This doesn't modify the widget info if the widget info
|
|
44218
43734
|
// is at a later version than is supported, which is important
|
|
44219
43735
|
// for our latestVersion test below.
|
|
44220
|
-
const upgradedWidgetInfo = upgradeWidgetInfoToLatestVersion(widgetInfo);
|
|
43736
|
+
const upgradedWidgetInfo = perseusCore.upgradeWidgetInfoToLatestVersion(widgetInfo);
|
|
44221
43737
|
const latestVersion = getVersion(upgradedWidgetInfo.type);
|
|
44222
43738
|
|
|
44223
43739
|
// Only traverse our children if we can understand this version
|
|
@@ -44329,7 +43845,7 @@ const isRawWidgetInfoRenderableBy = function (widgetInfo, rendererContentVersion
|
|
|
44329
43845
|
|
|
44330
43846
|
// NOTE: This doesn't modify the widget info if the widget info
|
|
44331
43847
|
// is at a later version than is supported.
|
|
44332
|
-
const upgradedWidgetInfo = upgradeWidgetInfoToLatestVersion(widgetInfo);
|
|
43848
|
+
const upgradedWidgetInfo = perseusCore.upgradeWidgetInfoToLatestVersion(widgetInfo);
|
|
44333
43849
|
return isUpgradedWidgetInfoRenderableBy(upgradedWidgetInfo, rendererContentVersion[upgradedWidgetInfo.type]);
|
|
44334
43850
|
};
|
|
44335
43851
|
const isRendererContentRenderableBy = function (rendererOptions, rendererContentVersion) {
|
|
@@ -45677,7 +45193,6 @@ exports.mediaQueries = mediaQueries;
|
|
|
45677
45193
|
exports.parseDataFromJSONP = parseDataFromJSONP;
|
|
45678
45194
|
exports.preprocessTex = preprocessTex;
|
|
45679
45195
|
exports.registerAllWidgetsForTesting = registerAllWidgetsForTesting;
|
|
45680
|
-
exports.scorePerseusItem = scorePerseusItem;
|
|
45681
45196
|
exports.shouldHaveIndividualAnswer = shouldHaveIndividualAnswer;
|
|
45682
45197
|
exports.traverse = traverse;
|
|
45683
45198
|
exports.usePerseusI18n = usePerseusI18n;
|