@khanacademy/perseus 42.0.0 → 42.0.2
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.js +215 -4
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +223 -3
- package/dist/index.js.map +1 -1
- package/dist/util/deprecated-widgets/input-number.d.ts +13 -0
- package/dist/util/deprecated-widgets/modernize-widgets-utils.d.ts +12 -0
- package/dist/widgets/expression/expression.d.ts +2 -2
- package/package.json +1 -1
package/dist/es/index.js
CHANGED
|
@@ -18674,6 +18674,203 @@ class QuestionParagraph extends React.Component {
|
|
|
18674
18674
|
}
|
|
18675
18675
|
}
|
|
18676
18676
|
|
|
18677
|
+
// Methods to convert input-number widgets to numeric-input widgets
|
|
18678
|
+
|
|
18679
|
+
const inputNumberToNumericInput = json => {
|
|
18680
|
+
// First we need to create a map of the old input-number keys to the new numeric-input keys
|
|
18681
|
+
// so that we can ensure we update the content and widgets accordingly
|
|
18682
|
+
const renameMap = getInputNumberRenameMap(json);
|
|
18683
|
+
|
|
18684
|
+
// Then we can use this to update the JSON
|
|
18685
|
+
return convertInputNumberJson(json, renameMap);
|
|
18686
|
+
};
|
|
18687
|
+
|
|
18688
|
+
// We need to be able to run this code recursively in order to convert nested input-number widgets
|
|
18689
|
+
const convertInputNumberJson = (json, renameMap) => {
|
|
18690
|
+
const updatedContent = convertDeprecatedWidgetsInContent(json, renameMap);
|
|
18691
|
+
const updatedWidgets = convertInputNumberWidgetOptions(json, renameMap);
|
|
18692
|
+
const modernizedJson = _extends({}, json, {
|
|
18693
|
+
content: updatedContent,
|
|
18694
|
+
widgets: updatedWidgets
|
|
18695
|
+
});
|
|
18696
|
+
return modernizedJson;
|
|
18697
|
+
};
|
|
18698
|
+
|
|
18699
|
+
// Convert the input-number json in the widgets of a PerseusRenderer object
|
|
18700
|
+
const convertInputNumberWidgetOptions = (json, renameMap) => {
|
|
18701
|
+
const widgets = _extends({}, json.widgets);
|
|
18702
|
+
// The question.widgets is a dictionary map of widgets, so we need to loop through in order to convert input-number to numeric-input
|
|
18703
|
+
// which can exist as both the key or as a value on widget.type.
|
|
18704
|
+
for (const key of Object.keys(widgets)) {
|
|
18705
|
+
// Loop through the keys of the widgets dictionary
|
|
18706
|
+
if (widgets[key].options.widgets) {
|
|
18707
|
+
widgets[key].options = _extends({}, inputNumberToNumericInput(widgets[key].options));
|
|
18708
|
+
}
|
|
18709
|
+
// Check if the widget is an input-number
|
|
18710
|
+
if (widgets[key].type === "input-number") {
|
|
18711
|
+
// If the answerType is not number or percent, we need to provide
|
|
18712
|
+
// the answer form for the numeric-input widget
|
|
18713
|
+
const provideAnswerForm = widgets[key].options.answerType !== "number" && widgets[key].options.answerType !== "percent";
|
|
18714
|
+
// We need to determine the mathFormat for the numeric-input widget
|
|
18715
|
+
const mathFormat = widgets[key].options.answerType === "rational" ? "proper" // input-number uses "rational" for proper fractions
|
|
18716
|
+
: widgets[key].options.answerType; // Otherwise, we can use the answerType directly
|
|
18717
|
+
|
|
18718
|
+
// We need to update the answers prop to match the numeric-input widget format
|
|
18719
|
+
const answers = [{
|
|
18720
|
+
value: widgets[key].options.value,
|
|
18721
|
+
simplify: widgets[key].options.simplify,
|
|
18722
|
+
// Input Number doesn't have a strict prop, so we default to false
|
|
18723
|
+
strict: false,
|
|
18724
|
+
// We only want to set maxError if the inexact prop is true
|
|
18725
|
+
maxError: widgets[key].options.inexact ? widgets[key].options.maxError : 0,
|
|
18726
|
+
status: "correct",
|
|
18727
|
+
// Input-number only allows correct answers
|
|
18728
|
+
message: ""
|
|
18729
|
+
}];
|
|
18730
|
+
|
|
18731
|
+
// Add the required answerForms if provided/applicable
|
|
18732
|
+
if (provideAnswerForm) {
|
|
18733
|
+
answers[0].answerForms = [mathFormat];
|
|
18734
|
+
}
|
|
18735
|
+
|
|
18736
|
+
// Update the options prop to match the numeric-input widget format
|
|
18737
|
+
const upgradedWidgetOptions = {
|
|
18738
|
+
answers,
|
|
18739
|
+
size: widgets[key].options.size,
|
|
18740
|
+
coefficient: false,
|
|
18741
|
+
// input-number doesn't have a coefficient prop
|
|
18742
|
+
labelText: "",
|
|
18743
|
+
// input-number doesn't have a labelText prop
|
|
18744
|
+
static: false,
|
|
18745
|
+
// static is always false for numeric-input
|
|
18746
|
+
rightAlign: widgets[key].options.rightAlign || false
|
|
18747
|
+
};
|
|
18748
|
+
const upgradedWidget = {
|
|
18749
|
+
options: upgradedWidgetOptions,
|
|
18750
|
+
type: "numeric-input"
|
|
18751
|
+
};
|
|
18752
|
+
const newWidgetName = renameMap[key];
|
|
18753
|
+
// Create the new key entry
|
|
18754
|
+
widgets[newWidgetName] = upgradedWidget;
|
|
18755
|
+
|
|
18756
|
+
// We need to delete the old widget key
|
|
18757
|
+
delete widgets[key];
|
|
18758
|
+
}
|
|
18759
|
+
}
|
|
18760
|
+
return widgets;
|
|
18761
|
+
};
|
|
18762
|
+
|
|
18763
|
+
// Convert the deprecated widget refs in the content string
|
|
18764
|
+
// of a PerseusRenderer object to their renamed equivalents
|
|
18765
|
+
const convertDeprecatedWidgetsInContent = (json, renameMap) => {
|
|
18766
|
+
return Object.keys(renameMap).reduce((newContent, oldKey) => {
|
|
18767
|
+
const newKey = renameMap[oldKey];
|
|
18768
|
+
return newKey ? newContent.replace(oldKey, newKey) : newContent;
|
|
18769
|
+
}, json.content);
|
|
18770
|
+
};
|
|
18771
|
+
|
|
18772
|
+
// Create a map of the old input-number keys to the new numeric-input keys
|
|
18773
|
+
const getInputNumberRenameMap = json => {
|
|
18774
|
+
const numericRegex = /(?<=\[\[\u2603 )(numeric-input \d+)(?=\]\])/g;
|
|
18775
|
+
const inputNumberRegex = /(?<=\[\[\u2603 )(input-number \d+)(?=\]\])/g;
|
|
18776
|
+
|
|
18777
|
+
// Get all the content strings within the json, which might be nested within widgets
|
|
18778
|
+
const allContentStrings = json.content;
|
|
18779
|
+
|
|
18780
|
+
// Loop through the content strings to get all the input-number widgets
|
|
18781
|
+
const renameMap = {};
|
|
18782
|
+
const inputNumberMatches = [...(allContentStrings.match(inputNumberRegex) || [])];
|
|
18783
|
+
|
|
18784
|
+
// We want to count any pre-existing numeric-input widgets
|
|
18785
|
+
// so that we can start the new ids at the next number
|
|
18786
|
+
const numericMatches = [...(allContentStrings.match(numericRegex) || [])];
|
|
18787
|
+
let currentNumericCount = numericMatches.reduce((count, match) => {
|
|
18788
|
+
const id = parseInt(match.split(" ")[1], 10);
|
|
18789
|
+
return id >= count ? id + 1 : count; // We want to start at the next highest number
|
|
18790
|
+
}, 1);
|
|
18791
|
+
|
|
18792
|
+
// Now that we have all the necessary information, we can create the renameMap
|
|
18793
|
+
for (const match of inputNumberMatches) {
|
|
18794
|
+
const oldKey = match;
|
|
18795
|
+
const newKey = `numeric-input ${currentNumericCount}`;
|
|
18796
|
+
renameMap[oldKey] = newKey;
|
|
18797
|
+
currentNumericCount++;
|
|
18798
|
+
}
|
|
18799
|
+
return renameMap;
|
|
18800
|
+
};
|
|
18801
|
+
|
|
18802
|
+
// Convert the user input data keys from input-number to numeric-input
|
|
18803
|
+
const convertUserInputNumberData = (userInputMap, renameMap) => {
|
|
18804
|
+
const updatedUserInputMap = _extends({}, userInputMap);
|
|
18805
|
+
for (const key of Object.keys(userInputMap)) {
|
|
18806
|
+
if (key.includes("input-number")) {
|
|
18807
|
+
const updatedKey = renameMap[key];
|
|
18808
|
+
updatedUserInputMap[updatedKey] = userInputMap[key];
|
|
18809
|
+
delete updatedUserInputMap[key];
|
|
18810
|
+
}
|
|
18811
|
+
}
|
|
18812
|
+
return updatedUserInputMap;
|
|
18813
|
+
};
|
|
18814
|
+
|
|
18815
|
+
// Used to convert InputNumber widgets to NumericInput widgets for scoring
|
|
18816
|
+
const convertInputNumberForScoring = (rubric, userInputMap) => {
|
|
18817
|
+
// First we need to create a map of the old input-number keys to the new numeric-input keys
|
|
18818
|
+
// so that we can ensure we update the content, widgets, AND userInput accordingly
|
|
18819
|
+
const renameMap = getInputNumberRenameMap(rubric);
|
|
18820
|
+
const convertedRubric = convertInputNumberJson(rubric, renameMap);
|
|
18821
|
+
const convertedUserData = convertUserInputNumberData(userInputMap, renameMap);
|
|
18822
|
+
return {
|
|
18823
|
+
convertedRubric,
|
|
18824
|
+
convertedUserData
|
|
18825
|
+
};
|
|
18826
|
+
};
|
|
18827
|
+
|
|
18828
|
+
const widgetRegExes = [/input-number \d+/]; // We can add more regexes here in the future
|
|
18829
|
+
|
|
18830
|
+
// This utils in this file are used to modernize our Perseus JSON structure,
|
|
18831
|
+
// so that we can convert deprecated widgets to their modern equivalents when
|
|
18832
|
+
// content creators use the Editor Page to update content containing these widgets.
|
|
18833
|
+
// Currently, we're only converting input-number to numeric-input,
|
|
18834
|
+
// but we can add more conversions here in the future.
|
|
18835
|
+
|
|
18836
|
+
// Modernize the json content of a PerseusRenderer object
|
|
18837
|
+
// by converting deprecated widgets to their modern equivalents
|
|
18838
|
+
const convertDeprecatedWidgets = json => {
|
|
18839
|
+
// Currently we're only converting input-number to numeric-input,
|
|
18840
|
+
// But we can add more conversions here in the future
|
|
18841
|
+
return inputNumberToNumericInput(json);
|
|
18842
|
+
};
|
|
18843
|
+
const conversionRequired = json => {
|
|
18844
|
+
// If there's no content, then there's no conversion required
|
|
18845
|
+
if (!json.content) {
|
|
18846
|
+
return false;
|
|
18847
|
+
}
|
|
18848
|
+
// Check the content string for any top-level input-number widgets
|
|
18849
|
+
if (widgetRegExes.some(regex => regex.test(json.content))) {
|
|
18850
|
+
return true;
|
|
18851
|
+
}
|
|
18852
|
+
|
|
18853
|
+
// If there's no deprecated widget in the top-level, then check for any nested widgets
|
|
18854
|
+
for (const key of Object.keys(json.widgets)) {
|
|
18855
|
+
if (json.widgets[key].options.widgets) {
|
|
18856
|
+
const nestedJson = json.widgets[key].options;
|
|
18857
|
+
if (conversionRequired(nestedJson)) {
|
|
18858
|
+
return true;
|
|
18859
|
+
}
|
|
18860
|
+
}
|
|
18861
|
+
}
|
|
18862
|
+
return false;
|
|
18863
|
+
};
|
|
18864
|
+
|
|
18865
|
+
/**
|
|
18866
|
+
* Convert the deprecated widgets in the rubric and user data to their
|
|
18867
|
+
* modern equivalents for scoring. These need to be updated together
|
|
18868
|
+
* in order to ensure that the widgetKeys match between the rubric and user data.
|
|
18869
|
+
*/
|
|
18870
|
+
const convertDeprecatedWidgetsForScoring = (rubric, userInputMap) => {
|
|
18871
|
+
return convertInputNumberForScoring(rubric, userInputMap);
|
|
18872
|
+
};
|
|
18873
|
+
|
|
18677
18874
|
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
18678
18875
|
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
18679
18876
|
|
|
@@ -19276,11 +19473,25 @@ function scorePerseusItem(perseusRenderData, userInputMap,
|
|
|
19276
19473
|
// TODO(LEMS-2461,LEMS-2391): these probably
|
|
19277
19474
|
// need to be removed before we move this to the server
|
|
19278
19475
|
strings, locale) {
|
|
19476
|
+
let convertedRenderData = perseusRenderData;
|
|
19477
|
+
let convertedUserInputMap = userInputMap;
|
|
19478
|
+
|
|
19479
|
+
// Check if the PerseusRenderer object contains any deprecated widgets that need to be converted
|
|
19480
|
+
const mustConvertData = conversionRequired(perseusRenderData);
|
|
19481
|
+
if (mustConvertData) {
|
|
19482
|
+
const {
|
|
19483
|
+
convertedRubric,
|
|
19484
|
+
convertedUserData
|
|
19485
|
+
} = convertDeprecatedWidgetsForScoring(perseusRenderData, userInputMap);
|
|
19486
|
+
convertedRenderData = convertedRubric;
|
|
19487
|
+
convertedUserInputMap = convertedUserData;
|
|
19488
|
+
}
|
|
19489
|
+
|
|
19279
19490
|
// There seems to be a chance that PerseusRenderer.widgets might include
|
|
19280
19491
|
// widget data for widgets that are not in PerseusRenderer.content,
|
|
19281
19492
|
// so this checks that the widgets are being used before scoring them
|
|
19282
|
-
const usedWidgetIds = getWidgetIdsFromContent(
|
|
19283
|
-
const scores = scoreWidgetsFunctional(
|
|
19493
|
+
const usedWidgetIds = getWidgetIdsFromContent(convertedRenderData.content);
|
|
19494
|
+
const scores = scoreWidgetsFunctional(convertedRenderData.widgets, usedWidgetIds, convertedUserInputMap, strings, locale);
|
|
19284
19495
|
return Util.flattenScores(scores);
|
|
19285
19496
|
}
|
|
19286
19497
|
function scoreWidgetsFunctional(widgets,
|
|
@@ -44842,7 +45053,7 @@ const init = function init() {
|
|
|
44842
45053
|
|
|
44843
45054
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44844
45055
|
const libName = "@khanacademy/perseus";
|
|
44845
|
-
const libVersion = "42.0.
|
|
45056
|
+
const libVersion = "42.0.2";
|
|
44846
45057
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44847
45058
|
|
|
44848
45059
|
const apiVersion = {
|
|
@@ -49412,5 +49623,5 @@ const GrapherUtil = {
|
|
|
49412
49623
|
typeToButton
|
|
49413
49624
|
};
|
|
49414
49625
|
|
|
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 };
|
|
49626
|
+
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, conversionRequired, convertDeprecatedWidgets, 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 };
|
|
49416
49627
|
//# sourceMappingURL=index.js.map
|