@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/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
- // Move this all outside the SVG element
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?.slice();
36396
- // @ts-expect-error - TS2322 - Type 'Coord[] | undefined' is not assignable to type 'Coord[]'.
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?.coords);
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 (options) {
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.1.0";
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
- // @ts-expect-error TS2550: Property 'replaceAll' does not exist on type 'string'.
47744
- const testJSON = buildTestData(testingObject.replaceAll('"', '\\"'));
47745
- const parsedJSON = JSON.parse(testJSON);
47746
- const parsedItemData = parsedJSON.data.assessmentItem.item.itemData;
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: "",