@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/index.js
CHANGED
|
@@ -371,10 +371,11 @@ const mockStrings = {
|
|
|
371
371
|
graphKeyboardPrompt: "Press Shift + Enter to interact with the graph",
|
|
372
372
|
srPointAtCoordinates: _ref27 => {
|
|
373
373
|
let {
|
|
374
|
+
num,
|
|
374
375
|
x,
|
|
375
376
|
y
|
|
376
377
|
} = _ref27;
|
|
377
|
-
return `Point at ${x} comma ${y}`;
|
|
378
|
+
return `Point ${num} at ${x} comma ${y}`;
|
|
378
379
|
},
|
|
379
380
|
srInteractiveElements: _ref28 => {
|
|
380
381
|
let {
|
|
@@ -2712,7 +2713,7 @@ const keypadConfigurationForProps = widgetOptions => {
|
|
|
2712
2713
|
times: widgetOptions.times
|
|
2713
2714
|
};
|
|
2714
2715
|
};
|
|
2715
|
-
const propUpgrades$
|
|
2716
|
+
const propUpgrades$3 = {
|
|
2716
2717
|
/* c8 ignore next */
|
|
2717
2718
|
"1": v0props => ({
|
|
2718
2719
|
times: v0props.times,
|
|
@@ -2772,7 +2773,7 @@ var Expression$1 = {
|
|
|
2772
2773
|
major: 1,
|
|
2773
2774
|
minor: 0
|
|
2774
2775
|
},
|
|
2775
|
-
propUpgrades: propUpgrades$
|
|
2776
|
+
propUpgrades: propUpgrades$3,
|
|
2776
2777
|
// For use by the editor
|
|
2777
2778
|
isLintable: true,
|
|
2778
2779
|
scorer: scoreExpression,
|
|
@@ -18874,6 +18875,138 @@ class QuestionParagraph extends React__namespace.Component {
|
|
|
18874
18875
|
}
|
|
18875
18876
|
}
|
|
18876
18877
|
|
|
18878
|
+
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
18879
|
+
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
18880
|
+
|
|
18881
|
+
/**
|
|
18882
|
+
* Regex for widget placeholders in a string.
|
|
18883
|
+
*
|
|
18884
|
+
* First capture group is the widget ID (ex. 'radio 1')
|
|
18885
|
+
* Second capture group is the widget type (ex. "radio)
|
|
18886
|
+
* exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
|
|
18887
|
+
*/
|
|
18888
|
+
function getWidgetRegex() {
|
|
18889
|
+
return /\[\[☃ ([A-Za-z0-9- ]+)\]\]/g;
|
|
18890
|
+
}
|
|
18891
|
+
|
|
18892
|
+
/**
|
|
18893
|
+
* Add a widget placeholder using the widget ID.
|
|
18894
|
+
* ex. addWidget("radio 1") => "[[☃ radio 1]]"
|
|
18895
|
+
*
|
|
18896
|
+
* @param {string} id
|
|
18897
|
+
* @returns {string}
|
|
18898
|
+
*/
|
|
18899
|
+
function addWidget(id) {
|
|
18900
|
+
return `[[☃ ${id}]]`;
|
|
18901
|
+
}
|
|
18902
|
+
|
|
18903
|
+
/**
|
|
18904
|
+
* Get a widget type by a widget's ID
|
|
18905
|
+
* (please don't derive type from ID)
|
|
18906
|
+
*
|
|
18907
|
+
* @param {string} widgetId the ID of the widget
|
|
18908
|
+
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
18909
|
+
* @returns {string} the widget type (ie "radio")
|
|
18910
|
+
*/
|
|
18911
|
+
function getWidgetTypeByWidgetId(WidgetId, widgetMap) {
|
|
18912
|
+
const widget = widgetMap[WidgetId];
|
|
18913
|
+
return widget?.type ?? null;
|
|
18914
|
+
}
|
|
18915
|
+
|
|
18916
|
+
/**
|
|
18917
|
+
* Does the content have a specific type of widget?
|
|
18918
|
+
*
|
|
18919
|
+
* @param {string} type the type of the widget in question (ie "radio")
|
|
18920
|
+
* @param {string} content the string to search through
|
|
18921
|
+
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
18922
|
+
* @returns {boolean} if the content includes the widget type
|
|
18923
|
+
*/
|
|
18924
|
+
function contentHasWidgetType(type, content, widgetMap) {
|
|
18925
|
+
return getWidgetIdsFromContentByType(type, content, widgetMap).length > 0;
|
|
18926
|
+
}
|
|
18927
|
+
|
|
18928
|
+
/**
|
|
18929
|
+
* Extract all widget IDs, which includes the widget type and instance number.
|
|
18930
|
+
* example output: ['radio 1', 'categorizer 1', 'categorizor 2']
|
|
18931
|
+
*
|
|
18932
|
+
* Content should contain Perseus widget placeholders,
|
|
18933
|
+
* which look like: '[[☃ radio 1]]'.
|
|
18934
|
+
*
|
|
18935
|
+
* @param {string} content
|
|
18936
|
+
* @returns {ReadonlyArray<string>} widgetIds
|
|
18937
|
+
*/
|
|
18938
|
+
function getWidgetIdsFromContent(content) {
|
|
18939
|
+
const widgets = [];
|
|
18940
|
+
const localWidgetRegex = getWidgetRegex();
|
|
18941
|
+
let match = localWidgetRegex.exec(content);
|
|
18942
|
+
while (match !== null) {
|
|
18943
|
+
widgets.push(match[1]);
|
|
18944
|
+
match = localWidgetRegex.exec(content);
|
|
18945
|
+
}
|
|
18946
|
+
return widgets;
|
|
18947
|
+
}
|
|
18948
|
+
|
|
18949
|
+
/**
|
|
18950
|
+
* Get a list of widget IDs from content,
|
|
18951
|
+
* but only for specific widget types
|
|
18952
|
+
*
|
|
18953
|
+
* @param {string} type the type of widget (ie "radio")
|
|
18954
|
+
* @param {string} content the string to parse
|
|
18955
|
+
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
18956
|
+
* @returns {ReadonlyArray<string>} the widget type (ie "radio")
|
|
18957
|
+
*/
|
|
18958
|
+
function getWidgetIdsFromContentByType(type, content, widgetMap) {
|
|
18959
|
+
const rv = [];
|
|
18960
|
+
const widgetIdsInContent = getWidgetIdsFromContent(content);
|
|
18961
|
+
widgetIdsInContent.forEach(widgetId => {
|
|
18962
|
+
const widget = widgetMap[widgetId];
|
|
18963
|
+
if (widget?.type === type) {
|
|
18964
|
+
rv.push(widgetId);
|
|
18965
|
+
}
|
|
18966
|
+
});
|
|
18967
|
+
return rv;
|
|
18968
|
+
}
|
|
18969
|
+
|
|
18970
|
+
/**
|
|
18971
|
+
* Pull the widget map out of ItemData
|
|
18972
|
+
*
|
|
18973
|
+
* @param {PerseusItem} PerseusItem containing a widgetMap
|
|
18974
|
+
* @returns {WidgetMap} the widget map in the PerseusItem
|
|
18975
|
+
*/
|
|
18976
|
+
function getWidgetsMapFromItemData(itemData) {
|
|
18977
|
+
return itemData.question.widgets;
|
|
18978
|
+
}
|
|
18979
|
+
|
|
18980
|
+
/**
|
|
18981
|
+
* Get the widget information from a WidgetMap
|
|
18982
|
+
*
|
|
18983
|
+
* @param {string} widgetId the ID of the widget
|
|
18984
|
+
* @returns {PerseusWidget | null} the widget data if it exists, otherwise null
|
|
18985
|
+
*/
|
|
18986
|
+
function getWidgetFromWidgetMap(widgetId, widgetMap) {
|
|
18987
|
+
return widgetMap[widgetId] ?? null;
|
|
18988
|
+
}
|
|
18989
|
+
|
|
18990
|
+
/**
|
|
18991
|
+
* Get select widgets from a widget map.
|
|
18992
|
+
* Useful for multi-items that needs to split
|
|
18993
|
+
* one PerseusItem into several
|
|
18994
|
+
*
|
|
18995
|
+
* @param {ReadonlyArray<string>} widgetIds to extract
|
|
18996
|
+
* @param {PerseusWidgetsMap} widgetMap to extract from
|
|
18997
|
+
* @return {PerseusWidgetsMap} a new widget map with requested widgets
|
|
18998
|
+
*/
|
|
18999
|
+
function getWidgetsFromWidgetMap(widgetIds, widgetMap) {
|
|
19000
|
+
const widgets = {};
|
|
19001
|
+
widgetIds.forEach(widgetId => {
|
|
19002
|
+
const widget = getWidgetFromWidgetMap(widgetId, widgetMap);
|
|
19003
|
+
if (widget) {
|
|
19004
|
+
widgets[widgetId] = widget;
|
|
19005
|
+
}
|
|
19006
|
+
});
|
|
19007
|
+
return widgets;
|
|
19008
|
+
}
|
|
19009
|
+
|
|
18877
19010
|
const DEFAULT_ALIGNMENT$1 = "block";
|
|
18878
19011
|
// NOTE(kevinb): "default" is not one in `validAlignments`.
|
|
18879
19012
|
const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
|
|
@@ -18924,6 +19057,9 @@ const replaceDeprecatedWidgets = () => {
|
|
|
18924
19057
|
replaceWidget("sequence", "deprecated-standin");
|
|
18925
19058
|
replaceWidget("simulator", "deprecated-standin");
|
|
18926
19059
|
replaceWidget("unit-input", "deprecated-standin");
|
|
19060
|
+
|
|
19061
|
+
// Input-Number is a special case as it is being replaced by Numeric-Input
|
|
19062
|
+
replaceWidget("input-number", "numeric-input");
|
|
18927
19063
|
};
|
|
18928
19064
|
const registerEditors = editorsToRegister => {
|
|
18929
19065
|
editorsToRegister.forEach(editor => {
|
|
@@ -18958,6 +19094,11 @@ const replaceDeprecatedEditors = () => {
|
|
|
18958
19094
|
replaceEditor("sequence", "deprecated-standin");
|
|
18959
19095
|
replaceEditor("simulator", "deprecated-standin");
|
|
18960
19096
|
replaceEditor("unit-input", "deprecated-standin");
|
|
19097
|
+
|
|
19098
|
+
// We're replacing the input-number editor with the numeric-input editor here,
|
|
19099
|
+
// but we're also modifying the JSON content to convert input-number into the
|
|
19100
|
+
// numeric-input format over in editor-page.tsx on line 92.
|
|
19101
|
+
replaceEditor("input-number", "numeric-input");
|
|
18961
19102
|
};
|
|
18962
19103
|
const getWidget = name => {
|
|
18963
19104
|
// TODO(alex): Consider referring to these as renderers to avoid
|
|
@@ -19004,10 +19145,8 @@ const getVersionVector = () => {
|
|
|
19004
19145
|
const getPublicWidgets = () => {
|
|
19005
19146
|
// TODO(alex): Update underscore.js so that _.pick can take a function.
|
|
19006
19147
|
// @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.
|
|
19007
|
-
return ___default["default"].pick(widgets,
|
|
19008
|
-
|
|
19009
|
-
___default["default"].reject(___default["default"].keys(widgets), function (name) {
|
|
19010
|
-
return widgets[name].hidden;
|
|
19148
|
+
return ___default["default"].pick(widgets, ___default["default"].reject(___default["default"].keys(widgets), function (name) {
|
|
19149
|
+
return widgets[name].hidden || name === "input-number";
|
|
19011
19150
|
}));
|
|
19012
19151
|
};
|
|
19013
19152
|
const isAccessible = widgetInfo => {
|
|
@@ -19328,6 +19467,20 @@ widgetIds, userInputMap, strings, locale) {
|
|
|
19328
19467
|
}
|
|
19329
19468
|
});
|
|
19330
19469
|
}
|
|
19470
|
+
|
|
19471
|
+
// TODO: combine scorePerseusItem with scoreWidgetsFunctional
|
|
19472
|
+
// once scorePerseusItem is the only one calling scoreWidgetsFunctional
|
|
19473
|
+
function scorePerseusItem(perseusRenderData, userInputMap,
|
|
19474
|
+
// TODO(LEMS-2461,LEMS-2391): these probably
|
|
19475
|
+
// need to be removed before we move this to the server
|
|
19476
|
+
strings, locale) {
|
|
19477
|
+
// There seems to be a chance that PerseusRenderer.widgets might include
|
|
19478
|
+
// widget data for widgets that are not in PerseusRenderer.content,
|
|
19479
|
+
// so this checks that the widgets are being used before scoring them
|
|
19480
|
+
const usedWidgetIds = getWidgetIdsFromContent(perseusRenderData.content);
|
|
19481
|
+
const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
|
|
19482
|
+
return Util.flattenScores(scores);
|
|
19483
|
+
}
|
|
19331
19484
|
function scoreWidgetsFunctional(widgets,
|
|
19332
19485
|
// This is a port of old code, I'm not sure why
|
|
19333
19486
|
// we need widgetIds vs the keys of the widgets object
|
|
@@ -19348,7 +19501,7 @@ widgetIds, userInputMap, strings, locale) {
|
|
|
19348
19501
|
const userInput = userInputMap[id];
|
|
19349
19502
|
const scorer = getWidgetScorer(widget.type);
|
|
19350
19503
|
if (widget.type === "group") {
|
|
19351
|
-
const scores = scoreWidgetsFunctional(widget.options.widgets,
|
|
19504
|
+
const scores = scoreWidgetsFunctional(widget.options.widgets, getWidgetIdsFromContent(widget.options.content), userInputMap[id], strings, locale);
|
|
19352
19505
|
widgetScores[id] = Util.flattenScores(scores);
|
|
19353
19506
|
} else if (scorer) {
|
|
19354
19507
|
widgetScores[id] = scorer(userInput, widget.options, strings, locale);
|
|
@@ -20397,7 +20550,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20397
20550
|
// Instead, we center them with css magic in styles.less
|
|
20398
20551
|
// /cry(aria)
|
|
20399
20552
|
this._foundTextNodes = true;
|
|
20400
|
-
if (
|
|
20553
|
+
if (this.widgetIds.includes(node.id)) {
|
|
20401
20554
|
// We don't want to render a duplicate widget key/ref,
|
|
20402
20555
|
// as this causes problems with react (for obvious
|
|
20403
20556
|
// reasons). Instead we just notify the
|
|
@@ -20722,7 +20875,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20722
20875
|
};
|
|
20723
20876
|
getInputPaths = () => {
|
|
20724
20877
|
const inputPaths = [];
|
|
20725
|
-
|
|
20878
|
+
this.widgetIds.forEach(widgetId => {
|
|
20726
20879
|
const widget = this.getWidgetInstance(widgetId);
|
|
20727
20880
|
if (widget && widget.getInputPaths) {
|
|
20728
20881
|
// Grab all input paths and add widgetID to the front
|
|
@@ -20730,7 +20883,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20730
20883
|
// Prefix paths with their widgetID and add to collective
|
|
20731
20884
|
// list of paths.
|
|
20732
20885
|
// @ts-expect-error - TS2345 - Argument of type '(inputPath: string) => void' is not assignable to parameter of type 'CollectionIterator<FocusPath, void, readonly FocusPath[]>'.
|
|
20733
|
-
|
|
20886
|
+
widgetInputPaths.forEach(inputPath => {
|
|
20734
20887
|
const relativeInputPath = [widgetId].concat(inputPath);
|
|
20735
20888
|
inputPaths.push(relativeInputPath);
|
|
20736
20889
|
});
|
|
@@ -20923,33 +21076,30 @@ class Renderer extends React__namespace.Component {
|
|
|
20923
21076
|
}
|
|
20924
21077
|
|
|
20925
21078
|
/**
|
|
20926
|
-
*
|
|
20927
|
-
*
|
|
20928
|
-
*
|
|
20929
|
-
*/
|
|
20930
|
-
scoreWidgets() {
|
|
20931
|
-
return scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
|
|
20932
|
-
}
|
|
20933
|
-
|
|
20934
|
-
/**
|
|
20935
|
-
* Grades the content.
|
|
21079
|
+
* Scores the content.
|
|
21080
|
+
*
|
|
21081
|
+
* @deprecated use scorePerseusItem
|
|
20936
21082
|
*/
|
|
20937
21083
|
score() {
|
|
20938
|
-
const scores = this.
|
|
21084
|
+
const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
|
|
20939
21085
|
const combinedScore = Util.flattenScores(scores);
|
|
20940
21086
|
return combinedScore;
|
|
20941
21087
|
}
|
|
20942
|
-
|
|
21088
|
+
|
|
21089
|
+
/**
|
|
21090
|
+
* @deprecated use scorePerseusItem
|
|
21091
|
+
*/
|
|
21092
|
+
guessAndScore = () => {
|
|
20943
21093
|
const totalGuess = this.getUserInput();
|
|
20944
21094
|
const totalScore = this.score();
|
|
20945
21095
|
return [totalGuess, totalScore];
|
|
20946
|
-
}
|
|
21096
|
+
};
|
|
20947
21097
|
examples = () => {
|
|
20948
21098
|
const widgetIds = this.widgetIds;
|
|
20949
|
-
const examples =
|
|
21099
|
+
const examples = widgetIds.map(widgetId => {
|
|
20950
21100
|
const widget = this.getWidgetInstance(widgetId);
|
|
20951
21101
|
return widget != null && widget.examples ? widget.examples() : null;
|
|
20952
|
-
}));
|
|
21102
|
+
}).filter(Boolean);
|
|
20953
21103
|
|
|
20954
21104
|
// no widgets with examples
|
|
20955
21105
|
if (!examples.length) {
|
|
@@ -22377,7 +22527,7 @@ class NumericInput extends React__namespace.Component {
|
|
|
22377
22527
|
height: "auto"
|
|
22378
22528
|
}
|
|
22379
22529
|
});
|
|
22380
|
-
return /*#__PURE__*/React__namespace.createElement(
|
|
22530
|
+
return /*#__PURE__*/React__namespace.createElement(InputWithExamples, {
|
|
22381
22531
|
ref: ref => this.inputRef = ref,
|
|
22382
22532
|
value: this.props.currentValue,
|
|
22383
22533
|
onChange: this.handleChange,
|
|
@@ -22389,7 +22539,7 @@ class NumericInput extends React__namespace.Component {
|
|
|
22389
22539
|
id: this.props.widgetId,
|
|
22390
22540
|
disabled: this.props.apiOptions.readOnly,
|
|
22391
22541
|
style: styles.input
|
|
22392
|
-
})
|
|
22542
|
+
});
|
|
22393
22543
|
}
|
|
22394
22544
|
}
|
|
22395
22545
|
|
|
@@ -22447,13 +22597,67 @@ const propsTransform = function (widgetOptions) {
|
|
|
22447
22597
|
});
|
|
22448
22598
|
return rendererProps;
|
|
22449
22599
|
};
|
|
22600
|
+
|
|
22601
|
+
// This function is being used to replace the input-number widget
|
|
22602
|
+
// with the numeric-input widget
|
|
22603
|
+
const propUpgrades$2 = {
|
|
22604
|
+
/* c8 ignore next */
|
|
22605
|
+
"1": initialProps => {
|
|
22606
|
+
// If the initialProps has a value, it means we're upgrading from
|
|
22607
|
+
// input-number to numeric-input. In this case, we need to upgrade
|
|
22608
|
+
// the widget options accordingly.
|
|
22609
|
+
if (initialProps.value) {
|
|
22610
|
+
// If the answerType is not number or percent, we need to provide
|
|
22611
|
+
// the answer form for the numeric-input widget
|
|
22612
|
+
const provideAnswerForm = initialProps.answerType !== "number" && initialProps.answerType !== "percent";
|
|
22613
|
+
|
|
22614
|
+
// We need to determine the mathFormat for the numeric-input widget
|
|
22615
|
+
const mathFormat = initialProps.answerType === "rational" ? "proper" // input-number uses "rational" for proper fractions
|
|
22616
|
+
: initialProps.answerType; // Otherwise, we can use the answerType directly
|
|
22617
|
+
|
|
22618
|
+
// If adjusting this logic, also adjust the logic in the convertInputNumberWidgetOptions
|
|
22619
|
+
// function in input-number.ts in the Perseus Editor package's util folder
|
|
22620
|
+
const answers = [{
|
|
22621
|
+
value: initialProps.value,
|
|
22622
|
+
simplify: initialProps.simplify,
|
|
22623
|
+
answerForms: provideAnswerForm ? [mathFormat] : undefined,
|
|
22624
|
+
strict: initialProps.inexact,
|
|
22625
|
+
// We only want to set maxError if the inexact prop is true
|
|
22626
|
+
maxError: initialProps.inexact ? initialProps.maxError : 0,
|
|
22627
|
+
status: "correct",
|
|
22628
|
+
// Input-number only allows correct answers
|
|
22629
|
+
message: ""
|
|
22630
|
+
}];
|
|
22631
|
+
return {
|
|
22632
|
+
answers,
|
|
22633
|
+
size: initialProps.size,
|
|
22634
|
+
coefficient: false,
|
|
22635
|
+
// input-number doesn't have a coefficient prop
|
|
22636
|
+
labelText: "",
|
|
22637
|
+
// input-number doesn't have a labelText prop
|
|
22638
|
+
static: false,
|
|
22639
|
+
// static is always false for numeric-input
|
|
22640
|
+
rightAlign: initialProps.rightAlign || false
|
|
22641
|
+
};
|
|
22642
|
+
} else {
|
|
22643
|
+
// Otherwise simply return the initialProps as there's no differences
|
|
22644
|
+
// between v0 and v1 for numeric-input
|
|
22645
|
+
return initialProps;
|
|
22646
|
+
}
|
|
22647
|
+
}
|
|
22648
|
+
};
|
|
22450
22649
|
var NumericInput$1 = {
|
|
22451
22650
|
name: "numeric-input",
|
|
22452
22651
|
displayName: "Numeric input",
|
|
22453
22652
|
defaultAlignment: "inline-block",
|
|
22454
22653
|
accessible: true,
|
|
22455
22654
|
widget: NumericInput,
|
|
22655
|
+
version: {
|
|
22656
|
+
major: 1,
|
|
22657
|
+
minor: 0
|
|
22658
|
+
},
|
|
22456
22659
|
transform: propsTransform,
|
|
22660
|
+
propUpgrades: propUpgrades$2,
|
|
22457
22661
|
isLintable: true,
|
|
22458
22662
|
scorer: scoreNumericInput,
|
|
22459
22663
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
@@ -29470,6 +29674,9 @@ class Group extends React__namespace.Component {
|
|
|
29470
29674
|
this.change("widgets", this.rendererRef.props.widgets);
|
|
29471
29675
|
}
|
|
29472
29676
|
};
|
|
29677
|
+
|
|
29678
|
+
// TODO(LEMS-2391): replace this when there's a separate check
|
|
29679
|
+
// for valid/invalid state
|
|
29473
29680
|
const score = this.rendererRef?.score();
|
|
29474
29681
|
const isValid = score && score.type !== "invalid";
|
|
29475
29682
|
const isInvalid = score && score.type === "invalid";
|
|
@@ -31313,6 +31520,108 @@ function AxisArrows() {
|
|
|
31313
31520
|
})));
|
|
31314
31521
|
}
|
|
31315
31522
|
|
|
31523
|
+
/**
|
|
31524
|
+
* 44 is touch best practice and AAA compliant for WCAG
|
|
31525
|
+
* https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
|
|
31526
|
+
*/
|
|
31527
|
+
const TARGET_SIZE = 44;
|
|
31528
|
+
const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
|
|
31529
|
+
|
|
31530
|
+
// same as pointsFromNormalized in interactive-graph.tsx
|
|
31531
|
+
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
31532
|
+
// maps over [x, y]
|
|
31533
|
+
coords.map((coord, i) => {
|
|
31534
|
+
const axisRange = range[i];
|
|
31535
|
+
if (noSnap) {
|
|
31536
|
+
return axisRange[MIN] + size(axisRange) * coord;
|
|
31537
|
+
}
|
|
31538
|
+
const axisStep = step[i];
|
|
31539
|
+
const nSteps = Math.floor(size(axisRange) / axisStep);
|
|
31540
|
+
const tick = Math.round(coord * nSteps);
|
|
31541
|
+
return axisRange[MIN] + axisStep * tick;
|
|
31542
|
+
}));
|
|
31543
|
+
|
|
31544
|
+
// same as normalizeCoords in interactive-graph.tsx
|
|
31545
|
+
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
31546
|
+
return (coord + ranges[i][1]) / size(ranges[i]);
|
|
31547
|
+
}));
|
|
31548
|
+
|
|
31549
|
+
// Returns the closest point to the given `point` that is within the graph
|
|
31550
|
+
// bounds given in `state`.
|
|
31551
|
+
function bound$1(_ref) {
|
|
31552
|
+
let {
|
|
31553
|
+
snapStep,
|
|
31554
|
+
range,
|
|
31555
|
+
point
|
|
31556
|
+
} = _ref;
|
|
31557
|
+
const boundingBox = inset(snapStep, range);
|
|
31558
|
+
return clampToBox(boundingBox, point);
|
|
31559
|
+
}
|
|
31560
|
+
function isUnlimitedGraphState(state) {
|
|
31561
|
+
return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
|
|
31562
|
+
}
|
|
31563
|
+
|
|
31564
|
+
/**
|
|
31565
|
+
* Replace all text outside of the $ TeX blocks with `\\text{...}`
|
|
31566
|
+
* This way, the entire resulting string can be rendered within <TeX>
|
|
31567
|
+
* and the text outside of the $ blocks will be non-TeX text.
|
|
31568
|
+
*/
|
|
31569
|
+
function replaceOutsideTeX(mathString) {
|
|
31570
|
+
// All the information we need is in the first section,
|
|
31571
|
+
// whether it's typed as "blockmath" or "paragraph"
|
|
31572
|
+
const firstSection = SimpleMarkdown__namespace.parse(mathString)[0];
|
|
31573
|
+
|
|
31574
|
+
// If it's blockMath, the outer level has the full math content.
|
|
31575
|
+
if (firstSection.type === "blockMath") {
|
|
31576
|
+
return firstSection.content;
|
|
31577
|
+
}
|
|
31578
|
+
|
|
31579
|
+
// If it's a paragraph, we need to iterate through the sections
|
|
31580
|
+
// to look for individual math blocks.
|
|
31581
|
+
const condensedNodes = condenseTextNodes(firstSection.content);
|
|
31582
|
+
let result = "";
|
|
31583
|
+
for (const piece of condensedNodes) {
|
|
31584
|
+
piece.type === "math" ? result += piece.content : result += `\\text{${escapeSpecialChars(piece.content)}}`;
|
|
31585
|
+
}
|
|
31586
|
+
return result;
|
|
31587
|
+
}
|
|
31588
|
+
// Helper function for replaceOutsideTeX()
|
|
31589
|
+
// Condense adjacent text nodes into a single text node
|
|
31590
|
+
function condenseTextNodes(nodes) {
|
|
31591
|
+
const result = [];
|
|
31592
|
+
if (!nodes) {
|
|
31593
|
+
return result;
|
|
31594
|
+
}
|
|
31595
|
+
let currentText = "";
|
|
31596
|
+
for (const node of nodes) {
|
|
31597
|
+
if (node.type === "math") {
|
|
31598
|
+
if (currentText) {
|
|
31599
|
+
result.push({
|
|
31600
|
+
type: "text",
|
|
31601
|
+
content: currentText
|
|
31602
|
+
});
|
|
31603
|
+
currentText = "";
|
|
31604
|
+
}
|
|
31605
|
+
result.push(node);
|
|
31606
|
+
} else {
|
|
31607
|
+
currentText += node.content;
|
|
31608
|
+
}
|
|
31609
|
+
}
|
|
31610
|
+
if (currentText) {
|
|
31611
|
+
result.push({
|
|
31612
|
+
type: "text",
|
|
31613
|
+
content: currentText
|
|
31614
|
+
});
|
|
31615
|
+
}
|
|
31616
|
+
return result;
|
|
31617
|
+
}
|
|
31618
|
+
|
|
31619
|
+
// Helper function for replaceOutsideTeX()
|
|
31620
|
+
function escapeSpecialChars(str) {
|
|
31621
|
+
// Escape $, \, {, and } characters
|
|
31622
|
+
return str.replace(/([$\\{}])/g, "\\$1");
|
|
31623
|
+
}
|
|
31624
|
+
|
|
31316
31625
|
function AxisLabels() {
|
|
31317
31626
|
const {
|
|
31318
31627
|
range,
|
|
@@ -31344,7 +31653,7 @@ function AxisLabels() {
|
|
|
31344
31653
|
fontSize: "14px",
|
|
31345
31654
|
transform: "translate(7px, -50%)"
|
|
31346
31655
|
}
|
|
31347
|
-
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, xAxisLabelText)), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
31656
|
+
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, replaceOutsideTeX(xAxisLabelText))), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
31348
31657
|
style: {
|
|
31349
31658
|
position: "absolute",
|
|
31350
31659
|
left: x2,
|
|
@@ -31352,7 +31661,7 @@ function AxisLabels() {
|
|
|
31352
31661
|
fontSize: "14px",
|
|
31353
31662
|
transform: "translate(-50%, 0px)"
|
|
31354
31663
|
}
|
|
31355
|
-
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, yAxisLabelText)));
|
|
31664
|
+
}, /*#__PURE__*/React__default["default"].createElement(TeX, null, replaceOutsideTeX(yAxisLabelText))));
|
|
31356
31665
|
}
|
|
31357
31666
|
|
|
31358
31667
|
// The size of the ticks and labels in pixels
|
|
@@ -32259,17 +32568,12 @@ const ItemExtras = [
|
|
|
32259
32568
|
// prettier-ignore
|
|
32260
32569
|
|
|
32261
32570
|
// prettier-ignore
|
|
32262
|
-
|
|
32571
|
+
// While this widget is deprecated, we still need the type for conversion purposes
|
|
32263
32572
|
// prettier-ignore
|
|
32264
|
-
|
|
32265
32573
|
// prettier-ignore
|
|
32266
|
-
|
|
32267
32574
|
// prettier-ignore
|
|
32268
|
-
|
|
32269
32575
|
//prettier-ignore
|
|
32270
|
-
|
|
32271
32576
|
// A background image applied to various widgets.
|
|
32272
|
-
|
|
32273
32577
|
const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
|
|
32274
32578
|
|
|
32275
32579
|
// 2D range: xMin, xMax, yMin, yMax
|
|
@@ -32295,108 +32599,6 @@ const lockedFigureFillStyles = {
|
|
|
32295
32599
|
|
|
32296
32600
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
32297
32601
|
|
|
32298
|
-
/**
|
|
32299
|
-
* 44 is touch best practice and AAA compliant for WCAG
|
|
32300
|
-
* https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
|
|
32301
|
-
*/
|
|
32302
|
-
const TARGET_SIZE = 44;
|
|
32303
|
-
const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
|
|
32304
|
-
|
|
32305
|
-
// same as pointsFromNormalized in interactive-graph.tsx
|
|
32306
|
-
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
32307
|
-
// maps over [x, y]
|
|
32308
|
-
coords.map((coord, i) => {
|
|
32309
|
-
const axisRange = range[i];
|
|
32310
|
-
if (noSnap) {
|
|
32311
|
-
return axisRange[MIN] + size(axisRange) * coord;
|
|
32312
|
-
}
|
|
32313
|
-
const axisStep = step[i];
|
|
32314
|
-
const nSteps = Math.floor(size(axisRange) / axisStep);
|
|
32315
|
-
const tick = Math.round(coord * nSteps);
|
|
32316
|
-
return axisRange[MIN] + axisStep * tick;
|
|
32317
|
-
}));
|
|
32318
|
-
|
|
32319
|
-
// same as normalizeCoords in interactive-graph.tsx
|
|
32320
|
-
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
32321
|
-
return (coord + ranges[i][1]) / size(ranges[i]);
|
|
32322
|
-
}));
|
|
32323
|
-
|
|
32324
|
-
// Returns the closest point to the given `point` that is within the graph
|
|
32325
|
-
// bounds given in `state`.
|
|
32326
|
-
function bound$1(_ref) {
|
|
32327
|
-
let {
|
|
32328
|
-
snapStep,
|
|
32329
|
-
range,
|
|
32330
|
-
point
|
|
32331
|
-
} = _ref;
|
|
32332
|
-
const boundingBox = inset(snapStep, range);
|
|
32333
|
-
return clampToBox(boundingBox, point);
|
|
32334
|
-
}
|
|
32335
|
-
function isUnlimitedGraphState(state) {
|
|
32336
|
-
return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
|
|
32337
|
-
}
|
|
32338
|
-
|
|
32339
|
-
/**
|
|
32340
|
-
* Replace all text outside of the $ TeX blocks with `\\text{...}`
|
|
32341
|
-
* This way, the entire resulting string can be rendered within <TeX>
|
|
32342
|
-
* and the text outside of the $ blocks will be non-TeX text.
|
|
32343
|
-
*/
|
|
32344
|
-
function replaceOutsideTeX(mathString) {
|
|
32345
|
-
// All the information we need is in the first section,
|
|
32346
|
-
// whether it's typed as "blockmath" or "paragraph"
|
|
32347
|
-
const firstSection = SimpleMarkdown__namespace.parse(mathString)[0];
|
|
32348
|
-
|
|
32349
|
-
// If it's blockMath, the outer level has the full math content.
|
|
32350
|
-
if (firstSection.type === "blockMath") {
|
|
32351
|
-
return firstSection.content;
|
|
32352
|
-
}
|
|
32353
|
-
|
|
32354
|
-
// If it's a paragraph, we need to iterate through the sections
|
|
32355
|
-
// to look for individual math blocks.
|
|
32356
|
-
const condensedNodes = condenseTextNodes(firstSection.content);
|
|
32357
|
-
let result = "";
|
|
32358
|
-
for (const piece of condensedNodes) {
|
|
32359
|
-
piece.type === "math" ? result += piece.content : result += `\\text{${escapeSpecialChars(piece.content)}}`;
|
|
32360
|
-
}
|
|
32361
|
-
return result;
|
|
32362
|
-
}
|
|
32363
|
-
// Helper function for replaceOutsideTeX()
|
|
32364
|
-
// Condense adjacent text nodes into a single text node
|
|
32365
|
-
function condenseTextNodes(nodes) {
|
|
32366
|
-
const result = [];
|
|
32367
|
-
if (!nodes) {
|
|
32368
|
-
return result;
|
|
32369
|
-
}
|
|
32370
|
-
let currentText = "";
|
|
32371
|
-
for (const node of nodes) {
|
|
32372
|
-
if (node.type === "math") {
|
|
32373
|
-
if (currentText) {
|
|
32374
|
-
result.push({
|
|
32375
|
-
type: "text",
|
|
32376
|
-
content: currentText
|
|
32377
|
-
});
|
|
32378
|
-
currentText = "";
|
|
32379
|
-
}
|
|
32380
|
-
result.push(node);
|
|
32381
|
-
} else {
|
|
32382
|
-
currentText += node.content;
|
|
32383
|
-
}
|
|
32384
|
-
}
|
|
32385
|
-
if (currentText) {
|
|
32386
|
-
result.push({
|
|
32387
|
-
type: "text",
|
|
32388
|
-
content: currentText
|
|
32389
|
-
});
|
|
32390
|
-
}
|
|
32391
|
-
return result;
|
|
32392
|
-
}
|
|
32393
|
-
|
|
32394
|
-
// Helper function for replaceOutsideTeX()
|
|
32395
|
-
function escapeSpecialChars(str) {
|
|
32396
|
-
// Escape $, \, {, and } characters
|
|
32397
|
-
return str.replace(/([$\\{}])/g, "\\$1");
|
|
32398
|
-
}
|
|
32399
|
-
|
|
32400
32602
|
function LockedLabel(props) {
|
|
32401
32603
|
const {
|
|
32402
32604
|
coord,
|
|
@@ -33625,6 +33827,7 @@ function useControlPoint(params) {
|
|
|
33625
33827
|
} = useGraphConfig();
|
|
33626
33828
|
const {
|
|
33627
33829
|
point,
|
|
33830
|
+
sequenceNumber,
|
|
33628
33831
|
color,
|
|
33629
33832
|
cursor,
|
|
33630
33833
|
constrain = p => snap(snapStep, p),
|
|
@@ -33665,6 +33868,7 @@ function useControlPoint(params) {
|
|
|
33665
33868
|
ref: focusableHandleRef,
|
|
33666
33869
|
role: "button",
|
|
33667
33870
|
"aria-label": strings.srPointAtCoordinates({
|
|
33871
|
+
num: sequenceNumber,
|
|
33668
33872
|
x: srFormatNumber(point[X], locale),
|
|
33669
33873
|
y: srFormatNumber(point[Y], locale)
|
|
33670
33874
|
})
|
|
@@ -33742,6 +33946,7 @@ const MovableLine = props => {
|
|
|
33742
33946
|
focusableHandle: focusableHandle1
|
|
33743
33947
|
} = useControlPoint({
|
|
33744
33948
|
point: start,
|
|
33949
|
+
sequenceNumber: 1,
|
|
33745
33950
|
color,
|
|
33746
33951
|
onMove: p => onMovePoint(0, p)
|
|
33747
33952
|
});
|
|
@@ -33750,6 +33955,7 @@ const MovableLine = props => {
|
|
|
33750
33955
|
focusableHandle: focusableHandle2
|
|
33751
33956
|
} = useControlPoint({
|
|
33752
33957
|
point: end,
|
|
33958
|
+
sequenceNumber: 2,
|
|
33753
33959
|
color,
|
|
33754
33960
|
onMove: p => onMovePoint(1, p)
|
|
33755
33961
|
});
|
|
@@ -33931,14 +34137,17 @@ function AngleGraph(props) {
|
|
|
33931
34137
|
// Render the lines, angle, and then movable points
|
|
33932
34138
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, svgLines, /*#__PURE__*/React__namespace.createElement(Angle, angleParams), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
33933
34139
|
point: coords[1],
|
|
34140
|
+
sequenceNumber: 1,
|
|
33934
34141
|
constrain: p => p,
|
|
33935
34142
|
onMove: destination => dispatch(actions.angle.movePoint(1, destination))
|
|
33936
34143
|
}), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
33937
34144
|
point: coords[0],
|
|
34145
|
+
sequenceNumber: 2,
|
|
33938
34146
|
constrain: getAngleSideConstraint(coords[0], coords[1], snapDegrees || 1),
|
|
33939
34147
|
onMove: destination => dispatch(actions.angle.movePoint(0, destination))
|
|
33940
34148
|
}), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
33941
34149
|
point: coords[2],
|
|
34150
|
+
sequenceNumber: 3,
|
|
33942
34151
|
constrain: getAngleSideConstraint(coords[2], coords[1], snapDegrees || 1),
|
|
33943
34152
|
onMove: destination => dispatch(actions.angle.movePoint(2, destination))
|
|
33944
34153
|
}));
|
|
@@ -34103,6 +34312,7 @@ function CircleGraph(props) {
|
|
|
34103
34312
|
onMove: c => dispatch(actions.circle.moveCenter(c))
|
|
34104
34313
|
}), /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34105
34314
|
point: radiusPoint,
|
|
34315
|
+
sequenceNumber: 1,
|
|
34106
34316
|
cursor: "ew-resize",
|
|
34107
34317
|
onMove: newRadiusPoint => {
|
|
34108
34318
|
dispatch(actions.circle.moveRadiusPoint(newRadiusPoint));
|
|
@@ -34278,6 +34488,7 @@ function LimitedPointGraph(props) {
|
|
|
34278
34488
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34279
34489
|
key: i,
|
|
34280
34490
|
point: point,
|
|
34491
|
+
sequenceNumber: i + 1,
|
|
34281
34492
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
|
|
34282
34493
|
})));
|
|
34283
34494
|
}
|
|
@@ -34319,6 +34530,7 @@ function UnlimitedPointGraph(props) {
|
|
|
34319
34530
|
}), props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34320
34531
|
key: i,
|
|
34321
34532
|
point: point,
|
|
34533
|
+
sequenceNumber: i + 1,
|
|
34322
34534
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
|
|
34323
34535
|
ref: ref => {
|
|
34324
34536
|
itemsRef.current[i] = ref;
|
|
@@ -34350,9 +34562,10 @@ function describePointGraph(state, i18n) {
|
|
|
34350
34562
|
if (state.coords.length === 0) {
|
|
34351
34563
|
return strings.srNoInteractiveElements;
|
|
34352
34564
|
}
|
|
34353
|
-
const pointDescriptions = state.coords.map(_ref2 => {
|
|
34565
|
+
const pointDescriptions = state.coords.map((_ref2, index) => {
|
|
34354
34566
|
let [x, y] = _ref2;
|
|
34355
34567
|
return strings.srPointAtCoordinates({
|
|
34568
|
+
num: index + 1,
|
|
34356
34569
|
x: srFormatNumber(x, locale),
|
|
34357
34570
|
y: srFormatNumber(y, locale)
|
|
34358
34571
|
});
|
|
@@ -34472,6 +34685,7 @@ const LimitedPolygonGraph = props => {
|
|
|
34472
34685
|
key: "point-" + i,
|
|
34473
34686
|
constrain: constrain,
|
|
34474
34687
|
point: point,
|
|
34688
|
+
sequenceNumber: i + 1,
|
|
34475
34689
|
onMove: destination => {
|
|
34476
34690
|
const now = Date.now();
|
|
34477
34691
|
const targetFPS = 40;
|
|
@@ -34618,6 +34832,7 @@ const UnlimitedPolygonGraph = props => {
|
|
|
34618
34832
|
}), props.graphState.coords.map((point, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34619
34833
|
key: i,
|
|
34620
34834
|
point: point,
|
|
34835
|
+
sequenceNumber: i + 1,
|
|
34621
34836
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
|
|
34622
34837
|
ref: ref => {
|
|
34623
34838
|
pointRef.current[i] = ref;
|
|
@@ -34690,6 +34905,7 @@ function QuadraticGraph(props) {
|
|
|
34690
34905
|
}), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34691
34906
|
key: "point-" + i,
|
|
34692
34907
|
point: coord,
|
|
34908
|
+
sequenceNumber: i + 1,
|
|
34693
34909
|
onMove: destination => dispatch(actions.quadratic.movePoint(i, destination))
|
|
34694
34910
|
})));
|
|
34695
34911
|
}
|
|
@@ -34818,6 +35034,7 @@ function SinusoidGraph(props) {
|
|
|
34818
35034
|
}), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(MovablePoint$1, {
|
|
34819
35035
|
key: "point-" + i,
|
|
34820
35036
|
point: coord,
|
|
35037
|
+
sequenceNumber: i + 1,
|
|
34821
35038
|
onMove: destination => dispatch(actions.sinusoid.movePoint(i, destination))
|
|
34822
35039
|
})));
|
|
34823
35040
|
}
|
|
@@ -44986,7 +45203,7 @@ const init = function () {
|
|
|
44986
45203
|
|
|
44987
45204
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44988
45205
|
const libName = "@khanacademy/perseus";
|
|
44989
|
-
const libVersion = "
|
|
45206
|
+
const libVersion = "42.0.0";
|
|
44990
45207
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44991
45208
|
|
|
44992
45209
|
const apiVersion = {
|
|
@@ -45591,6 +45808,13 @@ class ServerItemRenderer extends React__namespace.Component {
|
|
|
45591
45808
|
this.props.onRendered(true);
|
|
45592
45809
|
}
|
|
45593
45810
|
}
|
|
45811
|
+
if (this.props.score && this.props.score !== prevProps.score) {
|
|
45812
|
+
const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
|
|
45813
|
+
this.setState({
|
|
45814
|
+
questionCompleted: this.props.score.correct,
|
|
45815
|
+
questionHighlightedWidgets: emptyQuestionAreaWidgets
|
|
45816
|
+
});
|
|
45817
|
+
}
|
|
45594
45818
|
}
|
|
45595
45819
|
componentWillUnmount() {
|
|
45596
45820
|
if (this.blurTimeoutID != null) {
|
|
@@ -45679,7 +45903,7 @@ class ServerItemRenderer extends React__namespace.Component {
|
|
|
45679
45903
|
|
|
45680
45904
|
/**
|
|
45681
45905
|
* Accepts a question area widgetId, or an answer area widgetId of
|
|
45682
|
-
* the form "answer-input
|
|
45906
|
+
* the form "answer-numeric-input 1", or the string "answer-area"
|
|
45683
45907
|
* for the whole answer area (if the answer area is a single widget).
|
|
45684
45908
|
*/
|
|
45685
45909
|
_setWidgetProps(widgetId, newProps, callback) {
|
|
@@ -45726,18 +45950,37 @@ class ServerItemRenderer extends React__namespace.Component {
|
|
|
45726
45950
|
return this.questionRenderer.getPromptJSON();
|
|
45727
45951
|
}
|
|
45728
45952
|
|
|
45953
|
+
/**
|
|
45954
|
+
* Returns an array of the widget `.getUserInput()` results
|
|
45955
|
+
*
|
|
45956
|
+
* TODO: can we remove this? Seems to be just for backwards
|
|
45957
|
+
* compatibility with old Perseus Chrome logging
|
|
45958
|
+
* @deprecated use getUserInput
|
|
45959
|
+
*/
|
|
45960
|
+
getUserInputLegacy() {
|
|
45961
|
+
return this.questionRenderer.getUserInput();
|
|
45962
|
+
}
|
|
45963
|
+
|
|
45964
|
+
/**
|
|
45965
|
+
* Returns an object of the widget `.getUserInput()` results
|
|
45966
|
+
*/
|
|
45967
|
+
getUserInput() {
|
|
45968
|
+
return this.questionRenderer.getUserInputMap();
|
|
45969
|
+
}
|
|
45970
|
+
|
|
45729
45971
|
/**
|
|
45730
45972
|
* Grades the item.
|
|
45973
|
+
*
|
|
45974
|
+
* @deprecated use scorePerseusItem
|
|
45731
45975
|
*/
|
|
45732
45976
|
scoreInput() {
|
|
45733
|
-
const
|
|
45734
|
-
const
|
|
45735
|
-
const score = guessAndScore[1];
|
|
45977
|
+
const guess = this.getUserInput();
|
|
45978
|
+
const score = scorePerseusItem(this.props.item.question, guess, this.context.strings, this.context.locale);
|
|
45736
45979
|
|
|
45737
45980
|
// Continue to include an empty guess for the now defunct answer area.
|
|
45738
45981
|
// TODO(alex): Check whether we rely on the format here for
|
|
45739
45982
|
// analyzing ProblemLogs. If not, remove this layer.
|
|
45740
|
-
const maxCompatGuess = [
|
|
45983
|
+
const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
|
|
45741
45984
|
const keScore = Util.keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
|
|
45742
45985
|
const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
|
|
45743
45986
|
this.setState({
|
|
@@ -47599,138 +47842,6 @@ const registerAllWidgetsForTesting = () => {
|
|
|
47599
47842
|
replaceDeprecatedWidgets();
|
|
47600
47843
|
};
|
|
47601
47844
|
|
|
47602
|
-
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
47603
|
-
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
47604
|
-
|
|
47605
|
-
/**
|
|
47606
|
-
* Regex for widget placeholders in a string.
|
|
47607
|
-
*
|
|
47608
|
-
* First capture group is the widget ID (ex. 'radio 1')
|
|
47609
|
-
* Second capture group is the widget type (ex. "radio)
|
|
47610
|
-
* exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
|
|
47611
|
-
*/
|
|
47612
|
-
function getWidgetRegex() {
|
|
47613
|
-
return /\[\[☃ ([A-Za-z0-9- ]+)\]\]/g;
|
|
47614
|
-
}
|
|
47615
|
-
|
|
47616
|
-
/**
|
|
47617
|
-
* Add a widget placeholder using the widget ID.
|
|
47618
|
-
* ex. addWidget("radio 1") => "[[☃ radio 1]]"
|
|
47619
|
-
*
|
|
47620
|
-
* @param {string} id
|
|
47621
|
-
* @returns {string}
|
|
47622
|
-
*/
|
|
47623
|
-
function addWidget(id) {
|
|
47624
|
-
return `[[☃ ${id}]]`;
|
|
47625
|
-
}
|
|
47626
|
-
|
|
47627
|
-
/**
|
|
47628
|
-
* Get a widget type by a widget's ID
|
|
47629
|
-
* (please don't derive type from ID)
|
|
47630
|
-
*
|
|
47631
|
-
* @param {string} widgetId the ID of the widget
|
|
47632
|
-
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
47633
|
-
* @returns {string} the widget type (ie "radio")
|
|
47634
|
-
*/
|
|
47635
|
-
function getWidgetTypeByWidgetId(WidgetId, widgetMap) {
|
|
47636
|
-
const widget = widgetMap[WidgetId];
|
|
47637
|
-
return widget?.type ?? null;
|
|
47638
|
-
}
|
|
47639
|
-
|
|
47640
|
-
/**
|
|
47641
|
-
* Does the content have a specific type of widget?
|
|
47642
|
-
*
|
|
47643
|
-
* @param {string} type the type of the widget in question (ie "radio")
|
|
47644
|
-
* @param {string} content the string to search through
|
|
47645
|
-
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
47646
|
-
* @returns {boolean} if the content includes the widget type
|
|
47647
|
-
*/
|
|
47648
|
-
function contentHasWidgetType(type, content, widgetMap) {
|
|
47649
|
-
return getWidgetIdsFromContentByType(type, content, widgetMap).length > 0;
|
|
47650
|
-
}
|
|
47651
|
-
|
|
47652
|
-
/**
|
|
47653
|
-
* Extract all widget IDs, which includes the widget type and instance number.
|
|
47654
|
-
* example output: ['radio 1', 'categorizer 1', 'categorizor 2']
|
|
47655
|
-
*
|
|
47656
|
-
* Content should contain Perseus widget placeholders,
|
|
47657
|
-
* which look like: '[[☃ radio 1]]'.
|
|
47658
|
-
*
|
|
47659
|
-
* @param {string} content
|
|
47660
|
-
* @returns {ReadonlyArray<string>} widgetIds
|
|
47661
|
-
*/
|
|
47662
|
-
function getWidgetIdsFromContent(content) {
|
|
47663
|
-
const widgets = [];
|
|
47664
|
-
const localWidgetRegex = getWidgetRegex();
|
|
47665
|
-
let match = localWidgetRegex.exec(content);
|
|
47666
|
-
while (match !== null) {
|
|
47667
|
-
widgets.push(match[1]);
|
|
47668
|
-
match = localWidgetRegex.exec(content);
|
|
47669
|
-
}
|
|
47670
|
-
return widgets;
|
|
47671
|
-
}
|
|
47672
|
-
|
|
47673
|
-
/**
|
|
47674
|
-
* Get a list of widget IDs from content,
|
|
47675
|
-
* but only for specific widget types
|
|
47676
|
-
*
|
|
47677
|
-
* @param {string} type the type of widget (ie "radio")
|
|
47678
|
-
* @param {string} content the string to parse
|
|
47679
|
-
* @param {PerseusWidgetsMap} widgetMap widget ID to widget map
|
|
47680
|
-
* @returns {ReadonlyArray<string>} the widget type (ie "radio")
|
|
47681
|
-
*/
|
|
47682
|
-
function getWidgetIdsFromContentByType(type, content, widgetMap) {
|
|
47683
|
-
const rv = [];
|
|
47684
|
-
const widgetIdsInContent = getWidgetIdsFromContent(content);
|
|
47685
|
-
widgetIdsInContent.forEach(widgetId => {
|
|
47686
|
-
const widget = widgetMap[widgetId];
|
|
47687
|
-
if (widget?.type === type) {
|
|
47688
|
-
rv.push(widgetId);
|
|
47689
|
-
}
|
|
47690
|
-
});
|
|
47691
|
-
return rv;
|
|
47692
|
-
}
|
|
47693
|
-
|
|
47694
|
-
/**
|
|
47695
|
-
* Pull the widget map out of ItemData
|
|
47696
|
-
*
|
|
47697
|
-
* @param {PerseusItem} PerseusItem containing a widgetMap
|
|
47698
|
-
* @returns {WidgetMap} the widget map in the PerseusItem
|
|
47699
|
-
*/
|
|
47700
|
-
function getWidgetsMapFromItemData(itemData) {
|
|
47701
|
-
return itemData.question.widgets;
|
|
47702
|
-
}
|
|
47703
|
-
|
|
47704
|
-
/**
|
|
47705
|
-
* Get the widget information from a WidgetMap
|
|
47706
|
-
*
|
|
47707
|
-
* @param {string} widgetId the ID of the widget
|
|
47708
|
-
* @returns {PerseusWidget | null} the widget data if it exists, otherwise null
|
|
47709
|
-
*/
|
|
47710
|
-
function getWidgetFromWidgetMap(widgetId, widgetMap) {
|
|
47711
|
-
return widgetMap[widgetId] ?? null;
|
|
47712
|
-
}
|
|
47713
|
-
|
|
47714
|
-
/**
|
|
47715
|
-
* Get select widgets from a widget map.
|
|
47716
|
-
* Useful for multi-items that needs to split
|
|
47717
|
-
* one PerseusItem into several
|
|
47718
|
-
*
|
|
47719
|
-
* @param {ReadonlyArray<string>} widgetIds to extract
|
|
47720
|
-
* @param {PerseusWidgetsMap} widgetMap to extract from
|
|
47721
|
-
* @return {PerseusWidgetsMap} a new widget map with requested widgets
|
|
47722
|
-
*/
|
|
47723
|
-
function getWidgetsFromWidgetMap(widgetIds, widgetMap) {
|
|
47724
|
-
const widgets = {};
|
|
47725
|
-
widgetIds.forEach(widgetId => {
|
|
47726
|
-
const widget = getWidgetFromWidgetMap(widgetId, widgetMap);
|
|
47727
|
-
if (widget) {
|
|
47728
|
-
widgets[widgetId] = widget;
|
|
47729
|
-
}
|
|
47730
|
-
});
|
|
47731
|
-
return widgets;
|
|
47732
|
-
}
|
|
47733
|
-
|
|
47734
47845
|
/**
|
|
47735
47846
|
* This function extracts the answers from the widgets.
|
|
47736
47847
|
*
|
|
@@ -48663,6 +48774,118 @@ const parseExpressionWidget = parseWidget(constant("expression"), object({
|
|
|
48663
48774
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
48664
48775
|
}));
|
|
48665
48776
|
|
|
48777
|
+
const parsePerseusRenderer = object({
|
|
48778
|
+
content: string,
|
|
48779
|
+
// This module has an import cycle with parseWidgetsMap, because the
|
|
48780
|
+
// `group` widget can contain another renderer.
|
|
48781
|
+
// The anonymous function below ensures that we don't try to access
|
|
48782
|
+
// parseWidgetsMap before it's defined.
|
|
48783
|
+
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
48784
|
+
metadata: optional(array(string)),
|
|
48785
|
+
images: record(string, object({
|
|
48786
|
+
width: number,
|
|
48787
|
+
height: number
|
|
48788
|
+
}))
|
|
48789
|
+
});
|
|
48790
|
+
|
|
48791
|
+
const parseGradedGroupWidgetOptions = object({
|
|
48792
|
+
title: string,
|
|
48793
|
+
hasHint: optional(nullable(boolean)),
|
|
48794
|
+
// This module has an import cycle with parsePerseusRenderer.
|
|
48795
|
+
// The anonymous function below ensures that we don't try to access
|
|
48796
|
+
// parsePerseusRenderer before it's defined.
|
|
48797
|
+
hint: optional(nullable((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx))),
|
|
48798
|
+
content: string,
|
|
48799
|
+
// This module has an import cycle with parseWidgetsMap.
|
|
48800
|
+
// The anonymous function below ensures that we don't try to access
|
|
48801
|
+
// parseWidgetsMap before it's defined.
|
|
48802
|
+
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
48803
|
+
widgetEnabled: optional(nullable(boolean)),
|
|
48804
|
+
immutableWidgets: optional(nullable(boolean)),
|
|
48805
|
+
images: record(string, object({
|
|
48806
|
+
width: number,
|
|
48807
|
+
height: number
|
|
48808
|
+
}))
|
|
48809
|
+
});
|
|
48810
|
+
const parseGradedGroupWidget = parseWidget(constant("graded-group"), parseGradedGroupWidgetOptions);
|
|
48811
|
+
|
|
48812
|
+
const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), object({
|
|
48813
|
+
gradedGroups: array(parseGradedGroupWidgetOptions)
|
|
48814
|
+
}));
|
|
48815
|
+
|
|
48816
|
+
const pairOfNumbers$3 = pair(number, number);
|
|
48817
|
+
const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
|
|
48818
|
+
const parseGrapherWidget = parseWidget(constant("grapher"), object({
|
|
48819
|
+
availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
|
|
48820
|
+
correct: union(object({
|
|
48821
|
+
type: constant("absolute_value"),
|
|
48822
|
+
coords: pairOfPoints
|
|
48823
|
+
})).or(object({
|
|
48824
|
+
type: constant("exponential"),
|
|
48825
|
+
asymptote: pairOfPoints,
|
|
48826
|
+
coords: pairOfPoints
|
|
48827
|
+
})).or(object({
|
|
48828
|
+
type: constant("linear"),
|
|
48829
|
+
coords: pairOfPoints
|
|
48830
|
+
})).or(object({
|
|
48831
|
+
type: constant("logarithm"),
|
|
48832
|
+
asymptote: pairOfPoints,
|
|
48833
|
+
coords: pairOfPoints
|
|
48834
|
+
})).or(object({
|
|
48835
|
+
type: constant("quadratic"),
|
|
48836
|
+
coords: pairOfPoints
|
|
48837
|
+
})).or(object({
|
|
48838
|
+
type: constant("sinusoid"),
|
|
48839
|
+
coords: pairOfPoints
|
|
48840
|
+
})).or(object({
|
|
48841
|
+
type: constant("tangent"),
|
|
48842
|
+
coords: pairOfPoints
|
|
48843
|
+
})).parser,
|
|
48844
|
+
graph: object({
|
|
48845
|
+
backgroundImage: object({
|
|
48846
|
+
bottom: optional(number),
|
|
48847
|
+
height: optional(number),
|
|
48848
|
+
left: optional(number),
|
|
48849
|
+
scale: optional(number),
|
|
48850
|
+
url: optional(nullable(string)),
|
|
48851
|
+
width: optional(number)
|
|
48852
|
+
}),
|
|
48853
|
+
box: optional(pairOfNumbers$3),
|
|
48854
|
+
editableSettings: optional(array(enumeration("graph", "snap", "image", "measure"))),
|
|
48855
|
+
gridStep: optional(pairOfNumbers$3),
|
|
48856
|
+
labels: pair(string, string),
|
|
48857
|
+
markings: enumeration("graph", "none", "grid"),
|
|
48858
|
+
range: pair(pairOfNumbers$3, pairOfNumbers$3),
|
|
48859
|
+
rulerLabel: constant(""),
|
|
48860
|
+
rulerTicks: number,
|
|
48861
|
+
showProtractor: optional(boolean),
|
|
48862
|
+
showRuler: optional(boolean),
|
|
48863
|
+
showTooltips: optional(boolean),
|
|
48864
|
+
snapStep: optional(pairOfNumbers$3),
|
|
48865
|
+
step: pairOfNumbers$3,
|
|
48866
|
+
valid: optional(union(boolean).or(string).parser)
|
|
48867
|
+
})
|
|
48868
|
+
}));
|
|
48869
|
+
|
|
48870
|
+
const parseGroupWidget = parseWidget(constant("group"),
|
|
48871
|
+
// This module has an import cycle with parsePerseusRenderer.
|
|
48872
|
+
// The anonymous function below ensures that we don't try to access
|
|
48873
|
+
// parsePerseusRenderer before it's defined.
|
|
48874
|
+
(rawVal, ctx) => parsePerseusRenderer(rawVal, ctx));
|
|
48875
|
+
|
|
48876
|
+
const parseIframeWidget = parseWidget(constant("iframe"), object({
|
|
48877
|
+
url: string,
|
|
48878
|
+
settings: array(object({
|
|
48879
|
+
name: string,
|
|
48880
|
+
value: string
|
|
48881
|
+
})),
|
|
48882
|
+
width: union(number).or(string).parser,
|
|
48883
|
+
height: union(number).or(string).parser,
|
|
48884
|
+
allowFullScreen: boolean,
|
|
48885
|
+
allowTopNavigation: optional(boolean),
|
|
48886
|
+
static: boolean
|
|
48887
|
+
}));
|
|
48888
|
+
|
|
48666
48889
|
const parsePerseusImageBackground = object({
|
|
48667
48890
|
url: optional(nullable(string)),
|
|
48668
48891
|
width: optional(number),
|
|
@@ -48674,6 +48897,160 @@ const parsePerseusImageBackground = object({
|
|
|
48674
48897
|
scale: optional(union(number).or(string).parser)
|
|
48675
48898
|
});
|
|
48676
48899
|
|
|
48900
|
+
const pairOfNumbers$2 = pair(number, number);
|
|
48901
|
+
const parseImageWidget = parseWidget(constant("image"), object({
|
|
48902
|
+
title: optional(string),
|
|
48903
|
+
caption: optional(string),
|
|
48904
|
+
alt: optional(string),
|
|
48905
|
+
backgroundImage: parsePerseusImageBackground,
|
|
48906
|
+
static: optional(boolean),
|
|
48907
|
+
labels: optional(array(object({
|
|
48908
|
+
content: string,
|
|
48909
|
+
alignment: string,
|
|
48910
|
+
coordinates: array(number)
|
|
48911
|
+
}))),
|
|
48912
|
+
range: optional(pair(pairOfNumbers$2, pairOfNumbers$2)),
|
|
48913
|
+
box: optional(pairOfNumbers$2)
|
|
48914
|
+
}));
|
|
48915
|
+
|
|
48916
|
+
const parseInputNumberWidget = parseWidget(constant("input-number"), object({
|
|
48917
|
+
answerType: optional(enumeration("number", "decimal", "integer", "rational", "improper", "mixed", "percent", "pi")),
|
|
48918
|
+
inexact: optional(boolean),
|
|
48919
|
+
maxError: optional(union(number).or(string).parser),
|
|
48920
|
+
rightAlign: optional(boolean),
|
|
48921
|
+
simplify: enumeration("required", "optional", "enforced"),
|
|
48922
|
+
size: enumeration("normal", "small"),
|
|
48923
|
+
value: union(number).or(string).parser,
|
|
48924
|
+
customKeypad: optional(boolean)
|
|
48925
|
+
}));
|
|
48926
|
+
|
|
48927
|
+
const pairOfNumbers$1 = pair(number, number);
|
|
48928
|
+
const parseFunctionElement = object({
|
|
48929
|
+
type: constant("function"),
|
|
48930
|
+
key: string,
|
|
48931
|
+
options: object({
|
|
48932
|
+
value: string,
|
|
48933
|
+
funcName: string,
|
|
48934
|
+
rangeMin: string,
|
|
48935
|
+
rangeMax: string,
|
|
48936
|
+
color: string,
|
|
48937
|
+
strokeDasharray: string,
|
|
48938
|
+
strokeWidth: number
|
|
48939
|
+
})
|
|
48940
|
+
});
|
|
48941
|
+
const parseLabelElement = object({
|
|
48942
|
+
type: constant("label"),
|
|
48943
|
+
key: string,
|
|
48944
|
+
options: object({
|
|
48945
|
+
label: string,
|
|
48946
|
+
color: string,
|
|
48947
|
+
coordX: string,
|
|
48948
|
+
coordY: string
|
|
48949
|
+
})
|
|
48950
|
+
});
|
|
48951
|
+
const parseLineElement = object({
|
|
48952
|
+
type: constant("line"),
|
|
48953
|
+
key: string,
|
|
48954
|
+
options: object({
|
|
48955
|
+
color: string,
|
|
48956
|
+
startX: string,
|
|
48957
|
+
startY: string,
|
|
48958
|
+
endX: string,
|
|
48959
|
+
endY: string,
|
|
48960
|
+
strokeDasharray: string,
|
|
48961
|
+
strokeWidth: number,
|
|
48962
|
+
arrows: string
|
|
48963
|
+
})
|
|
48964
|
+
});
|
|
48965
|
+
const parseMovableLineElement = object({
|
|
48966
|
+
type: constant("movable-line"),
|
|
48967
|
+
key: string,
|
|
48968
|
+
options: object({
|
|
48969
|
+
startX: string,
|
|
48970
|
+
startY: string,
|
|
48971
|
+
startSubscript: number,
|
|
48972
|
+
endX: string,
|
|
48973
|
+
endY: string,
|
|
48974
|
+
endSubscript: number,
|
|
48975
|
+
constraint: string,
|
|
48976
|
+
snap: number,
|
|
48977
|
+
constraintFn: string,
|
|
48978
|
+
constraintXMin: string,
|
|
48979
|
+
constraintXMax: string,
|
|
48980
|
+
constraintYMin: string,
|
|
48981
|
+
constraintYMax: string
|
|
48982
|
+
})
|
|
48983
|
+
});
|
|
48984
|
+
const parseMovablePointElement = object({
|
|
48985
|
+
type: constant("movable-point"),
|
|
48986
|
+
key: string,
|
|
48987
|
+
options: object({
|
|
48988
|
+
startX: string,
|
|
48989
|
+
startY: string,
|
|
48990
|
+
varSubscript: number,
|
|
48991
|
+
constraint: string,
|
|
48992
|
+
snap: number,
|
|
48993
|
+
constraintFn: string,
|
|
48994
|
+
constraintXMin: string,
|
|
48995
|
+
constraintXMax: string,
|
|
48996
|
+
constraintYMin: string,
|
|
48997
|
+
constraintYMax: string
|
|
48998
|
+
})
|
|
48999
|
+
});
|
|
49000
|
+
const parseParametricElement = object({
|
|
49001
|
+
type: constant("parametric"),
|
|
49002
|
+
key: string,
|
|
49003
|
+
options: object({
|
|
49004
|
+
x: string,
|
|
49005
|
+
y: string,
|
|
49006
|
+
rangeMin: string,
|
|
49007
|
+
rangeMax: string,
|
|
49008
|
+
color: string,
|
|
49009
|
+
strokeDasharray: string,
|
|
49010
|
+
strokeWidth: number
|
|
49011
|
+
})
|
|
49012
|
+
});
|
|
49013
|
+
const parsePointElement = object({
|
|
49014
|
+
type: constant("point"),
|
|
49015
|
+
key: string,
|
|
49016
|
+
options: object({
|
|
49017
|
+
color: string,
|
|
49018
|
+
coordX: string,
|
|
49019
|
+
coordY: string
|
|
49020
|
+
})
|
|
49021
|
+
});
|
|
49022
|
+
const parseRectangleElement = object({
|
|
49023
|
+
type: constant("rectangle"),
|
|
49024
|
+
key: string,
|
|
49025
|
+
options: object({
|
|
49026
|
+
color: string,
|
|
49027
|
+
coordX: string,
|
|
49028
|
+
coordY: string,
|
|
49029
|
+
width: string,
|
|
49030
|
+
height: string
|
|
49031
|
+
})
|
|
49032
|
+
});
|
|
49033
|
+
const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
49034
|
+
static: boolean,
|
|
49035
|
+
graph: object({
|
|
49036
|
+
editableSettings: optional(array(enumeration("canvas", "graph"))),
|
|
49037
|
+
box: pairOfNumbers$1,
|
|
49038
|
+
labels: array(string),
|
|
49039
|
+
range: pair(pairOfNumbers$1, pairOfNumbers$1),
|
|
49040
|
+
gridStep: pairOfNumbers$1,
|
|
49041
|
+
markings: enumeration("graph", "grid", "none"),
|
|
49042
|
+
snapStep: optional(pairOfNumbers$1),
|
|
49043
|
+
valid: optional(union(boolean).or(string).parser),
|
|
49044
|
+
backgroundImage: optional(parsePerseusImageBackground),
|
|
49045
|
+
showProtractor: optional(boolean),
|
|
49046
|
+
showRuler: optional(boolean),
|
|
49047
|
+
rulerLabel: optional(string),
|
|
49048
|
+
rulerTicks: optional(number),
|
|
49049
|
+
tickStep: pairOfNumbers$1
|
|
49050
|
+
}),
|
|
49051
|
+
elements: array(union(parseFunctionElement).or(parseLabelElement).or(parseLineElement).or(parseMovableLineElement).or(parseMovablePointElement).or(parseParametricElement).or(parsePointElement).or(parseRectangleElement).parser)
|
|
49052
|
+
}));
|
|
49053
|
+
|
|
48677
49054
|
// Used to represent 2-D points and ranges
|
|
48678
49055
|
const pairOfNumbers = pair(number, number);
|
|
48679
49056
|
const parsePerseusGraphTypeAngle = object({
|
|
@@ -48860,6 +49237,195 @@ const parseInteractiveGraphWidget = parseWidget(constant("interactive-graph"), o
|
|
|
48860
49237
|
fullGraphAriaDescription: optional(string)
|
|
48861
49238
|
}));
|
|
48862
49239
|
|
|
49240
|
+
const parseLabelImageWidget = parseWidget(constant("label-image"), object({
|
|
49241
|
+
choices: array(string),
|
|
49242
|
+
imageUrl: string,
|
|
49243
|
+
imageAlt: string,
|
|
49244
|
+
imageHeight: number,
|
|
49245
|
+
imageWidth: number,
|
|
49246
|
+
markers: array(object({
|
|
49247
|
+
answers: array(string),
|
|
49248
|
+
label: string,
|
|
49249
|
+
x: number,
|
|
49250
|
+
y: number
|
|
49251
|
+
})),
|
|
49252
|
+
hideChoicesFromInstructions: boolean,
|
|
49253
|
+
multipleAnswers: boolean,
|
|
49254
|
+
static: boolean
|
|
49255
|
+
}));
|
|
49256
|
+
|
|
49257
|
+
const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
49258
|
+
labels: array(string),
|
|
49259
|
+
left: array(string),
|
|
49260
|
+
right: array(string),
|
|
49261
|
+
orderMatters: boolean,
|
|
49262
|
+
padding: boolean
|
|
49263
|
+
}));
|
|
49264
|
+
|
|
49265
|
+
const parseMatrixWidget = parseWidget(constant("matrix"), object({
|
|
49266
|
+
prefix: string,
|
|
49267
|
+
suffix: string,
|
|
49268
|
+
answers: array(array(number)),
|
|
49269
|
+
cursorPosition: array(number),
|
|
49270
|
+
matrixBoardSize: array(number),
|
|
49271
|
+
static: boolean
|
|
49272
|
+
}));
|
|
49273
|
+
|
|
49274
|
+
const parseMeasurerWidget = parseWidget(constant("measurer"), object({
|
|
49275
|
+
image: parsePerseusImageBackground,
|
|
49276
|
+
showProtractor: boolean,
|
|
49277
|
+
showRuler: boolean,
|
|
49278
|
+
rulerLabel: string,
|
|
49279
|
+
rulerTicks: number,
|
|
49280
|
+
rulerPixels: number,
|
|
49281
|
+
rulerLength: number,
|
|
49282
|
+
box: pair(number, number),
|
|
49283
|
+
static: boolean
|
|
49284
|
+
}));
|
|
49285
|
+
|
|
49286
|
+
const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), object({
|
|
49287
|
+
widgetId: string,
|
|
49288
|
+
rotationAngle: optional(number),
|
|
49289
|
+
smiles: optional(string)
|
|
49290
|
+
}));
|
|
49291
|
+
|
|
49292
|
+
const parseNumberLineWidget = parseWidget(constant("number-line"), object({
|
|
49293
|
+
range: array(number),
|
|
49294
|
+
labelRange: array(nullable(number)),
|
|
49295
|
+
labelStyle: string,
|
|
49296
|
+
labelTicks: boolean,
|
|
49297
|
+
isTickCtrl: optional(nullable(boolean)),
|
|
49298
|
+
divisionRange: array(number),
|
|
49299
|
+
numDivisions: optional(nullable(number)),
|
|
49300
|
+
snapDivisions: number,
|
|
49301
|
+
tickStep: optional(nullable(number)),
|
|
49302
|
+
correctRel: optional(nullable(string)),
|
|
49303
|
+
correctX: number,
|
|
49304
|
+
initialX: optional(nullable(number)),
|
|
49305
|
+
showTooltip: optional(boolean),
|
|
49306
|
+
static: boolean
|
|
49307
|
+
}));
|
|
49308
|
+
|
|
49309
|
+
const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "decimal", "percent", "pi");
|
|
49310
|
+
const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
49311
|
+
answers: array(object({
|
|
49312
|
+
message: string,
|
|
49313
|
+
value: number,
|
|
49314
|
+
status: string,
|
|
49315
|
+
answerForms: optional(array(parseMathFormat)),
|
|
49316
|
+
strict: boolean,
|
|
49317
|
+
maxError: optional(nullable(number)),
|
|
49318
|
+
simplify: optional(nullable(string))
|
|
49319
|
+
})),
|
|
49320
|
+
labelText: string,
|
|
49321
|
+
size: string,
|
|
49322
|
+
coefficient: boolean,
|
|
49323
|
+
rightAlign: optional(boolean),
|
|
49324
|
+
static: boolean,
|
|
49325
|
+
answerForms: optional(array(object({
|
|
49326
|
+
name: parseMathFormat,
|
|
49327
|
+
simplify: optional(nullable(enumeration("required", "correct", "enforced", "optional")))
|
|
49328
|
+
})))
|
|
49329
|
+
}));
|
|
49330
|
+
|
|
49331
|
+
// There is an import cycle between orderer-widget.ts and perseus-renderer.ts.
|
|
49332
|
+
// This wrapper ensures that we don't refer to parsePerseusRenderer before
|
|
49333
|
+
// it's defined.
|
|
49334
|
+
function parseRenderer(rawValue, ctx) {
|
|
49335
|
+
return parsePerseusRenderer(rawValue, ctx);
|
|
49336
|
+
}
|
|
49337
|
+
const parseOrdererWidget = parseWidget(constant("orderer"), object({
|
|
49338
|
+
options: array(parseRenderer),
|
|
49339
|
+
correctOptions: array(parseRenderer),
|
|
49340
|
+
otherOptions: array(parseRenderer),
|
|
49341
|
+
height: string,
|
|
49342
|
+
layout: string
|
|
49343
|
+
}));
|
|
49344
|
+
|
|
49345
|
+
const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
49346
|
+
passageNumber: number,
|
|
49347
|
+
referenceNumber: number,
|
|
49348
|
+
summaryText: string
|
|
49349
|
+
}));
|
|
49350
|
+
|
|
49351
|
+
const parsePassageWidget = parseWidget(constant("passage"), object({
|
|
49352
|
+
footnotes: string,
|
|
49353
|
+
passageText: string,
|
|
49354
|
+
passageTitle: string,
|
|
49355
|
+
showLineNumbers: boolean,
|
|
49356
|
+
static: boolean
|
|
49357
|
+
}));
|
|
49358
|
+
|
|
49359
|
+
const parsePhetSimulationWidget = parseWidget(constant("phet-simulation"), object({
|
|
49360
|
+
url: string,
|
|
49361
|
+
description: string
|
|
49362
|
+
}));
|
|
49363
|
+
|
|
49364
|
+
const parsePlotterWidget = parseWidget(constant("plotter"), object({
|
|
49365
|
+
labels: array(string),
|
|
49366
|
+
categories: array(string),
|
|
49367
|
+
type: enumeration(...plotterPlotTypes),
|
|
49368
|
+
maxY: number,
|
|
49369
|
+
scaleY: number,
|
|
49370
|
+
labelInterval: optional(nullable(number)),
|
|
49371
|
+
snapsPerLine: number,
|
|
49372
|
+
starting: array(number),
|
|
49373
|
+
correct: array(number),
|
|
49374
|
+
picUrl: optional(nullable(string)),
|
|
49375
|
+
picSize: optional(nullable(number)),
|
|
49376
|
+
picBoxHeight: optional(nullable(number)),
|
|
49377
|
+
plotDimensions: array(number)
|
|
49378
|
+
}));
|
|
49379
|
+
|
|
49380
|
+
const parsePythonProgramWidget = parseWidget(constant("python-program"), object({
|
|
49381
|
+
programID: string,
|
|
49382
|
+
height: number
|
|
49383
|
+
}));
|
|
49384
|
+
|
|
49385
|
+
const parseRadioWidget = parseWidget(constant("radio"), object({
|
|
49386
|
+
choices: array(object({
|
|
49387
|
+
content: string,
|
|
49388
|
+
clue: optional(string),
|
|
49389
|
+
correct: optional(boolean),
|
|
49390
|
+
isNoneOfTheAbove: optional(boolean),
|
|
49391
|
+
// deprecated
|
|
49392
|
+
// There is an import cycle between radio-widget.ts and
|
|
49393
|
+
// widgets-map.ts. The anonymous function below ensures that we
|
|
49394
|
+
// don't refer to parseWidgetsMap before it's defined.
|
|
49395
|
+
widgets: optional((rawVal, ctx) => parseWidgetsMap(rawVal, ctx))
|
|
49396
|
+
})),
|
|
49397
|
+
hasNoneOfTheAbove: optional(boolean),
|
|
49398
|
+
countChoices: optional(boolean),
|
|
49399
|
+
randomize: optional(boolean),
|
|
49400
|
+
multipleSelect: optional(boolean),
|
|
49401
|
+
deselectEnabled: optional(boolean),
|
|
49402
|
+
// deprecated
|
|
49403
|
+
onePerLine: optional(boolean),
|
|
49404
|
+
// deprecated
|
|
49405
|
+
displayCount: optional(any),
|
|
49406
|
+
// v0 props
|
|
49407
|
+
// `noneOfTheAbove` is still in use (but only set to `false`).
|
|
49408
|
+
noneOfTheAbove: constant(false)
|
|
49409
|
+
}));
|
|
49410
|
+
|
|
49411
|
+
const parseSorterWidget = parseWidget(constant("sorter"), object({
|
|
49412
|
+
correct: array(string),
|
|
49413
|
+
padding: boolean,
|
|
49414
|
+
layout: enumeration("horizontal", "vertical")
|
|
49415
|
+
}));
|
|
49416
|
+
|
|
49417
|
+
const parseTableWidget = parseWidget(constant("table"), object({
|
|
49418
|
+
headers: array(string),
|
|
49419
|
+
rows: number,
|
|
49420
|
+
columns: number,
|
|
49421
|
+
answers: array(array(string))
|
|
49422
|
+
}));
|
|
49423
|
+
|
|
49424
|
+
const parseVideoWidget = parseWidget(constant("video"), object({
|
|
49425
|
+
location: string,
|
|
49426
|
+
static: optional(boolean)
|
|
49427
|
+
}));
|
|
49428
|
+
|
|
48863
49429
|
const parseWidgetsMap = (rawValue, ctx) => {
|
|
48864
49430
|
if (!isObject(rawValue)) {
|
|
48865
49431
|
return ctx.failure("PerseusWidgetsMap", rawValue);
|
|
@@ -48905,86 +49471,67 @@ const parseWidgetsMapEntry = (_ref, widgetMap, ctx) => {
|
|
|
48905
49471
|
case "expression":
|
|
48906
49472
|
return parseAndAssign(`expression ${id}`, parseExpressionWidget);
|
|
48907
49473
|
case "grapher":
|
|
48908
|
-
|
|
48909
|
-
return parseAndAssign(`grapher ${id}`, any);
|
|
49474
|
+
return parseAndAssign(`grapher ${id}`, parseGrapherWidget);
|
|
48910
49475
|
case "group":
|
|
48911
|
-
|
|
48912
|
-
return parseAndAssign(`group ${id}`, any);
|
|
49476
|
+
return parseAndAssign(`group ${id}`, parseGroupWidget);
|
|
48913
49477
|
case "graded-group":
|
|
48914
|
-
|
|
48915
|
-
return parseAndAssign(`graded-group ${id}`, any);
|
|
49478
|
+
return parseAndAssign(`graded-group ${id}`, parseGradedGroupWidget);
|
|
48916
49479
|
case "graded-group-set":
|
|
48917
|
-
|
|
48918
|
-
return parseAndAssign(`graded-group-set ${id}`, any);
|
|
49480
|
+
return parseAndAssign(`graded-group-set ${id}`, parseGradedGroupSetWidget);
|
|
48919
49481
|
case "iframe":
|
|
48920
|
-
|
|
48921
|
-
return parseAndAssign(`iframe ${id}`, any);
|
|
49482
|
+
return parseAndAssign(`iframe ${id}`, parseIframeWidget);
|
|
48922
49483
|
case "image":
|
|
48923
|
-
|
|
48924
|
-
return parseAndAssign(`image ${id}`, any);
|
|
49484
|
+
return parseAndAssign(`image ${id}`, parseImageWidget);
|
|
48925
49485
|
case "input-number":
|
|
48926
|
-
|
|
48927
|
-
return parseAndAssign(`input-number ${id}`, any);
|
|
49486
|
+
return parseAndAssign(`input-number ${id}`, parseInputNumberWidget);
|
|
48928
49487
|
case "interaction":
|
|
48929
|
-
|
|
48930
|
-
return parseAndAssign(`interaction ${id}`, any);
|
|
49488
|
+
return parseAndAssign(`interaction ${id}`, parseInteractionWidget);
|
|
48931
49489
|
case "interactive-graph":
|
|
48932
49490
|
return parseAndAssign(`interactive-graph ${id}`, parseInteractiveGraphWidget);
|
|
48933
49491
|
case "label-image":
|
|
48934
|
-
|
|
48935
|
-
return parseAndAssign(`label-image ${id}`, any);
|
|
49492
|
+
return parseAndAssign(`label-image ${id}`, parseLabelImageWidget);
|
|
48936
49493
|
case "matcher":
|
|
48937
|
-
|
|
48938
|
-
return parseAndAssign(`matcher ${id}`, any);
|
|
49494
|
+
return parseAndAssign(`matcher ${id}`, parseMatcherWidget);
|
|
48939
49495
|
case "matrix":
|
|
48940
|
-
|
|
48941
|
-
return parseAndAssign(`matrix ${id}`, any);
|
|
49496
|
+
return parseAndAssign(`matrix ${id}`, parseMatrixWidget);
|
|
48942
49497
|
case "measurer":
|
|
48943
|
-
|
|
48944
|
-
return parseAndAssign(`measurer ${id}`, any);
|
|
49498
|
+
return parseAndAssign(`measurer ${id}`, parseMeasurerWidget);
|
|
48945
49499
|
case "molecule-renderer":
|
|
48946
|
-
|
|
48947
|
-
return parseAndAssign(`molecule-renderer ${id}`, any);
|
|
49500
|
+
return parseAndAssign(`molecule-renderer ${id}`, parseMoleculeRendererWidget);
|
|
48948
49501
|
case "number-line":
|
|
48949
|
-
|
|
48950
|
-
return parseAndAssign(`number-line ${id}`, any);
|
|
49502
|
+
return parseAndAssign(`number-line ${id}`, parseNumberLineWidget);
|
|
48951
49503
|
case "numeric-input":
|
|
48952
|
-
|
|
48953
|
-
return parseAndAssign(`numeric-input ${id}`, any);
|
|
49504
|
+
return parseAndAssign(`numeric-input ${id}`, parseNumericInputWidget);
|
|
48954
49505
|
case "orderer":
|
|
48955
|
-
|
|
48956
|
-
return parseAndAssign(`orderer ${id}`, any);
|
|
49506
|
+
return parseAndAssign(`orderer ${id}`, parseOrdererWidget);
|
|
48957
49507
|
case "passage":
|
|
48958
|
-
|
|
48959
|
-
return parseAndAssign(`passage ${id}`, any);
|
|
49508
|
+
return parseAndAssign(`passage ${id}`, parsePassageWidget);
|
|
48960
49509
|
case "passage-ref":
|
|
48961
|
-
|
|
48962
|
-
return parseAndAssign(`passage-ref ${id}`, any);
|
|
49510
|
+
return parseAndAssign(`passage-ref ${id}`, parsePassageRefWidget);
|
|
48963
49511
|
case "passage-ref-target":
|
|
48964
|
-
//
|
|
49512
|
+
// NOTE(benchristel): as of 2024-11-12, passage-ref-target is only
|
|
49513
|
+
// used in test content. See:
|
|
49514
|
+
// https://www.khanacademy.org/devadmin/content/search?query=widget:passage-ref-target
|
|
48965
49515
|
return parseAndAssign(`passage-ref-target ${id}`, any);
|
|
48966
49516
|
case "phet-simulation":
|
|
48967
|
-
|
|
48968
|
-
return parseAndAssign(`phet-simulation ${id}`, any);
|
|
49517
|
+
return parseAndAssign(`phet-simulation ${id}`, parsePhetSimulationWidget);
|
|
48969
49518
|
case "plotter":
|
|
48970
|
-
|
|
48971
|
-
return parseAndAssign(`plotter ${id}`, any);
|
|
49519
|
+
return parseAndAssign(`plotter ${id}`, parsePlotterWidget);
|
|
48972
49520
|
case "python-program":
|
|
48973
|
-
|
|
48974
|
-
return parseAndAssign(`python-program ${id}`, any);
|
|
49521
|
+
return parseAndAssign(`python-program ${id}`, parsePythonProgramWidget);
|
|
48975
49522
|
case "radio":
|
|
48976
|
-
|
|
48977
|
-
return parseAndAssign(`radio ${id}`, any);
|
|
49523
|
+
return parseAndAssign(`radio ${id}`, parseRadioWidget);
|
|
48978
49524
|
case "sorter":
|
|
48979
|
-
|
|
48980
|
-
return parseAndAssign(`sorter ${id}`, any);
|
|
49525
|
+
return parseAndAssign(`sorter ${id}`, parseSorterWidget);
|
|
48981
49526
|
case "table":
|
|
48982
|
-
|
|
48983
|
-
return parseAndAssign(`table ${id}`, any);
|
|
49527
|
+
return parseAndAssign(`table ${id}`, parseTableWidget);
|
|
48984
49528
|
case "video":
|
|
48985
|
-
|
|
48986
|
-
return parseAndAssign(`video ${id}`, any);
|
|
49529
|
+
return parseAndAssign(`video ${id}`, parseVideoWidget);
|
|
48987
49530
|
default:
|
|
49531
|
+
if (getWidget(type)) {
|
|
49532
|
+
// @ts-expect-error - 'type' is not a valid widget type
|
|
49533
|
+
return parseAndAssign(`${type} ${id}`, any);
|
|
49534
|
+
}
|
|
48988
49535
|
return ctx.failure("a valid widget type", type);
|
|
48989
49536
|
}
|
|
48990
49537
|
};
|
|
@@ -49007,16 +49554,6 @@ const parseHint = object({
|
|
|
49007
49554
|
}))
|
|
49008
49555
|
});
|
|
49009
49556
|
|
|
49010
|
-
const parsePerseusRenderer = object({
|
|
49011
|
-
content: string,
|
|
49012
|
-
widgets: parseWidgetsMap,
|
|
49013
|
-
metadata: optional(array(string)),
|
|
49014
|
-
images: record(string, object({
|
|
49015
|
-
width: number,
|
|
49016
|
-
height: number
|
|
49017
|
-
}))
|
|
49018
|
-
});
|
|
49019
|
-
|
|
49020
49557
|
object({
|
|
49021
49558
|
question: parsePerseusRenderer,
|
|
49022
49559
|
hints: array(parseHint),
|
|
@@ -49357,6 +49894,7 @@ exports.parsePerseusItem = parsePerseusItem;
|
|
|
49357
49894
|
exports.plotterPlotTypes = plotterPlotTypes;
|
|
49358
49895
|
exports.preprocessTex = preprocessTex;
|
|
49359
49896
|
exports.registerAllWidgetsForTesting = registerAllWidgetsForTesting;
|
|
49897
|
+
exports.scorePerseusItem = scorePerseusItem;
|
|
49360
49898
|
exports.shapes = shapes;
|
|
49361
49899
|
exports.shouldHaveIndividualAnswer = shouldHaveIndividualAnswer;
|
|
49362
49900
|
exports.traverse = traverse;
|