@khanacademy/perseus 24.3.0 → 25.1.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.js CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var KAS = require('@khanacademy/kas');
6
6
  var mathInput = require('@khanacademy/math-input');
7
+ var perseusCore = require('@khanacademy/perseus-core');
7
8
  var PerseusLinter = require('@khanacademy/perseus-linter');
8
9
  var wonderBlocksCore = require('@khanacademy/wonder-blocks-core');
9
10
  var Tooltip$1 = require('@khanacademy/wonder-blocks-tooltip');
@@ -40,7 +41,6 @@ var Switch = require('@khanacademy/wonder-blocks-switch');
40
41
  var Pill = require('@khanacademy/wonder-blocks-pill');
41
42
  var reactPopper = require('react-popper');
42
43
  var Link = require('@khanacademy/wonder-blocks-link');
43
- var perseusCore = require('@khanacademy/perseus-core');
44
44
 
45
45
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
46
46
 
@@ -803,52 +803,9 @@ var dependencies = /*#__PURE__*/Object.freeze({
803
803
  // TODO(LP-11481): Change the primary API we use for logging in Perseus to use
804
804
  // getDependencies() internally... simplify usage to just using a "singleton"
805
805
  // which get's the injected logger.
806
- /**
807
- * @typedef {Object} Errors utility for referencing the Perseus error taxonomy.
808
- */
809
- const Errors = Object.freeze({
810
- /**
811
- * @property {ErrorKind} Unknown The kind of error is not known.
812
- */
813
- Unknown: "Unknown",
814
- /**
815
- * @property {ErrorKind} Internal The error is internal to the executing code.
816
- */
817
- Internal: "Internal",
818
- /**
819
- * @property {ErrorKind} InvalidInput There was a problem with the provided
820
- * input, such as the wrong format or a null value.
821
- */
822
- InvalidInput: "InvalidInput",
823
- /**
824
- * @property {ErrorKind} NotAllowed There was a problem due to the state of
825
- * the system not matching the requested operation or input. For example,
826
- * trying to create a username that is valid, but is already taken by
827
- * another user. Use {@link InvalidInput} instead when the input isn't
828
- * valid regardless of the state of the system. Use {@link NotFound} when
829
- * the failure is due to not being able to find a resource.
830
- */
831
- NotAllowed: "NotAllowed",
832
- /**
833
- * @property {ErrorKind} TransientService There was a problem when making a
834
- * request to a service.
835
- */
836
- TransientService: "TransientService",
837
- /**
838
- * @property {ErrorKind} Service There was a non-transient problem when
839
- * making a request to service.
840
- */
841
- Service: "Service"
842
- });
843
-
844
- /**
845
- * @type {ErrorKind} The kind of error being reported
846
- */
847
-
848
806
  /**
849
807
  * Provides logging infrastructure for Perseus
850
808
  */
851
-
852
809
  const Log = {
853
810
  // Logs a message
854
811
  log: (message, extra) => {
@@ -860,16 +817,6 @@ const Log = {
860
817
  }
861
818
  };
862
819
 
863
- class PerseusError extends Error {
864
- kind;
865
- metadata;
866
- constructor(message, kind, options) {
867
- super(message);
868
- this.kind = kind;
869
- this.metadata = options?.metadata;
870
- }
871
- }
872
-
873
820
  /**
874
821
  * These are things that widgets should exclude when serializing themselves.
875
822
  *
@@ -973,9 +920,9 @@ const change = function (newPropsOrSinglePropName, propValue, callback) {
973
920
  // string propName
974
921
  propValue, callback);
975
922
  }
976
- throw new PerseusError("Invalid types sent to this.change(): " +
923
+ throw new perseusCore.PerseusError("Invalid types sent to this.change(): " +
977
924
  // eslint-disable-next-line prefer-rest-params
978
- ___default["default"].toArray(arguments).join() + "\n" + USAGE, Errors.Internal);
925
+ ___default["default"].toArray(arguments).join() + "\n" + USAGE, perseusCore.Errors.Internal);
979
926
  };
980
927
  const propTypes = {
981
928
  onChange: PropTypes__default["default"].func.isRequired
@@ -2148,9 +2095,9 @@ const KhanAnswerTypes = {
2148
2095
  parseSolution: function (solutionString, options) {
2149
2096
  let solution = KAS__namespace.parse(solutionString, options);
2150
2097
  if (!solution.parsed) {
2151
- throw new PerseusError("The provided solution (" + solutionString + ") didn't parse.", Errors.InvalidInput);
2098
+ throw new perseusCore.PerseusError("The provided solution (" + solutionString + ") didn't parse.", perseusCore.Errors.InvalidInput);
2152
2099
  } else if (options.simplified && !solution.expr.isSimplified()) {
2153
- throw new PerseusError("The provided solution (" + solutionString + ") isn't fully expanded and simplified.", Errors.InvalidInput);
2100
+ throw new perseusCore.PerseusError("The provided solution (" + solutionString + ") isn't fully expanded and simplified.", perseusCore.Errors.InvalidInput);
2154
2101
  } else {
2155
2102
  solution = solution.expr;
2156
2103
  }
@@ -2341,7 +2288,7 @@ class Expression extends React__namespace.Component {
2341
2288
  // in the function variables list for the expression.
2342
2289
  if (!expression.parsed) {
2343
2290
  /* c8 ignore next */
2344
- Log.error("Unable to parse solution answer for expression", Errors.InvalidInput, {
2291
+ Log.error("Unable to parse solution answer for expression", perseusCore.Errors.InvalidInput, {
2345
2292
  loggedMetadata: {
2346
2293
  rubric: JSON.stringify(rubric)
2347
2294
  }
@@ -3032,7 +2979,7 @@ function combineScores(scoreA, scoreB) {
3032
2979
  * The above checks cover all combinations of score type, so if we get here
3033
2980
  * then something is amiss with our inputs.
3034
2981
  */
3035
- throw new PerseusError("PerseusScore with unknown type encountered", Errors.InvalidInput, {
2982
+ throw new perseusCore.PerseusError("PerseusScore with unknown type encountered", perseusCore.Errors.InvalidInput, {
3036
2983
  metadata: {
3037
2984
  scoreA: JSON.stringify(scoreA),
3038
2985
  scoreB: JSON.stringify(scoreB)
@@ -3062,9 +3009,9 @@ function keScoreFromPerseusScore(score, guess, state) {
3062
3009
  state: state
3063
3010
  };
3064
3011
  }
3065
- throw new PerseusError(
3012
+ throw new perseusCore.PerseusError(
3066
3013
  // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
3067
- "Invalid score type: " + score.type, Errors.InvalidInput, {
3014
+ "Invalid score type: " + score.type, perseusCore.Errors.InvalidInput, {
3068
3015
  metadata: {
3069
3016
  score: JSON.stringify(score),
3070
3017
  guess: JSON.stringify(guess),
@@ -4433,7 +4380,7 @@ const MovableHelperMethods = {
4433
4380
  return memo;
4434
4381
  }
4435
4382
  // Anything else is an error
4436
- throw new PerseusError("Constraint returned invalid result: " + result, Errors.Internal);
4383
+ throw new perseusCore.PerseusError("Constraint returned invalid result: " + result, perseusCore.Errors.Internal);
4437
4384
  }, current, this);
4438
4385
  },
4439
4386
  /**
@@ -4539,7 +4486,7 @@ const FUNCTION_ARRAY_OPTIONS = ["add", "modify", "draw", "remove", "constraints"
4539
4486
  const InteractiveUtil = {
4540
4487
  assert: function (isTrue, message) {
4541
4488
  if (!isTrue) {
4542
- throw new PerseusError("Assertion Error" + (message ? ": " + message : ""), Errors.Internal);
4489
+ throw new perseusCore.PerseusError("Assertion Error" + (message ? ": " + message : ""), perseusCore.Errors.Internal);
4543
4490
  }
4544
4491
  },
4545
4492
  /**
@@ -9120,7 +9067,7 @@ class Graphie$1 {
9120
9067
  // TODO(jeremy): This can be removed once we're confident that callers
9121
9068
  // obey the Typescript types.
9122
9069
  if (options.range == null) {
9123
- throw new PerseusError("range should be specified in graph init", Errors.Internal);
9070
+ throw new perseusCore.PerseusError("range should be specified in graph init", perseusCore.Errors.Internal);
9124
9071
  }
9125
9072
  this.#bounds = new GraphBounds(...options.range);
9126
9073
  this.#drawingTransform = new DrawingTransform(this.raphael, scale, this.bounds());
@@ -13707,7 +13654,7 @@ function MovableAngle(graphie, options) {
13707
13654
 
13708
13655
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
13709
13656
  if (!this.points || this.points.length !== 3) {
13710
- throw new PerseusError("MovableAngle requires 3 points", Errors.InvalidInput);
13657
+ throw new perseusCore.PerseusError("MovableAngle requires 3 points", perseusCore.Errors.InvalidInput);
13711
13658
  }
13712
13659
 
13713
13660
  // Handle coordinates that are not MovablePoints (i.e. [2, 4])
@@ -13998,9 +13945,9 @@ function GraphieMovable$1(descriptor) {
13998
13945
  ___default["default"].extend(this, descriptor);
13999
13946
  }
14000
13947
  const abstractMethod = function () {
14001
- throw new PerseusError("Abstract method! Must be implemented by Graphie Movable" +
13948
+ throw new perseusCore.PerseusError("Abstract method! Must be implemented by Graphie Movable" +
14002
13949
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
14003
- this.constructor.displayName, Errors.NotAllowed);
13950
+ this.constructor.displayName, perseusCore.Errors.NotAllowed);
14004
13951
  };
14005
13952
  ___default["default"].extend(GraphieMovable$1.prototype, {
14006
13953
  movableProps: [],
@@ -14040,7 +13987,7 @@ const createClass = function (spec) {
14040
13987
  const GraphieClass = function (props) {
14041
13988
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
14042
13989
  if (!(this instanceof GraphieClass)) {
14043
- throw new PerseusError("Use createElement or JSX with graphie movables", Errors.NotAllowed);
13990
+ throw new perseusCore.PerseusError("Use createElement or JSX with graphie movables", perseusCore.Errors.NotAllowed);
14044
13991
  }
14045
13992
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
14046
13993
  this.props = rewriteProps(props, props.children || []);
@@ -14359,7 +14306,7 @@ class Movable {
14359
14306
  return memo;
14360
14307
  }
14361
14308
  // Anything else is an error
14362
- throw new PerseusError("Constraint returned invalid result: " + result, Errors.Internal);
14309
+ throw new perseusCore.PerseusError("Constraint returned invalid result: " + result, perseusCore.Errors.Internal);
14363
14310
  }, current);
14364
14311
  }
14365
14312
 
@@ -15709,7 +15656,7 @@ class MovablePoint$4 {
15709
15656
  return memo;
15710
15657
  }
15711
15658
  // Anything else is an error
15712
- throw new PerseusError("Constraint returned invalid result: " + result, Errors.Internal);
15659
+ throw new perseusCore.PerseusError("Constraint returned invalid result: " + result, perseusCore.Errors.Internal);
15713
15660
  }, current);
15714
15661
  }
15715
15662
 
@@ -16864,7 +16811,7 @@ class Graphie extends React__namespace.Component {
16864
16811
  // would have horrible performance implications. In order to avoid
16865
16812
  // those, we just warn here.
16866
16813
  if (this.props.setup !== prevProps.setup) {
16867
- 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.", Errors.Internal);
16814
+ 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);
16868
16815
  }
16869
16816
  if (!deepEq$2(this.props.options, prevProps.options) || !deepEq$2(this.props.box, prevProps.box) || !deepEq$2(this.props.range, prevProps.range)) {
16870
16817
  this._setupGraphie();
@@ -17043,7 +16990,7 @@ class Graphie extends React__namespace.Component {
17043
16990
  // This generally is a bad idea, so warn about it if this
17044
16991
  // is being caused by implicit keys
17045
16992
  if (keyProp == null) {
17046
- Log.error("Replacing a <Graphie> child with a " + "child of a different type. Please add keys " + "to your <Graphie> children", Errors.Internal);
16993
+ Log.error("Replacing a <Graphie> child with a " + "child of a different type. Please add keys " + "to your <Graphie> children", perseusCore.Errors.Internal);
17047
16994
  }
17048
16995
  prevMovable.remove();
17049
16996
  child.add(graphie);
@@ -17320,7 +17267,7 @@ function getLocalizedDataUrl(url) {
17320
17267
  function getUrlHash(url) {
17321
17268
  const match = url.match(hashRegex);
17322
17269
  if (match == null) {
17323
- throw new PerseusError("not a valid URL", Errors.InvalidInput);
17270
+ throw new perseusCore.PerseusError("not a valid URL", perseusCore.Errors.InvalidInput);
17324
17271
  }
17325
17272
  return match && match[1];
17326
17273
  }
@@ -17457,7 +17404,7 @@ class SvgImage extends React__namespace.Component {
17457
17404
  // If there is isn't any localized data, fall back to
17458
17405
  // the original, unlocalized data
17459
17406
  retrieveData(Util.getDataUrl(this.props.src), (x, status, error) => {
17460
- Log.error("Data load failed for svg-image", Errors.Service, {
17407
+ Log.error("Data load failed for svg-image", perseusCore.Errors.Service, {
17461
17408
  cause: error,
17462
17409
  loggedMetadata: {
17463
17410
  dataUrl: Util.getDataUrl(this.props.src),
@@ -17468,7 +17415,7 @@ class SvgImage extends React__namespace.Component {
17468
17415
  });
17469
17416
  } else {
17470
17417
  retrieveData(Util.getDataUrl(this.props.src), (x, status, error) => {
17471
- Log.error("Data load failed for svg-image", Errors.Service, {
17418
+ Log.error("Data load failed for svg-image", perseusCore.Errors.Service, {
17472
17419
  cause: error,
17473
17420
  loggedMetadata: {
17474
17421
  dataUrl: Util.getDataUrl(this.props.src),
@@ -17720,7 +17667,7 @@ class SvgImage extends React__namespace.Component {
17720
17667
  } else if (this.state.imageDimensions) {
17721
17668
  box = [this.state.imageDimensions[0] * this.props.scale, this.state.imageDimensions[1] * this.props.scale];
17722
17669
  } else {
17723
- throw new PerseusError("svg-image has no dimensions", Errors.InvalidInput, {
17670
+ throw new perseusCore.PerseusError("svg-image has no dimensions", perseusCore.Errors.InvalidInput, {
17724
17671
  metadata: {
17725
17672
  src: this.props.src
17726
17673
  }
@@ -18148,7 +18095,7 @@ class ErrorBoundary extends React__namespace.Component {
18148
18095
  // differentiate between the different errors that are thrown, they
18149
18096
  // all end up being grouped as a single Sentry event, which is very
18150
18097
  // unhelpful.
18151
- "Unhandled Perseus error: " + error.message, Errors.Internal, {
18098
+ "Unhandled Perseus error: " + error.message, perseusCore.Errors.Internal, {
18152
18099
  cause: error,
18153
18100
  loggedMetadata: {
18154
18101
  componentStack: !!info && "componentStack" in info ? info.componentStack : "componentStack not provided",
@@ -19201,7 +19148,7 @@ const replaceWidget = (name, replacementName) => {
19201
19148
  // that doesn't exist at all.
19202
19149
  if (!substituteWidget) {
19203
19150
  const errorMsg = `Failed to replace ${name} with ${replacementName}`;
19204
- throw new PerseusError(errorMsg, Errors.Internal);
19151
+ throw new perseusCore.PerseusError(errorMsg, perseusCore.Errors.Internal);
19205
19152
  }
19206
19153
  registerWidget(name, substituteWidget);
19207
19154
  };
@@ -19215,7 +19162,7 @@ const replaceDeprecatedWidgets = () => {
19215
19162
  const registerEditors = editorsToRegister => {
19216
19163
  editorsToRegister.forEach(editor => {
19217
19164
  if (!editor.widgetName) {
19218
- throw new PerseusError(`Editor ${editor.displayName} doesn't have a widgetName property`, Errors.Internal);
19165
+ throw new perseusCore.PerseusError(`Editor ${editor.displayName} doesn't have a widgetName property`, perseusCore.Errors.Internal);
19219
19166
  }
19220
19167
  editors[editor.widgetName] = editor;
19221
19168
  });
@@ -19233,7 +19180,7 @@ const replaceEditor = (name, replacementName) => {
19233
19180
  const substituteEditor = editors[replacementName];
19234
19181
  if (!substituteEditor && Log) {
19235
19182
  const errorMsg = `Failed to replace editor ${name} with ${replacementName}`;
19236
- Log.error(errorMsg, Errors.Internal);
19183
+ Log.error(errorMsg, perseusCore.Errors.Internal);
19237
19184
  return;
19238
19185
  }
19239
19186
  editors[name] = substituteEditor;
@@ -19307,7 +19254,7 @@ const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
19307
19254
  // suspect we have legacy data (potentially unpublished) and we should
19308
19255
  // figure that out before depending solely on types.
19309
19256
  if (!___default["default"].isString(type)) {
19310
- throw new PerseusError("widget type must be a string, but was: " + type, Errors.Internal);
19257
+ throw new perseusCore.PerseusError("widget type must be a string, but was: " + type, perseusCore.Errors.Internal);
19311
19258
  }
19312
19259
  const widgetExports = widgets[type];
19313
19260
  if (widgetExports == null) {
@@ -19358,7 +19305,7 @@ const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
19358
19305
  // This is a Log.error because it is unlikely to be hit in
19359
19306
  // local testing, and a Log.error is slightly less scary in
19360
19307
  // prod than a `throw new Error`
19361
- Log.error("No upgrade found for widget. Cannot render.", Errors.Internal, {
19308
+ Log.error("No upgrade found for widget. Cannot render.", perseusCore.Errors.Internal, {
19362
19309
  loggedMetadata: {
19363
19310
  type,
19364
19311
  fromMajorVersion: nextVersion - 1,
@@ -19431,7 +19378,7 @@ const getRendererPropsForWidgetInfo = (widgetInfo, strings, problemNum) => {
19431
19378
  };
19432
19379
  const traverseChildWidgets$3 = (widgetInfo, traverseRenderer) => {
19433
19380
  if (!traverseRenderer) {
19434
- throw new PerseusError("traverseRenderer must be provided, but was not", Errors.Internal);
19381
+ throw new perseusCore.PerseusError("traverseRenderer must be provided, but was not", perseusCore.Errors.Internal);
19435
19382
  }
19436
19383
  if (!widgetInfo || !widgetInfo.type || !widgets[widgetInfo.type]) {
19437
19384
  return widgetInfo;
@@ -19500,12 +19447,12 @@ const validAlignments = ["block", "inline-block", "inline", "float-left", "float
19500
19447
  const validateAlignments = () => {
19501
19448
  ___default["default"].each(widgets, function (widgetInfo) {
19502
19449
  if (widgetInfo.defaultAlignment && !___default["default"].contains(validAlignments, widgetInfo.defaultAlignment)) {
19503
- throw new PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid defaultAlignment value: " + widgetInfo.defaultAlignment, Errors.InvalidInput);
19450
+ throw new perseusCore.PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid defaultAlignment value: " + widgetInfo.defaultAlignment, perseusCore.Errors.InvalidInput);
19504
19451
  }
19505
19452
  if (widgetInfo.supportedAlignments) {
19506
19453
  const unknownAlignments = ___default["default"].difference(widgetInfo.supportedAlignments, validAlignments);
19507
19454
  if (unknownAlignments.length) {
19508
- throw new PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid value for supportedAlignments: " + unknownAlignments.join(" "), Errors.InvalidInput);
19455
+ throw new perseusCore.PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid value for supportedAlignments: " + unknownAlignments.join(" "), perseusCore.Errors.InvalidInput);
19509
19456
  }
19510
19457
  }
19511
19458
  });
@@ -20131,7 +20078,7 @@ class Renderer extends React__namespace.Component {
20131
20078
 
20132
20079
  // If the two lists of IDs match (ignoring order)
20133
20080
  if (serializedWidgetIds.length !== widgetPropIds.length || ___default["default"].intersection(serializedWidgetIds, widgetPropIds).length !== serializedWidgetIds.length) {
20134
- Log.error("Refusing to restore bad serialized state:", Errors.Internal, {
20081
+ Log.error("Refusing to restore bad serialized state:", perseusCore.Errors.Internal, {
20135
20082
  loggedMetadata: {
20136
20083
  serializedState: JSON.stringify(serializedState),
20137
20084
  currentProps: JSON.stringify(this.state.widgetProps)
@@ -20289,7 +20236,7 @@ class Renderer extends React__namespace.Component {
20289
20236
  return function (id) {
20290
20237
  let focusPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
20291
20238
  if (!___default["default"].isArray(focusPath)) {
20292
- throw new PerseusError("widget props.onFocus focusPath must be an Array, " + "but was" + JSON.stringify(focusPath), Errors.Internal);
20239
+ throw new perseusCore.PerseusError("widget props.onFocus focusPath must be an Array, " + "but was" + JSON.stringify(focusPath), perseusCore.Errors.Internal);
20293
20240
  }
20294
20241
  _this2._setCurrentFocus([id].concat(focusPath));
20295
20242
  };
@@ -20773,7 +20720,7 @@ class Renderer extends React__namespace.Component {
20773
20720
  // object return value.
20774
20721
  // The result of focus was a {path, id} object itself
20775
20722
  path = [id].concat(focusResult.path || []);
20776
- Log.error("Renderer received a focus result of type 'object' " + "instead of the expected type 'boolean'", Errors.Internal, {
20723
+ Log.error("Renderer received a focus result of type 'object' " + "instead of the expected type 'boolean'", perseusCore.Errors.Internal, {
20777
20724
  loggedMetadata: {
20778
20725
  focusResult: JSON.stringify(focusResult),
20779
20726
  currentProps: JSON.stringify(this.state.widgetProps)
@@ -28723,7 +28670,44 @@ function inset(amount, box) {
28723
28670
  return [trim(amount[X], box[X]), trim(amount[Y], box[Y])];
28724
28671
  }
28725
28672
 
28673
+ // [L]inear Int[erp]olation: gets the weighted average of two values `a` and
28674
+ // `b`, with the given `fraction` specifying how much weight `a` and `b` get:
28675
+ // - if `fraction` is 0, `lerp` returns `a`.
28676
+ // - if `fraction` is 0.5, `lerp` returns the average of `a` and `b`.
28677
+ // - if `fraction` is 1, `lerp` returns `b`.
28678
+ function lerp(a, b, fraction) {
28679
+ return (b - a) * clamp(fraction, 0, 1) + a;
28680
+ }
28681
+
28682
+ // Determines whether two line segments have exactly one intersection point.
28683
+ // The segments are treated as "open" - that is, not inclusive of their
28684
+ // endpoints.
28685
+ // Algorithm from https://stackoverflow.com/a/24392281/7347484
28686
+ const segmentsIntersect = (_ref, _ref2) => {
28687
+ let [[a, b], [c, d]] = _ref;
28688
+ let [[p, q], [r, s]] = _ref2;
28689
+ const determinant = (c - a) * (s - q) - (r - p) * (d - b);
28690
+ if (determinant === 0) {
28691
+ return false;
28692
+ } else {
28693
+ const lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / determinant;
28694
+ const gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / determinant;
28695
+ return 0 < lambda && lambda < 1 && 0 < gamma && gamma < 1;
28696
+ }
28697
+ };
28698
+
28726
28699
  // This file contains helper functions for working with angles.
28700
+
28701
+ function convertDegreesToRadians(degrees) {
28702
+ return degrees / 180 * Math.PI;
28703
+ }
28704
+
28705
+ // Returns a value between -180 and 180, inclusive. The angle is measured
28706
+ // between the positive x-axis and the given vector.
28707
+ function calculateAngleInDegrees(_ref) {
28708
+ let [x, y] = _ref;
28709
+ return Math.atan2(y, x) * 180 / Math.PI;
28710
+ }
28727
28711
  function polar(r, th) {
28728
28712
  if (typeof r === "number") {
28729
28713
  r = [r, r];
@@ -29061,52 +29045,6 @@ function Arrowhead(props) {
29061
29045
  })));
29062
29046
  }
29063
29047
 
29064
- function calculateAngleInDegrees(_ref) {
29065
- let [x, y] = _ref;
29066
- return Math.atan2(y, x) * 180 / Math.PI;
29067
- }
29068
-
29069
- /**
29070
- * Given a ray and a rectangular box, find the point where the ray intersects
29071
- * the edge of the box. Assumes the `initialPoint` is inside the box.
29072
- * @param initialPoint - The starting point of the ray.
29073
- * @param throughPoint - A point that the ray passes through. Must be different from initialPoint.
29074
- * @param box - The box with which to intersect the ray, in the form [[xMin, xMax], [yMin, yMax]]
29075
- */
29076
- const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
29077
- const [[xMin, xMax], [yMin, yMax]] = box;
29078
- const [aX, aY] = initialPoint;
29079
- const [bX, bY] = throughPoint;
29080
- const yDiff = bY - aY;
29081
- const xDiff = bX - aX;
29082
- const slope = yDiff / xDiff;
29083
- const inverseSlope = 1 / slope;
29084
- const xExtreme = xDiff < 0 ? xMin : xMax;
29085
- const yExtreme = yDiff < 0 ? yMin : yMax;
29086
- const yAtXExtreme = aY + (xExtreme - aX) * slope;
29087
- const xAtYExtreme = aX + (yExtreme - aY) * inverseSlope;
29088
- switch (true) {
29089
- // does the ray exit the graph bounding box via the left or right edge?
29090
- case isBetween(yAtXExtreme, yMin, yMax):
29091
- return [xExtreme, yAtXExtreme];
29092
-
29093
- // does the ray exit the graph bounding box via the top or bottom edge?
29094
- case isBetween(xAtYExtreme, xMin, xMax):
29095
- return [xAtYExtreme, yExtreme];
29096
- default:
29097
- return [xExtreme, yExtreme];
29098
- }
29099
- };
29100
- const getLines = points => {
29101
- return points.map((point, i) => {
29102
- const next = points[(i + 1) % points.length];
29103
- return [point, next];
29104
- });
29105
- };
29106
- function isBetween(x, low, high) {
29107
- return x >= low && x <= high;
29108
- }
29109
-
29110
29048
  function SVGLine(props) {
29111
29049
  const {
29112
29050
  start,
@@ -29152,6 +29090,41 @@ function Vector(props) {
29152
29090
  }));
29153
29091
  }
29154
29092
 
29093
+ /**
29094
+ * Given a ray and a rectangular box, find the point where the ray intersects
29095
+ * the edge of the box. Assumes the `initialPoint` is inside the box.
29096
+ * @param initialPoint - The starting point of the ray.
29097
+ * @param throughPoint - A point that the ray passes through. Must be different from initialPoint.
29098
+ * @param box - The box with which to intersect the ray, in the form [[xMin, xMax], [yMin, yMax]]
29099
+ */
29100
+ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
29101
+ const [[xMin, xMax], [yMin, yMax]] = box;
29102
+ const [aX, aY] = initialPoint;
29103
+ const [bX, bY] = throughPoint;
29104
+ const yDiff = bY - aY;
29105
+ const xDiff = bX - aX;
29106
+ const slope = yDiff / xDiff;
29107
+ const inverseSlope = 1 / slope;
29108
+ const xExtreme = xDiff < 0 ? xMin : xMax;
29109
+ const yExtreme = yDiff < 0 ? yMin : yMax;
29110
+ const yAtXExtreme = aY + (xExtreme - aX) * slope;
29111
+ const xAtYExtreme = aX + (yExtreme - aY) * inverseSlope;
29112
+ switch (true) {
29113
+ // does the ray exit the graph bounding box via the left or right edge?
29114
+ case isBetween(yAtXExtreme, yMin, yMax):
29115
+ return [xExtreme, yAtXExtreme];
29116
+
29117
+ // does the ray exit the graph bounding box via the top or bottom edge?
29118
+ case isBetween(xAtYExtreme, xMin, xMax):
29119
+ return [xAtYExtreme, yExtreme];
29120
+ default:
29121
+ return [xExtreme, yExtreme];
29122
+ }
29123
+ };
29124
+ function isBetween(x, low, high) {
29125
+ return x >= low && x <= high;
29126
+ }
29127
+
29155
29128
  const LockedLine = props => {
29156
29129
  const {
29157
29130
  color,
@@ -29611,10 +29584,7 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
29611
29584
  })?.[0] ?? "blue";
29612
29585
  const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""} ${focusClass(focusBehavior)}`;
29613
29586
  const [[x, y]] = useTransformVectorsToPixels(point);
29614
-
29615
- // TODO(benchristel): destructure range in one line
29616
- const [xMin, xMax] = range[X];
29617
- const [yMin, yMax] = range[Y];
29587
+ const [[xMin, xMax], [yMin, yMax]] = range;
29618
29588
  const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
29619
29589
  const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
29620
29590
  const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
@@ -30201,26 +30171,11 @@ const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
30201
30171
  // Create a ray from the midpoint (inside angle) to the edge of the range
30202
30172
  const rangeIntersectionPoint = getIntersectionOfRayWithBox(midpoint, vertex, range);
30203
30173
  let lineIntersectionCount = 0;
30204
- polygonLines.forEach(line => linesIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
30174
+ polygonLines.forEach(line => segmentsIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
30205
30175
 
30206
30176
  // If the number of intersections is even, the angle is inside the polygon
30207
30177
  return !isEven(lineIntersectionCount);
30208
30178
  };
30209
-
30210
- // https://stackoverflow.com/a/24392281/7347484
30211
- // The "intersects" function in geometry doesn't seem to work for this use case
30212
- const linesIntersect = (_ref5, _ref6) => {
30213
- let [[a, b], [c, d]] = _ref5;
30214
- let [[p, q], [r, s]] = _ref6;
30215
- const determinant = (c - a) * (s - q) - (r - p) * (d - b);
30216
- if (determinant === 0) {
30217
- return false;
30218
- } else {
30219
- const lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / determinant;
30220
- const gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / determinant;
30221
- return 0 < lambda && lambda < 1 && 0 < gamma && gamma < 1;
30222
- }
30223
- };
30224
30179
  const isEven = n => n % 2 === 0;
30225
30180
 
30226
30181
  // This function calculates the bisector point of an angle formed by two points
@@ -30414,6 +30369,12 @@ const PolygonGraph = props => {
30414
30369
  onMove: destination => dispatch(movePoint(i, destination))
30415
30370
  })));
30416
30371
  };
30372
+ function getLines(points) {
30373
+ return points.map((point, i) => {
30374
+ const next = points[(i + 1) % points.length];
30375
+ return [point, next];
30376
+ });
30377
+ }
30417
30378
 
30418
30379
  function getGradableGraph(state, initialGraph) {
30419
30380
  if (!state.hasBeenInteractedWith) {
@@ -30803,12 +30764,8 @@ const AxisTickLabels = () => {
30803
30764
  tickStep,
30804
30765
  range
30805
30766
  } = graphConfig;
30806
-
30807
- // TODO(benchristel): use destructuring here
30808
- const [xMin, xMax] = range[X];
30809
- const [yMin, yMax] = range[Y];
30810
- const yTickStep = tickStep[Y];
30811
- const xTickStep = tickStep[X];
30767
+ const [[xMin, xMax], [yMin, yMax]] = range;
30768
+ const [xTickStep, yTickStep] = tickStep;
30812
30769
  const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
30813
30770
  const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
30814
30771
  return /*#__PURE__*/React__namespace.createElement("div", {
@@ -31192,12 +31149,8 @@ const AxisTicks = () => {
31192
31149
  width,
31193
31150
  height
31194
31151
  };
31195
-
31196
- // TODO(benchristel): destructure these in one line
31197
- const [xMin, xMax] = range[X];
31198
- const [yMin, yMax] = range[Y];
31199
- const yTickStep = tickStep[Y];
31200
- const xTickStep = tickStep[X];
31152
+ const [[xMin, xMax], [yMin, yMax]] = range;
31153
+ const [xTickStep, yTickStep] = tickStep;
31201
31154
  const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
31202
31155
  const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
31203
31156
  return /*#__PURE__*/React__namespace.createElement("g", {
@@ -31734,7 +31687,7 @@ const PropCheckBox = createReactClass__default["default"]({
31734
31687
  return !___default["default"].has(this.DEFAULT_PROPS, localPropName);
31735
31688
  }, this);
31736
31689
  if (!propName) {
31737
- throw new PerseusError("Attempted to create a PropCheckBox with no prop!", Errors.InvalidInput);
31690
+ throw new perseusCore.PerseusError("Attempted to create a PropCheckBox with no prop!", perseusCore.Errors.InvalidInput);
31738
31691
  }
31739
31692
  return propName;
31740
31693
  },
@@ -31927,7 +31880,7 @@ function Protractor() {
31927
31880
  function RotationArrow() {
31928
31881
  const radius = 175;
31929
31882
  const angleDeg = 10;
31930
- const angleRad = degreesToRadians(angleDeg);
31883
+ const angleRad = convertDegreesToRadians(angleDeg);
31931
31884
  const endX = radius * (1 - Math.cos(angleRad));
31932
31885
  const endY = radius * -Math.sin(angleRad);
31933
31886
  const rotationArrow = pathBuilder().move(0, 0).circularArc(radius, endX, endY, {
@@ -32006,17 +31959,135 @@ function useDraggablePx(args) {
32006
31959
  });
32007
31960
  }
32008
31961
 
32009
- // [L]inear Int[erp]olation: gets the weighted average of two values `a` and
32010
- // `b`, with the given `fraction` specifying how much weight `a` and `b` get:
32011
- // - if `fraction` is 0, `lerp` returns `a`.
32012
- // - if `fraction` is 0.5, `lerp` returns the average of `a` and `b`.
32013
- // - if `fraction` is 1, `lerp` returns `b`.
32014
- function lerp(a, b, fraction) {
32015
- return (b - a) * fraction + a;
32016
- }
32017
- function degreesToRadians(degrees) {
32018
- return degrees / 180 * Math.PI;
32019
- }
31962
+ const MafsGraph = props => {
31963
+ const {
31964
+ state,
31965
+ dispatch,
31966
+ labels
31967
+ } = props;
31968
+ const [width, height] = props.box;
31969
+ const tickStep = props.step;
31970
+ return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
31971
+ value: {
31972
+ range: state.range,
31973
+ snapStep: state.snapStep,
31974
+ markings: props.markings,
31975
+ tickStep: tickStep,
31976
+ gridStep: props.gridStep,
31977
+ showTooltips: !!props.showTooltips,
31978
+ graphDimensionsInPixels: props.box,
31979
+ width,
31980
+ height,
31981
+ labels
31982
+ }
31983
+ }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31984
+ className: "mafs-graph",
31985
+ style: {
31986
+ width,
31987
+ height,
31988
+ position: "relative",
31989
+ padding: "25px 25px 0 0",
31990
+ boxSizing: "content-box",
31991
+ marginLeft: "20px",
31992
+ marginBottom: "20px"
31993
+ }
31994
+ }, /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
31995
+ box: props.box,
31996
+ backgroundImage: props.backgroundImage
31997
+ }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
31998
+ style: {
31999
+ position: "absolute",
32000
+ bottom: 0,
32001
+ left: 0
32002
+ }
32003
+ }, props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, null), /*#__PURE__*/React__namespace.createElement(AxisTickLabels, null)), /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
32004
+ preserveAspectRatio: false,
32005
+ viewBox: {
32006
+ x: state.range[X],
32007
+ y: state.range[Y],
32008
+ padding: 0
32009
+ },
32010
+ pan: false,
32011
+ zoom: false,
32012
+ width: width,
32013
+ height: height
32014
+ }, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement(Grid, {
32015
+ tickStep: props.step,
32016
+ gridStep: props.gridStep,
32017
+ range: state.range,
32018
+ containerSizeClass: props.containerSizeClass,
32019
+ markings: props.markings
32020
+ }), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
32021
+ lockedFigures: props.lockedFigures,
32022
+ range: state.range
32023
+ }), "/* Protractor */", props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
32024
+ state,
32025
+ dispatch
32026
+ })))));
32027
+ };
32028
+ const renderGraph = props => {
32029
+ const {
32030
+ state,
32031
+ dispatch
32032
+ } = props;
32033
+ const {
32034
+ type
32035
+ } = state;
32036
+ switch (type) {
32037
+ case "angle":
32038
+ return /*#__PURE__*/React__namespace.createElement(AngleGraph, {
32039
+ graphState: state,
32040
+ dispatch: dispatch
32041
+ });
32042
+ case "segment":
32043
+ return /*#__PURE__*/React__namespace.createElement(SegmentGraph, {
32044
+ graphState: state,
32045
+ dispatch: dispatch
32046
+ });
32047
+ case "linear-system":
32048
+ return /*#__PURE__*/React__namespace.createElement(LinearSystemGraph, {
32049
+ graphState: state,
32050
+ dispatch: dispatch
32051
+ });
32052
+ case "linear":
32053
+ return /*#__PURE__*/React__namespace.createElement(LinearGraph, {
32054
+ graphState: state,
32055
+ dispatch: dispatch
32056
+ });
32057
+ case "ray":
32058
+ return /*#__PURE__*/React__namespace.createElement(RayGraph, {
32059
+ graphState: state,
32060
+ dispatch: dispatch
32061
+ });
32062
+ case "polygon":
32063
+ return /*#__PURE__*/React__namespace.createElement(PolygonGraph, {
32064
+ graphState: state,
32065
+ dispatch: dispatch
32066
+ });
32067
+ case "point":
32068
+ return /*#__PURE__*/React__namespace.createElement(PointGraph, {
32069
+ graphState: state,
32070
+ dispatch: dispatch
32071
+ });
32072
+ case "circle":
32073
+ return /*#__PURE__*/React__namespace.createElement(CircleGraph, {
32074
+ graphState: state,
32075
+ dispatch: dispatch
32076
+ });
32077
+ case "quadratic":
32078
+ return /*#__PURE__*/React__namespace.createElement(QuadraticGraph, {
32079
+ graphState: state,
32080
+ dispatch: dispatch
32081
+ });
32082
+ case "sinusoid":
32083
+ return /*#__PURE__*/React__namespace.createElement(SinusoidGraph, {
32084
+ graphState: state,
32085
+ dispatch: dispatch
32086
+ });
32087
+ default:
32088
+ return new wonderStuffCore.UnreachableCaseError(type);
32089
+ }
32090
+ };
32020
32091
 
32021
32092
  function initializeGraphState(params) {
32022
32093
  const {
@@ -32112,6 +32183,10 @@ function getPointCoords(graph, range, step) {
32112
32183
  if (coords) {
32113
32184
  return coords;
32114
32185
  }
32186
+ const startCoords = graph.startCoords?.slice();
32187
+ if (startCoords) {
32188
+ return startCoords;
32189
+ }
32115
32190
  switch (numPoints) {
32116
32191
  case 1:
32117
32192
  // Back in the day, one point's coords were in graph.coord
@@ -32145,6 +32220,9 @@ function getSegmentCoords(graph, range, step) {
32145
32220
  if (graph.coords) {
32146
32221
  return graph.coords;
32147
32222
  }
32223
+ if (graph.startCoords) {
32224
+ return graph.startCoords;
32225
+ }
32148
32226
  const ys = n => {
32149
32227
  switch (n) {
32150
32228
  case 2:
@@ -32174,12 +32252,18 @@ function getLineCoords(graph, range, step) {
32174
32252
  if (graph.coords) {
32175
32253
  return graph.coords;
32176
32254
  }
32255
+ if (graph.startCoords) {
32256
+ return graph.startCoords;
32257
+ }
32177
32258
  return normalizePoints(range, step, defaultLinearCoords[0]);
32178
32259
  }
32179
32260
  function getLinearSystemCoords(graph, range, step) {
32180
32261
  if (graph.coords) {
32181
32262
  return graph.coords;
32182
32263
  }
32264
+ if (graph.startCoords) {
32265
+ return graph.startCoords;
32266
+ }
32183
32267
  return defaultLinearCoords.map(points => normalizePoints(range, step, points));
32184
32268
  }
32185
32269
  function getPolygonCoords(graph, range, step) {
@@ -32187,6 +32271,10 @@ function getPolygonCoords(graph, range, step) {
32187
32271
  if (coords) {
32188
32272
  return coords;
32189
32273
  }
32274
+ const startCoords = graph.startCoords?.slice();
32275
+ if (startCoords) {
32276
+ return startCoords;
32277
+ }
32190
32278
  const n = graph.numSides || 3;
32191
32279
  if (n === "unlimited") {
32192
32280
  coords = [];
@@ -32210,6 +32298,9 @@ function getSinusoidCoords(graph, range, step) {
32210
32298
  if (graph.coords) {
32211
32299
  return [graph.coords[0], graph.coords[1]];
32212
32300
  }
32301
+ if (graph.startCoords) {
32302
+ return [graph.startCoords[0], graph.startCoords[1]];
32303
+ }
32213
32304
  let coords = [[0.5, 0.5], [0.65, 0.6]];
32214
32305
  coords = normalizePoints(range, step, coords, true);
32215
32306
  return coords;
@@ -32218,6 +32309,9 @@ function getQuadraticCoords(graph, range, step) {
32218
32309
  if (graph.coords) {
32219
32310
  return graph.coords;
32220
32311
  }
32312
+ if (graph.startCoords) {
32313
+ return graph.startCoords;
32314
+ }
32221
32315
  const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
32222
32316
  return normalizePoints(range, step, defaultCoords, true);
32223
32317
  }
@@ -32228,6 +32322,12 @@ function getCircleCoords(graph) {
32228
32322
  radiusPoint: mafs.vec.add(graph.center, [graph.radius, 0])
32229
32323
  };
32230
32324
  }
32325
+ if (graph.startCoords) {
32326
+ return {
32327
+ center: graph.startCoords,
32328
+ radiusPoint: mafs.vec.add(graph.startCoords, [2, 0])
32329
+ };
32330
+ }
32231
32331
  return {
32232
32332
  center: [0, 0],
32233
32333
  radiusPoint: [2, 0]
@@ -32968,70 +33068,6 @@ function setAtIndex(args) {
32968
33068
  return copy;
32969
33069
  }
32970
33070
 
32971
- const renderGraph = props => {
32972
- const {
32973
- state,
32974
- dispatch
32975
- } = props;
32976
- const {
32977
- type
32978
- } = state;
32979
- switch (type) {
32980
- case "angle":
32981
- return /*#__PURE__*/React__namespace.createElement(AngleGraph, {
32982
- graphState: state,
32983
- dispatch: dispatch
32984
- });
32985
- case "segment":
32986
- return /*#__PURE__*/React__namespace.createElement(SegmentGraph, {
32987
- graphState: state,
32988
- dispatch: dispatch
32989
- });
32990
- case "linear-system":
32991
- return /*#__PURE__*/React__namespace.createElement(LinearSystemGraph, {
32992
- graphState: state,
32993
- dispatch: dispatch
32994
- });
32995
- case "linear":
32996
- return /*#__PURE__*/React__namespace.createElement(LinearGraph, {
32997
- graphState: state,
32998
- dispatch: dispatch
32999
- });
33000
- case "ray":
33001
- return /*#__PURE__*/React__namespace.createElement(RayGraph, {
33002
- graphState: state,
33003
- dispatch: dispatch
33004
- });
33005
- case "polygon":
33006
- return /*#__PURE__*/React__namespace.createElement(PolygonGraph, {
33007
- graphState: state,
33008
- dispatch: dispatch
33009
- });
33010
- case "point":
33011
- return /*#__PURE__*/React__namespace.createElement(PointGraph, {
33012
- graphState: state,
33013
- dispatch: dispatch
33014
- });
33015
- case "circle":
33016
- return /*#__PURE__*/React__namespace.createElement(CircleGraph, {
33017
- graphState: state,
33018
- dispatch: dispatch
33019
- });
33020
- case "quadratic":
33021
- return /*#__PURE__*/React__namespace.createElement(QuadraticGraph, {
33022
- graphState: state,
33023
- dispatch: dispatch
33024
- });
33025
- case "sinusoid":
33026
- return /*#__PURE__*/React__namespace.createElement(SinusoidGraph, {
33027
- graphState: state,
33028
- dispatch: dispatch
33029
- });
33030
- default:
33031
- return new wonderStuffCore.UnreachableCaseError(type);
33032
- }
33033
- };
33034
-
33035
33071
  // Rather than be tightly bound to how data was structured in
33036
33072
  // the legacy interactive graph, this lets us store state
33037
33073
  // however we want and we just transform it before handing it off
@@ -33085,6 +33121,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
33085
33121
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
33086
33122
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
33087
33123
  const showSides = graph.type === "polygon" ? graph.showSides : null;
33124
+ const startCoords = graph.startCoords ?? null;
33088
33125
  const originalPropsRef = React.useRef(props);
33089
33126
  const latestPropsRef = wonderBlocksCore.useLatestRef(props);
33090
33127
  React.useEffect(() => {
@@ -33095,78 +33132,12 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
33095
33132
  if (latestPropsRef.current !== originalPropsRef.current) {
33096
33133
  dispatch(reinitialize(latestPropsRef.current));
33097
33134
  }
33098
- }, [graph.type, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef]);
33135
+ }, [graph.type, numSegments, numSides, snapTo, showAngles, showSides, startCoords, latestPropsRef, startCoords]);
33099
33136
  return /*#__PURE__*/React__namespace.createElement(MafsGraph, _extends({}, props, {
33100
33137
  state: state,
33101
33138
  dispatch: dispatch
33102
33139
  }));
33103
33140
  });
33104
- const MafsGraph = props => {
33105
- const {
33106
- state,
33107
- dispatch,
33108
- labels
33109
- } = props;
33110
- const [width, height] = props.box;
33111
- const tickStep = props.step;
33112
- return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
33113
- value: {
33114
- range: state.range,
33115
- snapStep: state.snapStep,
33116
- markings: props.markings,
33117
- tickStep: tickStep,
33118
- gridStep: props.gridStep,
33119
- showTooltips: !!props.showTooltips,
33120
- graphDimensionsInPixels: props.box,
33121
- width,
33122
- height,
33123
- labels
33124
- }
33125
- }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
33126
- className: "mafs-graph",
33127
- style: {
33128
- width,
33129
- height,
33130
- position: "relative",
33131
- padding: "25px 25px 0 0",
33132
- boxSizing: "content-box",
33133
- marginLeft: "20px",
33134
- marginBottom: "20px"
33135
- }
33136
- }, /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
33137
- box: props.box,
33138
- backgroundImage: props.backgroundImage
33139
- }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
33140
- style: {
33141
- position: "absolute",
33142
- bottom: 0,
33143
- left: 0
33144
- }
33145
- }, props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisLabels, null), /*#__PURE__*/React__namespace.createElement(AxisTickLabels, null)), /*#__PURE__*/React__namespace.createElement(mafs.Mafs, {
33146
- preserveAspectRatio: false,
33147
- viewBox: {
33148
- x: state.range[X],
33149
- y: state.range[Y],
33150
- padding: 0
33151
- },
33152
- pan: false,
33153
- zoom: false,
33154
- width: width,
33155
- height: height
33156
- }, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement(Grid, {
33157
- tickStep: props.step,
33158
- gridStep: props.gridStep,
33159
- range: state.range,
33160
- containerSizeClass: props.containerSizeClass,
33161
- markings: props.markings
33162
- }), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
33163
- lockedFigures: props.lockedFigures,
33164
- range: state.range
33165
- }), "/* Protractor */", props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
33166
- state,
33167
- dispatch
33168
- })))));
33169
- };
33170
33141
 
33171
33142
  const {
33172
33143
  DeprecationMixin
@@ -33215,7 +33186,7 @@ const _isClickToAddPoints = props => {
33215
33186
  return props.graph.type === "point" && props.graph.numPoints === UNLIMITED || props.graph.type === "polygon" && props.graph.numSides === UNLIMITED;
33216
33187
  };
33217
33188
  const makeInvalidTypeError = (functionName, graphType) => {
33218
- return new PerseusError(`${functionName} called but current graph type is not a '${graphType}'`, Errors.NotAllowed, {
33189
+ return new perseusCore.PerseusError(`${functionName} called but current graph type is not a '${graphType}'`, perseusCore.Errors.NotAllowed, {
33219
33190
  metadata: {
33220
33191
  graphType
33221
33192
  }
@@ -33314,7 +33285,7 @@ class LegacyInteractiveGraph extends React__namespace.Component {
33314
33285
  // This function should only be called when this.isClickToAddPoints()
33315
33286
  // is true
33316
33287
  if (!this.isClickToAddPoints()) {
33317
- throw new PerseusError("handleAddPointsClick should not be registered" + "when isClickToAddPoints() is false", Errors.NotAllowed);
33288
+ throw new perseusCore.PerseusError("handleAddPointsClick should not be registered" + "when isClickToAddPoints() is false", perseusCore.Errors.NotAllowed);
33318
33289
  }
33319
33290
  if (!this.isCoordInTrash(coord)) {
33320
33291
  let point;
@@ -34452,7 +34423,7 @@ class InteractiveGraph extends React__namespace.Component {
34452
34423
  if (this.legacyGraphRef.current) {
34453
34424
  return this.legacyGraphRef.current.getUserInput();
34454
34425
  }
34455
- throw new PerseusError("Cannot getUserInput from a graph that has never rendered", Errors.NotAllowed);
34426
+ throw new perseusCore.PerseusError("Cannot getUserInput from a graph that has never rendered", perseusCore.Errors.NotAllowed);
34456
34427
  };
34457
34428
  simpleValidate(rubric) {
34458
34429
  return InteractiveGraph.validate(this.getUserInput?.(), rubric, this);
@@ -40178,7 +40149,7 @@ class Card extends React__namespace.Component {
40178
40149
  // Event handlers should be unbound before component unmounting, but
40179
40150
  // just in case...
40180
40151
  if (this.mouseMoveUpBound) {
40181
- Log.error("Removing an element with bound event handlers.", Errors.Internal);
40152
+ Log.error("Removing an element with bound event handlers.", perseusCore.Errors.Internal);
40182
40153
  this.unbindMouseMoveUp();
40183
40154
  Util.resetTouchHandlers();
40184
40155
  }
@@ -40999,7 +40970,7 @@ function deserializeHighlight(serializedHighlight, wordRanges) {
40999
40970
  } = serializedHighlight.range;
41000
40971
  const firstWord = wordRanges[firstWordIndex];
41001
40972
  if (!firstWord) {
41002
- throw new PerseusError(`first word index ${firstWord} is out of bounds: ` + `must be 0–${wordRanges.length - 1} inclusive`, Errors.InvalidInput, {
40973
+ throw new perseusCore.PerseusError(`first word index ${firstWord} is out of bounds: ` + `must be 0–${wordRanges.length - 1} inclusive`, perseusCore.Errors.InvalidInput, {
41003
40974
  metadata: {
41004
40975
  firstWord,
41005
40976
  wordRanges: JSON.stringify(wordRanges)
@@ -41008,7 +40979,7 @@ function deserializeHighlight(serializedHighlight, wordRanges) {
41008
40979
  }
41009
40980
  const lastWord = wordRanges[lastWordIndex];
41010
40981
  if (!lastWord) {
41011
- throw new PerseusError(`last word index ${lastWord} is out of bounds: ` + `must be 0–${wordRanges.length - 1} inclusive`, Errors.InvalidInput, {
40982
+ throw new perseusCore.PerseusError(`last word index ${lastWord} is out of bounds: ` + `must be 0–${wordRanges.length - 1} inclusive`, perseusCore.Errors.InvalidInput, {
41012
40983
  metadata: {
41013
40984
  lastWord,
41014
40985
  wordRanges: JSON.stringify(wordRanges)
@@ -41761,7 +41732,7 @@ class WordIndexer extends React__namespace.PureComponent {
41761
41732
  _getWordRanges() {
41762
41733
  const container = this._container;
41763
41734
  if (!container) {
41764
- throw new PerseusError("invariant violation: `this._container` is not defined, but " + "this method is only called after mount/update", Errors.NotAllowed);
41735
+ throw new perseusCore.PerseusError("invariant violation: `this._container` is not defined, but " + "this method is only called after mount/update", perseusCore.Errors.NotAllowed);
41765
41736
  }
41766
41737
 
41767
41738
  // Create a TreeWalker that visits text nodes and element nodes.
@@ -42227,6 +42198,7 @@ const rules = {
42227
42198
  }, /*#__PURE__*/React__namespace.createElement("span", {
42228
42199
  style: SQUARE_LABEL_STYLE
42229
42200
  }, node.content)), /*#__PURE__*/React__namespace.createElement(AltText, {
42201
+ key: "alt-text",
42230
42202
  id: "questionMarker",
42231
42203
  number: node.content
42232
42204
  }), node.space ? "\u00A0" : null];
@@ -42251,6 +42223,7 @@ const rules = {
42251
42223
  }, /*#__PURE__*/React__namespace.createElement("span", {
42252
42224
  style: CIRCLE_LABEL_STYLE
42253
42225
  }, node.content)), /*#__PURE__*/React__namespace.createElement(AltText, {
42226
+ key: "alt-text",
42254
42227
  id: "circleMarker",
42255
42228
  number: node.content
42256
42229
  }), node.space ? "\u00A0" : null];
@@ -42274,6 +42247,7 @@ const rules = {
42274
42247
  className: "perseus-passage-bracket-label",
42275
42248
  "aria-hidden": "true"
42276
42249
  }, "[", node.content, "]"), /*#__PURE__*/React__namespace.createElement(AltText, {
42250
+ key: "alt-text",
42277
42251
  id: "sentenceMarker",
42278
42252
  number: node.content
42279
42253
  }), node.space ? "\u00A0" : null];
@@ -45283,7 +45257,7 @@ const init = function (options) {
45283
45257
 
45284
45258
  // This file is processed by a Rollup plugin (replace) to inject the production
45285
45259
  const libName = "@khanacademy/perseus";
45286
- const libVersion = "24.3.0";
45260
+ const libVersion = "25.1.0";
45287
45261
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45288
45262
 
45289
45263
  const apiVersion = {
@@ -46659,9 +46633,9 @@ tree, shape, path, mappers
46659
46633
  // @ts-expect-error - TS2315 - Type 'ArrayNode' is not generic.
46660
46634
  const array = tree;
46661
46635
  if (!Array.isArray(array)) {
46662
- throw new PerseusError(`Invalid object of type "${typeof array}" found at path ` +
46636
+ throw new perseusCore.PerseusError(`Invalid object of type "${typeof array}" found at path ` +
46663
46637
  // @ts-expect-error - TS2769 - No overload matches this call.
46664
- `${["<root>"].concat(path).join(".")}. Expected array.`, Errors.Internal);
46638
+ `${["<root>"].concat(path).join(".")}. Expected array.`, perseusCore.Errors.Internal);
46665
46639
  }
46666
46640
  const elementShape = shape.elementShape;
46667
46641
  // @ts-expect-error - TS2315 - Type 'ArrayNode' is not generic.
@@ -46671,30 +46645,30 @@ tree, shape, path, mappers
46671
46645
  if (shape.type === "object") {
46672
46646
  const object = tree;
46673
46647
  if (object && typeof object !== "object") {
46674
- throw new PerseusError(`Invalid object of type "${typeof object}" found at ` +
46648
+ throw new perseusCore.PerseusError(`Invalid object of type "${typeof object}" found at ` +
46675
46649
  // @ts-expect-error - TS2769 - No overload matches this call.
46676
- `path ${["<root>"].concat(path).join(".")}. Expected ` + `"object" type.`, Errors.InvalidInput);
46650
+ `path ${["<root>"].concat(path).join(".")}. Expected ` + `"object" type.`, perseusCore.Errors.InvalidInput);
46677
46651
  }
46678
46652
  const valueShapes = shape.shape;
46679
46653
  if (!valueShapes) {
46680
- throw new PerseusError(`Unexpected shape ${JSON.stringify(shape)} at path ` +
46654
+ throw new perseusCore.PerseusError(`Unexpected shape ${JSON.stringify(shape)} at path ` +
46681
46655
  // @ts-expect-error - TS2769 - No overload matches this call.
46682
- `${["<root>"].concat(path).join(".")}.`, Errors.InvalidInput);
46656
+ `${["<root>"].concat(path).join(".")}.`, perseusCore.Errors.InvalidInput);
46683
46657
  }
46684
46658
  const newObject = {};
46685
46659
  Object.keys(valueShapes).forEach(key => {
46686
46660
  if (!(key in object)) {
46687
- throw new PerseusError(`Key "${key}" is missing from shape at path ` +
46661
+ throw new perseusCore.PerseusError(`Key "${key}" is missing from shape at path ` +
46688
46662
  // @ts-expect-error - TS2769 - No overload matches this call.
46689
- `${["<root>"].concat(path).join(".")}.`, Errors.InvalidInput);
46663
+ `${["<root>"].concat(path).join(".")}.`, perseusCore.Errors.InvalidInput);
46690
46664
  }
46691
46665
  newObject[key] = mapTree(object[key], valueShapes[key], path.concat(key), mappers);
46692
46666
  });
46693
46667
  return newObject;
46694
46668
  }
46695
- throw new PerseusError(
46669
+ throw new perseusCore.PerseusError(
46696
46670
  // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
46697
- `unexpected shape type ${shape.type}`, Errors.InvalidInput);
46671
+ `unexpected shape type ${shape.type}`, perseusCore.Errors.InvalidInput);
46698
46672
  }
46699
46673
 
46700
46674
  /**
@@ -46746,9 +46720,9 @@ function buildEmptyItemTreeForShape(shape) {
46746
46720
  });
46747
46721
  return object;
46748
46722
  }
46749
- throw new PerseusError(
46723
+ throw new perseusCore.PerseusError(
46750
46724
  // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
46751
- `unexpected shape type ${shape.type}`, Errors.InvalidInput);
46725
+ `unexpected shape type ${shape.type}`, perseusCore.Errors.InvalidInput);
46752
46726
  }
46753
46727
 
46754
46728
  /**
@@ -46823,7 +46797,7 @@ function inferItemTreeShape(node) {
46823
46797
  });
46824
46798
  return shapes.shape(valueShapes);
46825
46799
  }
46826
- throw new PerseusError(`unexpected multi-item node ${JSON.stringify(node)}`, Errors.InvalidInput);
46800
+ throw new perseusCore.PerseusError(`unexpected multi-item node ${JSON.stringify(node)}`, perseusCore.Errors.InvalidInput);
46827
46801
  }
46828
46802
 
46829
46803
  /**
@@ -46901,7 +46875,7 @@ class MultiRenderer extends React__namespace.Component {
46901
46875
  renderError: null
46902
46876
  };
46903
46877
  } catch (e) {
46904
- Log.error("Error building tree state", Errors.Internal, {
46878
+ Log.error("Error building tree state", perseusCore.Errors.Internal, {
46905
46879
  cause: e
46906
46880
  });
46907
46881
  return {
@@ -47265,9 +47239,9 @@ function buildTreePropTypeForShape(shape) {
47265
47239
  });
47266
47240
  return PropTypes__default["default"].shape(propTypeShape);
47267
47241
  }
47268
- throw new PerseusError(
47242
+ throw new perseusCore.PerseusError(
47269
47243
  // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
47270
- `unexpected shape type ${shape.type}`, Errors.InvalidInput);
47244
+ `unexpected shape type ${shape.type}`, perseusCore.Errors.InvalidInput);
47271
47245
  }
47272
47246
 
47273
47247
  /**
@@ -47390,7 +47364,7 @@ const isRendererContentRenderableBy = function (rendererOptions, rendererContent
47390
47364
  };
47391
47365
  const isItemRenderableByVersion = function (itemData, rendererContentVersion) {
47392
47366
  if (itemData == null || rendererContentVersion == null) {
47393
- throw new PerseusError("missing parameter to Perseus.isRenderable.item", Errors.InvalidInput);
47367
+ throw new perseusCore.PerseusError("missing parameter to Perseus.isRenderable.item", perseusCore.Errors.InvalidInput);
47394
47368
  }
47395
47369
  if (itemData._multi) {
47396
47370
  const shape = inferItemShape$1(itemData);
@@ -48311,7 +48285,6 @@ exports.DashPicker = DashPicker;
48311
48285
  exports.Dependencies = dependencies;
48312
48286
  exports.EditorJsonify = EditorJsonify;
48313
48287
  exports.ElementContainer = ElementContainer;
48314
- exports.Errors = Errors;
48315
48288
  exports.Expression = Expression;
48316
48289
  exports.GrapherUtil = util;
48317
48290
  exports.GrapherWidget = Grapher$1;
@@ -48328,7 +48301,6 @@ exports.Log = Log;
48328
48301
  exports.MatrixWidget = Matrix$1;
48329
48302
  exports.MultiItems = MultiItems;
48330
48303
  exports.NumericInput = NumericInput$1;
48331
- exports.PerseusError = PerseusError;
48332
48304
  exports.PerseusExpressionAnswerFormConsidered = PerseusExpressionAnswerFormConsidered;
48333
48305
  exports.PerseusI18nContext = PerseusI18nContext;
48334
48306
  exports.PerseusI18nContextProvider = PerseusI18nContextProvider;
@@ -48363,6 +48335,7 @@ exports.genericPerseusItemData = genericPerseusItemData;
48363
48335
  exports.getAnswerFromUserInput = getAnswerFromUserInput;
48364
48336
  exports.getAnswersFromWidgets = getAnswersFromWidgets;
48365
48337
  exports.getCorrectAnswerForWidgetId = getCorrectAnswerForWidgetId;
48338
+ exports.getLineCoords = getLineCoords;
48366
48339
  exports.getValidWidgetIds = getValidWidgetIds;
48367
48340
  exports.getWidgetFromWidgetMap = getWidgetFromWidgetMap;
48368
48341
  exports.getWidgetIdsFromContent = getWidgetIdsFromContent;