@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/es/index.js
CHANGED
|
@@ -31598,6 +31598,63 @@ const lockedFigureFillStyles = {
|
|
|
31598
31598
|
|
|
31599
31599
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
31600
31600
|
|
|
31601
|
+
/**
|
|
31602
|
+
* 44 is touch best practice and AAA compliant for WCAG
|
|
31603
|
+
* https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
|
|
31604
|
+
*/
|
|
31605
|
+
const TARGET_SIZE = 44;
|
|
31606
|
+
const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
|
|
31607
|
+
|
|
31608
|
+
// same as pointsFromNormalized in interactive-graph.tsx
|
|
31609
|
+
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
31610
|
+
// maps over [x, y]
|
|
31611
|
+
coords.map((coord, i) => {
|
|
31612
|
+
const axisRange = range[i];
|
|
31613
|
+
if (noSnap) {
|
|
31614
|
+
return axisRange[MIN] + size(axisRange) * coord;
|
|
31615
|
+
}
|
|
31616
|
+
const axisStep = step[i];
|
|
31617
|
+
const nSteps = Math.floor(size(axisRange) / axisStep);
|
|
31618
|
+
const tick = Math.round(coord * nSteps);
|
|
31619
|
+
return axisRange[MIN] + axisStep * tick;
|
|
31620
|
+
}));
|
|
31621
|
+
|
|
31622
|
+
// same as normalizeCoords in interactive-graph.tsx
|
|
31623
|
+
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
31624
|
+
return (coord + ranges[i][1]) / size(ranges[i]);
|
|
31625
|
+
}));
|
|
31626
|
+
|
|
31627
|
+
// Returns the closest point to the given `point` that is within the graph
|
|
31628
|
+
// bounds given in `state`.
|
|
31629
|
+
function bound$1({
|
|
31630
|
+
snapStep,
|
|
31631
|
+
range,
|
|
31632
|
+
point
|
|
31633
|
+
}) {
|
|
31634
|
+
const boundingBox = inset(snapStep, range);
|
|
31635
|
+
return clampToBox(boundingBox, point);
|
|
31636
|
+
}
|
|
31637
|
+
function isUnlimitedGraphState(state) {
|
|
31638
|
+
return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
|
|
31639
|
+
}
|
|
31640
|
+
|
|
31641
|
+
/**
|
|
31642
|
+
* Replace all text outside of the $ TeX blocks with `\\text{...}`
|
|
31643
|
+
* This way, the entire resulting string can be rendered within <TeX>
|
|
31644
|
+
* and the text outside of the $ blocks will be non-TeX text.
|
|
31645
|
+
*/
|
|
31646
|
+
function replaceOutsideTeX(mathString) {
|
|
31647
|
+
let currentlyTeX = mathString[0] === "$";
|
|
31648
|
+
let result = "";
|
|
31649
|
+
const splitString = mathString.split("$").filter(part => part !== "");
|
|
31650
|
+
for (let i = 0; i < splitString.length; i++) {
|
|
31651
|
+
const part = splitString[i];
|
|
31652
|
+
result += currentlyTeX ? part : `\\text{${part}}`;
|
|
31653
|
+
currentlyTeX = !currentlyTeX;
|
|
31654
|
+
}
|
|
31655
|
+
return result;
|
|
31656
|
+
}
|
|
31657
|
+
|
|
31601
31658
|
function LockedLabel(props) {
|
|
31602
31659
|
const {
|
|
31603
31660
|
coord,
|
|
@@ -31610,7 +31667,7 @@ function LockedLabel(props) {
|
|
|
31610
31667
|
TeX
|
|
31611
31668
|
} = getDependencies();
|
|
31612
31669
|
|
|
31613
|
-
//
|
|
31670
|
+
// Note: The TeX component cannot be rendered within an SVG
|
|
31614
31671
|
return /*#__PURE__*/React.createElement("span", {
|
|
31615
31672
|
className: "locked-label",
|
|
31616
31673
|
style: {
|
|
@@ -31622,7 +31679,7 @@ function LockedLabel(props) {
|
|
|
31622
31679
|
backgroundColor: "rgba(255, 255, 255, 0.8)"
|
|
31623
31680
|
},
|
|
31624
31681
|
"aria-hidden": true
|
|
31625
|
-
}, /*#__PURE__*/React.createElement(TeX, null, text));
|
|
31682
|
+
}, /*#__PURE__*/React.createElement(TeX, null, replaceOutsideTeX(text)));
|
|
31626
31683
|
}
|
|
31627
31684
|
|
|
31628
31685
|
function GraphLockedLabelsLayer(props) {
|
|
@@ -32542,46 +32599,6 @@ function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, a
|
|
|
32542
32599
|
return scaledBisector;
|
|
32543
32600
|
}
|
|
32544
32601
|
|
|
32545
|
-
/**
|
|
32546
|
-
* 44 is touch best practice and AAA compliant for WCAG
|
|
32547
|
-
* https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
|
|
32548
|
-
*/
|
|
32549
|
-
const TARGET_SIZE = 44;
|
|
32550
|
-
const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
|
|
32551
|
-
|
|
32552
|
-
// same as pointsFromNormalized in interactive-graph.tsx
|
|
32553
|
-
const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coords =>
|
|
32554
|
-
// maps over [x, y]
|
|
32555
|
-
coords.map((coord, i) => {
|
|
32556
|
-
const axisRange = range[i];
|
|
32557
|
-
if (noSnap) {
|
|
32558
|
-
return axisRange[MIN] + size(axisRange) * coord;
|
|
32559
|
-
}
|
|
32560
|
-
const axisStep = step[i];
|
|
32561
|
-
const nSteps = Math.floor(size(axisRange) / axisStep);
|
|
32562
|
-
const tick = Math.round(coord * nSteps);
|
|
32563
|
-
return axisRange[MIN] + axisStep * tick;
|
|
32564
|
-
}));
|
|
32565
|
-
|
|
32566
|
-
// same as normalizeCoords in interactive-graph.tsx
|
|
32567
|
-
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
32568
|
-
return (coord + ranges[i][1]) / size(ranges[i]);
|
|
32569
|
-
}));
|
|
32570
|
-
|
|
32571
|
-
// Returns the closest point to the given `point` that is within the graph
|
|
32572
|
-
// bounds given in `state`.
|
|
32573
|
-
function bound$1({
|
|
32574
|
-
snapStep,
|
|
32575
|
-
range,
|
|
32576
|
-
point
|
|
32577
|
-
}) {
|
|
32578
|
-
const boundingBox = inset(snapStep, range);
|
|
32579
|
-
return clampToBox(boundingBox, point);
|
|
32580
|
-
}
|
|
32581
|
-
function isUnlimitedGraphState(state) {
|
|
32582
|
-
return state.type === "point" && state.numPoints === "unlimited" || state.type === "polygon" && state.numSides === "unlimited";
|
|
32583
|
-
}
|
|
32584
|
-
|
|
32585
32602
|
var isProduction = process.env.NODE_ENV === 'production';
|
|
32586
32603
|
var prefix = 'Invariant failed';
|
|
32587
32604
|
function invariant(condition, message) {
|
|
@@ -35924,11 +35941,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
35924
35941
|
|
|
35925
35942
|
// If both of the guess points are on the correct line, it's
|
|
35926
35943
|
// correct.
|
|
35927
|
-
if (
|
|
35928
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
35929
|
-
collinear(correct[0], correct[1], guess[0]) &&
|
|
35930
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
35931
|
-
collinear(correct[0], correct[1], guess[1])) {
|
|
35944
|
+
if (collinear(correct[0], correct[1], guess[0]) && collinear(correct[0], correct[1], guess[1])) {
|
|
35932
35945
|
return {
|
|
35933
35946
|
type: "points",
|
|
35934
35947
|
earned: 1,
|
|
@@ -35950,9 +35963,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
35950
35963
|
} else if (userInput.type === "quadratic" && rubric.correct.type === "quadratic" && userInput.coords != null) {
|
|
35951
35964
|
// If the parabola coefficients match, it's correct.
|
|
35952
35965
|
const guessCoeffs = getQuadraticCoefficients(userInput.coords);
|
|
35953
|
-
const correctCoeffs = getQuadraticCoefficients(
|
|
35954
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
35955
|
-
rubric.correct.coords);
|
|
35966
|
+
const correctCoeffs = getQuadraticCoefficients(rubric.correct.coords);
|
|
35956
35967
|
if (deepEq$2(guessCoeffs, correctCoeffs)) {
|
|
35957
35968
|
return {
|
|
35958
35969
|
type: "points",
|
|
@@ -35963,9 +35974,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
35963
35974
|
}
|
|
35964
35975
|
} else if (userInput.type === "sinusoid" && rubric.correct.type === "sinusoid" && userInput.coords != null) {
|
|
35965
35976
|
const guessCoeffs = getSinusoidCoefficients(userInput.coords);
|
|
35966
|
-
const correctCoeffs = getSinusoidCoefficients(
|
|
35967
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
35968
|
-
rubric.correct.coords);
|
|
35977
|
+
const correctCoeffs = getSinusoidCoefficients(rubric.correct.coords);
|
|
35969
35978
|
const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
|
|
35970
35979
|
const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
|
|
35971
35980
|
// If the canonical coefficients match, it's correct.
|
|
@@ -36009,10 +36018,8 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36009
36018
|
};
|
|
36010
36019
|
}
|
|
36011
36020
|
} else if (userInput.type === "polygon" && rubric.correct.type === "polygon" && userInput.coords != null) {
|
|
36012
|
-
|
|
36013
|
-
const
|
|
36014
|
-
// @ts-expect-error - TS2322 - Type 'Coord[] | undefined' is not assignable to type 'Coord[]'.
|
|
36015
|
-
const correct = (_rubric$correct$coord = rubric.correct.coords) == null ? void 0 : _rubric$correct$coord.slice();
|
|
36021
|
+
const guess = userInput.coords.slice();
|
|
36022
|
+
const correct = rubric.correct.coords.slice();
|
|
36016
36023
|
let match;
|
|
36017
36024
|
if (rubric.correct.match === "similar") {
|
|
36018
36025
|
match = similar(guess, correct, Number.POSITIVE_INFINITY);
|
|
@@ -36035,11 +36042,9 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36035
36042
|
};
|
|
36036
36043
|
}
|
|
36037
36044
|
} else if (userInput.type === "segment" && rubric.correct.type === "segment" && userInput.coords != null) {
|
|
36038
|
-
var _rubric$correct;
|
|
36039
36045
|
let guess = Util.deepClone(userInput.coords);
|
|
36040
|
-
let correct = Util.deepClone(
|
|
36046
|
+
let correct = Util.deepClone(rubric.correct.coords);
|
|
36041
36047
|
guess = _.invoke(guess, "sort").sort();
|
|
36042
|
-
// @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
36043
36048
|
correct = _.invoke(correct, "sort").sort();
|
|
36044
36049
|
if (deepEq$2(guess, correct)) {
|
|
36045
36050
|
return {
|
|
@@ -36052,11 +36057,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36052
36057
|
} else if (userInput.type === "ray" && rubric.correct.type === "ray" && userInput.coords != null) {
|
|
36053
36058
|
const guess = userInput.coords;
|
|
36054
36059
|
const correct = rubric.correct.coords;
|
|
36055
|
-
if (
|
|
36056
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
36057
|
-
deepEq$2(guess[0], correct[0]) &&
|
|
36058
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
36059
|
-
collinear(correct[0], correct[1], guess[1])) {
|
|
36060
|
+
if (deepEq$2(guess[0], correct[0]) && collinear(correct[0], correct[1], guess[1])) {
|
|
36060
36061
|
return {
|
|
36061
36062
|
type: "points",
|
|
36062
36063
|
earned: 1,
|
|
@@ -44012,41 +44013,16 @@ var extraWidgets = [Categorizer$1, CSProgram$1, Dropdown$1, Explanation$1, Defin
|
|
|
44012
44013
|
* This should be called by all clients, specifying whether extra widgets are
|
|
44013
44014
|
* needed via `loadExtraWidgets`. It is idempotent, so it's not a problem to
|
|
44014
44015
|
* call it multiple times.
|
|
44015
|
-
*
|
|
44016
|
-
* skipMathJax:
|
|
44017
|
-
* If false/undefined, MathJax will be configured, and the
|
|
44018
|
-
* promise will wait for MathJax to load (if it hasn't already).
|
|
44019
44016
|
*/
|
|
44020
|
-
const init = function init(
|
|
44017
|
+
const init = function init() {
|
|
44021
44018
|
registerWidgets(basicWidgets);
|
|
44022
44019
|
registerWidgets(extraWidgets);
|
|
44023
44020
|
replaceDeprecatedWidgets();
|
|
44024
|
-
|
|
44025
|
-
// Pass skipMathJax: true if MathJax is already loaded and configured.
|
|
44026
|
-
const skipMathJax = options.skipMathJax;
|
|
44027
|
-
if (skipMathJax) {
|
|
44028
|
-
// @ts-expect-error - TS2322 - Type 'Promise<void>' is not assignable to type 'Promise<undefined>'.
|
|
44029
|
-
return Promise.resolve();
|
|
44030
|
-
}
|
|
44031
|
-
return new Promise((resolve, reject) => {
|
|
44032
|
-
MathJax.Hub.Config({
|
|
44033
|
-
messageStyle: "none",
|
|
44034
|
-
skipStartupTypeset: "none",
|
|
44035
|
-
"HTML-CSS": {
|
|
44036
|
-
availableFonts: ["TeX"],
|
|
44037
|
-
imageFont: null,
|
|
44038
|
-
scale: 100,
|
|
44039
|
-
showMathMenu: false
|
|
44040
|
-
}
|
|
44041
|
-
});
|
|
44042
|
-
MathJax.Hub.Configured();
|
|
44043
|
-
MathJax.Hub.Queue(resolve);
|
|
44044
|
-
});
|
|
44045
44021
|
};
|
|
44046
44022
|
|
|
44047
44023
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44048
44024
|
const libName = "@khanacademy/perseus";
|
|
44049
|
-
const libVersion = "41.
|
|
44025
|
+
const libVersion = "41.2.0";
|
|
44050
44026
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44051
44027
|
|
|
44052
44028
|
const apiVersion = {
|
|
@@ -47301,16 +47277,7 @@ const getValidWidgetIds = perseusItem => {
|
|
|
47301
47277
|
};
|
|
47302
47278
|
|
|
47303
47279
|
const deepEq = Util.deepEq;
|
|
47304
|
-
|
|
47305
|
-
/**
|
|
47306
|
-
* Helper to parse PerseusItem JSON
|
|
47307
|
-
* Why not just use JSON.parse? We want:
|
|
47308
|
-
* - To make sure types are correct
|
|
47309
|
-
* - To give us a central place to validate/transform output if needed
|
|
47310
|
-
* @param {string} json - the stringified PerseusItem JSON
|
|
47311
|
-
* @returns {PerseusItem} the parsed PerseusItem object
|
|
47312
|
-
*/
|
|
47313
|
-
function parsePerseusItem(json) {
|
|
47280
|
+
function isRealJSONParse(jsonParse) {
|
|
47314
47281
|
const randomPhrase = buildRandomPhrase();
|
|
47315
47282
|
const randomHintPhrase = buildRandomPhrase();
|
|
47316
47283
|
const randomString = buildRandomString();
|
|
@@ -47363,15 +47330,10 @@ function parsePerseusItem(json) {
|
|
|
47363
47330
|
}
|
|
47364
47331
|
}
|
|
47365
47332
|
});
|
|
47366
|
-
|
|
47367
|
-
const
|
|
47368
|
-
const
|
|
47369
|
-
|
|
47370
|
-
const isNotCheating = deepEq(parsedItemData, testingObject);
|
|
47371
|
-
if (isNotCheating) {
|
|
47372
|
-
return JSON.parse(json);
|
|
47373
|
-
}
|
|
47374
|
-
throw new Error("Something went wrong.");
|
|
47333
|
+
const testJSON = buildTestData(testingObject.replace(/"/g, '\\"'));
|
|
47334
|
+
const parsedTestJSON = jsonParse(testJSON);
|
|
47335
|
+
const parsedTestItemData = parsedTestJSON.data.assessmentItem.item.itemData;
|
|
47336
|
+
return deepEq(parsedTestItemData, testingObject);
|
|
47375
47337
|
}
|
|
47376
47338
|
function buildRandomString(capitalize = false) {
|
|
47377
47339
|
let randomString = "";
|
|
@@ -47397,6 +47359,23 @@ function buildTestData(testObject) {
|
|
|
47397
47359
|
return `{"data":{"assessmentItem":{"__typename":"AssessmentItemOrError","error":null,"item":{"__typename":"AssessmentItem","id":"x890b3c70f3e8f4a6","itemData":"${testObject}","problemType":"Type 1","sha":"c7284a3ad65214b4e62bccce236d92f7f5d35941"}}}}`;
|
|
47398
47360
|
}
|
|
47399
47361
|
|
|
47362
|
+
/**
|
|
47363
|
+
* Helper to parse PerseusItem JSON
|
|
47364
|
+
* Why not just use JSON.parse? We want:
|
|
47365
|
+
* - To make sure types are correct
|
|
47366
|
+
* - To give us a central place to validate/transform output if needed
|
|
47367
|
+
* @param {string} json - the stringified PerseusItem JSON
|
|
47368
|
+
* @returns {PerseusItem} the parsed PerseusItem object
|
|
47369
|
+
*/
|
|
47370
|
+
function parsePerseusItem(json) {
|
|
47371
|
+
// Try to block a cheating vector which relies on monkey-patching
|
|
47372
|
+
// JSON.parse
|
|
47373
|
+
if (isRealJSONParse(JSON.parse)) {
|
|
47374
|
+
return JSON.parse(json);
|
|
47375
|
+
}
|
|
47376
|
+
throw new Error("Something went wrong.");
|
|
47377
|
+
}
|
|
47378
|
+
|
|
47400
47379
|
const genericPerseusItemData = {
|
|
47401
47380
|
question: {
|
|
47402
47381
|
content: "",
|