@khanacademy/perseus 36.0.0 → 36.1.1

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
@@ -61,7 +61,7 @@ export { default as KhanColors } from "./util/colors";
61
61
  export { default as preprocessTex } from "./util/tex-preprocess";
62
62
  export { registerAllWidgetsForTesting } from "./util/register-all-widgets-for-testing";
63
63
  export * as SizingUtils from "./util/sizing-utils";
64
- export { getAnswersFromWidgets, injectWidgets, isWrongAnswerSupported, shouldHaveIndividualAnswer, isWidgetIdInContent, getValidWidgetIds, getCorrectAnswerForWidgetId, getAnswerFromUserInput, } from "./util/extract-perseus-data";
64
+ export { getAnswersFromWidgets, injectWidgets, isWrongAnswerSupported, shouldHaveIndividualAnswer, isWidgetIdInContent, getValidWidgetIds, getCorrectAnswerForWidgetId, getAnswerFromUserInput, getImagesWithoutAltData, } from "./util/extract-perseus-data";
65
65
  export { parsePerseusItem } from "./util/parse-perseus-json";
66
66
  export { generateTestPerseusItem, genericPerseusItemData, generateTestRadioWidget, generateTestInteractiveGraphWidget, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestNumericInputWidget, } from "./util/test-utils";
67
67
  export { getWidgetTypeByWidgetId, contentHasWidgetType, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetsMapFromItemData, getWidgetFromWidgetMap, getWidgetsFromWidgetMap, } from "./widget-type-utils";
