@khanacademy/perseus 41.1.0 → 41.2.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.js +90 -111
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +91 -110
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +1 -8
- package/dist/perseus-types.d.ts +48 -0
- package/dist/util/is-real-json-parse.d.ts +1 -0
- package/dist/validation.types.d.ts +2 -2
- package/dist/widgets/interactive-graphs/utils.d.ts +6 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -84,7 +84,6 @@ export { default as WIDGET_PROP_DENYLIST } from "./mixins/widget-prop-denylist";
|
|
|
84
84
|
/**
|
|
85
85
|
* Types
|
|
86
86
|
*/
|
|
87
|
-
export type { PerseusOptions } from "./init";
|
|
88
87
|
export type { ILogger, LogErrorOptions } from "./logging/log";
|
|
89
88
|
export type { ServerItemRenderer as ServerItemRendererComponent } from "./server-item-renderer";
|
|
90
89
|
export type { Alignment, APIOptions, APIOptionsWithDefaults, ChangeHandler, DeviceType, DomInsertCheckFn, EditorMode, FocusPath, ImageDict, ImageUploader, JiptLabelStore, JiptRenderer, PerseusDependencies, PerseusDependenciesV2, PerseusScore, Version, VideoData, VideoKind, WidgetExports, SharedRendererProps, } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -31906,6 +31906,64 @@ const lockedFigureFillStyles = {
|
|
|
31906
31906
|
|
|
31907
31907
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
31908
31908
|
|
|
31909
|
+
/**
|
|
31910
|
+
* 44 is touch best practice and AAA compliant for WCAG
|
|
31911
|
+
* https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
|
|
31912
|
+
*/
|
|
31913
|
+
const TARGET_SIZE = 44;
|
|
31914
|
+
const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
|
|
31915
|
+
|
|
31916
|
+
// same as pointsFromNormalized in interactive-graph.tsx
|
|
31917
|
+
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
31918
|
+
// maps over [x, y]
|
|
31919
|
+
coords.map((coord, i) => {
|
|
31920
|
+
const axisRange = range[i];
|
|
31921
|
+
if (noSnap) {
|
|
31922
|
+
return axisRange[MIN] + size(axisRange) * coord;
|
|
31923
|
+
}
|
|
31924
|
+
const axisStep = step[i];
|
|
31925
|
+
const nSteps = Math.floor(size(axisRange) / axisStep);
|
|
31926
|
+
const tick = Math.round(coord * nSteps);
|
|
31927
|
+
return axisRange[MIN] + axisStep * tick;
|
|
31928
|
+
}));
|
|
31929
|
+
|
|
31930
|
+
// same as normalizeCoords in interactive-graph.tsx
|
|
31931
|
+
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
31932
|
+
return (coord + ranges[i][1]) / size(ranges[i]);
|
|
31933
|
+
}));
|
|
31934
|
+
|
|
31935
|
+
// Returns the closest point to the given `point` that is within the graph
|
|
31936
|
+
// bounds given in `state`.
|
|
31937
|
+
function bound$1(_ref) {
|
|
31938
|
+
let {
|
|
31939
|
+
snapStep,
|
|
31940
|
+
range,
|
|
31941
|
+
point
|
|
31942
|
+
} = _ref;
|
|
31943
|
+
const boundingBox = inset(snapStep, range);
|
|
31944
|
+
return clampToBox(boundingBox, point);
|
|
31945
|
+
}
|
|
31946
|
+
function isUnlimitedGraphState(state) {
|
|
31947
|
+
return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
|
|
31948
|
+
}
|
|
31949
|
+
|
|
31950
|
+
/**
|
|
31951
|
+
* Replace all text outside of the $ TeX blocks with `\\text{...}`
|
|
31952
|
+
* This way, the entire resulting string can be rendered within <TeX>
|
|
31953
|
+
* and the text outside of the $ blocks will be non-TeX text.
|
|
31954
|
+
*/
|
|
31955
|
+
function replaceOutsideTeX(mathString) {
|
|
31956
|
+
let currentlyTeX = mathString[0] === "$";
|
|
31957
|
+
let result = "";
|
|
31958
|
+
const splitString = mathString.split("$").filter(part => part !== "");
|
|
31959
|
+
for (let i = 0; i < splitString.length; i++) {
|
|
31960
|
+
const part = splitString[i];
|
|
31961
|
+
result += currentlyTeX ? part : `\\text{${part}}`;
|
|
31962
|
+
currentlyTeX = !currentlyTeX;
|
|
31963
|
+
}
|
|
31964
|
+
return result;
|
|
31965
|
+
}
|
|
31966
|
+
|
|
31909
31967
|
function LockedLabel(props) {
|
|
31910
31968
|
const {
|
|
31911
31969
|
coord,
|
|
@@ -31918,7 +31976,7 @@ function LockedLabel(props) {
|
|
|
31918
31976
|
TeX
|
|
31919
31977
|
} = getDependencies();
|
|
31920
31978
|
|
|
31921
|
-
//
|
|
31979
|
+
// Note: The TeX component cannot be rendered within an SVG
|
|
31922
31980
|
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
31923
31981
|
className: "locked-label",
|
|
31924
31982
|
style: {
|
|
@@ -31930,7 +31988,7 @@ function LockedLabel(props) {
|
|
|
31930
31988
|
backgroundColor: "rgba(255, 255, 255, 0.8)"
|
|
31931
31989
|
},
|
|
31932
31990
|
"aria-hidden": true
|
|
31933
|
-
}, /*#__PURE__*/React__namespace.createElement(TeX, null, text));
|
|
31991
|
+
}, /*#__PURE__*/React__namespace.createElement(TeX, null, replaceOutsideTeX(text)));
|
|
31934
31992
|
}
|
|
31935
31993
|
|
|
31936
31994
|
function GraphLockedLabelsLayer(props) {
|
|
@@ -32850,47 +32908,6 @@ function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, a
|
|
|
32850
32908
|
return scaledBisector;
|
|
32851
32909
|
}
|
|
32852
32910
|
|
|
32853
|
-
/**
|
|
32854
|
-
* 44 is touch best practice and AAA compliant for WCAG
|
|
32855
|
-
* https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
|
|
32856
|
-
*/
|
|
32857
|
-
const TARGET_SIZE = 44;
|
|
32858
|
-
const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
|
|
32859
|
-
|
|
32860
|
-
// same as pointsFromNormalized in interactive-graph.tsx
|
|
32861
|
-
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
32862
|
-
// maps over [x, y]
|
|
32863
|
-
coords.map((coord, i) => {
|
|
32864
|
-
const axisRange = range[i];
|
|
32865
|
-
if (noSnap) {
|
|
32866
|
-
return axisRange[MIN] + size(axisRange) * coord;
|
|
32867
|
-
}
|
|
32868
|
-
const axisStep = step[i];
|
|
32869
|
-
const nSteps = Math.floor(size(axisRange) / axisStep);
|
|
32870
|
-
const tick = Math.round(coord * nSteps);
|
|
32871
|
-
return axisRange[MIN] + axisStep * tick;
|
|
32872
|
-
}));
|
|
32873
|
-
|
|
32874
|
-
// same as normalizeCoords in interactive-graph.tsx
|
|
32875
|
-
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
32876
|
-
return (coord + ranges[i][1]) / size(ranges[i]);
|
|
32877
|
-
}));
|
|
32878
|
-
|
|
32879
|
-
// Returns the closest point to the given `point` that is within the graph
|
|
32880
|
-
// bounds given in `state`.
|
|
32881
|
-
function bound$1(_ref) {
|
|
32882
|
-
let {
|
|
32883
|
-
snapStep,
|
|
32884
|
-
range,
|
|
32885
|
-
point
|
|
32886
|
-
} = _ref;
|
|
32887
|
-
const boundingBox = inset(snapStep, range);
|
|
32888
|
-
return clampToBox(boundingBox, point);
|
|
32889
|
-
}
|
|
32890
|
-
function isUnlimitedGraphState(state) {
|
|
32891
|
-
return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
|
|
32892
|
-
}
|
|
32893
|
-
|
|
32894
32911
|
var isProduction = process.env.NODE_ENV === 'production';
|
|
32895
32912
|
var prefix = 'Invariant failed';
|
|
32896
32913
|
function invariant(condition, message) {
|
|
@@ -36307,11 +36324,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36307
36324
|
|
|
36308
36325
|
// If both of the guess points are on the correct line, it's
|
|
36309
36326
|
// correct.
|
|
36310
|
-
if (
|
|
36311
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
36312
|
-
collinear(correct[0], correct[1], guess[0]) &&
|
|
36313
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
36314
|
-
collinear(correct[0], correct[1], guess[1])) {
|
|
36327
|
+
if (collinear(correct[0], correct[1], guess[0]) && collinear(correct[0], correct[1], guess[1])) {
|
|
36315
36328
|
return {
|
|
36316
36329
|
type: "points",
|
|
36317
36330
|
earned: 1,
|
|
@@ -36333,9 +36346,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36333
36346
|
} else if (userInput.type === "quadratic" && rubric.correct.type === "quadratic" && userInput.coords != null) {
|
|
36334
36347
|
// If the parabola coefficients match, it's correct.
|
|
36335
36348
|
const guessCoeffs = getQuadraticCoefficients(userInput.coords);
|
|
36336
|
-
const correctCoeffs = getQuadraticCoefficients(
|
|
36337
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
36338
|
-
rubric.correct.coords);
|
|
36349
|
+
const correctCoeffs = getQuadraticCoefficients(rubric.correct.coords);
|
|
36339
36350
|
if (deepEq$2(guessCoeffs, correctCoeffs)) {
|
|
36340
36351
|
return {
|
|
36341
36352
|
type: "points",
|
|
@@ -36346,9 +36357,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36346
36357
|
}
|
|
36347
36358
|
} else if (userInput.type === "sinusoid" && rubric.correct.type === "sinusoid" && userInput.coords != null) {
|
|
36348
36359
|
const guessCoeffs = getSinusoidCoefficients(userInput.coords);
|
|
36349
|
-
const correctCoeffs = getSinusoidCoefficients(
|
|
36350
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
36351
|
-
rubric.correct.coords);
|
|
36360
|
+
const correctCoeffs = getSinusoidCoefficients(rubric.correct.coords);
|
|
36352
36361
|
const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
|
|
36353
36362
|
const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
|
|
36354
36363
|
// If the canonical coefficients match, it's correct.
|
|
@@ -36392,9 +36401,8 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36392
36401
|
};
|
|
36393
36402
|
}
|
|
36394
36403
|
} else if (userInput.type === "polygon" && rubric.correct.type === "polygon" && userInput.coords != null) {
|
|
36395
|
-
const guess = userInput.coords
|
|
36396
|
-
|
|
36397
|
-
const correct = rubric.correct.coords?.slice();
|
|
36404
|
+
const guess = userInput.coords.slice();
|
|
36405
|
+
const correct = rubric.correct.coords.slice();
|
|
36398
36406
|
let match;
|
|
36399
36407
|
if (rubric.correct.match === "similar") {
|
|
36400
36408
|
match = similar(guess, correct, Number.POSITIVE_INFINITY);
|
|
@@ -36418,9 +36426,8 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36418
36426
|
}
|
|
36419
36427
|
} else if (userInput.type === "segment" && rubric.correct.type === "segment" && userInput.coords != null) {
|
|
36420
36428
|
let guess = Util.deepClone(userInput.coords);
|
|
36421
|
-
let correct = Util.deepClone(rubric.correct
|
|
36429
|
+
let correct = Util.deepClone(rubric.correct.coords);
|
|
36422
36430
|
guess = ___default["default"].invoke(guess, "sort").sort();
|
|
36423
|
-
// @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
36424
36431
|
correct = ___default["default"].invoke(correct, "sort").sort();
|
|
36425
36432
|
if (deepEq$2(guess, correct)) {
|
|
36426
36433
|
return {
|
|
@@ -36433,11 +36440,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36433
36440
|
} else if (userInput.type === "ray" && rubric.correct.type === "ray" && userInput.coords != null) {
|
|
36434
36441
|
const guess = userInput.coords;
|
|
36435
36442
|
const correct = rubric.correct.coords;
|
|
36436
|
-
if (
|
|
36437
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
36438
|
-
deepEq$2(guess[0], correct[0]) &&
|
|
36439
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
36440
|
-
collinear(correct[0], correct[1], guess[1])) {
|
|
36443
|
+
if (deepEq$2(guess[0], correct[0]) && collinear(correct[0], correct[1], guess[1])) {
|
|
36441
36444
|
return {
|
|
36442
36445
|
type: "points",
|
|
36443
36446
|
earned: 1,
|
|
@@ -44378,41 +44381,16 @@ var extraWidgets = [Categorizer$1, CSProgram$1, Dropdown$1, Explanation$1, Defin
|
|
|
44378
44381
|
* This should be called by all clients, specifying whether extra widgets are
|
|
44379
44382
|
* needed via `loadExtraWidgets`. It is idempotent, so it's not a problem to
|
|
44380
44383
|
* call it multiple times.
|
|
44381
|
-
*
|
|
44382
|
-
* skipMathJax:
|
|
44383
|
-
* If false/undefined, MathJax will be configured, and the
|
|
44384
|
-
* promise will wait for MathJax to load (if it hasn't already).
|
|
44385
44384
|
*/
|
|
44386
|
-
const init = function (
|
|
44385
|
+
const init = function () {
|
|
44387
44386
|
registerWidgets(basicWidgets);
|
|
44388
44387
|
registerWidgets(extraWidgets);
|
|
44389
44388
|
replaceDeprecatedWidgets();
|
|
44390
|
-
|
|
44391
|
-
// Pass skipMathJax: true if MathJax is already loaded and configured.
|
|
44392
|
-
const skipMathJax = options.skipMathJax;
|
|
44393
|
-
if (skipMathJax) {
|
|
44394
|
-
// @ts-expect-error - TS2322 - Type 'Promise<void>' is not assignable to type 'Promise<undefined>'.
|
|
44395
|
-
return Promise.resolve();
|
|
44396
|
-
}
|
|
44397
|
-
return new Promise((resolve, reject) => {
|
|
44398
|
-
MathJax.Hub.Config({
|
|
44399
|
-
messageStyle: "none",
|
|
44400
|
-
skipStartupTypeset: "none",
|
|
44401
|
-
"HTML-CSS": {
|
|
44402
|
-
availableFonts: ["TeX"],
|
|
44403
|
-
imageFont: null,
|
|
44404
|
-
scale: 100,
|
|
44405
|
-
showMathMenu: false
|
|
44406
|
-
}
|
|
44407
|
-
});
|
|
44408
|
-
MathJax.Hub.Configured();
|
|
44409
|
-
MathJax.Hub.Queue(resolve);
|
|
44410
|
-
});
|
|
44411
44389
|
};
|
|
44412
44390
|
|
|
44413
44391
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44414
44392
|
const libName = "@khanacademy/perseus";
|
|
44415
|
-
const libVersion = "41.
|
|
44393
|
+
const libVersion = "41.2.0";
|
|
44416
44394
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44417
44395
|
|
|
44418
44396
|
const apiVersion = {
|
|
@@ -47678,16 +47656,7 @@ const getValidWidgetIds = perseusItem => {
|
|
|
47678
47656
|
};
|
|
47679
47657
|
|
|
47680
47658
|
const deepEq = Util.deepEq;
|
|
47681
|
-
|
|
47682
|
-
/**
|
|
47683
|
-
* Helper to parse PerseusItem JSON
|
|
47684
|
-
* Why not just use JSON.parse? We want:
|
|
47685
|
-
* - To make sure types are correct
|
|
47686
|
-
* - To give us a central place to validate/transform output if needed
|
|
47687
|
-
* @param {string} json - the stringified PerseusItem JSON
|
|
47688
|
-
* @returns {PerseusItem} the parsed PerseusItem object
|
|
47689
|
-
*/
|
|
47690
|
-
function parsePerseusItem(json) {
|
|
47659
|
+
function isRealJSONParse(jsonParse) {
|
|
47691
47660
|
const randomPhrase = buildRandomPhrase();
|
|
47692
47661
|
const randomHintPhrase = buildRandomPhrase();
|
|
47693
47662
|
const randomString = buildRandomString();
|
|
@@ -47740,15 +47709,10 @@ function parsePerseusItem(json) {
|
|
|
47740
47709
|
}
|
|
47741
47710
|
}
|
|
47742
47711
|
});
|
|
47743
|
-
|
|
47744
|
-
const
|
|
47745
|
-
const
|
|
47746
|
-
|
|
47747
|
-
const isNotCheating = deepEq(parsedItemData, testingObject);
|
|
47748
|
-
if (isNotCheating) {
|
|
47749
|
-
return JSON.parse(json);
|
|
47750
|
-
}
|
|
47751
|
-
throw new Error("Something went wrong.");
|
|
47712
|
+
const testJSON = buildTestData(testingObject.replace(/"/g, '\\"'));
|
|
47713
|
+
const parsedTestJSON = jsonParse(testJSON);
|
|
47714
|
+
const parsedTestItemData = parsedTestJSON.data.assessmentItem.item.itemData;
|
|
47715
|
+
return deepEq(parsedTestItemData, testingObject);
|
|
47752
47716
|
}
|
|
47753
47717
|
function buildRandomString() {
|
|
47754
47718
|
let capitalize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
|
@@ -47775,6 +47739,23 @@ function buildTestData(testObject) {
|
|
|
47775
47739
|
return `{"data":{"assessmentItem":{"__typename":"AssessmentItemOrError","error":null,"item":{"__typename":"AssessmentItem","id":"x890b3c70f3e8f4a6","itemData":"${testObject}","problemType":"Type 1","sha":"c7284a3ad65214b4e62bccce236d92f7f5d35941"}}}}`;
|
|
47776
47740
|
}
|
|
47777
47741
|
|
|
47742
|
+
/**
|
|
47743
|
+
* Helper to parse PerseusItem JSON
|
|
47744
|
+
* Why not just use JSON.parse? We want:
|
|
47745
|
+
* - To make sure types are correct
|
|
47746
|
+
* - To give us a central place to validate/transform output if needed
|
|
47747
|
+
* @param {string} json - the stringified PerseusItem JSON
|
|
47748
|
+
* @returns {PerseusItem} the parsed PerseusItem object
|
|
47749
|
+
*/
|
|
47750
|
+
function parsePerseusItem(json) {
|
|
47751
|
+
// Try to block a cheating vector which relies on monkey-patching
|
|
47752
|
+
// JSON.parse
|
|
47753
|
+
if (isRealJSONParse(JSON.parse)) {
|
|
47754
|
+
return JSON.parse(json);
|
|
47755
|
+
}
|
|
47756
|
+
throw new Error("Something went wrong.");
|
|
47757
|
+
}
|
|
47758
|
+
|
|
47778
47759
|
const genericPerseusItemData = {
|
|
47779
47760
|
question: {
|
|
47780
47761
|
content: "",
|