@khanacademy/perseus 41.4.0 → 42.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/es/index.css +3 -0
- package/dist/es/index.js +879 -343
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +3 -2
- package/dist/es/strings.js.map +1 -1
- package/dist/index.css +3 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +876 -338
- package/dist/index.js.map +1 -1
- package/dist/renderer-util.d.ts +3 -2
- package/dist/renderer.d.ts +7 -10
- package/dist/server-item-renderer.d.ts +34 -3
- package/dist/strings.d.ts +2 -1
- package/dist/strings.js +3 -2
- package/dist/strings.js.map +1 -1
- package/dist/types.d.ts +6 -1
- package/dist/util/parse-perseus-json/exhaustive-test-tool/index.d.ts +2 -0
- package/dist/util/parse-perseus-json/perseus-parsers/graded-group-set-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/graded-group-widget.d.ts +16 -0
- package/dist/util/parse-perseus-json/perseus-parsers/grapher-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/group-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/iframe-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/image-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/input-number-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/interaction-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/label-image-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/matcher-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/matrix-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/measurer-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/molecule-renderer-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/number-line-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/numeric-input-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/orderer-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/passage-ref-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/passage-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/phet-simulation-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/plotter-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/python-program-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/radio-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/sorter-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/table-widget.d.ts +3 -0
- package/dist/util/parse-perseus-json/perseus-parsers/video-widget.d.ts +3 -0
- package/dist/widget-ai-utils/graded-group/{prompt-utils.d.ts → graded-group-ai-utils.d.ts} +1 -1
- package/dist/widget-ai-utils/graded-group-set/{prompt-utils.d.ts → graded-group-set-ai-utils.d.ts} +1 -1
- package/dist/widget-ai-utils/group/{prompt-utils.d.ts → group-ai-utils.d.ts} +1 -1
- package/dist/widget-ai-utils/prompt-types.d.ts +33 -0
- package/dist/widget-ai-utils/unsupported-widget.d.ts +1 -1
- package/dist/widgets/categorizer/categorizer.d.ts +1 -1
- package/dist/widgets/definition/definition.d.ts +1 -1
- package/dist/widgets/dropdown/dropdown.d.ts +1 -1
- package/dist/widgets/explanation/explanation.d.ts +1 -1
- package/dist/widgets/expression/expression.d.ts +1 -1
- package/dist/widgets/graded-group/graded-group.d.ts +1 -1
- package/dist/widgets/graded-group-set/graded-group-set.d.ts +1 -1
- package/dist/widgets/grapher/grapher.d.ts +1 -1
- package/dist/widgets/group/group.d.ts +1 -1
- package/dist/widgets/image/image.d.ts +1 -1
- package/dist/widgets/input-number/input-number.d.ts +1 -1
- package/dist/widgets/interactive-graph.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/components/movable-point.d.ts +9 -0
- package/dist/widgets/interactive-graphs/graphs/components/use-control-point.d.ts +9 -0
- package/dist/widgets/label-image/label-image.d.ts +1 -1
- package/dist/widgets/matcher/matcher.d.ts +1 -1
- package/dist/widgets/matrix/matrix.d.ts +1 -1
- package/dist/widgets/number-line/number-line.d.ts +1 -1
- package/dist/widgets/numeric-input/numeric-input.d.ts +7 -0
- package/dist/widgets/orderer/orderer.d.ts +1 -1
- package/dist/widgets/passage/passage.d.ts +1 -1
- package/dist/widgets/passage-ref/passage-ref.d.ts +1 -1
- package/dist/widgets/radio/radio-component.d.ts +1 -1
- package/dist/widgets/sorter/sorter.d.ts +1 -1
- package/package.json +1 -1
- package/dist/prompt-types.d.ts +0 -33
- /package/dist/widget-ai-utils/categorizer/{prompt-utils.d.ts → categorizer-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/cs-program/{prompt-utils.d.ts → cs-program-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/definition/{prompt-utils.d.ts → definition-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/dropdown/{prompt-utils.d.ts → dropdown-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/explanation/{prompt-utils.d.ts → explanation-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/expression/{prompt-utils.d.ts → expression-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/grapher/{prompt-utils.d.ts → grapher-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/iframe/{prompt-utils.d.ts → iframe-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/image/{prompt-utils.d.ts → image-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/input-number/{prompt-utils.d.ts → input-number-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/interaction/{prompt-utils.d.ts → interaction-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/interactive-graph/{prompt-utils.d.ts → interactive-graph-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/label-image/{prompt-utils.d.ts → label-image-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/matcher/{prompt-utils.d.ts → matcher-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/matrix/{prompt-utils.d.ts → matrix-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/measurer/{prompt-utils.d.ts → measurer-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/number-line/{prompt-utils.d.ts → number-line-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/orderer/{prompt-utils.d.ts → orderer-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/passage/{prompt-utils.d.ts → passage-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/passage-ref/{prompt-utils.d.ts → passage-ref-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/phet-simulation/{prompt-utils.d.ts → phet-simulation-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/plotter/{prompt-utils.d.ts → plotter-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/python-program/{prompt-utils.d.ts → python-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/radio/{prompt-utils.d.ts → radio-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/sorter/{prompt-utils.d.ts → sorter-ai-utils.d.ts} +0 -0
- /package/dist/widget-ai-utils/video/{prompt-utils.d.ts → video-ai-utils.d.ts} +0 -0
package/dist/es/index.js
CHANGED
|
@@ -241,9 +241,10 @@ const mockStrings = {
|
|
|
241
241
|
removePoint: "Remove Point",
|
|
242
242
|
graphKeyboardPrompt: "Press Shift + Enter to interact with the graph",
|
|
243
243
|
srPointAtCoordinates: ({
|
|
244
|
+
num,
|
|
244
245
|
x,
|
|
245
246
|
y
|
|
246
|
-
}) => `Point at ${x} comma ${y}`,
|
|
247
|
+
}) => `Point ${num} at ${x} comma ${y}`,
|
|
247
248
|
srInteractiveElements: ({
|
|
248
249
|
elements
|
|
249
250
|
}) => `Interactive elements: ${elements}`,
|
|
@@ -2591,7 +2592,7 @@ const keypadConfigurationForProps = widgetOptions => {
|
|
|
2591
2592
|
times: widgetOptions.times
|
|
2592
2593
|
};
|
|
2593
2594
|
};
|
|
2594
|
-
const propUpgrades$
|
|
2595
|
+
const propUpgrades$3 = {
|
|
2595
2596
|
/* c8 ignore next */
|
|
2596
2597
|
"1": v0props => ({
|
|
2597
2598
|
times: v0props.times,
|
|
@@ -2651,7 +2652,7 @@ var Expression$1 = {
|
|
|
2651
2652
|
major: 1,
|
|
2652
2653
|
minor: 0
|
|
2653
2654
|
},
|
|
2654
|
-
propUpgrades: propUpgrades$
|
|
2655
|
+
propUpgrades: propUpgrades$3,
|
|
2655
2656
|
// For use by the editor
|
|
2656
2657
|
isLintable: true,
|
|
2657
2658
|
scorer: scoreExpression,
|
|
@@ -18673,6 +18674,140 @@ class QuestionParagraph extends React.Component {
|
|
|
18673
18674
|
}
|
|
18674
18675
|
}
|
|
18675
18676
|
|
|
18677
|
+
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
18678
|
+
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
18679
|
+
|
|
18680
|
+
/**
|
|
18681
|
+
* Regex for widget placeholders in a string.
|
|
18682
|
+
*
|
|
18683
|
+
* First capture group is the widget ID (ex. 'radio 1')
|
|
18684
|
+
* Second capture group is the widget type (ex. "radio)
|
|
18685
|
+
* exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
|
|
18686
|
+
*/
|
|
18687
|
+
function getWidgetRegex() {
|
|
18688
|
+
return /\[\[☃ ([A-Za-z0-9- ]+)\]\]/g;
|
|
18689
|
+
}
|
|
18690
|
+
|
|
18691
|
+
/**
|
|
18692
|
+
* Add a widget placeholder using the widget ID.
|
|
18693
|
+
* ex. addWidget("radio 1") => "[[☃ radio 1]]"
|
|
18694
|
+
*
|
|
18695
|
+
* @param {string} id
|
|
18696
|
+
* @returns {string}
|
|
18697
|
+
*/
|
|
18698
|
+
function addWidget(id) {
|
|
18699
|
+
return `[[☃ ${id}]]`;
|
|
18700
|
+
}
|
|
18701
|
+
|
|
18702
|
+
/**
|
|
18703
|
+
* Get a widget type by a widget's ID
|
|
18704
|
+
* (please don't derive type from ID)
|
|
18705
|
+
*
|
|
18706
|
+
* @param {string} widgetId the ID of the widget
|
|
18707
|
+
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
18708
|
+
* @returns {string} the widget type (ie "radio")
|
|
18709
|
+
*/
|
|
18710
|
+
function getWidgetTypeByWidgetId(WidgetId, widgetMap) {
|
|
18711
|
+
var _widget$type;
|
|
18712
|
+
const widget = widgetMap[WidgetId];
|
|
18713
|
+
return (_widget$type = widget == null ? void 0 : widget.type) != null ? _widget$type : null;
|
|
18714
|
+
}
|
|
18715
|
+
|
|
18716
|
+
/**
|
|
18717
|
+
* Does the content have a specific type of widget?
|
|
18718
|
+
*
|
|
18719
|
+
* @param {string} type the type of the widget in question (ie "radio")
|
|
18720
|
+
* @param {string} content the string to search through
|
|
18721
|
+
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
18722
|
+
* @returns {boolean} if the content includes the widget type
|
|
18723
|
+
*/
|
|
18724
|
+
function contentHasWidgetType(type, content, widgetMap) {
|
|
18725
|
+
return getWidgetIdsFromContentByType(type, content, widgetMap).length > 0;
|
|
18726
|
+
}
|
|
18727
|
+
|
|
18728
|
+
/**
|
|
18729
|
+
* Extract all widget IDs, which includes the widget type and instance number.
|
|
18730
|
+
* example output: ['radio 1', 'categorizer 1', 'categorizor 2']
|
|
18731
|
+
*
|
|
18732
|
+
* Content should contain Perseus widget placeholders,
|
|
18733
|
+
* which look like: '[[☃ radio 1]]'.
|
|
18734
|
+
*
|
|
18735
|
+
* @param {string} content
|
|
18736
|
+
* @returns {ReadonlyArray<string>} widgetIds
|
|
18737
|
+
*/
|
|
18738
|
+
function getWidgetIdsFromContent(content) {
|
|
18739
|
+
const widgets = [];
|
|
18740
|
+
const localWidgetRegex = getWidgetRegex();
|
|
18741
|
+
let match = localWidgetRegex.exec(content);
|
|
18742
|
+
while (match !== null) {
|
|
18743
|
+
widgets.push(match[1]);
|
|
18744
|
+
match = localWidgetRegex.exec(content);
|
|
18745
|
+
}
|
|
18746
|
+
return widgets;
|
|
18747
|
+
}
|
|
18748
|
+
|
|
18749
|
+
/**
|
|
18750
|
+
* Get a list of widget IDs from content,
|
|
18751
|
+
* but only for specific widget types
|
|
18752
|
+
*
|
|
18753
|
+
* @param {string} type the type of widget (ie "radio")
|
|
18754
|
+
* @param {string} content the string to parse
|
|
18755
|
+
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
18756
|
+
* @returns {ReadonlyArray<string>} the widget type (ie "radio")
|
|
18757
|
+
*/
|
|
18758
|
+
function getWidgetIdsFromContentByType(type, content, widgetMap) {
|
|
18759
|
+
const rv = [];
|
|
18760
|
+
const widgetIdsInContent = getWidgetIdsFromContent(content);
|
|
18761
|
+
widgetIdsInContent.forEach(widgetId => {
|
|
18762
|
+
const widget = widgetMap[widgetId];
|
|
18763
|
+
if ((widget == null ? void 0 : widget.type) === type) {
|
|
18764
|
+
rv.push(widgetId);
|
|
18765
|
+
}
|
|
18766
|
+
});
|
|
18767
|
+
return rv;
|
|
18768
|
+
}
|
|
18769
|
+
|
|
18770
|
+
/**
|
|
18771
|
+
* Pull the widget map out of ItemData
|
|
18772
|
+
*
|
|
18773
|
+
* @param {PerseusItem} PerseusItem containing a widgetMap
|
|
18774
|
+
* @returns {WidgetMap} the widget map in the PerseusItem
|
|
18775
|
+
*/
|
|
18776
|
+
function getWidgetsMapFromItemData(itemData) {
|
|
18777
|
+
return itemData.question.widgets;
|
|
18778
|
+
}
|
|
18779
|
+
|
|
18780
|
+
/**
|
|
18781
|
+
* Get the widget information from a WidgetMap
|
|
18782
|
+
*
|
|
18783
|
+
* @param {string} widgetId the ID of the widget
|
|
18784
|
+
* @returns {PerseusWidget | null} the widget data if it exists, otherwise null
|
|
18785
|
+
*/
|
|
18786
|
+
function getWidgetFromWidgetMap(widgetId, widgetMap) {
|
|
18787
|
+
var _widgetMap$widgetId;
|
|
18788
|
+
return (_widgetMap$widgetId = widgetMap[widgetId]) != null ? _widgetMap$widgetId : null;
|
|
18789
|
+
}
|
|
18790
|
+
|
|
18791
|
+
/**
|
|
18792
|
+
* Get select widgets from a widget map.
|
|
18793
|
+
* Useful for multi-items that needs to split
|
|
18794
|
+
* one PerseusItem into several
|
|
18795
|
+
*
|
|
18796
|
+
* @param {ReadonlyArray<string>} widgetIds to extract
|
|
18797
|
+
* @param {PerseusWidgetsMap} widgetMap to extract from
|
|
18798
|
+
* @return {PerseusWidgetsMap} a new widget map with requested widgets
|
|
18799
|
+
*/
|
|
18800
|
+
function getWidgetsFromWidgetMap(widgetIds, widgetMap) {
|
|
18801
|
+
const widgets = {};
|
|
18802
|
+
widgetIds.forEach(widgetId => {
|
|
18803
|
+
const widget = getWidgetFromWidgetMap(widgetId, widgetMap);
|
|
18804
|
+
if (widget) {
|
|
18805
|
+
widgets[widgetId] = widget;
|
|
18806
|
+
}
|
|
18807
|
+
});
|
|
18808
|
+
return widgets;
|
|
18809
|
+
}
|
|
18810
|
+
|
|
18676
18811
|
const DEFAULT_ALIGNMENT$1 = "block";
|
|
18677
18812
|
// NOTE(kevinb): "default" is not one in `validAlignments`.
|
|
18678
18813
|
const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
|
|
@@ -18723,6 +18858,9 @@ const replaceDeprecatedWidgets = () => {
|
|
|
18723
18858
|
replaceWidget("sequence", "deprecated-standin");
|
|
18724
18859
|
replaceWidget("simulator", "deprecated-standin");
|
|
18725
18860
|
replaceWidget("unit-input", "deprecated-standin");
|
|
18861
|
+
|
|
18862
|
+
// Input-Number is a special case as it is being replaced by Numeric-Input
|
|
18863
|
+
replaceWidget("input-number", "numeric-input");
|
|
18726
18864
|
};
|
|
18727
18865
|
const registerEditors = editorsToRegister => {
|
|
18728
18866
|
editorsToRegister.forEach(editor => {
|
|
@@ -18757,6 +18895,11 @@ const replaceDeprecatedEditors = () => {
|
|
|
18757
18895
|
replaceEditor("sequence", "deprecated-standin");
|
|
18758
18896
|
replaceEditor("simulator", "deprecated-standin");
|
|
18759
18897
|
replaceEditor("unit-input", "deprecated-standin");
|
|
18898
|
+
|
|
18899
|
+
// We're replacing the input-number editor with the numeric-input editor here,
|
|
18900
|
+
// but we're also modifying the JSON content to convert input-number into the
|
|
18901
|
+
// numeric-input format over in editor-page.tsx on line 92.
|
|
18902
|
+
replaceEditor("input-number", "numeric-input");
|
|
18760
18903
|
};
|
|
18761
18904
|
const getWidget = name => {
|
|
18762
18905
|
var _widgets$name;
|
|
@@ -18807,10 +18950,8 @@ const getVersionVector = () => {
|
|
|
18807
18950
|
const getPublicWidgets = () => {
|
|
18808
18951
|
// TODO(alex): Update underscore.js so that _.pick can take a function.
|
|
18809
18952
|
// @ts-expect-error - TS2740 - Type 'Pick<{ [key: string]: Readonly<{ name: string; displayName: string; getWidget?: (() => ComponentType<any>) | undefined; accessible?: boolean | ((props: any) => boolean) | undefined; hidden?: boolean | undefined; ... 10 more ...; widget: ComponentType<...>; }>; }, string>' is missing the following properties from type 'readonly Readonly<{ name: string; displayName: string; getWidget?: (() => ComponentType<any>) | undefined; accessible?: boolean | ((props: any) => boolean) | undefined; hidden?: boolean | undefined; ... 10 more ...; widget: ComponentType<...>; }>[]': length, concat, join, slice, and 18 more.
|
|
18810
|
-
return _.pick(widgets,
|
|
18811
|
-
|
|
18812
|
-
_.reject(_.keys(widgets), function (name) {
|
|
18813
|
-
return widgets[name].hidden;
|
|
18953
|
+
return _.pick(widgets, _.reject(_.keys(widgets), function (name) {
|
|
18954
|
+
return widgets[name].hidden || name === "input-number";
|
|
18814
18955
|
}));
|
|
18815
18956
|
};
|
|
18816
18957
|
const isAccessible = widgetInfo => {
|
|
@@ -19128,6 +19269,20 @@ widgetIds, userInputMap, strings, locale) {
|
|
|
19128
19269
|
}
|
|
19129
19270
|
});
|
|
19130
19271
|
}
|
|
19272
|
+
|
|
19273
|
+
// TODO: combine scorePerseusItem with scoreWidgetsFunctional
|
|
19274
|
+
// once scorePerseusItem is the only one calling scoreWidgetsFunctional
|
|
19275
|
+
function scorePerseusItem(perseusRenderData, userInputMap,
|
|
19276
|
+
// TODO(LEMS-2461,LEMS-2391): these probably
|
|
19277
|
+
// need to be removed before we move this to the server
|
|
19278
|
+
strings, locale) {
|
|
19279
|
+
// There seems to be a chance that PerseusRenderer.widgets might include
|
|
19280
|
+
// widget data for widgets that are not in PerseusRenderer.content,
|
|
19281
|
+
// so this checks that the widgets are being used before scoring them
|
|
19282
|
+
const usedWidgetIds = getWidgetIdsFromContent(perseusRenderData.content);
|
|
19283
|
+
const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
|
|
19284
|
+
return Util.flattenScores(scores);
|
|
19285
|
+
}
|
|
19131
19286
|
function scoreWidgetsFunctional(widgets,
|
|
19132
19287
|
// This is a port of old code, I'm not sure why
|
|
19133
19288
|
// we need widgetIds vs the keys of the widgets object
|
|
@@ -19148,7 +19303,7 @@ widgetIds, userInputMap, strings, locale) {
|
|
|
19148
19303
|
const userInput = userInputMap[id];
|
|
19149
19304
|
const scorer = getWidgetScorer(widget.type);
|
|
19150
19305
|
if (widget.type === "group") {
|
|
19151
|
-
const scores = scoreWidgetsFunctional(widget.options.widgets,
|
|
19306
|
+
const scores = scoreWidgetsFunctional(widget.options.widgets, getWidgetIdsFromContent(widget.options.content), userInputMap[id], strings, locale);
|
|
19152
19307
|
widgetScores[id] = Util.flattenScores(scores);
|
|
19153
19308
|
} else if (scorer) {
|
|
19154
19309
|
widgetScores[id] = scorer(userInput, widget.options, strings, locale);
|
|
@@ -19980,7 +20135,7 @@ class Renderer extends React.Component {
|
|
|
19980
20135
|
// Instead, we center them with css magic in styles.less
|
|
19981
20136
|
// /cry(aria)
|
|
19982
20137
|
this._foundTextNodes = true;
|
|
19983
|
-
if (
|
|
20138
|
+
if (this.widgetIds.includes(node.id)) {
|
|
19984
20139
|
// We don't want to render a duplicate widget key/ref,
|
|
19985
20140
|
// as this causes problems with react (for obvious
|
|
19986
20141
|
// reasons). Instead we just notify the
|
|
@@ -20284,7 +20439,7 @@ class Renderer extends React.Component {
|
|
|
20284
20439
|
};
|
|
20285
20440
|
this.getInputPaths = () => {
|
|
20286
20441
|
const inputPaths = [];
|
|
20287
|
-
|
|
20442
|
+
this.widgetIds.forEach(widgetId => {
|
|
20288
20443
|
const widget = this.getWidgetInstance(widgetId);
|
|
20289
20444
|
if (widget && widget.getInputPaths) {
|
|
20290
20445
|
// Grab all input paths and add widgetID to the front
|
|
@@ -20292,7 +20447,7 @@ class Renderer extends React.Component {
|
|
|
20292
20447
|
// Prefix paths with their widgetID and add to collective
|
|
20293
20448
|
// list of paths.
|
|
20294
20449
|
// @ts-expect-error - TS2345 - Argument of type '(inputPath: string) => void' is not assignable to parameter of type 'CollectionIterator<FocusPath, void, readonly FocusPath[]>'.
|
|
20295
|
-
|
|
20450
|
+
widgetInputPaths.forEach(inputPath => {
|
|
20296
20451
|
const relativeInputPath = [widgetId].concat(inputPath);
|
|
20297
20452
|
inputPaths.push(relativeInputPath);
|
|
20298
20453
|
});
|
|
@@ -20421,12 +20576,20 @@ class Renderer extends React.Component {
|
|
|
20421
20576
|
this.getWidgetIds = () => {
|
|
20422
20577
|
return this.widgetIds;
|
|
20423
20578
|
};
|
|
20579
|
+
/**
|
|
20580
|
+
* @deprecated use scorePerseusItem
|
|
20581
|
+
*/
|
|
20582
|
+
this.guessAndScore = () => {
|
|
20583
|
+
const totalGuess = this.getUserInput();
|
|
20584
|
+
const totalScore = this.score();
|
|
20585
|
+
return [totalGuess, totalScore];
|
|
20586
|
+
};
|
|
20424
20587
|
this.examples = () => {
|
|
20425
20588
|
const widgetIds = this.widgetIds;
|
|
20426
|
-
const examples =
|
|
20589
|
+
const examples = widgetIds.map(widgetId => {
|
|
20427
20590
|
const widget = this.getWidgetInstance(widgetId);
|
|
20428
20591
|
return widget != null && widget.examples ? widget.examples() : null;
|
|
20429
|
-
}));
|
|
20592
|
+
}).filter(Boolean);
|
|
20430
20593
|
|
|
20431
20594
|
// no widgets with examples
|
|
20432
20595
|
if (!examples.length) {
|
|
@@ -20681,27 +20844,15 @@ class Renderer extends React.Component {
|
|
|
20681
20844
|
}
|
|
20682
20845
|
|
|
20683
20846
|
/**
|
|
20684
|
-
*
|
|
20685
|
-
*
|
|
20686
|
-
*
|
|
20687
|
-
*/
|
|
20688
|
-
scoreWidgets() {
|
|
20689
|
-
return scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
|
|
20690
|
-
}
|
|
20691
|
-
|
|
20692
|
-
/**
|
|
20693
|
-
* Grades the content.
|
|
20847
|
+
* Scores the content.
|
|
20848
|
+
*
|
|
20849
|
+
* @deprecated use scorePerseusItem
|
|
20694
20850
|
*/
|
|
20695
20851
|
score() {
|
|
20696
|
-
const scores = this.
|
|
20852
|
+
const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
|
|
20697
20853
|
const combinedScore = Util.flattenScores(scores);
|
|
20698
20854
|
return combinedScore;
|
|
20699
20855
|
}
|
|
20700
|
-
guessAndScore() {
|
|
20701
|
-
const totalGuess = this.getUserInput();
|
|
20702
|
-
const totalScore = this.score();
|
|
20703
|
-
return [totalGuess, totalScore];
|
|
20704
|
-
}
|
|
20705
20856
|
render() {
|
|
20706
20857
|
const apiOptions = this.getApiOptions();
|
|
20707
20858
|
if (this.reuseMarkdown) {
|
|
@@ -22150,7 +22301,7 @@ class NumericInput extends React.Component {
|
|
|
22150
22301
|
height: "auto"
|
|
22151
22302
|
}
|
|
22152
22303
|
});
|
|
22153
|
-
return /*#__PURE__*/React.createElement(
|
|
22304
|
+
return /*#__PURE__*/React.createElement(InputWithExamples, {
|
|
22154
22305
|
ref: ref => this.inputRef = ref,
|
|
22155
22306
|
value: this.props.currentValue,
|
|
22156
22307
|
onChange: this.handleChange,
|
|
@@ -22162,7 +22313,7 @@ class NumericInput extends React.Component {
|
|
|
22162
22313
|
id: this.props.widgetId,
|
|
22163
22314
|
disabled: this.props.apiOptions.readOnly,
|
|
22164
22315
|
style: styles.input
|
|
22165
|
-
})
|
|
22316
|
+
});
|
|
22166
22317
|
}
|
|
22167
22318
|
}
|
|
22168
22319
|
|
|
@@ -22231,13 +22382,67 @@ const propsTransform = function propsTransform(widgetOptions) {
|
|
|
22231
22382
|
});
|
|
22232
22383
|
return rendererProps;
|
|
22233
22384
|
};
|
|
22385
|
+
|
|
22386
|
+
// This function is being used to replace the input-number widget
|
|
22387
|
+
// with the numeric-input widget
|
|
22388
|
+
const propUpgrades$2 = {
|
|
22389
|
+
/* c8 ignore next */
|
|
22390
|
+
"1": initialProps => {
|
|
22391
|
+
// If the initialProps has a value, it means we're upgrading from
|
|
22392
|
+
// input-number to numeric-input. In this case, we need to upgrade
|
|
22393
|
+
// the widget options accordingly.
|
|
22394
|
+
if (initialProps.value) {
|
|
22395
|
+
// If the answerType is not number or percent, we need to provide
|
|
22396
|
+
// the answer form for the numeric-input widget
|
|
22397
|
+
const provideAnswerForm = initialProps.answerType !== "number" && initialProps.answerType !== "percent";
|
|
22398
|
+
|
|
22399
|
+
// We need to determine the mathFormat for the numeric-input widget
|
|
22400
|
+
const mathFormat = initialProps.answerType === "rational" ? "proper" // input-number uses "rational" for proper fractions
|
|
22401
|
+
: initialProps.answerType; // Otherwise, we can use the answerType directly
|
|
22402
|
+
|
|
22403
|
+
// If adjusting this logic, also adjust the logic in the convertInputNumberWidgetOptions
|
|
22404
|
+
// function in input-number.ts in the Perseus Editor package's util folder
|
|
22405
|
+
const answers = [{
|
|
22406
|
+
value: initialProps.value,
|
|
22407
|
+
simplify: initialProps.simplify,
|
|
22408
|
+
answerForms: provideAnswerForm ? [mathFormat] : undefined,
|
|
22409
|
+
strict: initialProps.inexact,
|
|
22410
|
+
// We only want to set maxError if the inexact prop is true
|
|
22411
|
+
maxError: initialProps.inexact ? initialProps.maxError : 0,
|
|
22412
|
+
status: "correct",
|
|
22413
|
+
// Input-number only allows correct answers
|
|
22414
|
+
message: ""
|
|
22415
|
+
}];
|
|
22416
|
+
return {
|
|
22417
|
+
answers,
|
|
22418
|
+
size: initialProps.size,
|
|
22419
|
+
coefficient: false,
|
|
22420
|
+
// input-number doesn't have a coefficient prop
|
|
22421
|
+
labelText: "",
|
|
22422
|
+
// input-number doesn't have a labelText prop
|
|
22423
|
+
static: false,
|
|
22424
|
+
// static is always false for numeric-input
|
|
22425
|
+
rightAlign: initialProps.rightAlign || false
|
|
22426
|
+
};
|
|
22427
|
+
} else {
|
|
22428
|
+
// Otherwise simply return the initialProps as there's no differences
|
|
22429
|
+
// between v0 and v1 for numeric-input
|
|
22430
|
+
return initialProps;
|
|
22431
|
+
}
|
|
22432
|
+
}
|
|
22433
|
+
};
|
|
22234
22434
|
var NumericInput$1 = {
|
|
22235
22435
|
name: "numeric-input",
|
|
22236
22436
|
displayName: "Numeric input",
|
|
22237
22437
|
defaultAlignment: "inline-block",
|
|
22238
22438
|
accessible: true,
|
|
22239
22439
|
widget: NumericInput,
|
|
22440
|
+
version: {
|
|
22441
|
+
major: 1,
|
|
22442
|
+
minor: 0
|
|
22443
|
+
},
|
|
22240
22444
|
transform: propsTransform,
|
|
22445
|
+
propUpgrades: propUpgrades$2,
|
|
22241
22446
|
isLintable: true,
|
|
22242
22447
|
scorer: scoreNumericInput,
|
|
22243
22448
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
@@ -29197,6 +29402,9 @@ class Group extends React.Component {
|
|
|
29197
29402
|
this.change("widgets", this.rendererRef.props.widgets);
|
|
29198
29403
|
}
|
|
29199
29404
|
};
|
|
29405
|
+
|
|
29406
|
+
// TODO(LEMS-2391): replace this when there's a separate check
|
|
29407
|
+
// for valid/invalid state
|
|
29200
29408
|
const score = (_this$rendererRef12 = this.rendererRef) == null ? void 0 : _this$rendererRef12.score();
|
|
29201
29409
|
const isValid = score && score.type !== "invalid";
|
|
29202
29410
|
const isInvalid = score && score.type === "invalid";
|
|
@@ -31015,6 +31223,107 @@ function AxisArrows() {
|
|
|
31015
31223
|
})));
|
|
31016
31224
|
}
|
|
31017
31225
|
|
|
31226
|
+
/**
|
|
31227
|
+
* 44 is touch best practice and AAA compliant for WCAG
|
|
31228
|
+
* https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
|
|
31229
|
+
*/
|
|
31230
|
+
const TARGET_SIZE = 44;
|
|
31231
|
+
const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
|
|
31232
|
+
|
|
31233
|
+
// same as pointsFromNormalized in interactive-graph.tsx
|
|
31234
|
+
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
31235
|
+
// maps over [x, y]
|
|
31236
|
+
coords.map((coord, i) => {
|
|
31237
|
+
const axisRange = range[i];
|
|
31238
|
+
if (noSnap) {
|
|
31239
|
+
return axisRange[MIN] + size(axisRange) * coord;
|
|
31240
|
+
}
|
|
31241
|
+
const axisStep = step[i];
|
|
31242
|
+
const nSteps = Math.floor(size(axisRange) / axisStep);
|
|
31243
|
+
const tick = Math.round(coord * nSteps);
|
|
31244
|
+
return axisRange[MIN] + axisStep * tick;
|
|
31245
|
+
}));
|
|
31246
|
+
|
|
31247
|
+
// same as normalizeCoords in interactive-graph.tsx
|
|
31248
|
+
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
31249
|
+
return (coord + ranges[i][1]) / size(ranges[i]);
|
|
31250
|
+
}));
|
|
31251
|
+
|
|
31252
|
+
// Returns the closest point to the given `point` that is within the graph
|
|
31253
|
+
// bounds given in `state`.
|
|
31254
|
+
function bound$1({
|
|
31255
|
+
snapStep,
|
|
31256
|
+
range,
|
|
31257
|
+
point
|
|
31258
|
+
}) {
|
|
31259
|
+
const boundingBox = inset(snapStep, range);
|
|
31260
|
+
return clampToBox(boundingBox, point);
|
|
31261
|
+
}
|
|
31262
|
+
function isUnlimitedGraphState(state) {
|
|
31263
|
+
return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
|
|
31264
|
+
}
|
|
31265
|
+
|
|
31266
|
+
/**
|
|
31267
|
+
* Replace all text outside of the $ TeX blocks with `\\text{...}`
|
|
31268
|
+
* This way, the entire resulting string can be rendered within <TeX>
|
|
31269
|
+
* and the text outside of the $ blocks will be non-TeX text.
|
|
31270
|
+
*/
|
|
31271
|
+
function replaceOutsideTeX(mathString) {
|
|
31272
|
+
// All the information we need is in the first section,
|
|
31273
|
+
// whether it's typed as "blockmath" or "paragraph"
|
|
31274
|
+
const firstSection = SimpleMarkdown.parse(mathString)[0];
|
|
31275
|
+
|
|
31276
|
+
// If it's blockMath, the outer level has the full math content.
|
|
31277
|
+
if (firstSection.type === "blockMath") {
|
|
31278
|
+
return firstSection.content;
|
|
31279
|
+
}
|
|
31280
|
+
|
|
31281
|
+
// If it's a paragraph, we need to iterate through the sections
|
|
31282
|
+
// to look for individual math blocks.
|
|
31283
|
+
const condensedNodes = condenseTextNodes(firstSection.content);
|
|
31284
|
+
let result = "";
|
|
31285
|
+
for (const piece of condensedNodes) {
|
|
31286
|
+
piece.type === "math" ? result += piece.content : result += `\\text{${escapeSpecialChars(piece.content)}}`;
|
|
31287
|
+
}
|
|
31288
|
+
return result;
|
|
31289
|
+
}
|
|
31290
|
+
// Helper function for replaceOutsideTeX()
|
|
31291
|
+
// Condense adjacent text nodes into a single text node
|
|
31292
|
+
function condenseTextNodes(nodes) {
|
|
31293
|
+
const result = [];
|
|
31294
|
+
if (!nodes) {
|
|
31295
|
+
return result;
|
|
31296
|
+
}
|
|
31297
|
+
let currentText = "";
|
|
31298
|
+
for (const node of nodes) {
|
|
31299
|
+
if (node.type === "math") {
|
|
31300
|
+
if (currentText) {
|
|
31301
|
+
result.push({
|
|
31302
|
+
type: "text",
|
|
31303
|
+
content: currentText
|
|
31304
|
+
});
|
|
31305
|
+
currentText = "";
|
|
31306
|
+
}
|
|
31307
|
+
result.push(node);
|
|
31308
|
+
} else {
|
|
31309
|
+
currentText += node.content;
|
|
31310
|
+
}
|
|
31311
|
+
}
|
|
31312
|
+
if (currentText) {
|
|
31313
|
+
result.push({
|
|
31314
|
+
type: "text",
|
|
31315
|
+
content: currentText
|
|
31316
|
+
});
|
|
31317
|
+
}
|
|
31318
|
+
return result;
|
|
31319
|
+
}
|
|
31320
|
+
|
|
31321
|
+
// Helper function for replaceOutsideTeX()
|
|
31322
|
+
function escapeSpecialChars(str) {
|
|
31323
|
+
// Escape $, \, {, and } characters
|
|
31324
|
+
return str.replace(/([$\\{}])/g, "\\$1");
|
|
31325
|
+
}
|
|
31326
|
+
|
|
31018
31327
|
function AxisLabels() {
|
|
31019
31328
|
const {
|
|
31020
31329
|
range,
|
|
@@ -31046,7 +31355,7 @@ function AxisLabels() {
|
|
|
31046
31355
|
fontSize: "14px",
|
|
31047
31356
|
transform: "translate(7px, -50%)"
|
|
31048
31357
|
}
|
|
31049
|
-
}, /*#__PURE__*/React__default.createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default.createElement("span", {
|
|
31358
|
+
}, /*#__PURE__*/React__default.createElement(TeX, null, replaceOutsideTeX(xAxisLabelText))), /*#__PURE__*/React__default.createElement("span", {
|
|
31050
31359
|
style: {
|
|
31051
31360
|
position: "absolute",
|
|
31052
31361
|
left: x2,
|
|
@@ -31054,7 +31363,7 @@ function AxisLabels() {
|
|
|
31054
31363
|
fontSize: "14px",
|
|
31055
31364
|
transform: "translate(-50%, 0px)"
|
|
31056
31365
|
}
|
|
31057
|
-
}, /*#__PURE__*/React__default.createElement(TeX, null, yAxisLabelText)));
|
|
31366
|
+
}, /*#__PURE__*/React__default.createElement(TeX, null, replaceOutsideTeX(yAxisLabelText))));
|
|
31058
31367
|
}
|
|
31059
31368
|
|
|
31060
31369
|
// The size of the ticks and labels in pixels
|
|
@@ -31957,17 +32266,12 @@ const ItemExtras = [
|
|
|
31957
32266
|
// prettier-ignore
|
|
31958
32267
|
|
|
31959
32268
|
// prettier-ignore
|
|
31960
|
-
|
|
32269
|
+
// While this widget is deprecated, we still need the type for conversion purposes
|
|
31961
32270
|
// prettier-ignore
|
|
31962
|
-
|
|
31963
32271
|
// prettier-ignore
|
|
31964
|
-
|
|
31965
32272
|
// prettier-ignore
|
|
31966
|
-
|
|
31967
32273
|
//prettier-ignore
|
|
31968
|
-
|
|
31969
32274
|
// A background image applied to various widgets.
|
|
31970
|
-
|
|
31971
32275
|
const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
|
|
31972
32276
|
|
|
31973
32277
|
// 2D range: xMin, xMax, yMin, yMax
|
|
@@ -31993,107 +32297,6 @@ const lockedFigureFillStyles = {
|
|
|
31993
32297
|
|
|
31994
32298
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
31995
32299
|
|
|
31996
|
-
/**
|
|
31997
|
-
* 44 is touch best practice and AAA compliant for WCAG
|
|
31998
|
-
* https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
|
|
31999
|
-
*/
|
|
32000
|
-
const TARGET_SIZE = 44;
|
|
32001
|
-
const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
|
|
32002
|
-
|
|
32003
|
-
// same as pointsFromNormalized in interactive-graph.tsx
|
|
32004
|
-
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
32005
|
-
// maps over [x, y]
|
|
32006
|
-
coords.map((coord, i) => {
|
|
32007
|
-
const axisRange = range[i];
|
|
32008
|
-
if (noSnap) {
|
|
32009
|
-
return axisRange[MIN] + size(axisRange) * coord;
|
|
32010
|
-
}
|
|
32011
|
-
const axisStep = step[i];
|
|
32012
|
-
const nSteps = Math.floor(size(axisRange) / axisStep);
|
|
32013
|
-
const tick = Math.round(coord * nSteps);
|
|
32014
|
-
return axisRange[MIN] + axisStep * tick;
|
|
32015
|
-
}));
|
|
32016
|
-
|
|
32017
|
-
// same as normalizeCoords in interactive-graph.tsx
|
|
32018
|
-
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
32019
|
-
return (coord + ranges[i][1]) / size(ranges[i]);
|
|
32020
|
-
}));
|
|
32021
|
-
|
|
32022
|
-
// Returns the closest point to the given `point` that is within the graph
|
|
32023
|
-
// bounds given in `state`.
|
|
32024
|
-
function bound$1({
|
|
32025
|
-
snapStep,
|
|
32026
|
-
range,
|
|
32027
|
-
point
|
|
32028
|
-
}) {
|
|
32029
|
-
const boundingBox = inset(snapStep, range);
|
|
32030
|
-
return clampToBox(boundingBox, point);
|
|
32031
|
-
}
|
|
32032
|
-
function isUnlimitedGraphState(state) {
|
|
32033
|
-
return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
|
|
32034
|
-
}
|
|
32035
|
-
|
|
32036
|
-
/**
|
|
32037
|
-
* Replace all text outside of the $ TeX blocks with `\\text{...}`
|
|
32038
|
-
* This way, the entire resulting string can be rendered within <TeX>
|
|
32039
|
-
* and the text outside of the $ blocks will be non-TeX text.
|
|
32040
|
-
*/
|
|
32041
|
-
function replaceOutsideTeX(mathString) {
|
|
32042
|
-
// All the information we need is in the first section,
|
|
32043
|
-
// whether it's typed as "blockmath" or "paragraph"
|
|
32044
|
-
const firstSection = SimpleMarkdown.parse(mathString)[0];
|
|
32045
|
-
|
|
32046
|
-
// If it's blockMath, the outer level has the full math content.
|
|
32047
|
-
if (firstSection.type === "blockMath") {
|
|
32048
|
-
return firstSection.content;
|
|
32049
|
-
}
|
|
32050
|
-
|
|
32051
|
-
// If it's a paragraph, we need to iterate through the sections
|
|
32052
|
-
// to look for individual math blocks.
|
|
32053
|
-
const condensedNodes = condenseTextNodes(firstSection.content);
|
|
32054
|
-
let result = "";
|
|
32055
|
-
for (const piece of condensedNodes) {
|
|
32056
|
-
piece.type === "math" ? result += piece.content : result += `\\text{${escapeSpecialChars(piece.content)}}`;
|
|
32057
|
-
}
|
|
32058
|
-
return result;
|
|
32059
|
-
}
|
|
32060
|
-
// Helper function for replaceOutsideTeX()
|
|
32061
|
-
// Condense adjacent text nodes into a single text node
|
|
32062
|
-
function condenseTextNodes(nodes) {
|
|
32063
|
-
const result = [];
|
|
32064
|
-
if (!nodes) {
|
|
32065
|
-
return result;
|
|
32066
|
-
}
|
|
32067
|
-
let currentText = "";
|
|
32068
|
-
for (const node of nodes) {
|
|
32069
|
-
if (node.type === "math") {
|
|
32070
|
-
if (currentText) {
|
|
32071
|
-
result.push({
|
|
32072
|
-
type: "text",
|
|
32073
|
-
content: currentText
|
|
32074
|
-
});
|
|
32075
|
-
currentText = "";
|
|
32076
|
-
}
|
|
32077
|
-
result.push(node);
|
|
32078
|
-
} else {
|
|
32079
|
-
currentText += node.content;
|
|
32080
|
-
}
|
|
32081
|
-
}
|
|
32082
|
-
if (currentText) {
|
|
32083
|
-
result.push({
|
|
32084
|
-
type: "text",
|
|
32085
|
-
content: currentText
|
|
32086
|
-
});
|
|
32087
|
-
}
|
|
32088
|
-
return result;
|
|
32089
|
-
}
|
|
32090
|
-
|
|
32091
|
-
// Helper function for replaceOutsideTeX()
|
|
32092
|
-
function escapeSpecialChars(str) {
|
|
32093
|
-
// Escape $, \, {, and } characters
|
|
32094
|
-
return str.replace(/([$\\{}])/g, "\\$1");
|
|
32095
|
-
}
|
|
32096
|
-
|
|
32097
32300
|
function LockedLabel(props) {
|
|
32098
32301
|
const {
|
|
32099
32302
|
coord,
|
|
@@ -33316,6 +33519,7 @@ function useControlPoint(params) {
|
|
|
33316
33519
|
} = useGraphConfig();
|
|
33317
33520
|
const {
|
|
33318
33521
|
point,
|
|
33522
|
+
sequenceNumber,
|
|
33319
33523
|
color,
|
|
33320
33524
|
cursor,
|
|
33321
33525
|
constrain = p => snap(snapStep, p),
|
|
@@ -33356,6 +33560,7 @@ function useControlPoint(params) {
|
|
|
33356
33560
|
ref: focusableHandleRef,
|
|
33357
33561
|
role: "button",
|
|
33358
33562
|
"aria-label": strings.srPointAtCoordinates({
|
|
33563
|
+
num: sequenceNumber,
|
|
33359
33564
|
x: srFormatNumber(point[X], locale),
|
|
33360
33565
|
y: srFormatNumber(point[Y], locale)
|
|
33361
33566
|
})
|
|
@@ -33434,6 +33639,7 @@ const MovableLine = props => {
|
|
|
33434
33639
|
focusableHandle: focusableHandle1
|
|
33435
33640
|
} = useControlPoint({
|
|
33436
33641
|
point: start,
|
|
33642
|
+
sequenceNumber: 1,
|
|
33437
33643
|
color,
|
|
33438
33644
|
onMove: p => onMovePoint(0, p)
|
|
33439
33645
|
});
|
|
@@ -33442,6 +33648,7 @@ const MovableLine = props => {
|
|
|
33442
33648
|
focusableHandle: focusableHandle2
|
|
33443
33649
|
} = useControlPoint({
|
|
33444
33650
|
point: end,
|
|
33651
|
+
sequenceNumber: 2,
|
|
33445
33652
|
color,
|
|
33446
33653
|
onMove: p => onMovePoint(1, p)
|
|
33447
33654
|
});
|
|
@@ -33621,14 +33828,17 @@ function AngleGraph(props) {
|
|
|
33621
33828
|
// Render the lines, angle, and then movable points
|
|
33622
33829
|
return /*#__PURE__*/React.createElement(React.Fragment, null, svgLines, /*#__PURE__*/React.createElement(Angle, angleParams), /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
33623
33830
|
point: coords[1],
|
|
33831
|
+
sequenceNumber: 1,
|
|
33624
33832
|
constrain: p => p,
|
|
33625
33833
|
onMove: destination => dispatch(actions.angle.movePoint(1, destination))
|
|
33626
33834
|
}), /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
33627
33835
|
point: coords[0],
|
|
33836
|
+
sequenceNumber: 2,
|
|
33628
33837
|
constrain: getAngleSideConstraint(coords[0], coords[1], snapDegrees || 1),
|
|
33629
33838
|
onMove: destination => dispatch(actions.angle.movePoint(0, destination))
|
|
33630
33839
|
}), /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
33631
33840
|
point: coords[2],
|
|
33841
|
+
sequenceNumber: 3,
|
|
33632
33842
|
constrain: getAngleSideConstraint(coords[2], coords[1], snapDegrees || 1),
|
|
33633
33843
|
onMove: destination => dispatch(actions.angle.movePoint(2, destination))
|
|
33634
33844
|
}));
|
|
@@ -33782,6 +33992,7 @@ function CircleGraph(props) {
|
|
|
33782
33992
|
onMove: c => dispatch(actions.circle.moveCenter(c))
|
|
33783
33993
|
}), /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
33784
33994
|
point: radiusPoint,
|
|
33995
|
+
sequenceNumber: 1,
|
|
33785
33996
|
cursor: "ew-resize",
|
|
33786
33997
|
onMove: newRadiusPoint => {
|
|
33787
33998
|
dispatch(actions.circle.moveRadiusPoint(newRadiusPoint));
|
|
@@ -33957,6 +34168,7 @@ function LimitedPointGraph(props) {
|
|
|
33957
34168
|
return /*#__PURE__*/React.createElement(React.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
33958
34169
|
key: i,
|
|
33959
34170
|
point: point,
|
|
34171
|
+
sequenceNumber: i + 1,
|
|
33960
34172
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
|
|
33961
34173
|
})));
|
|
33962
34174
|
}
|
|
@@ -33999,6 +34211,7 @@ function UnlimitedPointGraph(props) {
|
|
|
33999
34211
|
}), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34000
34212
|
key: i,
|
|
34001
34213
|
point: point,
|
|
34214
|
+
sequenceNumber: i + 1,
|
|
34002
34215
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
|
|
34003
34216
|
ref: ref => {
|
|
34004
34217
|
itemsRef.current[i] = ref;
|
|
@@ -34029,7 +34242,8 @@ function describePointGraph(state, i18n) {
|
|
|
34029
34242
|
if (state.coords.length === 0) {
|
|
34030
34243
|
return strings.srNoInteractiveElements;
|
|
34031
34244
|
}
|
|
34032
|
-
const pointDescriptions = state.coords.map(([x, y]) => strings.srPointAtCoordinates({
|
|
34245
|
+
const pointDescriptions = state.coords.map(([x, y], index) => strings.srPointAtCoordinates({
|
|
34246
|
+
num: index + 1,
|
|
34033
34247
|
x: srFormatNumber(x, locale),
|
|
34034
34248
|
y: srFormatNumber(y, locale)
|
|
34035
34249
|
}));
|
|
@@ -34147,6 +34361,7 @@ const LimitedPolygonGraph = props => {
|
|
|
34147
34361
|
key: "point-" + i,
|
|
34148
34362
|
constrain: constrain,
|
|
34149
34363
|
point: point,
|
|
34364
|
+
sequenceNumber: i + 1,
|
|
34150
34365
|
onMove: destination => {
|
|
34151
34366
|
const now = Date.now();
|
|
34152
34367
|
const targetFPS = 40;
|
|
@@ -34293,6 +34508,7 @@ const UnlimitedPolygonGraph = props => {
|
|
|
34293
34508
|
}), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34294
34509
|
key: i,
|
|
34295
34510
|
point: point,
|
|
34511
|
+
sequenceNumber: i + 1,
|
|
34296
34512
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
|
|
34297
34513
|
ref: ref => {
|
|
34298
34514
|
pointRef.current[i] = ref;
|
|
@@ -34368,6 +34584,7 @@ function QuadraticGraph(props) {
|
|
|
34368
34584
|
}), coords.map((coord, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34369
34585
|
key: "point-" + i,
|
|
34370
34586
|
point: coord,
|
|
34587
|
+
sequenceNumber: i + 1,
|
|
34371
34588
|
onMove: destination => dispatch(actions.quadratic.movePoint(i, destination))
|
|
34372
34589
|
})));
|
|
34373
34590
|
}
|
|
@@ -34496,6 +34713,7 @@ function SinusoidGraph(props) {
|
|
|
34496
34713
|
}), coords.map((coord, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34497
34714
|
key: "point-" + i,
|
|
34498
34715
|
point: coord,
|
|
34716
|
+
sequenceNumber: i + 1,
|
|
34499
34717
|
onMove: destination => dispatch(actions.sinusoid.movePoint(i, destination))
|
|
34500
34718
|
})));
|
|
34501
34719
|
}
|
|
@@ -44624,7 +44842,7 @@ const init = function init() {
|
|
|
44624
44842
|
|
|
44625
44843
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44626
44844
|
const libName = "@khanacademy/perseus";
|
|
44627
|
-
const libVersion = "
|
|
44845
|
+
const libVersion = "42.0.0";
|
|
44628
44846
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44629
44847
|
|
|
44630
44848
|
const apiVersion = {
|
|
@@ -45248,6 +45466,13 @@ class ServerItemRenderer extends React.Component {
|
|
|
45248
45466
|
this.props.onRendered(true);
|
|
45249
45467
|
}
|
|
45250
45468
|
}
|
|
45469
|
+
if (this.props.score && this.props.score !== prevProps.score) {
|
|
45470
|
+
const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
|
|
45471
|
+
this.setState({
|
|
45472
|
+
questionCompleted: this.props.score.correct,
|
|
45473
|
+
questionHighlightedWidgets: emptyQuestionAreaWidgets
|
|
45474
|
+
});
|
|
45475
|
+
}
|
|
45251
45476
|
}
|
|
45252
45477
|
componentWillUnmount() {
|
|
45253
45478
|
if (this.blurTimeoutID != null) {
|
|
@@ -45328,7 +45553,7 @@ class ServerItemRenderer extends React.Component {
|
|
|
45328
45553
|
|
|
45329
45554
|
/**
|
|
45330
45555
|
* Accepts a question area widgetId, or an answer area widgetId of
|
|
45331
|
-
* the form "answer-input
|
|
45556
|
+
* the form "answer-numeric-input 1", or the string "answer-area"
|
|
45332
45557
|
* for the whole answer area (if the answer area is a single widget).
|
|
45333
45558
|
*/
|
|
45334
45559
|
_setWidgetProps(widgetId, newProps, callback) {
|
|
@@ -45365,18 +45590,37 @@ class ServerItemRenderer extends React.Component {
|
|
|
45365
45590
|
return this.questionRenderer.getPromptJSON();
|
|
45366
45591
|
}
|
|
45367
45592
|
|
|
45593
|
+
/**
|
|
45594
|
+
* Returns an array of the widget `.getUserInput()` results
|
|
45595
|
+
*
|
|
45596
|
+
* TODO: can we remove this? Seems to be just for backwards
|
|
45597
|
+
* compatibility with old Perseus Chrome logging
|
|
45598
|
+
* @deprecated use getUserInput
|
|
45599
|
+
*/
|
|
45600
|
+
getUserInputLegacy() {
|
|
45601
|
+
return this.questionRenderer.getUserInput();
|
|
45602
|
+
}
|
|
45603
|
+
|
|
45604
|
+
/**
|
|
45605
|
+
* Returns an object of the widget `.getUserInput()` results
|
|
45606
|
+
*/
|
|
45607
|
+
getUserInput() {
|
|
45608
|
+
return this.questionRenderer.getUserInputMap();
|
|
45609
|
+
}
|
|
45610
|
+
|
|
45368
45611
|
/**
|
|
45369
45612
|
* Grades the item.
|
|
45613
|
+
*
|
|
45614
|
+
* @deprecated use scorePerseusItem
|
|
45370
45615
|
*/
|
|
45371
45616
|
scoreInput() {
|
|
45372
|
-
const
|
|
45373
|
-
const
|
|
45374
|
-
const score = guessAndScore[1];
|
|
45617
|
+
const guess = this.getUserInput();
|
|
45618
|
+
const score = scorePerseusItem(this.props.item.question, guess, this.context.strings, this.context.locale);
|
|
45375
45619
|
|
|
45376
45620
|
// Continue to include an empty guess for the now defunct answer area.
|
|
45377
45621
|
// TODO(alex): Check whether we rely on the format here for
|
|
45378
45622
|
// analyzing ProblemLogs. If not, remove this layer.
|
|
45379
|
-
const maxCompatGuess = [
|
|
45623
|
+
const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
|
|
45380
45624
|
const keScore = Util.keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
|
|
45381
45625
|
const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
|
|
45382
45626
|
this.setState({
|
|
@@ -47224,140 +47468,6 @@ const registerAllWidgetsForTesting = () => {
|
|
|
47224
47468
|
replaceDeprecatedWidgets();
|
|
47225
47469
|
};
|
|
47226
47470
|
|
|
47227
|
-
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
47228
|
-
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
47229
|
-
|
|
47230
|
-
/**
|
|
47231
|
-
* Regex for widget placeholders in a string.
|
|
47232
|
-
*
|
|
47233
|
-
* First capture group is the widget ID (ex. 'radio 1')
|
|
47234
|
-
* Second capture group is the widget type (ex. "radio)
|
|
47235
|
-
* exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
|
|
47236
|
-
*/
|
|
47237
|
-
function getWidgetRegex() {
|
|
47238
|
-
return /\[\[☃ ([A-Za-z0-9- ]+)\]\]/g;
|
|
47239
|
-
}
|
|
47240
|
-
|
|
47241
|
-
/**
|
|
47242
|
-
* Add a widget placeholder using the widget ID.
|
|
47243
|
-
* ex. addWidget("radio 1") => "[[☃ radio 1]]"
|
|
47244
|
-
*
|
|
47245
|
-
* @param {string} id
|
|
47246
|
-
* @returns {string}
|
|
47247
|
-
*/
|
|
47248
|
-
function addWidget(id) {
|
|
47249
|
-
return `[[☃ ${id}]]`;
|
|
47250
|
-
}
|
|
47251
|
-
|
|
47252
|
-
/**
|
|
47253
|
-
* Get a widget type by a widget's ID
|
|
47254
|
-
* (please don't derive type from ID)
|
|
47255
|
-
*
|
|
47256
|
-
* @param {string} widgetId the ID of the widget
|
|
47257
|
-
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
47258
|
-
* @returns {string} the widget type (ie "radio")
|
|
47259
|
-
*/
|
|
47260
|
-
function getWidgetTypeByWidgetId(WidgetId, widgetMap) {
|
|
47261
|
-
var _widget$type;
|
|
47262
|
-
const widget = widgetMap[WidgetId];
|
|
47263
|
-
return (_widget$type = widget == null ? void 0 : widget.type) != null ? _widget$type : null;
|
|
47264
|
-
}
|
|
47265
|
-
|
|
47266
|
-
/**
|
|
47267
|
-
* Does the content have a specific type of widget?
|
|
47268
|
-
*
|
|
47269
|
-
* @param {string} type the type of the widget in question (ie "radio")
|
|
47270
|
-
* @param {string} content the string to search through
|
|
47271
|
-
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
47272
|
-
* @returns {boolean} if the content includes the widget type
|
|
47273
|
-
*/
|
|
47274
|
-
function contentHasWidgetType(type, content, widgetMap) {
|
|
47275
|
-
return getWidgetIdsFromContentByType(type, content, widgetMap).length > 0;
|
|
47276
|
-
}
|
|
47277
|
-
|
|
47278
|
-
/**
|
|
47279
|
-
* Extract all widget IDs, which includes the widget type and instance number.
|
|
47280
|
-
* example output: ['radio 1', 'categorizer 1', 'categorizor 2']
|
|
47281
|
-
*
|
|
47282
|
-
* Content should contain Perseus widget placeholders,
|
|
47283
|
-
* which look like: '[[☃ radio 1]]'.
|
|
47284
|
-
*
|
|
47285
|
-
* @param {string} content
|
|
47286
|
-
* @returns {ReadonlyArray<string>} widgetIds
|
|
47287
|
-
*/
|
|
47288
|
-
function getWidgetIdsFromContent(content) {
|
|
47289
|
-
const widgets = [];
|
|
47290
|
-
const localWidgetRegex = getWidgetRegex();
|
|
47291
|
-
let match = localWidgetRegex.exec(content);
|
|
47292
|
-
while (match !== null) {
|
|
47293
|
-
widgets.push(match[1]);
|
|
47294
|
-
match = localWidgetRegex.exec(content);
|
|
47295
|
-
}
|
|
47296
|
-
return widgets;
|
|
47297
|
-
}
|
|
47298
|
-
|
|
47299
|
-
/**
|
|
47300
|
-
* Get a list of widget IDs from content,
|
|
47301
|
-
* but only for specific widget types
|
|
47302
|
-
*
|
|
47303
|
-
* @param {string} type the type of widget (ie "radio")
|
|
47304
|
-
* @param {string} content the string to parse
|
|
47305
|
-
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
47306
|
-
* @returns {ReadonlyArray<string>} the widget type (ie "radio")
|
|
47307
|
-
*/
|
|
47308
|
-
function getWidgetIdsFromContentByType(type, content, widgetMap) {
|
|
47309
|
-
const rv = [];
|
|
47310
|
-
const widgetIdsInContent = getWidgetIdsFromContent(content);
|
|
47311
|
-
widgetIdsInContent.forEach(widgetId => {
|
|
47312
|
-
const widget = widgetMap[widgetId];
|
|
47313
|
-
if ((widget == null ? void 0 : widget.type) === type) {
|
|
47314
|
-
rv.push(widgetId);
|
|
47315
|
-
}
|
|
47316
|
-
});
|
|
47317
|
-
return rv;
|
|
47318
|
-
}
|
|
47319
|
-
|
|
47320
|
-
/**
|
|
47321
|
-
* Pull the widget map out of ItemData
|
|
47322
|
-
*
|
|
47323
|
-
* @param {PerseusItem} PerseusItem containing a widgetMap
|
|
47324
|
-
* @returns {WidgetMap} the widget map in the PerseusItem
|
|
47325
|
-
*/
|
|
47326
|
-
function getWidgetsMapFromItemData(itemData) {
|
|
47327
|
-
return itemData.question.widgets;
|
|
47328
|
-
}
|
|
47329
|
-
|
|
47330
|
-
/**
|
|
47331
|
-
* Get the widget information from a WidgetMap
|
|
47332
|
-
*
|
|
47333
|
-
* @param {string} widgetId the ID of the widget
|
|
47334
|
-
* @returns {PerseusWidget | null} the widget data if it exists, otherwise null
|
|
47335
|
-
*/
|
|
47336
|
-
function getWidgetFromWidgetMap(widgetId, widgetMap) {
|
|
47337
|
-
var _widgetMap$widgetId;
|
|
47338
|
-
return (_widgetMap$widgetId = widgetMap[widgetId]) != null ? _widgetMap$widgetId : null;
|
|
47339
|
-
}
|
|
47340
|
-
|
|
47341
|
-
/**
|
|
47342
|
-
* Get select widgets from a widget map.
|
|
47343
|
-
* Useful for multi-items that needs to split
|
|
47344
|
-
* one PerseusItem into several
|
|
47345
|
-
*
|
|
47346
|
-
* @param {ReadonlyArray<string>} widgetIds to extract
|
|
47347
|
-
* @param {PerseusWidgetsMap} widgetMap to extract from
|
|
47348
|
-
* @return {PerseusWidgetsMap} a new widget map with requested widgets
|
|
47349
|
-
*/
|
|
47350
|
-
function getWidgetsFromWidgetMap(widgetIds, widgetMap) {
|
|
47351
|
-
const widgets = {};
|
|
47352
|
-
widgetIds.forEach(widgetId => {
|
|
47353
|
-
const widget = getWidgetFromWidgetMap(widgetId, widgetMap);
|
|
47354
|
-
if (widget) {
|
|
47355
|
-
widgets[widgetId] = widget;
|
|
47356
|
-
}
|
|
47357
|
-
});
|
|
47358
|
-
return widgets;
|
|
47359
|
-
}
|
|
47360
|
-
|
|
47361
47471
|
/**
|
|
47362
47472
|
* This function extracts the answers from the widgets.
|
|
47363
47473
|
*
|
|
@@ -48283,6 +48393,118 @@ const parseExpressionWidget = parseWidget(constant("expression"), object({
|
|
|
48283
48393
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48284
48394
|
}));
|
|
48285
48395
|
|
|
48396
|
+
const parsePerseusRenderer = object({
|
|
48397
|
+
content: string,
|
|
48398
|
+
// This module has an import cycle with parseWidgetsMap, because the
|
|
48399
|
+
// `group` widget can contain another renderer.
|
|
48400
|
+
// The anonymous function below ensures that we don't try to access
|
|
48401
|
+
// parseWidgetsMap before it's defined.
|
|
48402
|
+
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
48403
|
+
metadata: optional(array(string)),
|
|
48404
|
+
images: record(string, object({
|
|
48405
|
+
width: number,
|
|
48406
|
+
height: number
|
|
48407
|
+
}))
|
|
48408
|
+
});
|
|
48409
|
+
|
|
48410
|
+
const parseGradedGroupWidgetOptions = object({
|
|
48411
|
+
title: string,
|
|
48412
|
+
hasHint: optional(nullable(boolean)),
|
|
48413
|
+
// This module has an import cycle with parsePerseusRenderer.
|
|
48414
|
+
// The anonymous function below ensures that we don't try to access
|
|
48415
|
+
// parsePerseusRenderer before it's defined.
|
|
48416
|
+
hint: optional(nullable((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx))),
|
|
48417
|
+
content: string,
|
|
48418
|
+
// This module has an import cycle with parseWidgetsMap.
|
|
48419
|
+
// The anonymous function below ensures that we don't try to access
|
|
48420
|
+
// parseWidgetsMap before it's defined.
|
|
48421
|
+
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
48422
|
+
widgetEnabled: optional(nullable(boolean)),
|
|
48423
|
+
immutableWidgets: optional(nullable(boolean)),
|
|
48424
|
+
images: record(string, object({
|
|
48425
|
+
width: number,
|
|
48426
|
+
height: number
|
|
48427
|
+
}))
|
|
48428
|
+
});
|
|
48429
|
+
const parseGradedGroupWidget = parseWidget(constant("graded-group"), parseGradedGroupWidgetOptions);
|
|
48430
|
+
|
|
48431
|
+
const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), object({
|
|
48432
|
+
gradedGroups: array(parseGradedGroupWidgetOptions)
|
|
48433
|
+
}));
|
|
48434
|
+
|
|
48435
|
+
const pairOfNumbers$3 = pair(number, number);
|
|
48436
|
+
const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
|
|
48437
|
+
const parseGrapherWidget = parseWidget(constant("grapher"), object({
|
|
48438
|
+
availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
|
|
48439
|
+
correct: union(object({
|
|
48440
|
+
type: constant("absolute_value"),
|
|
48441
|
+
coords: pairOfPoints
|
|
48442
|
+
})).or(object({
|
|
48443
|
+
type: constant("exponential"),
|
|
48444
|
+
asymptote: pairOfPoints,
|
|
48445
|
+
coords: pairOfPoints
|
|
48446
|
+
})).or(object({
|
|
48447
|
+
type: constant("linear"),
|
|
48448
|
+
coords: pairOfPoints
|
|
48449
|
+
})).or(object({
|
|
48450
|
+
type: constant("logarithm"),
|
|
48451
|
+
asymptote: pairOfPoints,
|
|
48452
|
+
coords: pairOfPoints
|
|
48453
|
+
})).or(object({
|
|
48454
|
+
type: constant("quadratic"),
|
|
48455
|
+
coords: pairOfPoints
|
|
48456
|
+
})).or(object({
|
|
48457
|
+
type: constant("sinusoid"),
|
|
48458
|
+
coords: pairOfPoints
|
|
48459
|
+
})).or(object({
|
|
48460
|
+
type: constant("tangent"),
|
|
48461
|
+
coords: pairOfPoints
|
|
48462
|
+
})).parser,
|
|
48463
|
+
graph: object({
|
|
48464
|
+
backgroundImage: object({
|
|
48465
|
+
bottom: optional(number),
|
|
48466
|
+
height: optional(number),
|
|
48467
|
+
left: optional(number),
|
|
48468
|
+
scale: optional(number),
|
|
48469
|
+
url: optional(nullable(string)),
|
|
48470
|
+
width: optional(number)
|
|
48471
|
+
}),
|
|
48472
|
+
box: optional(pairOfNumbers$3),
|
|
48473
|
+
editableSettings: optional(array(enumeration("graph", "snap", "image", "measure"))),
|
|
48474
|
+
gridStep: optional(pairOfNumbers$3),
|
|
48475
|
+
labels: pair(string, string),
|
|
48476
|
+
markings: enumeration("graph", "none", "grid"),
|
|
48477
|
+
range: pair(pairOfNumbers$3, pairOfNumbers$3),
|
|
48478
|
+
rulerLabel: constant(""),
|
|
48479
|
+
rulerTicks: number,
|
|
48480
|
+
showProtractor: optional(boolean),
|
|
48481
|
+
showRuler: optional(boolean),
|
|
48482
|
+
showTooltips: optional(boolean),
|
|
48483
|
+
snapStep: optional(pairOfNumbers$3),
|
|
48484
|
+
step: pairOfNumbers$3,
|
|
48485
|
+
valid: optional(union(boolean).or(string).parser)
|
|
48486
|
+
})
|
|
48487
|
+
}));
|
|
48488
|
+
|
|
48489
|
+
const parseGroupWidget = parseWidget(constant("group"),
|
|
48490
|
+
// This module has an import cycle with parsePerseusRenderer.
|
|
48491
|
+
// The anonymous function below ensures that we don't try to access
|
|
48492
|
+
// parsePerseusRenderer before it's defined.
|
|
48493
|
+
(rawVal, ctx) => parsePerseusRenderer(rawVal, ctx));
|
|
48494
|
+
|
|
48495
|
+
const parseIframeWidget = parseWidget(constant("iframe"), object({
|
|
48496
|
+
url: string,
|
|
48497
|
+
settings: array(object({
|
|
48498
|
+
name: string,
|
|
48499
|
+
value: string
|
|
48500
|
+
})),
|
|
48501
|
+
width: union(number).or(string).parser,
|
|
48502
|
+
height: union(number).or(string).parser,
|
|
48503
|
+
allowFullScreen: boolean,
|
|
48504
|
+
allowTopNavigation: optional(boolean),
|
|
48505
|
+
static: boolean
|
|
48506
|
+
}));
|
|
48507
|
+
|
|
48286
48508
|
const parsePerseusImageBackground = object({
|
|
48287
48509
|
url: optional(nullable(string)),
|
|
48288
48510
|
width: optional(number),
|
|
@@ -48294,6 +48516,160 @@ const parsePerseusImageBackground = object({
|
|
|
48294
48516
|
scale: optional(union(number).or(string).parser)
|
|
48295
48517
|
});
|
|
48296
48518
|
|
|
48519
|
+
const pairOfNumbers$2 = pair(number, number);
|
|
48520
|
+
const parseImageWidget = parseWidget(constant("image"), object({
|
|
48521
|
+
title: optional(string),
|
|
48522
|
+
caption: optional(string),
|
|
48523
|
+
alt: optional(string),
|
|
48524
|
+
backgroundImage: parsePerseusImageBackground,
|
|
48525
|
+
static: optional(boolean),
|
|
48526
|
+
labels: optional(array(object({
|
|
48527
|
+
content: string,
|
|
48528
|
+
alignment: string,
|
|
48529
|
+
coordinates: array(number)
|
|
48530
|
+
}))),
|
|
48531
|
+
range: optional(pair(pairOfNumbers$2, pairOfNumbers$2)),
|
|
48532
|
+
box: optional(pairOfNumbers$2)
|
|
48533
|
+
}));
|
|
48534
|
+
|
|
48535
|
+
const parseInputNumberWidget = parseWidget(constant("input-number"), object({
|
|
48536
|
+
answerType: optional(enumeration("number", "decimal", "integer", "rational", "improper", "mixed", "percent", "pi")),
|
|
48537
|
+
inexact: optional(boolean),
|
|
48538
|
+
maxError: optional(union(number).or(string).parser),
|
|
48539
|
+
rightAlign: optional(boolean),
|
|
48540
|
+
simplify: enumeration("required", "optional", "enforced"),
|
|
48541
|
+
size: enumeration("normal", "small"),
|
|
48542
|
+
value: union(number).or(string).parser,
|
|
48543
|
+
customKeypad: optional(boolean)
|
|
48544
|
+
}));
|
|
48545
|
+
|
|
48546
|
+
const pairOfNumbers$1 = pair(number, number);
|
|
48547
|
+
const parseFunctionElement = object({
|
|
48548
|
+
type: constant("function"),
|
|
48549
|
+
key: string,
|
|
48550
|
+
options: object({
|
|
48551
|
+
value: string,
|
|
48552
|
+
funcName: string,
|
|
48553
|
+
rangeMin: string,
|
|
48554
|
+
rangeMax: string,
|
|
48555
|
+
color: string,
|
|
48556
|
+
strokeDasharray: string,
|
|
48557
|
+
strokeWidth: number
|
|
48558
|
+
})
|
|
48559
|
+
});
|
|
48560
|
+
const parseLabelElement = object({
|
|
48561
|
+
type: constant("label"),
|
|
48562
|
+
key: string,
|
|
48563
|
+
options: object({
|
|
48564
|
+
label: string,
|
|
48565
|
+
color: string,
|
|
48566
|
+
coordX: string,
|
|
48567
|
+
coordY: string
|
|
48568
|
+
})
|
|
48569
|
+
});
|
|
48570
|
+
const parseLineElement = object({
|
|
48571
|
+
type: constant("line"),
|
|
48572
|
+
key: string,
|
|
48573
|
+
options: object({
|
|
48574
|
+
color: string,
|
|
48575
|
+
startX: string,
|
|
48576
|
+
startY: string,
|
|
48577
|
+
endX: string,
|
|
48578
|
+
endY: string,
|
|
48579
|
+
strokeDasharray: string,
|
|
48580
|
+
strokeWidth: number,
|
|
48581
|
+
arrows: string
|
|
48582
|
+
})
|
|
48583
|
+
});
|
|
48584
|
+
const parseMovableLineElement = object({
|
|
48585
|
+
type: constant("movable-line"),
|
|
48586
|
+
key: string,
|
|
48587
|
+
options: object({
|
|
48588
|
+
startX: string,
|
|
48589
|
+
startY: string,
|
|
48590
|
+
startSubscript: number,
|
|
48591
|
+
endX: string,
|
|
48592
|
+
endY: string,
|
|
48593
|
+
endSubscript: number,
|
|
48594
|
+
constraint: string,
|
|
48595
|
+
snap: number,
|
|
48596
|
+
constraintFn: string,
|
|
48597
|
+
constraintXMin: string,
|
|
48598
|
+
constraintXMax: string,
|
|
48599
|
+
constraintYMin: string,
|
|
48600
|
+
constraintYMax: string
|
|
48601
|
+
})
|
|
48602
|
+
});
|
|
48603
|
+
const parseMovablePointElement = object({
|
|
48604
|
+
type: constant("movable-point"),
|
|
48605
|
+
key: string,
|
|
48606
|
+
options: object({
|
|
48607
|
+
startX: string,
|
|
48608
|
+
startY: string,
|
|
48609
|
+
varSubscript: number,
|
|
48610
|
+
constraint: string,
|
|
48611
|
+
snap: number,
|
|
48612
|
+
constraintFn: string,
|
|
48613
|
+
constraintXMin: string,
|
|
48614
|
+
constraintXMax: string,
|
|
48615
|
+
constraintYMin: string,
|
|
48616
|
+
constraintYMax: string
|
|
48617
|
+
})
|
|
48618
|
+
});
|
|
48619
|
+
const parseParametricElement = object({
|
|
48620
|
+
type: constant("parametric"),
|
|
48621
|
+
key: string,
|
|
48622
|
+
options: object({
|
|
48623
|
+
x: string,
|
|
48624
|
+
y: string,
|
|
48625
|
+
rangeMin: string,
|
|
48626
|
+
rangeMax: string,
|
|
48627
|
+
color: string,
|
|
48628
|
+
strokeDasharray: string,
|
|
48629
|
+
strokeWidth: number
|
|
48630
|
+
})
|
|
48631
|
+
});
|
|
48632
|
+
const parsePointElement = object({
|
|
48633
|
+
type: constant("point"),
|
|
48634
|
+
key: string,
|
|
48635
|
+
options: object({
|
|
48636
|
+
color: string,
|
|
48637
|
+
coordX: string,
|
|
48638
|
+
coordY: string
|
|
48639
|
+
})
|
|
48640
|
+
});
|
|
48641
|
+
const parseRectangleElement = object({
|
|
48642
|
+
type: constant("rectangle"),
|
|
48643
|
+
key: string,
|
|
48644
|
+
options: object({
|
|
48645
|
+
color: string,
|
|
48646
|
+
coordX: string,
|
|
48647
|
+
coordY: string,
|
|
48648
|
+
width: string,
|
|
48649
|
+
height: string
|
|
48650
|
+
})
|
|
48651
|
+
});
|
|
48652
|
+
const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
48653
|
+
static: boolean,
|
|
48654
|
+
graph: object({
|
|
48655
|
+
editableSettings: optional(array(enumeration("canvas", "graph"))),
|
|
48656
|
+
box: pairOfNumbers$1,
|
|
48657
|
+
labels: array(string),
|
|
48658
|
+
range: pair(pairOfNumbers$1, pairOfNumbers$1),
|
|
48659
|
+
gridStep: pairOfNumbers$1,
|
|
48660
|
+
markings: enumeration("graph", "grid", "none"),
|
|
48661
|
+
snapStep: optional(pairOfNumbers$1),
|
|
48662
|
+
valid: optional(union(boolean).or(string).parser),
|
|
48663
|
+
backgroundImage: optional(parsePerseusImageBackground),
|
|
48664
|
+
showProtractor: optional(boolean),
|
|
48665
|
+
showRuler: optional(boolean),
|
|
48666
|
+
rulerLabel: optional(string),
|
|
48667
|
+
rulerTicks: optional(number),
|
|
48668
|
+
tickStep: pairOfNumbers$1
|
|
48669
|
+
}),
|
|
48670
|
+
elements: array(union(parseFunctionElement).or(parseLabelElement).or(parseLineElement).or(parseMovableLineElement).or(parseMovablePointElement).or(parseParametricElement).or(parsePointElement).or(parseRectangleElement).parser)
|
|
48671
|
+
}));
|
|
48672
|
+
|
|
48297
48673
|
// Used to represent 2-D points and ranges
|
|
48298
48674
|
const pairOfNumbers = pair(number, number);
|
|
48299
48675
|
const parsePerseusGraphTypeAngle = object({
|
|
@@ -48480,6 +48856,195 @@ const parseInteractiveGraphWidget = parseWidget(constant("interactive-graph"), o
|
|
|
48480
48856
|
fullGraphAriaDescription: optional(string)
|
|
48481
48857
|
}));
|
|
48482
48858
|
|
|
48859
|
+
const parseLabelImageWidget = parseWidget(constant("label-image"), object({
|
|
48860
|
+
choices: array(string),
|
|
48861
|
+
imageUrl: string,
|
|
48862
|
+
imageAlt: string,
|
|
48863
|
+
imageHeight: number,
|
|
48864
|
+
imageWidth: number,
|
|
48865
|
+
markers: array(object({
|
|
48866
|
+
answers: array(string),
|
|
48867
|
+
label: string,
|
|
48868
|
+
x: number,
|
|
48869
|
+
y: number
|
|
48870
|
+
})),
|
|
48871
|
+
hideChoicesFromInstructions: boolean,
|
|
48872
|
+
multipleAnswers: boolean,
|
|
48873
|
+
static: boolean
|
|
48874
|
+
}));
|
|
48875
|
+
|
|
48876
|
+
const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
48877
|
+
labels: array(string),
|
|
48878
|
+
left: array(string),
|
|
48879
|
+
right: array(string),
|
|
48880
|
+
orderMatters: boolean,
|
|
48881
|
+
padding: boolean
|
|
48882
|
+
}));
|
|
48883
|
+
|
|
48884
|
+
const parseMatrixWidget = parseWidget(constant("matrix"), object({
|
|
48885
|
+
prefix: string,
|
|
48886
|
+
suffix: string,
|
|
48887
|
+
answers: array(array(number)),
|
|
48888
|
+
cursorPosition: array(number),
|
|
48889
|
+
matrixBoardSize: array(number),
|
|
48890
|
+
static: boolean
|
|
48891
|
+
}));
|
|
48892
|
+
|
|
48893
|
+
const parseMeasurerWidget = parseWidget(constant("measurer"), object({
|
|
48894
|
+
image: parsePerseusImageBackground,
|
|
48895
|
+
showProtractor: boolean,
|
|
48896
|
+
showRuler: boolean,
|
|
48897
|
+
rulerLabel: string,
|
|
48898
|
+
rulerTicks: number,
|
|
48899
|
+
rulerPixels: number,
|
|
48900
|
+
rulerLength: number,
|
|
48901
|
+
box: pair(number, number),
|
|
48902
|
+
static: boolean
|
|
48903
|
+
}));
|
|
48904
|
+
|
|
48905
|
+
const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), object({
|
|
48906
|
+
widgetId: string,
|
|
48907
|
+
rotationAngle: optional(number),
|
|
48908
|
+
smiles: optional(string)
|
|
48909
|
+
}));
|
|
48910
|
+
|
|
48911
|
+
const parseNumberLineWidget = parseWidget(constant("number-line"), object({
|
|
48912
|
+
range: array(number),
|
|
48913
|
+
labelRange: array(nullable(number)),
|
|
48914
|
+
labelStyle: string,
|
|
48915
|
+
labelTicks: boolean,
|
|
48916
|
+
isTickCtrl: optional(nullable(boolean)),
|
|
48917
|
+
divisionRange: array(number),
|
|
48918
|
+
numDivisions: optional(nullable(number)),
|
|
48919
|
+
snapDivisions: number,
|
|
48920
|
+
tickStep: optional(nullable(number)),
|
|
48921
|
+
correctRel: optional(nullable(string)),
|
|
48922
|
+
correctX: number,
|
|
48923
|
+
initialX: optional(nullable(number)),
|
|
48924
|
+
showTooltip: optional(boolean),
|
|
48925
|
+
static: boolean
|
|
48926
|
+
}));
|
|
48927
|
+
|
|
48928
|
+
const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "decimal", "percent", "pi");
|
|
48929
|
+
const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
48930
|
+
answers: array(object({
|
|
48931
|
+
message: string,
|
|
48932
|
+
value: number,
|
|
48933
|
+
status: string,
|
|
48934
|
+
answerForms: optional(array(parseMathFormat)),
|
|
48935
|
+
strict: boolean,
|
|
48936
|
+
maxError: optional(nullable(number)),
|
|
48937
|
+
simplify: optional(nullable(string))
|
|
48938
|
+
})),
|
|
48939
|
+
labelText: string,
|
|
48940
|
+
size: string,
|
|
48941
|
+
coefficient: boolean,
|
|
48942
|
+
rightAlign: optional(boolean),
|
|
48943
|
+
static: boolean,
|
|
48944
|
+
answerForms: optional(array(object({
|
|
48945
|
+
name: parseMathFormat,
|
|
48946
|
+
simplify: optional(nullable(enumeration("required", "correct", "enforced", "optional")))
|
|
48947
|
+
})))
|
|
48948
|
+
}));
|
|
48949
|
+
|
|
48950
|
+
// There is an import cycle between orderer-widget.ts and perseus-renderer.ts.
|
|
48951
|
+
// This wrapper ensures that we don't refer to parsePerseusRenderer before
|
|
48952
|
+
// it's defined.
|
|
48953
|
+
function parseRenderer(rawValue, ctx) {
|
|
48954
|
+
return parsePerseusRenderer(rawValue, ctx);
|
|
48955
|
+
}
|
|
48956
|
+
const parseOrdererWidget = parseWidget(constant("orderer"), object({
|
|
48957
|
+
options: array(parseRenderer),
|
|
48958
|
+
correctOptions: array(parseRenderer),
|
|
48959
|
+
otherOptions: array(parseRenderer),
|
|
48960
|
+
height: string,
|
|
48961
|
+
layout: string
|
|
48962
|
+
}));
|
|
48963
|
+
|
|
48964
|
+
const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
48965
|
+
passageNumber: number,
|
|
48966
|
+
referenceNumber: number,
|
|
48967
|
+
summaryText: string
|
|
48968
|
+
}));
|
|
48969
|
+
|
|
48970
|
+
const parsePassageWidget = parseWidget(constant("passage"), object({
|
|
48971
|
+
footnotes: string,
|
|
48972
|
+
passageText: string,
|
|
48973
|
+
passageTitle: string,
|
|
48974
|
+
showLineNumbers: boolean,
|
|
48975
|
+
static: boolean
|
|
48976
|
+
}));
|
|
48977
|
+
|
|
48978
|
+
const parsePhetSimulationWidget = parseWidget(constant("phet-simulation"), object({
|
|
48979
|
+
url: string,
|
|
48980
|
+
description: string
|
|
48981
|
+
}));
|
|
48982
|
+
|
|
48983
|
+
const parsePlotterWidget = parseWidget(constant("plotter"), object({
|
|
48984
|
+
labels: array(string),
|
|
48985
|
+
categories: array(string),
|
|
48986
|
+
type: enumeration(...plotterPlotTypes),
|
|
48987
|
+
maxY: number,
|
|
48988
|
+
scaleY: number,
|
|
48989
|
+
labelInterval: optional(nullable(number)),
|
|
48990
|
+
snapsPerLine: number,
|
|
48991
|
+
starting: array(number),
|
|
48992
|
+
correct: array(number),
|
|
48993
|
+
picUrl: optional(nullable(string)),
|
|
48994
|
+
picSize: optional(nullable(number)),
|
|
48995
|
+
picBoxHeight: optional(nullable(number)),
|
|
48996
|
+
plotDimensions: array(number)
|
|
48997
|
+
}));
|
|
48998
|
+
|
|
48999
|
+
const parsePythonProgramWidget = parseWidget(constant("python-program"), object({
|
|
49000
|
+
programID: string,
|
|
49001
|
+
height: number
|
|
49002
|
+
}));
|
|
49003
|
+
|
|
49004
|
+
const parseRadioWidget = parseWidget(constant("radio"), object({
|
|
49005
|
+
choices: array(object({
|
|
49006
|
+
content: string,
|
|
49007
|
+
clue: optional(string),
|
|
49008
|
+
correct: optional(boolean),
|
|
49009
|
+
isNoneOfTheAbove: optional(boolean),
|
|
49010
|
+
// deprecated
|
|
49011
|
+
// There is an import cycle between radio-widget.ts and
|
|
49012
|
+
// widgets-map.ts. The anonymous function below ensures that we
|
|
49013
|
+
// don't refer to parseWidgetsMap before it's defined.
|
|
49014
|
+
widgets: optional((rawVal, ctx) => parseWidgetsMap(rawVal, ctx))
|
|
49015
|
+
})),
|
|
49016
|
+
hasNoneOfTheAbove: optional(boolean),
|
|
49017
|
+
countChoices: optional(boolean),
|
|
49018
|
+
randomize: optional(boolean),
|
|
49019
|
+
multipleSelect: optional(boolean),
|
|
49020
|
+
deselectEnabled: optional(boolean),
|
|
49021
|
+
// deprecated
|
|
49022
|
+
onePerLine: optional(boolean),
|
|
49023
|
+
// deprecated
|
|
49024
|
+
displayCount: optional(any),
|
|
49025
|
+
// v0 props
|
|
49026
|
+
// `noneOfTheAbove` is still in use (but only set to `false`).
|
|
49027
|
+
noneOfTheAbove: constant(false)
|
|
49028
|
+
}));
|
|
49029
|
+
|
|
49030
|
+
const parseSorterWidget = parseWidget(constant("sorter"), object({
|
|
49031
|
+
correct: array(string),
|
|
49032
|
+
padding: boolean,
|
|
49033
|
+
layout: enumeration("horizontal", "vertical")
|
|
49034
|
+
}));
|
|
49035
|
+
|
|
49036
|
+
const parseTableWidget = parseWidget(constant("table"), object({
|
|
49037
|
+
headers: array(string),
|
|
49038
|
+
rows: number,
|
|
49039
|
+
columns: number,
|
|
49040
|
+
answers: array(array(string))
|
|
49041
|
+
}));
|
|
49042
|
+
|
|
49043
|
+
const parseVideoWidget = parseWidget(constant("video"), object({
|
|
49044
|
+
location: string,
|
|
49045
|
+
static: optional(boolean)
|
|
49046
|
+
}));
|
|
49047
|
+
|
|
48483
49048
|
const parseWidgetsMap = (rawValue, ctx) => {
|
|
48484
49049
|
if (!isObject(rawValue)) {
|
|
48485
49050
|
return ctx.failure("PerseusWidgetsMap", rawValue);
|
|
@@ -48524,86 +49089,67 @@ const parseWidgetsMapEntry = ([key, widget], widgetMap, ctx) => {
|
|
|
48524
49089
|
case "expression":
|
|
48525
49090
|
return parseAndAssign(`expression ${id}`, parseExpressionWidget);
|
|
48526
49091
|
case "grapher":
|
|
48527
|
-
|
|
48528
|
-
return parseAndAssign(`grapher ${id}`, any);
|
|
49092
|
+
return parseAndAssign(`grapher ${id}`, parseGrapherWidget);
|
|
48529
49093
|
case "group":
|
|
48530
|
-
|
|
48531
|
-
return parseAndAssign(`group ${id}`, any);
|
|
49094
|
+
return parseAndAssign(`group ${id}`, parseGroupWidget);
|
|
48532
49095
|
case "graded-group":
|
|
48533
|
-
|
|
48534
|
-
return parseAndAssign(`graded-group ${id}`, any);
|
|
49096
|
+
return parseAndAssign(`graded-group ${id}`, parseGradedGroupWidget);
|
|
48535
49097
|
case "graded-group-set":
|
|
48536
|
-
|
|
48537
|
-
return parseAndAssign(`graded-group-set ${id}`, any);
|
|
49098
|
+
return parseAndAssign(`graded-group-set ${id}`, parseGradedGroupSetWidget);
|
|
48538
49099
|
case "iframe":
|
|
48539
|
-
|
|
48540
|
-
return parseAndAssign(`iframe ${id}`, any);
|
|
49100
|
+
return parseAndAssign(`iframe ${id}`, parseIframeWidget);
|
|
48541
49101
|
case "image":
|
|
48542
|
-
|
|
48543
|
-
return parseAndAssign(`image ${id}`, any);
|
|
49102
|
+
return parseAndAssign(`image ${id}`, parseImageWidget);
|
|
48544
49103
|
case "input-number":
|
|
48545
|
-
|
|
48546
|
-
return parseAndAssign(`input-number ${id}`, any);
|
|
49104
|
+
return parseAndAssign(`input-number ${id}`, parseInputNumberWidget);
|
|
48547
49105
|
case "interaction":
|
|
48548
|
-
|
|
48549
|
-
return parseAndAssign(`interaction ${id}`, any);
|
|
49106
|
+
return parseAndAssign(`interaction ${id}`, parseInteractionWidget);
|
|
48550
49107
|
case "interactive-graph":
|
|
48551
49108
|
return parseAndAssign(`interactive-graph ${id}`, parseInteractiveGraphWidget);
|
|
48552
49109
|
case "label-image":
|
|
48553
|
-
|
|
48554
|
-
return parseAndAssign(`label-image ${id}`, any);
|
|
49110
|
+
return parseAndAssign(`label-image ${id}`, parseLabelImageWidget);
|
|
48555
49111
|
case "matcher":
|
|
48556
|
-
|
|
48557
|
-
return parseAndAssign(`matcher ${id}`, any);
|
|
49112
|
+
return parseAndAssign(`matcher ${id}`, parseMatcherWidget);
|
|
48558
49113
|
case "matrix":
|
|
48559
|
-
|
|
48560
|
-
return parseAndAssign(`matrix ${id}`, any);
|
|
49114
|
+
return parseAndAssign(`matrix ${id}`, parseMatrixWidget);
|
|
48561
49115
|
case "measurer":
|
|
48562
|
-
|
|
48563
|
-
return parseAndAssign(`measurer ${id}`, any);
|
|
49116
|
+
return parseAndAssign(`measurer ${id}`, parseMeasurerWidget);
|
|
48564
49117
|
case "molecule-renderer":
|
|
48565
|
-
|
|
48566
|
-
return parseAndAssign(`molecule-renderer ${id}`, any);
|
|
49118
|
+
return parseAndAssign(`molecule-renderer ${id}`, parseMoleculeRendererWidget);
|
|
48567
49119
|
case "number-line":
|
|
48568
|
-
|
|
48569
|
-
return parseAndAssign(`number-line ${id}`, any);
|
|
49120
|
+
return parseAndAssign(`number-line ${id}`, parseNumberLineWidget);
|
|
48570
49121
|
case "numeric-input":
|
|
48571
|
-
|
|
48572
|
-
return parseAndAssign(`numeric-input ${id}`, any);
|
|
49122
|
+
return parseAndAssign(`numeric-input ${id}`, parseNumericInputWidget);
|
|
48573
49123
|
case "orderer":
|
|
48574
|
-
|
|
48575
|
-
return parseAndAssign(`orderer ${id}`, any);
|
|
49124
|
+
return parseAndAssign(`orderer ${id}`, parseOrdererWidget);
|
|
48576
49125
|
case "passage":
|
|
48577
|
-
|
|
48578
|
-
return parseAndAssign(`passage ${id}`, any);
|
|
49126
|
+
return parseAndAssign(`passage ${id}`, parsePassageWidget);
|
|
48579
49127
|
case "passage-ref":
|
|
48580
|
-
|
|
48581
|
-
return parseAndAssign(`passage-ref ${id}`, any);
|
|
49128
|
+
return parseAndAssign(`passage-ref ${id}`, parsePassageRefWidget);
|
|
48582
49129
|
case "passage-ref-target":
|
|
48583
|
-
//
|
|
49130
|
+
// NOTE(benchristel): as of 2024-11-12, passage-ref-target is only
|
|
49131
|
+
// used in test content. See:
|
|
49132
|
+
// https://www.khanacademy.org/devadmin/content/search?query=widget:passage-ref-target
|
|
48584
49133
|
return parseAndAssign(`passage-ref-target ${id}`, any);
|
|
48585
49134
|
case "phet-simulation":
|
|
48586
|
-
|
|
48587
|
-
return parseAndAssign(`phet-simulation ${id}`, any);
|
|
49135
|
+
return parseAndAssign(`phet-simulation ${id}`, parsePhetSimulationWidget);
|
|
48588
49136
|
case "plotter":
|
|
48589
|
-
|
|
48590
|
-
return parseAndAssign(`plotter ${id}`, any);
|
|
49137
|
+
return parseAndAssign(`plotter ${id}`, parsePlotterWidget);
|
|
48591
49138
|
case "python-program":
|
|
48592
|
-
|
|
48593
|
-
return parseAndAssign(`python-program ${id}`, any);
|
|
49139
|
+
return parseAndAssign(`python-program ${id}`, parsePythonProgramWidget);
|
|
48594
49140
|
case "radio":
|
|
48595
|
-
|
|
48596
|
-
return parseAndAssign(`radio ${id}`, any);
|
|
49141
|
+
return parseAndAssign(`radio ${id}`, parseRadioWidget);
|
|
48597
49142
|
case "sorter":
|
|
48598
|
-
|
|
48599
|
-
return parseAndAssign(`sorter ${id}`, any);
|
|
49143
|
+
return parseAndAssign(`sorter ${id}`, parseSorterWidget);
|
|
48600
49144
|
case "table":
|
|
48601
|
-
|
|
48602
|
-
return parseAndAssign(`table ${id}`, any);
|
|
49145
|
+
return parseAndAssign(`table ${id}`, parseTableWidget);
|
|
48603
49146
|
case "video":
|
|
48604
|
-
|
|
48605
|
-
return parseAndAssign(`video ${id}`, any);
|
|
49147
|
+
return parseAndAssign(`video ${id}`, parseVideoWidget);
|
|
48606
49148
|
default:
|
|
49149
|
+
if (getWidget(type)) {
|
|
49150
|
+
// @ts-expect-error - 'type' is not a valid widget type
|
|
49151
|
+
return parseAndAssign(`${type} ${id}`, any);
|
|
49152
|
+
}
|
|
48607
49153
|
return ctx.failure("a valid widget type", type);
|
|
48608
49154
|
}
|
|
48609
49155
|
};
|
|
@@ -48626,16 +49172,6 @@ const parseHint = object({
|
|
|
48626
49172
|
}))
|
|
48627
49173
|
});
|
|
48628
49174
|
|
|
48629
|
-
const parsePerseusRenderer = object({
|
|
48630
|
-
content: string,
|
|
48631
|
-
widgets: parseWidgetsMap,
|
|
48632
|
-
metadata: optional(array(string)),
|
|
48633
|
-
images: record(string, object({
|
|
48634
|
-
width: number,
|
|
48635
|
-
height: number
|
|
48636
|
-
}))
|
|
48637
|
-
});
|
|
48638
|
-
|
|
48639
49175
|
object({
|
|
48640
49176
|
question: parsePerseusRenderer,
|
|
48641
49177
|
hints: array(parseHint),
|
|
@@ -48876,5 +49412,5 @@ const GrapherUtil = {
|
|
|
48876
49412
|
typeToButton
|
|
48877
49413
|
};
|
|
48878
49414
|
|
|
48879
|
-
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, buildEmptyItemTreeForShape, buildMapper, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
49415
|
+
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, buildEmptyItemTreeForShape, buildMapper, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, scorePerseusItem, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
48880
49416
|
//# sourceMappingURL=index.js.map
|