package/dist/index.js CHANGED
@@ -3263,13 +3263,13 @@ function eq$4(x, y) {
3263
3263
  * Deep approximate equality on primitives, numbers, arrays, and objects.
3264
3264
  * Recursive.
3265
3265
  */
3266
- function deepEq$5(x, y) {
3266
+ function deepEq$6(x, y) {
3267
3267
  if (Array.isArray(x) && Array.isArray(y)) {
3268
3268
  if (x.length !== y.length) {
3269
3269
  return false;
3270
3270
  }
3271
3271
  for (let i = 0; i < x.length; i++) {
3272
- if (!deepEq$5(x[i], y[i])) {
3272
+ if (!deepEq$6(x[i], y[i])) {
3273
3273
  return false;
3274
3274
  }
3275
3275
  }
@@ -3287,10 +3287,10 @@ function deepEq$5(x, y) {
3287
3287
  if (typeof x === "object" && typeof y === "object" && !!x && !!y) {
3288
3288
  return x === y || ___default["default"].all(x, function (v, k) {
3289
3289
  // @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
3290
- return deepEq$5(y[k], v);
3290
+ return deepEq$6(y[k], v);
3291
3291
  }) && ___default["default"].all(y, function (v, k) {
3292
3292
  // @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
3293
- return deepEq$5(x[k], v);
3293
+ return deepEq$6(x[k], v);
3294
3294
  });
3295
3295
  }
3296
3296
  if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
@@ -3568,7 +3568,7 @@ const Util = {
3568
3568
  tickStepFromNumTicks,
3569
3569
  constrainedTickStepsFromTickSteps,
3570
3570
  eq: eq$4,
3571
- deepEq: deepEq$5,
3571
+ deepEq: deepEq$6,
3572
3572
  parseQueryString,
3573
3573
  updateQueryString: updateQueryString$2,
3574
3574
  strongEncodeURIComponent,
@@ -10892,7 +10892,7 @@ class WrappedLine extends WrappedDrawing {
10892
10892
  */
10893
10893
  const {
10894
10894
  eq: eq$3,
10895
- deepEq: deepEq$4
10895
+ deepEq: deepEq$5
10896
10896
  } = Util;
10897
10897
 
10898
10898
  // This should really be a readonly tuple of [number, number]
@@ -11052,7 +11052,7 @@ function similar(coords1, coords2, tolerance) {
11052
11052
  angles = rotate(angles, i);
11053
11053
  // @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
11054
11054
  sides = rotate(sides, i);
11055
- if (deepEq$4(angles1, angles)) {
11055
+ if (deepEq$5(angles1, angles)) {
11056
11056
  const sidePairs = ___default["default"].zip(sides1, sides);
11057
11057
  const factors = ___default["default"].map(sidePairs, function (pair) {
11058
11058
  return pair[0] / pair[1];
@@ -13991,7 +13991,7 @@ ___default["default"].extend(MovableAngle.prototype, {
13991
13991
 
13992
13992
  /* eslint-disable @babel/no-invalid-this */
13993
13993
  const nestedMap$1 = Util.nestedMap;
13994
- const deepEq$3 = Util.deepEq;
13994
+ const deepEq$4 = Util.deepEq;
13995
13995
 
13996
13996
  /**
13997
13997
  * A base class for all Graphie Movables
@@ -14088,7 +14088,7 @@ const createSimpleClass = function (addFunction) {
14088
14088
  this._prevProps = this.props;
14089
14089
  },
14090
14090
  modify: function (graphie) {
14091
- if (!deepEq$3(this.props, this._prevProps)) {
14091
+ if (!deepEq$4(this.props, this._prevProps)) {
14092
14092
  this.remove();
14093
14093
  this.add(graphie);
14094
14094
  this._prevProps = this.props;
@@ -16835,7 +16835,7 @@ var Movables = {
16835
16835
  const GraphieMovable = GraphieClasses.GraphieMovable;
16836
16836
  const createGraphie = GraphUtils.createGraphie;
16837
16837
  const {
16838
- deepEq: deepEq$2,
16838
+ deepEq: deepEq$3,
16839
16839
  nestedMap
16840
16840
  } = Util;
16841
16841
  const {
@@ -16873,7 +16873,7 @@ class Graphie extends React__namespace.Component {
16873
16873
  if (this.props.setup !== prevProps.setup) {
16874
16874
  Log.error("<Graphie> was given a new setup function. " + "This is a bad idea; please refactor your code to give " + "the same setup function reference to <Graphie> on " + "every render.", perseusCore.Errors.Internal);
16875
16875
  }
16876
- if (!deepEq$2(this.props.options, prevProps.options) || !deepEq$2(this.props.box, prevProps.box) || !deepEq$2(this.props.range, prevProps.range)) {
16876
+ if (!deepEq$3(this.props.options, prevProps.options) || !deepEq$3(this.props.box, prevProps.box) || !deepEq$3(this.props.range, prevProps.range)) {
16877
16877
  this._setupGraphie();
16878
16878
  }
16879
16879
  this._updateMovables();
@@ -31299,10 +31299,16 @@ const shouldShowLabel = (currentTick, range, tickStep) => {
31299
31299
  function generateTickLocations(tickStep, min, max) {
31300
31300
  const ticks = [];
31301
31301
 
31302
+ // Calculate the number of significant decimals in the tick step so
31303
+ // that we can match the desired precision when generating ticks.
31304
+ const decimalSigFigs = countSignificantDecimals(tickStep);
31305
+
31302
31306
  // Add ticks in the positive direction
31303
31307
  const start = Math.max(min, 0);
31304
31308
  for (let i = start + tickStep; i < max; i += tickStep) {
31305
- ticks.push(i);
31309
+ // Match to the same number of decimal places as the tick step
31310
+ // to avoid floating point errors when working with small numbers
31311
+ ticks.push(parseFloat(i.toFixed(decimalSigFigs)));
31306
31312
  }
31307
31313
 
31308
31314
  // Add ticks in the negative direction
@@ -31313,6 +31319,15 @@ function generateTickLocations(tickStep, min, max) {
31313
31319
  }
31314
31320
  return ticks;
31315
31321
  }
31322
+
31323
+ // Count the number of significant digits after the decimal point
31324
+ const countSignificantDecimals = number => {
31325
+ const numStr = number.toString();
31326
+ if (!numStr.includes(".")) {
31327
+ return 0;
31328
+ }
31329
+ return numStr.split(".")[1].length;
31330
+ };
31316
31331
  const AxisTicks = () => {
31317
31332
  const {
31318
31333
  tickStep,
@@ -33597,7 +33612,7 @@ function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, a
33597
33612
  return scaledBisector;
33598
33613
  }
33599
33614
 
33600
- const MovablePoint$1 = props => {
33615
+ const MovablePoint$1 = /*#__PURE__*/React__namespace.forwardRef((props, pointRef) => {
33601
33616
  const {
33602
33617
  snapStep
33603
33618
  } = useGraphConfig();
@@ -33620,7 +33635,21 @@ const MovablePoint$1 = props => {
33620
33635
  constrainKeyboardMovement: constrain
33621
33636
  });
33622
33637
  return /*#__PURE__*/React__namespace.createElement(MovablePointView, {
33623
- ref: elementRef,
33638
+ ref: ref => {
33639
+ // Note: This handles the case where pointRef is either a
33640
+ // function or a MutableRefObject. Mostly this is to satisfy
33641
+ // the type system, the function branch seems to be the only
33642
+ // one that occurs
33643
+ if (typeof pointRef === "function") {
33644
+ pointRef(ref);
33645
+ } else if (pointRef !== null) {
33646
+ pointRef.current = ref;
33647
+ }
33648
+
33649
+ // This ref is for the useDraggable and is not passed up the
33650
+ // chain
33651
+ elementRef.current = ref;
33652
+ },
33624
33653
  point: point,
33625
33654
  color: color,
33626
33655
  dragging: dragging,
@@ -33635,7 +33664,7 @@ const MovablePoint$1 = props => {
33635
33664
  },
33636
33665
  cursor: cursor
33637
33666
  });
33638
- };
33667
+ });
33639
33668
 
33640
33669
  const PolygonGraph = props => {
33641
33670
  const [hovered, setHovered] = React__namespace.useState(false);
@@ -34225,6 +34254,13 @@ function UnlimitedPointGraph(props) {
34225
34254
  const height = maxY - minY;
34226
34255
  const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
34227
34256
  const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
34257
+ const itemsRef = React__namespace.useRef([]);
34258
+ React__namespace.useEffect(() => {
34259
+ const focusedIndex = props.graphState.focusedPointIndex;
34260
+ if (focusedIndex != null) {
34261
+ itemsRef.current[focusedIndex]?.focus();
34262
+ }
34263
+ }, [props.graphState.focusedPointIndex, itemsRef]);
34228
34264
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
34229
34265
  style: {
34230
34266
  fill: "rgba(0,0,0,0)",
@@ -34245,12 +34281,23 @@ function UnlimitedPointGraph(props) {
34245
34281
  key: i,
34246
34282
  point: point,
34247
34283
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
34284
+ ref: ref => {
34285
+ itemsRef.current[i] = ref;
34286
+ },
34248
34287
  onFocusChange: (event, isFocused) => {
34249
34288
  if (isFocused) {
34250
34289
  dispatch(actions.pointGraph.focusPoint(i));
34251
34290
  } else {
34252
34291
  if (event.relatedTarget?.id === REMOVE_BUTTON_ID) {
34253
34292
  return;
34293
+ // This is an optimization: If the next target
34294
+ // is a point then don't blur because it casues
34295
+ // the remove button to get taken off the page
34296
+ // and then put back on The new point will
34297
+ // receive focus and set the correct state in
34298
+ // the reducer
34299
+ } else if (event.relatedTarget?.classList.contains("movable-point")) {
34300
+ return;
34254
34301
  }
34255
34302
  dispatch(actions.pointGraph.blurPoint());
34256
34303
  }
@@ -34425,11 +34472,17 @@ const MafsGraph = props => {
34425
34472
  fullGraphAriaLabel,
34426
34473
  fullGraphAriaDescription
34427
34474
  } = props;
34475
+ const {
34476
+ type
34477
+ } = state;
34428
34478
  const [width, height] = props.box;
34429
34479
  const tickStep = props.step;
34430
34480
  const uniqueId = React__namespace.useId();
34431
34481
  const descriptionId = `interactive-graph-description-${uniqueId}`;
34432
34482
  const graphRef = React__namespace.useRef(null);
34483
+ const {
34484
+ analytics
34485
+ } = useDependencies();
34433
34486
 
34434
34487
  // Set up the SVG attributes for the nested SVGs that help lock
34435
34488
  // the grid and graph elements to the bounds of the graph.
@@ -34449,6 +34502,14 @@ const MafsGraph = props => {
34449
34502
  const {
34450
34503
  strings
34451
34504
  } = usePerseusI18n();
34505
+ wonderBlocksCore.useOnMountEffect(() => {
34506
+ analytics.onAnalyticsEvent({
34507
+ type: "perseus:interactive-graph-widget:rendered",
34508
+ payload: {
34509
+ type
34510
+ }
34511
+ });
34512
+ });
34452
34513
  return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
34453
34514
  value: {
34454
34515
  range: state.range,
@@ -35233,11 +35294,14 @@ function doFocusPoint(state, action) {
35233
35294
  function doBlurPoint(state, action) {
35234
35295
  switch (state.type) {
35235
35296
  case "point":
35236
- return {
35297
+ const nextState = {
35237
35298
  ...state,
35238
- focusedPointIndex: null,
35239
35299
  showRemovePointButton: false
35240
35300
  };
35301
+ if (state.interactionMode === "mouse") {
35302
+ nextState.focusedPointIndex = null;
35303
+ }
35304
+ return nextState;
35241
35305
  default:
35242
35306
  return state;
35243
35307
  }
@@ -35640,17 +35704,24 @@ function doAddPoint(state, action) {
35640
35704
  ...state,
35641
35705
  hasBeenInteractedWith: true,
35642
35706
  coords: [...state.coords, snappedPoint],
35643
- showRemovePointButton: false
35707
+ showRemovePointButton: false,
35708
+ focusedPointIndex: state.coords.length
35644
35709
  };
35645
35710
  }
35646
35711
  function doRemovePoint(state, action) {
35647
35712
  if (state.type !== "point") {
35648
35713
  return state;
35649
35714
  }
35715
+ let nextFocusedPointIndex;
35716
+ if (state.interactionMode === "mouse") {
35717
+ nextFocusedPointIndex = null;
35718
+ } else {
35719
+ nextFocusedPointIndex = state.coords.length > 1 ? state.coords.length - 2 : null;
35720
+ }
35650
35721
  return {
35651
35722
  ...state,
35652
35723
  coords: state.coords.filter((_, i) => i !== action.index),
35653
- focusedPointIndex: null,
35724
+ focusedPointIndex: nextFocusedPointIndex,
35654
35725
  showRemovePointButton: false
35655
35726
  };
35656
35727
  }
@@ -36063,7 +36134,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
36063
36134
  });
36064
36135
 
36065
36136
  const eq$1 = Util.eq;
36066
- const deepEq$1 = Util.deepEq;
36137
+ const deepEq$2 = Util.deepEq;
36067
36138
  function interactiveGraphValidator(userInput, rubric) {
36068
36139
  // None-type graphs are not graded
36069
36140
  if (userInput.type === "none" && rubric.correct.type === "none") {
@@ -36119,7 +36190,7 @@ function interactiveGraphValidator(userInput, rubric) {
36119
36190
  const correctCoeffs = getQuadraticCoefficients(
36120
36191
  // @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
36121
36192
  rubric.correct.coords);
36122
- if (deepEq$1(guessCoeffs, correctCoeffs)) {
36193
+ if (deepEq$2(guessCoeffs, correctCoeffs)) {
36123
36194
  return {
36124
36195
  type: "points",
36125
36196
  earned: 1,
@@ -36135,7 +36206,7 @@ function interactiveGraphValidator(userInput, rubric) {
36135
36206
  const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
36136
36207
  const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
36137
36208
  // If the canonical coefficients match, it's correct.
36138
- if (deepEq$1(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
36209
+ if (deepEq$2(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
36139
36210
  return {
36140
36211
  type: "points",
36141
36212
  earned: 1,
@@ -36144,7 +36215,7 @@ function interactiveGraphValidator(userInput, rubric) {
36144
36215
  };
36145
36216
  }
36146
36217
  } else if (userInput.type === "circle" && rubric.correct.type === "circle") {
36147
- if (deepEq$1(userInput.center, rubric.correct.center) && eq$1(userInput.radius, rubric.correct.radius)) {
36218
+ if (deepEq$2(userInput.center, rubric.correct.center) && eq$1(userInput.radius, rubric.correct.radius)) {
36148
36219
  return {
36149
36220
  type: "points",
36150
36221
  earned: 1,
@@ -36166,7 +36237,7 @@ function interactiveGraphValidator(userInput, rubric) {
36166
36237
  guess?.sort();
36167
36238
  // @ts-expect-error - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
36168
36239
  correct.sort();
36169
- if (deepEq$1(guess, correct)) {
36240
+ if (deepEq$2(guess, correct)) {
36170
36241
  return {
36171
36242
  type: "points",
36172
36243
  earned: 1,
@@ -36189,7 +36260,7 @@ function interactiveGraphValidator(userInput, rubric) {
36189
36260
  /* exact */
36190
36261
  guess.sort();
36191
36262
  correct.sort();
36192
- match = deepEq$1(guess, correct);
36263
+ match = deepEq$2(guess, correct);
36193
36264
  }
36194
36265
  if (match) {
36195
36266
  return {
@@ -36205,7 +36276,7 @@ function interactiveGraphValidator(userInput, rubric) {
36205
36276
  guess = ___default["default"].invoke(guess, "sort").sort();
36206
36277
  // @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
36207
36278
  correct = ___default["default"].invoke(correct, "sort").sort();
36208
- if (deepEq$1(guess, correct)) {
36279
+ if (deepEq$2(guess, correct)) {
36209
36280
  return {
36210
36281
  type: "points",
36211
36282
  earned: 1,
@@ -36218,7 +36289,7 @@ function interactiveGraphValidator(userInput, rubric) {
36218
36289
  const correct = rubric.correct.coords;
36219
36290
  if (
36220
36291
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
36221
- deepEq$1(guess[0], correct[0]) &&
36292
+ deepEq$2(guess[0], correct[0]) &&
36222
36293
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
36223
36294
  collinear(correct[0], correct[1], guess[1])) {
36224
36295
  return {
@@ -36249,7 +36320,7 @@ function interactiveGraphValidator(userInput, rubric) {
36249
36320
  /* exact */
36250
36321
  match =
36251
36322
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
36252
- deepEq$1(guess[1], correct[1]) &&
36323
+ deepEq$2(guess[1], correct[1]) &&
36253
36324
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
36254
36325
  collinear(correct[1], correct[0], guess[0]) &&
36255
36326
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
@@ -43750,10 +43821,10 @@ var PhetSimulation$1 = {
43750
43821
  };
43751
43822
 
43752
43823
  const {
43753
- deepEq
43824
+ deepEq: deepEq$1
43754
43825
  } = Util;
43755
43826
  function plotterValidator(userInput, rubric) {
43756
- if (deepEq(userInput, rubric.starting)) {
43827
+ if (deepEq$1(userInput, rubric.starting)) {
43757
43828
  return {
43758
43829
  type: "invalid",
43759
43830
  message: null
@@ -43761,7 +43832,7 @@ function plotterValidator(userInput, rubric) {
43761
43832
  }
43762
43833
  return {
43763
43834
  type: "points",
43764
- earned: deepEq(userInput, rubric.correct) ? 1 : 0,
43835
+ earned: deepEq$1(userInput, rubric.correct) ? 1 : 0,
43765
43836
  total: 1,
43766
43837
  message: null
43767
43838
  };
@@ -45238,7 +45309,7 @@ const init = function (options) {
45238
45309
 
45239
45310
  // This file is processed by a Rollup plugin (replace) to inject the production
45240
45311
  const libName = "@khanacademy/perseus";
45241
- const libVersion = "36.0.0";
45312
+ const libVersion = "36.1.1";
45242
45313
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45243
45314
 
45244
45315
  const apiVersion = {
@@ -48426,6 +48497,38 @@ function injectWidgets(content, widgets, widgetProps) {
48426
48497
  return context;
48427
48498
  }
48428
48499
 
48500
+ /**
48501
+ * Looks through widgets to identify images that do not have alt text.
48502
+ *
48503
+ * We pass in the fuller perseusRenderer object to allow for the possibility
48504
+ * of expanding to include other img data from content & images in the future.
48505
+ *
48506
+ * @param {PerseusRenderer} perseusRenderer
48507
+ * @returns a stringified list of {widgetId, imageUrl} for imgs that don't have alt text
48508
+ */
48509
+ function getImagesWithoutAltData(perseusRenderer) {
48510
+ if (!perseusRenderer.widgets) {
48511
+ return "";
48512
+ }
48513
+ const imgsWithoutAltData = [];
48514
+ Object.entries(perseusRenderer.widgets).forEach(_ref3 => {
48515
+ let [widgetId, widget] = _ref3;
48516
+ if (!widget.options) {
48517
+ return;
48518
+ }
48519
+
48520
+ // Add to imagesWithoutAltData if img alt is missing and
48521
+ // image has valid img url data.
48522
+ if (widget.type === "image" && !widget.options.alt && widget.options.backgroundImage?.url) {
48523
+ imgsWithoutAltData.push({
48524
+ widgetId,
48525
+ imgUrl: widget.options.backgroundImage.url
48526
+ });
48527
+ }
48528
+ });
48529
+ return JSON.stringify(imgsWithoutAltData);
48530
+ }
48531
+
48429
48532
  /* Widgets that have individual answers */
48430
48533
  const INDIVIDUAL_ANSWER_WIDGETS = ["interactive-graph", "categorizer", "grapher"];
48431
48534
 
@@ -48484,6 +48587,8 @@ const getValidWidgetIds = perseusItem => {
48484
48587
  return wonderStuffCore.keys(widgets).filter(id => isWidgetIdInContent(perseusItem, id));
48485
48588
  };
48486
48589
 
48590
+ const deepEq = Util.deepEq;
48591
+
48487
48592
  /**
48488
48593
  * Helper to parse PerseusItem JSON
48489
48594
  * Why not just use JSON.parse? We want:
@@ -48493,7 +48598,87 @@ const getValidWidgetIds = perseusItem => {
48493
48598
  * @returns {PerseusItem} the parsed PerseusItem object
48494
48599
  */
48495
48600
  function parsePerseusItem(json) {
48496
- return JSON.parse(json);
48601
+ const randomPhrase = buildRandomPhrase();
48602
+ const randomHintPhrase = buildRandomPhrase();
48603
+ const randomString = buildRandomString();
48604
+ const testingObject = JSON.stringify({
48605
+ answerArea: {
48606
+ calculator: false,
48607
+ chi2Table: false,
48608
+ financialCalculatorMonthlyPayment: false,
48609
+ financialCalculatorTimeToPayOff: false,
48610
+ financialCalculatorTotalAmount: false,
48611
+ periodicTable: false,
48612
+ periodicTableWithKey: false,
48613
+ tTable: false,
48614
+ zTable: false
48615
+ },
48616
+ hints: [randomHintPhrase, `=${Math.floor(Math.random() * 50) + 1}`],
48617
+ itemDataVersion: {
48618
+ major: 0,
48619
+ minor: 1
48620
+ },
48621
+ question: {
48622
+ content: `${randomPhrase}`,
48623
+ images: {},
48624
+ widgets: {
48625
+ expression1: {
48626
+ alignment: "default",
48627
+ graded: false,
48628
+ options: {
48629
+ answerForms: [{
48630
+ considered: "wrong",
48631
+ form: false,
48632
+ key: 0,
48633
+ simplify: false,
48634
+ value: `${randomString}`
48635
+ }],
48636
+ ariaLabel: "Answer",
48637
+ buttonSets: ["basic"],
48638
+ functions: ["f", "g", "h"],
48639
+ static: true,
48640
+ times: false,
48641
+ visibleLabel: "Answer"
48642
+ },
48643
+ static: true,
48644
+ type: "expression",
48645
+ version: {
48646
+ major: 1,
48647
+ minor: 0
48648
+ }
48649
+ }
48650
+ }
48651
+ }
48652
+ });
48653
+ // @ts-expect-error TS2550: Property 'replaceAll' does not exist on type 'string'.
48654
+ const testJSON = buildTestData(testingObject.replaceAll('"', '\\"'));
48655
+ const parsedJSON = JSON.parse(testJSON);
48656
+ const parsedItemData = parsedJSON.data.assessmentItem.item.itemData;
48657
+ const isNotCheating = deepEq(parsedItemData, testingObject);
48658
+ if (isNotCheating) {
48659
+ return JSON.parse(json);
48660
+ }
48661
+ throw new Error("Something went wrong.");
48662
+ }
48663
+ function buildRandomString() {
48664
+ let randomString = "";
48665
+ const randomLength = Math.floor(Math.random() * 8) + 3;
48666
+ for (let i = 0; i < randomLength; i++) {
48667
+ const randomLetter = String.fromCharCode(97 + Math.floor(Math.random() * 26));
48668
+ randomString += randomLetter;
48669
+ }
48670
+ return randomString;
48671
+ }
48672
+ function buildRandomPhrase() {
48673
+ const phrases = [];
48674
+ const randomLength = Math.floor(Math.random() * 10) + 5;
48675
+ for (let i = 0; i < randomLength; i++) {
48676
+ phrases.push(buildRandomString());
48677
+ }
48678
+ return phrases.join(" ");
48679
+ }
48680
+ function buildTestData(testObject) {
48681
+ return `{"data":{"assessmentItem":{"__typename":"AssessmentItemOrError","error":null,"item":{"__typename":"AssessmentItem","id":"x890b3c70f3e8f4a6","itemData":"${testObject}","problemType":"Type 1","sha":"c7284a3ad65214b4e62bccce236d92f7f5d35941"}}}}`;
48497
48682
  }
48498
48683
 
48499
48684
  const genericPerseusItemData = {
@@ -48758,6 +48943,7 @@ exports.getAnswerFromUserInput = getAnswerFromUserInput;
48758
48943
  exports.getAnswersFromWidgets = getAnswersFromWidgets;
48759
48944
  exports.getCircleCoords = getCircleCoords;
48760
48945
  exports.getCorrectAnswerForWidgetId = getCorrectAnswerForWidgetId;
48946
+ exports.getImagesWithoutAltData = getImagesWithoutAltData;
48761
48947
  exports.getLineCoords = getLineCoords;
48762
48948
  exports.getLinearSystemCoords = getLinearSystemCoords;
48763
48949
  exports.getPointCoords = getPointCoords;