@khanacademy/perseus 18.0.0 → 19.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -18,7 +18,7 @@ import $ from 'jquery';
18
18
  import PropTypes from 'prop-types';
19
19
  import * as ReactDOM from 'react-dom';
20
20
  import ReactDOM__default from 'react-dom';
21
- import { number, point, vector as vector$1, line, ray } from '@khanacademy/kmath';
21
+ import { number, point, vector as vector$1, line } from '@khanacademy/kmath';
22
22
  import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
23
23
  import SimpleMarkdown from '@khanacademy/simple-markdown';
24
24
  import { pureMarkdownRules } from '@khanacademy/pure-markdown';
@@ -2748,7 +2748,7 @@ function gridDimensionConfig(absTickStep, extent, dimensionConstraint, gridStep)
2748
2748
  *
2749
2749
  * TODO(somewhatabstract, FEI-3464): Consolidate query string parsing functions.
2750
2750
  */
2751
- function getGridStep$1(range, step, boxSize) {
2751
+ function getGridStep(range, step, boxSize) {
2752
2752
  // @ts-expect-error - TS2322 - Type '(number | null | undefined)[]' is not assignable to type 'Coordinates'.
2753
2753
  return _(2).times(function (i) {
2754
2754
  const scale = scaleFromExtent(range[i], boxSize);
@@ -2930,13 +2930,13 @@ function eq$2(x, y) {
2930
2930
  * Deep approximate equality on primitives, numbers, arrays, and objects.
2931
2931
  * Recursive.
2932
2932
  */
2933
- function deepEq$6(x, y) {
2933
+ function deepEq$5(x, y) {
2934
2934
  if (Array.isArray(x) && Array.isArray(y)) {
2935
2935
  if (x.length !== y.length) {
2936
2936
  return false;
2937
2937
  }
2938
2938
  for (let i = 0; i < x.length; i++) {
2939
- if (!deepEq$6(x[i], y[i])) {
2939
+ if (!deepEq$5(x[i], y[i])) {
2940
2940
  return false;
2941
2941
  }
2942
2942
  }
@@ -2954,10 +2954,10 @@ function deepEq$6(x, y) {
2954
2954
  if (typeof x === "object" && typeof y === "object" && !!x && !!y) {
2955
2955
  return x === y || _.all(x, function (v, k) {
2956
2956
  // @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
2957
- return deepEq$6(y[k], v);
2957
+ return deepEq$5(y[k], v);
2958
2958
  }) && _.all(y, function (v, k) {
2959
2959
  // @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
2960
- return deepEq$6(x[k], v);
2960
+ return deepEq$5(x[k], v);
2961
2961
  });
2962
2962
  }
2963
2963
  if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
@@ -3157,7 +3157,7 @@ const supportsPassiveEvents = () => {
3157
3157
  * Pass this function as the touchstart for an element to
3158
3158
  * avoid sending the touch to the mobile scratchpad
3159
3159
  */
3160
- function captureScratchpadTouchStart$4(e) {
3160
+ function captureScratchpadTouchStart$3(e) {
3161
3161
  e.stopPropagation();
3162
3162
  }
3163
3163
  function getImageSize(url, callback) {
@@ -3247,7 +3247,7 @@ const Util = {
3247
3247
  firstNumericalParse: firstNumericalParse$1,
3248
3248
  stringArrayOfSize: stringArrayOfSize$1,
3249
3249
  gridDimensionConfig,
3250
- getGridStep: getGridStep$1,
3250
+ getGridStep,
3251
3251
  snapStepFromGridStep,
3252
3252
  scaleFromExtent,
3253
3253
  tickStepFromExtent,
@@ -3256,7 +3256,7 @@ const Util = {
3256
3256
  constrainedTickStepsFromTickSteps,
3257
3257
  DeprecationMixin: DeprecationMixin$1,
3258
3258
  eq: eq$2,
3259
- deepEq: deepEq$6,
3259
+ deepEq: deepEq$5,
3260
3260
  parseQueryString,
3261
3261
  updateQueryString: updateQueryString$2,
3262
3262
  strongEncodeURIComponent,
@@ -3266,7 +3266,7 @@ const Util = {
3266
3266
  resetTouchHandlers,
3267
3267
  extractPointerLocation,
3268
3268
  supportsPassiveEvents,
3269
- captureScratchpadTouchStart: captureScratchpadTouchStart$4,
3269
+ captureScratchpadTouchStart: captureScratchpadTouchStart$3,
3270
3270
  getImageSize,
3271
3271
  getImageSizeModern: getImageSizeModern,
3272
3272
  getRealImageUrl: getRealImageUrl,
@@ -13475,7 +13475,7 @@ _.extend(MovableAngle.prototype, {
13475
13475
 
13476
13476
  /* eslint-disable @babel/no-invalid-this */
13477
13477
  const nestedMap$1 = Util.nestedMap;
13478
- const deepEq$5 = Util.deepEq;
13478
+ const deepEq$4 = Util.deepEq;
13479
13479
 
13480
13480
  /**
13481
13481
  * A base class for all Graphie Movables
@@ -13572,7 +13572,7 @@ const createSimpleClass = function createSimpleClass(addFunction) {
13572
13572
  this._prevProps = this.props;
13573
13573
  },
13574
13574
  modify: function (graphie) {
13575
- if (!deepEq$5(this.props, this._prevProps)) {
13575
+ if (!deepEq$4(this.props, this._prevProps)) {
13576
13576
  this.remove();
13577
13577
  this.add(graphie);
13578
13578
  this._prevProps = this.props;
@@ -13617,7 +13617,7 @@ var GraphieClasses = {
13617
13617
  * take appropriate action in onMoveStart, onMove, onMoveEnd
13618
13618
  */
13619
13619
  const normalizeOptions$3 = InteractiveUtil.normalizeOptions;
13620
- const assert$9 = InteractiveUtil.assert;
13620
+ const assert$8 = InteractiveUtil.assert;
13621
13621
 
13622
13622
  // state parameters that should be converted into an array of
13623
13623
  // functions
@@ -13688,7 +13688,7 @@ _.extend(Movable.prototype, {
13688
13688
  * Simulates a mouse grab event on the movable object.
13689
13689
  */
13690
13690
  grab: function (coord) {
13691
- assert$9(point.is(coord));
13691
+ assert$8(point.is(coord));
13692
13692
  const self = this;
13693
13693
  const graphie = self.graphie;
13694
13694
  const state = self.state;
@@ -14154,7 +14154,7 @@ var MovableLineOptions = {
14154
14154
  /**
14155
14155
  * MovableLine
14156
14156
  */
14157
- const assert$8 = InteractiveUtil.assert;
14157
+ const assert$7 = InteractiveUtil.assert;
14158
14158
  const normalizeOptions$2 = InteractiveUtil.normalizeOptions;
14159
14159
  const FUNCTION_ARRAY_OPTIONS$2 = ["add", "draw", "remove", "onMoveStart", "constraints", "onMove", "onMoveEnd"];
14160
14160
 
@@ -14180,8 +14180,8 @@ const DEFAULT_STATE$2 = {
14180
14180
  mouseTarget: null
14181
14181
  };
14182
14182
  const MovableLine$4 = function MovableLine(graphie, movable, options) {
14183
- assert$8(graphie != null);
14184
- assert$8(options != null);
14183
+ assert$7(graphie != null);
14184
+ assert$7(options != null);
14185
14185
 
14186
14186
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
14187
14187
  _.extend(this, {
@@ -14734,7 +14734,7 @@ var MovablePointOptions = {
14734
14734
  }
14735
14735
  };
14736
14736
 
14737
- const assert$7 = InteractiveUtil.assert;
14737
+ const assert$6 = InteractiveUtil.assert;
14738
14738
  const normalizeOptions$1 = InteractiveUtil.normalizeOptions;
14739
14739
  const {
14740
14740
  processMath
@@ -14870,7 +14870,7 @@ _.extend(MovablePoint$5.prototype, {
14870
14870
  const self = this;
14871
14871
  const graphie = self.graphie;
14872
14872
  const state = _.extend(self.state, normalizeOptions$1(FUNCTION_ARRAY_OPTIONS$1, options));
14873
- assert$7(point.is(state.coord));
14873
+ assert$6(point.is(state.coord));
14874
14874
 
14875
14875
  // Default things inside the state.normalStyle object, because
14876
14876
  // _.extend is not deep.
@@ -15072,12 +15072,12 @@ _.extend(MovablePoint$5.prototype, {
15072
15072
  return result !== false;
15073
15073
  },
15074
15074
  setCoord: function (coord) {
15075
- assert$7(point.is(coord, 2));
15075
+ assert$6(point.is(coord, 2));
15076
15076
  this.state.coord = _.clone(coord);
15077
15077
  this.draw();
15078
15078
  },
15079
15079
  setCoordConstrained: function (coord) {
15080
- assert$7(point.is(coord, 2));
15080
+ assert$6(point.is(coord, 2));
15081
15081
  const result = this._applyConstraints(coord, this.coord());
15082
15082
  if (result !== false) {
15083
15083
  this.state.coord = _.clone(result);
@@ -15473,7 +15473,7 @@ var MovablePolygonOptions = {
15473
15473
  * Creates and adds a polygon to the graph that can be dragged around.
15474
15474
  * It allows constraints on its movement and draws when moves happen.
15475
15475
  */
15476
- const assert$6 = InteractiveUtil.assert;
15476
+ const assert$5 = InteractiveUtil.assert;
15477
15477
  const normalizeOptions = InteractiveUtil.normalizeOptions;
15478
15478
 
15479
15479
  // State parameters that should be converted into an array of
@@ -15512,8 +15512,8 @@ const DEFAULT_STATE = {
15512
15512
  mouseTarget: null
15513
15513
  };
15514
15514
  const MovablePolygon = function MovablePolygon(graphie, movable, options) {
15515
- assert$6(graphie != null);
15516
- assert$6(options != null);
15515
+ assert$5(graphie != null);
15516
+ assert$5(options != null);
15517
15517
 
15518
15518
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
15519
15519
  _.extend(this, {
@@ -16029,11 +16029,11 @@ var Movables = {
16029
16029
  const GraphieMovable = GraphieClasses.GraphieMovable;
16030
16030
  const createGraphie = GraphUtils.createGraphie;
16031
16031
  const {
16032
- deepEq: deepEq$4,
16032
+ deepEq: deepEq$3,
16033
16033
  nestedMap
16034
16034
  } = Util;
16035
16035
  const {
16036
- assert: assert$5
16036
+ assert: assert$4
16037
16037
  } = InteractiveUtil;
16038
16038
  class Graphie extends React.Component {
16039
16039
  constructor(...args) {
@@ -16175,7 +16175,7 @@ class Graphie extends React.Component {
16175
16175
 
16176
16176
  // Instantiate the descriptor to turn it into a real Movable
16177
16177
  const child = new childDescriptor.type(childDescriptor.props);
16178
- assert$5(child instanceof GraphieMovable, "All children of a Graphie component must be Graphie " + "movables");
16178
+ assert$4(child instanceof GraphieMovable, "All children of a Graphie component must be Graphie " + "movables");
16179
16179
 
16180
16180
  // Give each child a key
16181
16181
  const keyProp = childDescriptor.key;
@@ -16275,7 +16275,7 @@ class Graphie extends React.Component {
16275
16275
  if (this.props.setup !== prevProps.setup) {
16276
16276
  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);
16277
16277
  }
16278
- if (!deepEq$4(this.props.options, prevProps.options) || !deepEq$4(this.props.box, prevProps.box) || !deepEq$4(this.props.range, prevProps.range)) {
16278
+ if (!deepEq$3(this.props.options, prevProps.options) || !deepEq$3(this.props.box, prevProps.box) || !deepEq$3(this.props.range, prevProps.range)) {
16279
16279
  this._setupGraphie();
16280
16280
  }
16281
16281
  this._updateMovables();
@@ -18369,7 +18369,7 @@ const registerWidgets = widgets => {
18369
18369
  * @param name - the widget that you are trying to replace
18370
18370
  * @param replacementName - the name of the widget that takes its place
18371
18371
  *
18372
- * e.g. replaceWidget("transformer", "always-correct") will make it so the
18372
+ * e.g. replaceWidget("transformer", "deprecated-standin") will make it so the
18373
18373
  * transformer widget is replaced by the always correct widget
18374
18374
  */
18375
18375
  const replaceWidget = (name, replacementName) => {
@@ -18380,9 +18380,6 @@ const replaceWidget = (name, replacementName) => {
18380
18380
  return;
18381
18381
  }
18382
18382
  registerWidget(name, substituteWidget);
18383
- if (Log) {
18384
- Log.log(`INFO: Replacing widget ${name} with ${replacementName}`);
18385
- }
18386
18383
  };
18387
18384
  const registerEditors = editorsToRegister => {
18388
18385
  editorsToRegister.forEach(editor => {
@@ -18409,9 +18406,6 @@ const replaceEditor = (name, replacementName) => {
18409
18406
  return;
18410
18407
  }
18411
18408
  editors[name] = substituteEditor;
18412
- if (Log) {
18413
- Log.log(`INFO: Replacing editor ${name} with ${replacementName}`);
18414
- }
18415
18409
  };
18416
18410
  const getWidget = name => {
18417
18411
  // TODO(alex): Consider referring to these as renderers to avoid
@@ -20874,7 +20868,7 @@ Tooltip.defaultProps = {
20874
20868
 
20875
20869
  /* eslint-disable react/sort-comp */
20876
20870
  const {
20877
- captureScratchpadTouchStart: captureScratchpadTouchStart$3
20871
+ captureScratchpadTouchStart: captureScratchpadTouchStart$2
20878
20872
  } = Util;
20879
20873
  class InputWithExamples extends React.Component {
20880
20874
  constructor(...args) {
@@ -20925,7 +20919,7 @@ class InputWithExamples extends React.Component {
20925
20919
  // Add useful props required for MATH and TEXT modes
20926
20920
  _.extend(inputProps, {
20927
20921
  onChange: this.props.onChange,
20928
- onTouchStart: captureScratchpadTouchStart$3
20922
+ onTouchStart: captureScratchpadTouchStart$2
20929
20923
  });
20930
20924
 
20931
20925
  // And add final props that are MATH- and TEXT-specific
@@ -22966,7 +22960,7 @@ var ChoiceNoneAbove$1 = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE_
22966
22960
  })));
22967
22961
 
22968
22962
  const {
22969
- captureScratchpadTouchStart: captureScratchpadTouchStart$2
22963
+ captureScratchpadTouchStart: captureScratchpadTouchStart$1
22970
22964
  } = Util;
22971
22965
 
22972
22966
  // exported for tests
@@ -23218,7 +23212,7 @@ const BaseRadio = function BaseRadio(props) {
23218
23212
  onClick: clickHandler
23219
23213
  // @ts-expect-error - TS2322 - Type '((e: TouchEvent) => void) | null' is not assignable to type 'TouchEventHandler<HTMLLIElement> | undefined'.
23220
23214
  ,
23221
- onTouchStart: !labelWrap ? null : captureScratchpadTouchStart$2
23215
+ onTouchStart: !labelWrap ? null : captureScratchpadTouchStart$1
23222
23216
  }, /*#__PURE__*/React.createElement(Element, _extends({}, elementProps, {
23223
23217
  ref: ref
23224
23218
  })));
@@ -26720,9 +26714,9 @@ var Iframe$1 = {
26720
26714
  };
26721
26715
 
26722
26716
  /* eslint-disable react/sort-comp */
26723
- const defaultBoxSize$2 = 400;
26717
+ const defaultBoxSize$1 = 400;
26724
26718
  const defaultRange = [0, 10];
26725
- const defaultBackgroundImage$3 = {
26719
+ const defaultBackgroundImage$2 = {
26726
26720
  url: null,
26727
26721
  width: 0,
26728
26722
  height: 0
@@ -26918,8 +26912,8 @@ ImageWidget.defaultProps = {
26918
26912
  alignment: DEFAULT_ALIGNMENT,
26919
26913
  title: "",
26920
26914
  range: [defaultRange, defaultRange],
26921
- box: [defaultBoxSize$2, defaultBoxSize$2],
26922
- backgroundImage: defaultBackgroundImage$3,
26915
+ box: [defaultBoxSize$1, defaultBoxSize$1],
26916
+ backgroundImage: defaultBackgroundImage$2,
26923
26917
  labels: [],
26924
26918
  alt: "",
26925
26919
  caption: "",
@@ -27547,7 +27541,7 @@ var Interactive = {
27547
27541
  };
27548
27542
 
27549
27543
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
27550
- const defaultBackgroundImage$2 = {
27544
+ const defaultBackgroundImage$1 = {
27551
27545
  url: null
27552
27546
  };
27553
27547
 
@@ -27840,7 +27834,7 @@ Graph.defaultProps = {
27840
27834
  gridStep: [1, 1],
27841
27835
  snapStep: [0.5, 0.5],
27842
27836
  markings: "graph",
27843
- backgroundImage: defaultBackgroundImage$2,
27837
+ backgroundImage: defaultBackgroundImage$1,
27844
27838
  showProtractor: false,
27845
27839
  showRuler: false,
27846
27840
  rulerLabel: "",
@@ -27853,7 +27847,7 @@ Graph.defaultProps = {
27853
27847
  */
27854
27848
  const {
27855
27849
  eq: eq$1,
27856
- deepEq: deepEq$3
27850
+ deepEq: deepEq$2
27857
27851
  } = Util;
27858
27852
 
27859
27853
  // This should really be a readonly tuple of [number, number]
@@ -27966,7 +27960,7 @@ function similar(coords1, coords2, tolerance) {
27966
27960
  angles = rotate(angles, i);
27967
27961
  // @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
27968
27962
  sides = rotate(sides, i);
27969
- if (deepEq$3(angles1, angles)) {
27963
+ if (deepEq$2(angles1, angles)) {
27970
27964
  const sidePairs = _.zip(sides1, sides);
27971
27965
  const factors = _.map(sidePairs, function (pair) {
27972
27966
  return pair[0] / pair[1];
@@ -28058,11 +28052,11 @@ const {
28058
28052
  DeprecationMixin
28059
28053
  } = Util;
28060
28054
  const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
28061
- const defaultBackgroundImage$1 = {
28055
+ const defaultBackgroundImage = {
28062
28056
  url: null
28063
28057
  };
28064
28058
  const eq = Util.eq;
28065
- const deepEq$2 = Util.deepEq;
28059
+ const deepEq$1 = Util.deepEq;
28066
28060
  const UNLIMITED = "unlimited";
28067
28061
 
28068
28062
  // Sample background image:
@@ -29731,7 +29725,7 @@ class InteractiveGraph extends React.Component {
29731
29725
  const correctCoeffs = this.getQuadraticCoefficients(
29732
29726
  // @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
29733
29727
  rubric.correct.coords);
29734
- if (deepEq$2(guessCoeffs, correctCoeffs)) {
29728
+ if (deepEq$1(guessCoeffs, correctCoeffs)) {
29735
29729
  return {
29736
29730
  type: "points",
29737
29731
  earned: 1,
@@ -29747,7 +29741,7 @@ class InteractiveGraph extends React.Component {
29747
29741
  const canonicalGuessCoeffs = canonicalSineCoefficients(guessCoeffs);
29748
29742
  const canonicalCorrectCoeffs = canonicalSineCoefficients(correctCoeffs);
29749
29743
  // If the canonical coefficients match, it's correct.
29750
- if (deepEq$2(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
29744
+ if (deepEq$1(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
29751
29745
  return {
29752
29746
  type: "points",
29753
29747
  earned: 1,
@@ -29756,7 +29750,7 @@ class InteractiveGraph extends React.Component {
29756
29750
  };
29757
29751
  }
29758
29752
  } else if (state.type === "circle" && rubric.correct.type === "circle") {
29759
- if (deepEq$2(state.center, rubric.correct.center) && eq(state.radius, rubric.correct.radius)) {
29753
+ if (deepEq$1(state.center, rubric.correct.center) && eq(state.radius, rubric.correct.radius)) {
29760
29754
  return {
29761
29755
  type: "points",
29762
29756
  earned: 1,
@@ -29776,7 +29770,7 @@ class InteractiveGraph extends React.Component {
29776
29770
  guess == null || guess.sort();
29777
29771
  // @ts-expect-error - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
29778
29772
  correct.sort();
29779
- if (deepEq$2(guess, correct)) {
29773
+ if (deepEq$1(guess, correct)) {
29780
29774
  return {
29781
29775
  type: "points",
29782
29776
  earned: 1,
@@ -29800,7 +29794,7 @@ class InteractiveGraph extends React.Component {
29800
29794
  /* exact */
29801
29795
  guess.sort();
29802
29796
  correct.sort();
29803
- match = deepEq$2(guess, correct);
29797
+ match = deepEq$1(guess, correct);
29804
29798
  }
29805
29799
  if (match) {
29806
29800
  return {
@@ -29817,7 +29811,7 @@ class InteractiveGraph extends React.Component {
29817
29811
  guess = _.invoke(guess, "sort").sort();
29818
29812
  // @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
29819
29813
  correct = _.invoke(correct, "sort").sort();
29820
- if (deepEq$2(guess, correct)) {
29814
+ if (deepEq$1(guess, correct)) {
29821
29815
  return {
29822
29816
  type: "points",
29823
29817
  earned: 1,
@@ -29830,7 +29824,7 @@ class InteractiveGraph extends React.Component {
29830
29824
  const correct = rubric.correct.coords;
29831
29825
  if (
29832
29826
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
29833
- deepEq$2(guess[0], correct[0]) &&
29827
+ deepEq$1(guess[0], correct[0]) &&
29834
29828
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
29835
29829
  collinear(correct[0], correct[1], guess[1])) {
29836
29830
  return {
@@ -29861,7 +29855,7 @@ class InteractiveGraph extends React.Component {
29861
29855
  /* exact */
29862
29856
  match =
29863
29857
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
29864
- deepEq$2(guess[1], correct[1]) &&
29858
+ deepEq$1(guess[1], correct[1]) &&
29865
29859
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
29866
29860
  collinear(correct[1], correct[0], guess[0]) &&
29867
29861
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
@@ -29902,7 +29896,7 @@ InteractiveGraph.defaultProps = {
29902
29896
  labels: ["x", "y"],
29903
29897
  range: [[-10, 10], [-10, 10]],
29904
29898
  step: [1, 1],
29905
- backgroundImage: defaultBackgroundImage$1,
29899
+ backgroundImage: defaultBackgroundImage,
29906
29900
  markings: "graph",
29907
29901
  showTooltips: false,
29908
29902
  showProtractor: false,
@@ -31008,7 +31002,7 @@ const AnswerPill = props => {
31008
31002
  id: idFactory.get("perseus-label-image-widget-answer-pill"),
31009
31003
  onClick: correct ? undefined : onClick,
31010
31004
  ref: ref,
31011
- style: [style, popperStyle, styles$h.relative, correct && styles$h.correct, incorrect && styles$h.incorrect, (focused || hovered) && BringToFront]
31005
+ style: [style, popperStyle, styles$h.pill, correct && styles$h.correct, incorrect && styles$h.incorrect, (focused || hovered) && BringToFront]
31012
31006
  }, /*#__PURE__*/React.createElement(Renderer, {
31013
31007
  content: answerString,
31014
31008
  inline: true
@@ -31021,6 +31015,11 @@ const styles$h = StyleSheet.create({
31021
31015
  },
31022
31016
  incorrect: {
31023
31017
  backgroundColor: Color.offBlack64
31018
+ },
31019
+ pill: {
31020
+ // Reset the Pill's default height in order to account
31021
+ // for multi-line pills.
31022
+ height: "auto"
31024
31023
  }
31025
31024
  });
31026
31025
 
@@ -33019,7 +33018,7 @@ var Matcher$1 = {
33019
33018
  const _excluded$1 = ["onFormatChange", "checkValidity", "useArrowKeys"];
33020
33019
  const {
33021
33020
  firstNumericalParse,
33022
- captureScratchpadTouchStart: captureScratchpadTouchStart$1
33021
+ captureScratchpadTouchStart
33023
33022
  } = Util;
33024
33023
  const toNumericString = KhanMath.toNumericString;
33025
33024
  const getNumericFormat = KhanMath.getNumericFormat;
@@ -33177,7 +33176,7 @@ class NumberInput extends React.Component {
33177
33176
  onKeyDown: this._onKeyDown
33178
33177
  // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLInputElement>'.
33179
33178
  ,
33180
- onTouchStart: captureScratchpadTouchStart$1,
33179
+ onTouchStart: captureScratchpadTouchStart,
33181
33180
  defaultValue: toNumericString(this.props.value, this.state.format),
33182
33181
  value: undefined
33183
33182
  }));
@@ -33210,7 +33209,7 @@ NumberInput.defaultProps = {
33210
33209
  // Type imports
33211
33210
 
33212
33211
  const {
33213
- assert: assert$4
33212
+ assert: assert$3
33214
33213
  } = InteractiveUtil;
33215
33214
  const {
33216
33215
  stringArrayOfSize
@@ -33244,12 +33243,12 @@ const getDefaultPath$1 = function getDefaultPath() {
33244
33243
  };
33245
33244
  const getRowFromPath$1 = function getRowFromPath(path) {
33246
33245
  // 'path' should be a (row, column) pair
33247
- assert$4(_.isArray(path) && path.length === 2);
33246
+ assert$3(_.isArray(path) && path.length === 2);
33248
33247
  return +path[0];
33249
33248
  };
33250
33249
  const getColumnFromPath$1 = function getColumnFromPath(path) {
33251
33250
  // 'path' should be a (row, column) pair
33252
- assert$4(_.isArray(path) && path.length === 2);
33251
+ assert$3(_.isArray(path) && path.length === 2);
33253
33252
  return +path[1];
33254
33253
  };
33255
33254
  const getRefForPath$1 = function getRefForPath(path) {
@@ -34752,7 +34751,7 @@ const MovablePoint$1 = Graphie.MovablePoint;
34752
34751
  // @ts-expect-error - TS2339 - Property 'Line' does not exist on type 'typeof Graphie'.
34753
34752
  const Line$1 = Graphie.Line;
34754
34753
  const {
34755
- assert: assert$3
34754
+ assert: assert$2
34756
34755
  } = InteractiveUtil;
34757
34756
  const bound = (x, gt, lt) => Math.min(Math.max(x, gt), lt);
34758
34757
  const EN_DASH = "\u2013";
@@ -35010,7 +35009,7 @@ class NumberLine extends React.Component {
35010
35009
  const snapX = props.tickStep / props.snapDivisions;
35011
35010
  let x = bound(numLinePosition, left, right);
35012
35011
  x = left + number.roundTo(x - left, snapX);
35013
- assert$3(_.isFinite(x));
35012
+ assert$2(_.isFinite(x));
35014
35013
  return x;
35015
35014
  };
35016
35015
  // This function is intended to be used by the client code
@@ -38131,7 +38130,7 @@ var PassageRefTarget$1 = {
38131
38130
 
38132
38131
  /* eslint-disable react/no-unsafe */
38133
38132
  const {
38134
- deepEq: deepEq$1
38133
+ deepEq
38135
38134
  } = Util;
38136
38135
  class Plotter extends React.Component {
38137
38136
  constructor(..._args) {
@@ -38926,7 +38925,7 @@ Plotter.defaultProps = {
38926
38925
  };
38927
38926
  _.extend(Plotter, {
38928
38927
  validate: function (guess, rubric) {
38929
- if (deepEq$1(guess, rubric.starting)) {
38928
+ if (deepEq(guess, rubric.starting)) {
38930
38929
  return {
38931
38930
  type: "invalid",
38932
38931
  message: null
@@ -38934,7 +38933,7 @@ _.extend(Plotter, {
38934
38933
  }
38935
38934
  return {
38936
38935
  type: "points",
38937
- earned: deepEq$1(guess, rubric.correct) ? 1 : 0,
38936
+ earned: deepEq(guess, rubric.correct) ? 1 : 0,
38938
38937
  total: 1,
38939
38938
  message: null
38940
38939
  };
@@ -39302,7 +39301,7 @@ class InfoTip extends React.Component {
39302
39301
  }
39303
39302
 
39304
39303
  const {
39305
- assert: assert$2
39304
+ assert: assert$1
39306
39305
  } = InteractiveUtil;
39307
39306
  // @ts-expect-error - TS2339 - Property 'Path' does not exist on type 'typeof Graphie'. | TS2339 - Property 'Arc' does not exist on type 'typeof Graphie'. | TS2339 - Property 'Circle' does not exist on type 'typeof Graphie'. | TS2339 - Property 'Label' does not exist on type 'typeof Graphie'. | TS2339 - Property 'Line' does not exist on type 'typeof Graphie'. | TS2339 - Property 'MovablePoint' does not exist on type 'typeof Graphie'. | TS2339 - Property 'MovableLine' does not exist on type 'typeof Graphie'.
39308
39307
  const {
@@ -39314,7 +39313,7 @@ const {
39314
39313
  MovablePoint,
39315
39314
  MovableLine
39316
39315
  } = Graphie;
39317
- const defaultBoxSize$1 = 400;
39316
+ const defaultBoxSize = 400;
39318
39317
  const maxSampleSize = 1000;
39319
39318
  class Histogram extends React.Component {
39320
39319
  constructor(...args) {
@@ -39596,7 +39595,7 @@ Histogram.defaultProps = {
39596
39595
  data: null,
39597
39596
  xAxisLabel: "Proportion (%)",
39598
39597
  yAxisLabel: "Number of times seen",
39599
- box: [defaultBoxSize$1, defaultBoxSize$1]
39598
+ box: [defaultBoxSize, defaultBoxSize]
39600
39599
  };
39601
39600
  class Simulator extends React.Component {
39602
39601
  constructor(..._args) {
@@ -39694,7 +39693,7 @@ class Simulator extends React.Component {
39694
39693
  return true;
39695
39694
  };
39696
39695
  this.focusInputPath = path => {
39697
- assert$2(path.length > 0);
39696
+ assert$1(path.length > 0);
39698
39697
  const inputID = _.head(path);
39699
39698
  // eslint-disable-next-line react/no-string-refs
39700
39699
  const inputComponent = this.refs[inputID];
@@ -39702,7 +39701,7 @@ class Simulator extends React.Component {
39702
39701
  inputComponent.focus();
39703
39702
  };
39704
39703
  this.blurInputPath = path => {
39705
- assert$2(path.length > 0);
39704
+ assert$1(path.length > 0);
39706
39705
  const inputID = _.head(path);
39707
39706
  // eslint-disable-next-line react/no-string-refs
39708
39707
  const inputComponent = this.refs[inputID];
@@ -39710,17 +39709,17 @@ class Simulator extends React.Component {
39710
39709
  inputComponent.blur();
39711
39710
  };
39712
39711
  this.getDOMNodeForPath = path => {
39713
- assert$2(path.length > 0);
39712
+ assert$1(path.length > 0);
39714
39713
  const inputID = _.head(path);
39715
39714
  // eslint-disable-next-line react/no-string-refs
39716
39715
  return ReactDOM__default.findDOMNode(this.refs[inputID]);
39717
39716
  };
39718
39717
  this.getGrammarTypeForPath = path => {
39719
- assert$2(path.length > 0);
39718
+ assert$1(path.length > 0);
39720
39719
  return "number";
39721
39720
  };
39722
39721
  this.setInputValue = (path, newValue, cb) => {
39723
- assert$2(path.length > 0);
39722
+ assert$1(path.length > 0);
39724
39723
  const inputID = _.head(path);
39725
39724
  const capitalizedID = inputID.charAt(0).toUpperCase() + inputID.slice(1);
39726
39725
  const functionName = "handle" + capitalizedID + "Change";
@@ -40011,7 +40010,7 @@ var Sorter$1 = {
40011
40010
  };
40012
40011
 
40013
40012
  const {
40014
- assert: assert$1
40013
+ assert
40015
40014
  } = InteractiveUtil;
40016
40015
 
40017
40016
  /* Input handling: Maps a (row, column) pair to a unique ref used by React,
@@ -40025,12 +40024,12 @@ const getDefaultPath = function getDefaultPath() {
40025
40024
  };
40026
40025
  const getRowFromPath = function getRowFromPath(path) {
40027
40026
  // 'path' should be a (row, column) pair
40028
- assert$1(_.isArray(path) && path.length === 2);
40027
+ assert(_.isArray(path) && path.length === 2);
40029
40028
  return +path[0];
40030
40029
  };
40031
40030
  const getColumnFromPath = function getColumnFromPath(path) {
40032
40031
  // 'path' should be a (row, column) pair
40033
- assert$1(_.isArray(path) && path.length === 2);
40032
+ assert(_.isArray(path) && path.length === 2);
40034
40033
  return +path[1];
40035
40034
  };
40036
40035
  const getRefForPath = function getRefForPath(path) {
@@ -40307,2414 +40306,6 @@ var Table$1 = {
40307
40306
  isLintable: true
40308
40307
  };
40309
40308
 
40310
- const {
40311
- deepEq,
40312
- getGridStep,
40313
- captureScratchpadTouchStart
40314
- } = Util;
40315
- const {
40316
- assert
40317
- } = InteractiveUtil;
40318
- const ROTATE_SNAP_DEGREES = 15;
40319
- const DEGREE_SIGN = "\u00B0";
40320
- const RENDER_TRANSFORM_DELAY_IN_MS = 300;
40321
- const ROTATE_HANDLE_DIST = 1.5;
40322
- const REFLECT_ROTATE_HANDLE_DIST = 2;
40323
- const REFLECT_BUTTON_SIZE = 1;
40324
- const defaultBoxSize = 400;
40325
- const defaultBackgroundImage = {
40326
- url: null
40327
- };
40328
-
40329
- /* Does a pluck on keys inside objects in an object
40330
- *
40331
- * Ex:
40332
- * tools = {
40333
- * translation: {
40334
- * enabled: true
40335
- * },
40336
- * rotation: {
40337
- * enabled: false
40338
- * }
40339
- * };
40340
- * pluckObject(tools, "enabled") returns {
40341
- * translation: true
40342
- * rotation: false
40343
- * }
40344
- */
40345
- function pluckObject(object, subKey) {
40346
- return _.object(_.map(object, function (value, key) {
40347
- return [key, value[subKey]];
40348
- }));
40349
- }
40350
- const defaultGraphProps = function defaultGraphProps(setProps, boxSize) {
40351
- setProps = setProps || {};
40352
- const labels = setProps.labels || ["x", "y"];
40353
- const range = setProps.range || [[-10, 10], [-10, 10]];
40354
- const step = setProps.step || [1, 1];
40355
- const gridStep = setProps.gridStep || getGridStep(range, step, boxSize);
40356
- return {
40357
- box: [boxSize, boxSize],
40358
- labels: labels,
40359
- range: range,
40360
- step: step,
40361
- gridStep: gridStep,
40362
- valid: true,
40363
- backgroundImage: defaultBackgroundImage,
40364
- markings: "grid",
40365
- showProtractor: false
40366
- };
40367
- };
40368
- const defaultTransformerProps = {
40369
- apiOptions: ApiOptions.defaults,
40370
- gradeEmpty: false,
40371
- graphMode: "interactive",
40372
- listMode: "dynamic",
40373
- graph: {},
40374
- tools: {
40375
- translation: {
40376
- enabled: true,
40377
- required: false,
40378
- constraints: {}
40379
- },
40380
- rotation: {
40381
- enabled: true,
40382
- required: false,
40383
- constraints: {
40384
- fixed: false
40385
- },
40386
- coord: [1, 6]
40387
- },
40388
- reflection: {
40389
- enabled: true,
40390
- required: false,
40391
- constraints: {
40392
- fixed: false
40393
- },
40394
- coords: [[2, -4], [2, 2]]
40395
- },
40396
- dilation: {
40397
- enabled: true,
40398
- required: false,
40399
- constraints: {
40400
- fixed: false
40401
- },
40402
- coord: [6, 6]
40403
- }
40404
- },
40405
- drawSolutionShape: true,
40406
- starting: {
40407
- shape: {
40408
- type: "polygon-3",
40409
- coords: [[2, 2], [2, 6], [7, 2]]
40410
- },
40411
- transformations: []
40412
- },
40413
- correct: {
40414
- shape: {
40415
- type: "polygon-3",
40416
- coords: [[2, 2], [2, 6], [7, 2]]
40417
- },
40418
- transformations: []
40419
- }
40420
- };
40421
- function colorForTool(tool) {
40422
- return tool.constraints.fixed ? KhanColors.DYNAMIC : KhanColors.INTERACTIVE;
40423
- }
40424
-
40425
- /* Scales a distance from the default range of
40426
- * [-10, 10] to a given props.range pair
40427
- *
40428
- * Used for sizing various transformation tools
40429
- * (rotation handle, dilation circle)
40430
- */
40431
- function scaleToRange(dist, range) {
40432
- const spreadX = range[0][1] - range[0][0];
40433
- const spreadY = range[1][1] - range[1][0];
40434
- return dist * Math.max(spreadX, spreadY) / 20;
40435
- }
40436
- function dilatePointFromCenter(point, dilationCenter, scale) {
40437
- const pv = vector$1.subtract(point, dilationCenter);
40438
- const pvScaled = vector$1.scale(pv, scale);
40439
- const transformedPoint = vector$1.add(dilationCenter, pvScaled);
40440
- return transformedPoint;
40441
- }
40442
-
40443
- // TODO(jack): i18nize this
40444
- function stringFromDecimal(number) {
40445
- return String(KhanMath.roundTo(9, number));
40446
- }
40447
- function stringFromFraction(number$1) {
40448
- const frac = KhanMath.toFraction(number$1, number.DEFAULT_TOLERANCE);
40449
- if (frac[1] === 1) {
40450
- return stringFromDecimal(number$1);
40451
- }
40452
- return stringFromDecimal(frac[0]) + "/" + stringFromDecimal(frac[1]);
40453
- }
40454
- function texFromPoint(point) {
40455
- const {
40456
- TeX
40457
- } = getDependencies();
40458
- return [/*#__PURE__*/React.createElement(TeX, {
40459
- key: "("
40460
- }, "("), stringFromDecimal(point[0]), /*#__PURE__*/React.createElement(TeX, {
40461
- key: ","
40462
- }, ", {}"), stringFromDecimal(point[1]), /*#__PURE__*/React.createElement(TeX, {
40463
- key: ")"
40464
- }, ")")];
40465
- }
40466
- function texFromVector(vector) {
40467
- const {
40468
- TeX
40469
- } = getDependencies();
40470
- return [/*#__PURE__*/React.createElement(TeX, {
40471
- key: "<"
40472
- }, i18n.doNotTranslate("\\langle")), stringFromDecimal(vector[0]), /*#__PURE__*/React.createElement(TeX, {
40473
- key: ","
40474
- }, ", {}"), stringFromDecimal(vector[1]), /*#__PURE__*/React.createElement(TeX, {
40475
- key: ">"
40476
- }, i18n.doNotTranslate("\\rangle"))];
40477
- }
40478
- function texFromAngleDeg(angleDeg) {
40479
- return stringFromDecimal(angleDeg) + DEGREE_SIGN;
40480
- }
40481
- function orderInsensitiveCoordsEqual(coords1, coords2) {
40482
- coords1 = _.clone(coords1).sort(point.compare);
40483
- coords2 = _.clone(coords2).sort(point.compare);
40484
- return _.all(_.map(coords1, function (coord1, i) {
40485
- const coord2 = coords2[i];
40486
- return point.equal(coord1, coord2);
40487
- }));
40488
- }
40489
- const inputComponentForApiOptions = apiOptions => {
40490
- if (apiOptions.customKeypad) {
40491
- return SimpleKeypadInput;
40492
- }
40493
- return NumberInput;
40494
- };
40495
-
40496
- /* Perform operations on raw transform objects */
40497
- const TransformOps = {
40498
- apply: function (transform) {
40499
- // Any transformation with empty text boxes is a no-op until
40500
- // filled out (these show up as nulls in transform.vector/line/etc).
40501
- // TODO (jack): Merge this just into reflections now that other
40502
- // transforms are always valid (after merging transformation
40503
- // collapsing, which may use isValid)
40504
- // @ts-expect-error - TS2345 - Argument of type 'PerseusTransformerTransformation' is not assignable to parameter of type 'never'.
40505
- if (!Transformations[transform.type].isValid(transform)) {
40506
- return _.identity; // do not transform the coord
40507
- }
40508
- // @ts-expect-error - TS2345 - Argument of type 'PerseusTransformerTransformation' is not assignable to parameter of type 'never'.
40509
- return Transformations[transform.type].apply(transform);
40510
- },
40511
- append: function (transformList, newTransform) {
40512
- // Append newTransform to transformList, and collapse the last
40513
- // two transforms if they are collapsable
40514
- const results = TransformOps._appendAndCollapseLastTwo(transformList, newTransform);
40515
- // Collapse any no-ops at the end of the transformation list
40516
- return TransformOps._collapseFinalNoOps(results);
40517
- },
40518
- _collapseFinalNoOps: function (transforms) {
40519
- // Collapse no-op transformations at the end of the list
40520
- if (transforms.length && TransformOps.isNoOp(_.last(transforms))) {
40521
- return _.initial(transforms);
40522
- }
40523
- return transforms;
40524
- },
40525
- _appendAndCollapseLastTwo: function (transformList, newTransform) {
40526
- if (!transformList.length) {
40527
- return [newTransform];
40528
- }
40529
- const collapsed = TransformOps.collapse(_.last(transformList), newTransform);
40530
- return _.initial(transformList).concat(collapsed);
40531
- },
40532
- isNoOp: function (transform) {
40533
- return Transformations[transform.type].isNoOp(transform);
40534
- },
40535
- collapse: function (transform1, transform2) {
40536
- // We can only collapse transforms that have the same type
40537
- if (transform1.type !== transform2.type) {
40538
- return [transform1, transform2];
40539
- }
40540
-
40541
- // Clicking the button again removes empty transformations
40542
- if (TransformOps.isEmpty(transform1) && TransformOps.isEmpty(transform2)) {
40543
- return [];
40544
- }
40545
-
40546
- // Don't collapse invalid transformations otherwise
40547
- if (!TransformOps.isValid(transform1) || !TransformOps.isValid(transform2)) {
40548
- return [transform1, transform2];
40549
- }
40550
- return TransformOps._collapseValidMonotypedTransforms(transform1, transform2);
40551
- },
40552
- isValid: function (transform) {
40553
- // @ts-expect-error - TS2345 - Argument of type 'PerseusTransformerTransformation' is not assignable to parameter of type 'never'.
40554
- return Transformations[transform.type].isValid(transform);
40555
- },
40556
- isEmpty: function (transform) {
40557
- // @ts-expect-error - TS2345 - Argument of type 'PerseusTransformerTransformation' is not assignable to parameter of type 'never'.
40558
- return Transformations[transform.type].isEmpty(transform);
40559
- },
40560
- _collapseValidMonotypedTransforms: function (transform1, transform2) {
40561
- let collapsed = Transformations[transform1.type].collapse(transform1, transform2);
40562
- if (collapsed) {
40563
- // Force all answers into an array
40564
- if (!_.isArray(collapsed)) {
40565
- collapsed = [collapsed];
40566
- }
40567
- // Add types to all transforms in the answer
40568
- _.each(collapsed, function (transform) {
40569
- transform.type = transform1.type;
40570
- });
40571
- return collapsed;
40572
- }
40573
- // These transforms can't be collapsed together
40574
- return [transform1, transform2];
40575
- },
40576
- toTeX: function (transform) {
40577
- return Transformations[transform.type].toTeX(transform);
40578
- },
40579
- /* A react representation of this transform object */
40580
- ListItem: createReactClass({
40581
- displayName: "ListItem",
40582
- render: function () {
40583
- if (this.props.mode === "dynamic") {
40584
- return /*#__PURE__*/React.createElement("div", null, TransformOps.toTeX(this.props.transform));
40585
- }
40586
- if (this.props.mode === "interactive") {
40587
- const TransformClass = Transformations[this.props.transform.type].Input;
40588
- return /*#__PURE__*/React.createElement(TransformClass
40589
- // eslint-disable-next-line react/no-string-refs
40590
- , _extends({
40591
- ref: "transform",
40592
- onChange: this.handleChange,
40593
- onFocus: this.props.onFocus,
40594
- onBlur: this.props.onBlur,
40595
- keypadElement: this.props.keypadElement,
40596
- apiOptions: this.props.apiOptions
40597
- }, this.props.transform));
40598
- }
40599
- throw new PerseusError("Invalid mode: " + this.props.mode, Errors.InvalidInput);
40600
- },
40601
- value: function () {
40602
- if (this.props.mode === "interactive") {
40603
- return _.extend({
40604
- type: this.props.transform.type
40605
- },
40606
- // eslint-disable-next-line react/no-string-refs
40607
- this.refs.transform.value());
40608
- }
40609
- return this.props.transform;
40610
- },
40611
- handleChange: _.debounce(function (callback) {
40612
- // @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.
40613
- this.props.onChange(this.value(), callback);
40614
- }, RENDER_TRANSFORM_DELAY_IN_MS),
40615
- /* InputPath API: depending on the API call, this could involve simply
40616
- * navigating to the right ref and calling the function on that
40617
- * component, or threading the call down and returning the result. */
40618
- _getComponentAtPath: function (path) {
40619
- // eslint-disable-next-line react/no-string-refs
40620
- const transform = this.refs.transform;
40621
- const ref = _.head(path);
40622
- return transform.refs[ref];
40623
- },
40624
- focus: function () {
40625
- // eslint-disable-next-line react/no-string-refs
40626
- const transform = this.refs.transform;
40627
- const path = _.head(transform.getInputPaths());
40628
- if (path) {
40629
- this.focusInputPath(path);
40630
- }
40631
- },
40632
- focusInputPath: function (path) {
40633
- this._getComponentAtPath(path).focus();
40634
- },
40635
- blurInputPath: function (path) {
40636
- this._getComponentAtPath(path).blur();
40637
- },
40638
- getDOMNodeForPath: function (path) {
40639
- return ReactDOM__default.findDOMNode(this._getComponentAtPath(path));
40640
- },
40641
- getGrammarTypeForPath: function (path) {
40642
- return "number";
40643
- },
40644
- setInputValue: function (path, value, cb) {
40645
- // `value` comes in as a string on mobile, but we need a number
40646
- // We let through the empty string so that "Clear" works -- in
40647
- // that case, the transformer widget will just act as if there is
40648
- // no input, which is what we want.
40649
- // @ts-expect-error - TS2339 - Property 'length' does not exist on type 'string | number'.
40650
- if (value.length) {
40651
- // @ts-expect-error - TS2345 - Argument of type 'string | number' is not assignable to parameter of type 'string'.
40652
- value = parseFloat(value);
40653
- if (isNaN(value)) {
40654
- return;
40655
- }
40656
- }
40657
- // eslint-disable-next-line react/no-string-refs
40658
- this.refs.transform.setInputValue(path, value, cb);
40659
- },
40660
- getInputPaths: function () {
40661
- // If we're in dynamic mode, then the list items are made up of
40662
- // static text.
40663
- if (this.props.mode === "dynamic") {
40664
- return [];
40665
- }
40666
- // eslint-disable-next-line react/no-string-refs
40667
- return this.refs.transform.getInputPaths();
40668
- }
40669
- })
40670
- };
40671
- const Transformations = {
40672
- translation: {
40673
- // I18N: As in the command, "Translate the polygon"
40674
- verbName: i18n._("Translate"),
40675
- nounName: i18n._("Translation"),
40676
- lowerNounName: i18n._("translation"),
40677
- apply: function (transform) {
40678
- return function (coord) {
40679
- return vector$1.add(coord, transform.vector);
40680
- };
40681
- },
40682
- isValid: function (transform) {
40683
- return _.isFinite(transform.vector[0]) && _.isFinite(transform.vector[1]);
40684
- },
40685
- isEmpty: function (transform) {
40686
- return transform.vector[0] === null && transform.vector[1] === null;
40687
- },
40688
- isNoOp: function (transform) {
40689
- return vector$1.equal(transform.vector, [0, 0]);
40690
- },
40691
- collapse: function (transform1, transform2) {
40692
- return {
40693
- vector: vector$1.add(transform1.vector, transform2.vector)
40694
- };
40695
- },
40696
- toTeX: function (transform) {
40697
- // I18N: As in the command, "Translation by <3, 1>"
40698
- return i18n.$_("Translation by %(vector)s", {
40699
- vector: texFromVector(transform.vector)
40700
- });
40701
- },
40702
- Input: class Input extends React.Component {
40703
- constructor(...args) {
40704
- super(...args);
40705
- this.state = {
40706
- vector: this.props.vector || [null, null]
40707
- };
40708
- this.value = () => {
40709
- // eslint-disable-next-line react/no-string-refs
40710
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40711
- const x = this.refs.x.getValue();
40712
- // eslint-disable-next-line react/no-string-refs
40713
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40714
- const y = this.refs.y.getValue();
40715
- return {
40716
- vector: [x, y]
40717
- };
40718
- };
40719
- /* InputPath API */
40720
- this.setInputValue = (path, value, cb) => {
40721
- const id = _.first(path);
40722
- const vector = _.clone(this.state.vector);
40723
- if (id === "x") {
40724
- vector[0] = value;
40725
- } else if (id === "y") {
40726
- vector[1] = value;
40727
- }
40728
- this.setState({
40729
- vector: vector
40730
- }, () => {
40731
- this.props.onChange(cb);
40732
- });
40733
- };
40734
- this.getInputPaths = () => {
40735
- return [["x"], ["y"]];
40736
- };
40737
- }
40738
- componentDidUpdate(prevProps) {
40739
- if (!deepEq(this.props, prevProps)) {
40740
- this.setState({
40741
- vector: this.props.vector
40742
- });
40743
- }
40744
- }
40745
- render() {
40746
- const InputComponent = inputComponentForApiOptions(this.props.apiOptions);
40747
- const {
40748
- TeX
40749
- } = getDependencies();
40750
- const vector = [/*#__PURE__*/React.createElement(TeX, {
40751
- key: "<"
40752
- }, i18n.doNotTranslate("\\langle")), /*#__PURE__*/React.createElement(InputComponent, {
40753
- key: "vec_x"
40754
- // eslint-disable-next-line react/no-string-refs
40755
- ,
40756
- ref: "x",
40757
- placeholder: 0,
40758
- value: this.state.vector[0],
40759
- useArrowKeys: true,
40760
- onChange: val0 => {
40761
- const val1 = this.state.vector[1];
40762
- this.setState({
40763
- vector: [val0, val1]
40764
- }, () => {
40765
- this.props.onChange();
40766
- });
40767
- },
40768
- onFocus: _.partial(this.props.onFocus, "x"),
40769
- onBlur: _.partial(this.props.onBlur, "x"),
40770
- keypadElement: this.props.keypadElement
40771
- }), /*#__PURE__*/React.createElement(TeX, {
40772
- key: ","
40773
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
40774
- key: "vec_y"
40775
- // eslint-disable-next-line react/no-string-refs
40776
- ,
40777
- ref: "y",
40778
- placeholder: 0,
40779
- value: this.state.vector[1],
40780
- useArrowKeys: true,
40781
- onChange: val1 => {
40782
- const val0 = this.state.vector[0];
40783
- this.setState({
40784
- vector: [val0, val1]
40785
- }, () => {
40786
- this.props.onChange();
40787
- });
40788
- },
40789
- onFocus: _.partial(this.props.onFocus, "y"),
40790
- onBlur: _.partial(this.props.onBlur, "y"),
40791
- keypadElement: this.props.keypadElement
40792
- }), /*#__PURE__*/React.createElement(TeX, {
40793
- key: ">"
40794
- }, i18n.doNotTranslate("\\rangle"))];
40795
- return /*#__PURE__*/React.createElement("div", null, i18n.$_("Translation by %(vector)s", {
40796
- vector: vector
40797
- }));
40798
- }
40799
- }
40800
- },
40801
- rotation: {
40802
- // I18N: As in the command, "Rotate the polygon"
40803
- verbName: i18n._("Rotate"),
40804
- nounName: i18n._("Rotation"),
40805
- lowerNounName: i18n._("rotation"),
40806
- apply: function (transform) {
40807
- return function (coord) {
40808
- return point.rotateDeg(coord, transform.angleDeg, transform.center);
40809
- };
40810
- },
40811
- isValid: function (transform) {
40812
- return _.isFinite(transform.angleDeg) && _.isFinite(transform.center[0]) && _.isFinite(transform.center[1]);
40813
- },
40814
- isEmpty: function (transform) {
40815
- return transform.angleDeg === null && transform.center[0] === null && transform.center[1] === null;
40816
- },
40817
- isNoOp: function (transform) {
40818
- return number.equal(transform.angleDeg, 0);
40819
- },
40820
- collapse: function (transform1, transform2) {
40821
- if (!point.equal(transform1.center, transform2.center)) {
40822
- return false;
40823
- }
40824
- return {
40825
- center: transform1.center,
40826
- angleDeg: transform1.angleDeg + transform2.angleDeg
40827
- };
40828
- },
40829
- toTeX: function (transform) {
40830
- return i18n.$_("Rotation by %(degrees)s about %(point)s", {
40831
- degrees: texFromAngleDeg(transform.angleDeg),
40832
- point: texFromPoint(transform.center)
40833
- });
40834
- },
40835
- Input: class Input extends React.Component {
40836
- constructor(...args) {
40837
- super(...args);
40838
- this.state = {
40839
- center: this.props.center || [null, null],
40840
- angleDeg: this.props.angleDeg || null
40841
- };
40842
- this.value = () => {
40843
- // eslint-disable-next-line react/no-string-refs
40844
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40845
- const angleDeg = this.refs.angleDeg.getValue();
40846
- // eslint-disable-next-line react/no-string-refs
40847
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40848
- const centerX = this.refs.centerX.getValue();
40849
- // eslint-disable-next-line react/no-string-refs
40850
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40851
- const centerY = this.refs.centerY.getValue();
40852
- return {
40853
- angleDeg: angleDeg,
40854
- center: [centerX, centerY]
40855
- };
40856
- };
40857
- /* InputPath API */
40858
- this.setInputValue = (path, value, cb) => {
40859
- const id = _.first(path);
40860
- let angleDeg = _.clone(this.state.angleDeg);
40861
- const center = _.clone(this.state.center);
40862
- if (id === "angleDeg") {
40863
- angleDeg = value;
40864
- } else if (id === "centerX") {
40865
- center[0] = value;
40866
- } else if (id === "centerY") {
40867
- center[1] = value;
40868
- }
40869
- this.setState({
40870
- angleDeg: angleDeg,
40871
- center: center
40872
- }, () => {
40873
- this.props.onChange(cb);
40874
- });
40875
- };
40876
- this.getInputPaths = () => {
40877
- return [["centerX"], ["centerY"], ["angleDeg"]];
40878
- };
40879
- }
40880
- componentDidUpdate(prevProps) {
40881
- if (!deepEq(this.props, prevProps)) {
40882
- this.setState({
40883
- center: this.props.center,
40884
- angleDeg: this.props.angleDeg
40885
- });
40886
- }
40887
- }
40888
- render() {
40889
- const InputComponent = inputComponentForApiOptions(this.props.apiOptions);
40890
- const {
40891
- TeX
40892
- } = getDependencies();
40893
- const point = [/*#__PURE__*/React.createElement(TeX, {
40894
- key: "("
40895
- }, "("), /*#__PURE__*/React.createElement(InputComponent, {
40896
- key: "X"
40897
- // eslint-disable-next-line react/no-string-refs
40898
- ,
40899
- ref: "centerX",
40900
- placeholder: 0,
40901
- value: this.state.center[0],
40902
- useArrowKeys: true,
40903
- onChange: val0 => {
40904
- const val1 = this.state.center[1];
40905
- this.setState({
40906
- center: [val0, val1]
40907
- }, () => {
40908
- this.props.onChange();
40909
- });
40910
- },
40911
- onFocus: _.partial(this.props.onFocus, "centerX"),
40912
- onBlur: _.partial(this.props.onBlur, "centerX"),
40913
- keypadElement: this.props.keypadElement
40914
- }), /*#__PURE__*/React.createElement(TeX, {
40915
- key: ","
40916
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
40917
- key: "Y"
40918
- // eslint-disable-next-line react/no-string-refs
40919
- ,
40920
- ref: "centerY",
40921
- placeholder: 0,
40922
- value: this.state.center[1],
40923
- useArrowKeys: true,
40924
- onChange: val1 => {
40925
- const val0 = this.state.center[0];
40926
- this.setState({
40927
- center: [val0, val1]
40928
- }, () => {
40929
- this.props.onChange();
40930
- });
40931
- },
40932
- onFocus: _.partial(this.props.onFocus, "centerY"),
40933
- onBlur: _.partial(this.props.onBlur, "centerY"),
40934
- keypadElement: this.props.keypadElement
40935
- }), /*#__PURE__*/React.createElement(TeX, {
40936
- key: ")"
40937
- }, ")")];
40938
- const degrees = [/*#__PURE__*/React.createElement(InputComponent, {
40939
- key: "deg"
40940
- // eslint-disable-next-line react/no-string-refs
40941
- ,
40942
- ref: "angleDeg",
40943
- placeholder: 0,
40944
- value: this.state.angleDeg,
40945
- useArrowKeys: true,
40946
- onChange: val => {
40947
- this.setState({
40948
- angleDeg: val
40949
- }, () => {
40950
- this.props.onChange();
40951
- });
40952
- },
40953
- onFocus: _.partial(this.props.onFocus, "angleDeg"),
40954
- onBlur: _.partial(this.props.onBlur, "angleDeg"),
40955
- keypadElement: this.props.keypadElement
40956
- }), DEGREE_SIGN];
40957
- // I18N: %(point)s must come before %(degrees)s in this phrase
40958
- const text = i18n.$_("Rotation about %(point)s by %(degrees)s", {
40959
- point,
40960
- degrees
40961
- });
40962
- return /*#__PURE__*/React.createElement("div", null, text);
40963
- }
40964
- }
40965
- },
40966
- reflection: {
40967
- // I18N: As in the command, "Reflect the polygon"
40968
- verbName: i18n._("Reflect"),
40969
- nounName: i18n._("Reflection"),
40970
- lowerNounName: i18n._("reflection"),
40971
- apply: function (transform) {
40972
- return function (coord) {
40973
- return point.reflectOverLine(coord, transform.line);
40974
- };
40975
- },
40976
- isValid: function (transform) {
40977
- // A bit hacky, but we'll also define reflecting over a
40978
- // single point as a no-op, to avoid NaN fun.
40979
- return _.all(_.flatten(transform.line), _.isFinite) && !point.equal(transform.line[0], transform.line[1]);
40980
- },
40981
- isEmpty: function (transform) {
40982
- return _.all(_.flatten(transform.line), _.isNull);
40983
- },
40984
- isNoOp: function (transform) {
40985
- // Invalid transforms are implicitly no-ops, so we don't
40986
- // have to catch that case here.
40987
- return false;
40988
- },
40989
- collapse: function (transform1, transform2) {
40990
- if (!line.equal(transform1.line, transform2.line)) {
40991
- return false;
40992
- }
40993
- return [];
40994
- },
40995
- toTeX: function (transform) {
40996
- const point1 = transform.line[0];
40997
- const point2 = transform.line[1];
40998
- return i18n.$_("Reflection over the line from %(point1)s to %(point2)s", {
40999
- point1: texFromPoint(point1),
41000
- point2: texFromPoint(point2)
41001
- });
41002
- },
41003
- Input: class Input extends React.Component {
41004
- constructor(...args) {
41005
- super(...args);
41006
- this.state = {
41007
- line: this.props.line || [[null, null], [null, null]]
41008
- };
41009
- this.changePoint = (i, j, val, cb) => {
41010
- const line = _.map(this.state.line, _.clone);
41011
- // @ts-expect-error - TS2538 - Type 'undefined' cannot be used as an index type.
41012
- line[i][j] = val;
41013
- this.setState({
41014
- line: line
41015
- }, () => {
41016
- this.props.onChange(cb);
41017
- });
41018
- };
41019
- this.value = () => {
41020
- // eslint-disable-next-line react/no-string-refs
41021
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41022
- const x1 = this.refs.x1.getValue();
41023
- // eslint-disable-next-line react/no-string-refs
41024
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41025
- const y1 = this.refs.y1.getValue();
41026
- // eslint-disable-next-line react/no-string-refs
41027
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41028
- const x2 = this.refs.x2.getValue();
41029
- // eslint-disable-next-line react/no-string-refs
41030
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41031
- const y2 = this.refs.y2.getValue();
41032
- return {
41033
- line: [[x1, y1], [x2, y2]]
41034
- };
41035
- };
41036
- /* InputPath API */
41037
- this.setInputValue = (path, value, cb) => {
41038
- const id = _.first(path);
41039
- let j;
41040
- if (id[0] === "x") {
41041
- j = 0;
41042
- } else if (id[0] === "y") {
41043
- j = 1;
41044
- }
41045
- let i;
41046
- if (id[1] === "1") {
41047
- i = 0;
41048
- } else if (id[1] === "2") {
41049
- i = 1;
41050
- }
41051
- this.changePoint(i, j, value, cb);
41052
- };
41053
- this.getInputPaths = () => {
41054
- return [["x1"], ["y1"], ["x2"], ["y2"]];
41055
- };
41056
- }
41057
- componentDidUpdate(prevProps) {
41058
- if (!deepEq(this.props, prevProps)) {
41059
- this.setState({
41060
- line: this.props.line
41061
- });
41062
- }
41063
- }
41064
- render() {
41065
- const InputComponent = inputComponentForApiOptions(this.props.apiOptions);
41066
- const {
41067
- TeX
41068
- } = getDependencies();
41069
- const point1 = [/*#__PURE__*/React.createElement(TeX, {
41070
- key: "("
41071
- }, "("), /*#__PURE__*/React.createElement(InputComponent, {
41072
- key: "x1"
41073
- // eslint-disable-next-line react/no-string-refs
41074
- ,
41075
- ref: "x1",
41076
- value: this.state.line[0][0],
41077
- useArrowKeys: true
41078
- // eslint-disable-next-line react/jsx-no-bind
41079
- ,
41080
- onChange: this.changePoint.bind(this, 0, 0),
41081
- onFocus: _.partial(this.props.onFocus, "x1"),
41082
- onBlur: _.partial(this.props.onBlur, "x1"),
41083
- keypadElement: this.props.keypadElement
41084
- }), /*#__PURE__*/React.createElement(TeX, {
41085
- key: ","
41086
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
41087
- key: "y1"
41088
- // eslint-disable-next-line react/no-string-refs
41089
- ,
41090
- ref: "y1",
41091
- value: this.state.line[0][1],
41092
- useArrowKeys: true
41093
- // eslint-disable-next-line react/jsx-no-bind
41094
- ,
41095
- onChange: this.changePoint.bind(this, 0, 1),
41096
- onFocus: _.partial(this.props.onFocus, "y1"),
41097
- onBlur: _.partial(this.props.onBlur, "y1"),
41098
- keypadElement: this.props.keypadElement
41099
- }), /*#__PURE__*/React.createElement(TeX, {
41100
- key: ")"
41101
- }, ")")];
41102
- const point2 = [/*#__PURE__*/React.createElement(TeX, {
41103
- key: "("
41104
- }, "("), /*#__PURE__*/React.createElement(InputComponent, {
41105
- key: "x2"
41106
- // eslint-disable-next-line react/no-string-refs
41107
- ,
41108
- ref: "x2",
41109
- value: this.state.line[1][0],
41110
- useArrowKeys: true
41111
- // eslint-disable-next-line react/jsx-no-bind
41112
- ,
41113
- onChange: this.changePoint.bind(this, 1, 0),
41114
- onFocus: _.partial(this.props.onFocus, "x2"),
41115
- onBlur: _.partial(this.props.onBlur, "x2"),
41116
- keypadElement: this.props.keypadElement
41117
- }), /*#__PURE__*/React.createElement(TeX, {
41118
- key: ","
41119
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
41120
- key: "y2"
41121
- // eslint-disable-next-line react/no-string-refs
41122
- ,
41123
- ref: "y2",
41124
- value: this.state.line[1][1],
41125
- useArrowKeys: true
41126
- // eslint-disable-next-line react/jsx-no-bind
41127
- ,
41128
- onChange: this.changePoint.bind(this, 1, 1),
41129
- onFocus: _.partial(this.props.onFocus, "y2"),
41130
- onBlur: _.partial(this.props.onBlur, "y2"),
41131
- keypadElement: this.props.keypadElement
41132
- }), /*#__PURE__*/React.createElement(TeX, {
41133
- key: ")"
41134
- }, ")")];
41135
- return /*#__PURE__*/React.createElement("div", null, i18n.$_("Reflection over the line from " + "%(point1)s to %(point2)s", {
41136
- point1,
41137
- point2
41138
- }));
41139
- }
41140
- }
41141
- },
41142
- dilation: {
41143
- // I18N: As in the command, "Dilate the polygon"
41144
- verbName: i18n._("Dilate"),
41145
- nounName: i18n._("Dilation"),
41146
- lowerNounName: i18n._("dilation"),
41147
- apply: function (transform) {
41148
- return function (coord) {
41149
- return dilatePointFromCenter(coord, transform.center, transform.scale);
41150
- };
41151
- },
41152
- isValid: function (transform) {
41153
- return _.isFinite(transform.scale) && _.isFinite(transform.center[0]) && _.isFinite(transform.center[1]);
41154
- },
41155
- isEmpty: function (transform) {
41156
- return transform.scale === null && transform.center[0] === null && transform.center[1] === null;
41157
- },
41158
- isNoOp: function (transform) {
41159
- return number.equal(transform.scale, 1);
41160
- },
41161
- collapse: function (transform1, transform2) {
41162
- if (!point.equal(transform1.center, transform2.center)) {
41163
- return false;
41164
- }
41165
- return {
41166
- center: transform1.center,
41167
- scale: transform1.scale * transform2.scale
41168
- };
41169
- },
41170
- toTeX: function (transform) {
41171
- const scaleString = stringFromFraction(transform.scale);
41172
- return i18n.$_("Dilation of scale %(scale)s about %(point)s", {
41173
- scale: scaleString,
41174
- point: texFromPoint(transform.center)
41175
- });
41176
- },
41177
- Input: class Input extends React.Component {
41178
- constructor(...args) {
41179
- super(...args);
41180
- this.state = {
41181
- center: this.props.center || [null, null],
41182
- scale: this.props.scale || null
41183
- };
41184
- this.value = () => {
41185
- // eslint-disable-next-line react/no-string-refs
41186
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41187
- const scale = this.refs.scale.getValue();
41188
- // eslint-disable-next-line react/no-string-refs
41189
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41190
- const x = this.refs.x.getValue();
41191
- // eslint-disable-next-line react/no-string-refs
41192
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41193
- const y = this.refs.y.getValue();
41194
- return {
41195
- scale: scale,
41196
- center: [x, y]
41197
- };
41198
- };
41199
- /* InputPath API */
41200
- this.setInputValue = (path, value, cb) => {
41201
- const id = _.first(path);
41202
- let scale = this.state.scale;
41203
- const center = _.clone(this.state.center);
41204
- if (id === "x") {
41205
- center[0] = value;
41206
- } else if (id === "y") {
41207
- center[1] = value;
41208
- } else if (id === "scale") {
41209
- scale = value;
41210
- }
41211
- this.setState({
41212
- scale: scale,
41213
- center: center
41214
- }, () => {
41215
- this.props.onChange(cb);
41216
- });
41217
- };
41218
- this.getInputPaths = () => {
41219
- return [["x"], ["y"], ["scale"]];
41220
- };
41221
- }
41222
- componentDidUpdate(prevProps) {
41223
- if (!deepEq(this.props, prevProps)) {
41224
- this.setState({
41225
- center: this.props.center,
41226
- scale: this.props.scale
41227
- });
41228
- }
41229
- }
41230
- render() {
41231
- const InputComponent = inputComponentForApiOptions(this.props.apiOptions);
41232
- const {
41233
- TeX
41234
- } = getDependencies();
41235
- const point = [/*#__PURE__*/React.createElement(TeX, {
41236
- key: "("
41237
- }, "("), /*#__PURE__*/React.createElement(InputComponent, {
41238
- key: "point_x"
41239
- // eslint-disable-next-line react/no-string-refs
41240
- ,
41241
- ref: "x",
41242
- placeholder: 0,
41243
- value: this.state.center[0],
41244
- useArrowKeys: true,
41245
- onChange: val0 => {
41246
- const val1 = this.state.center[1];
41247
- this.setState({
41248
- center: [val0, val1]
41249
- }, () => {
41250
- this.props.onChange();
41251
- });
41252
- },
41253
- onFocus: _.partial(this.props.onFocus, "x"),
41254
- onBlur: _.partial(this.props.onBlur, "x"),
41255
- keypadElement: this.props.keypadElement
41256
- }), /*#__PURE__*/React.createElement(TeX, {
41257
- key: ","
41258
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
41259
- key: "point_y"
41260
- // eslint-disable-next-line react/no-string-refs
41261
- ,
41262
- ref: "y",
41263
- placeholder: 0,
41264
- value: this.state.center[1],
41265
- useArrowKeys: true,
41266
- onChange: val1 => {
41267
- const val0 = this.state.center[0];
41268
- this.setState({
41269
- center: [val0, val1]
41270
- }, () => {
41271
- this.props.onChange();
41272
- });
41273
- },
41274
- onFocus: _.partial(this.props.onFocus, "y"),
41275
- onBlur: _.partial(this.props.onBlur, "y"),
41276
- keypadElement: this.props.keypadElement
41277
- }), /*#__PURE__*/React.createElement(TeX, {
41278
- key: ")"
41279
- }, ")")];
41280
- const scale = /*#__PURE__*/React.createElement(InputComponent
41281
- // eslint-disable-next-line react/no-string-refs
41282
- , {
41283
- ref: "scale",
41284
- placeholder: 1,
41285
- value: this.state.scale,
41286
- useArrowKeys: true,
41287
- onChange: val => {
41288
- this.setState({
41289
- scale: val
41290
- }, () => {
41291
- this.props.onChange();
41292
- });
41293
- },
41294
- onFocus: _.partial(this.props.onFocus, "scale"),
41295
- onBlur: _.partial(this.props.onBlur, "scale"),
41296
- keypadElement: this.props.keypadElement
41297
- });
41298
- return /*#__PURE__*/React.createElement("div", null, i18n.$_("Dilation about %(point)s by %(scale)s", {
41299
- point,
41300
- scale
41301
- }));
41302
- }
41303
- }
41304
- }
41305
- };
41306
-
41307
- /* Various functions to deal with different shape types */
41308
- const ShapeTypes = {
41309
- getPointCountForType: function (type) {
41310
- const splitType = type.split("-");
41311
- if (splitType[0] === "polygon") {
41312
- return splitType[1] || 3;
41313
- }
41314
- if (splitType[0] === "line" || splitType[0] === "lineSegment") {
41315
- return 2;
41316
- }
41317
- if (splitType[0] === "angle") {
41318
- return 3;
41319
- }
41320
- if (splitType[0] === "circle") {
41321
- return 2;
41322
- }
41323
- if (splitType[0] === "point") {
41324
- return 1;
41325
- }
41326
- },
41327
- addMovableShape: function (graphie, options) {
41328
- if (options.editable && options.translatable) {
41329
- throw new PerseusError("It doesn't make sense to have a movable shape " + "where you can stretch the points and translate them " + "simultaneously. options: " + JSON.stringify(options), Errors.InvalidInput);
41330
- }
41331
- const points = _.map(options.shape.coords, function (coord) {
41332
- let isMoving = false;
41333
- let previousCoord = coord;
41334
- const onMove = function onMove(x, y) {
41335
- if (!isMoving) {
41336
- previousCoord = currentPoint.coord;
41337
- isMoving = true;
41338
- }
41339
- let moveVector = vector$1.subtract([x, y], currentPoint.coord);
41340
-
41341
- // Translate from (x, y) semantics to (dX, dY) semantics
41342
- // This is more useful for translations on multiple points,
41343
- // where we care about how the points moved, not where any
41344
- // individual point ended up
41345
- if (options.onMove) {
41346
- moveVector = options.onMove(moveVector[0], moveVector[1]);
41347
- }
41348
-
41349
- // Perform a translation on all points in this shape when
41350
- // any point moves
41351
- if (options.translatable) {
41352
- _.each(points, function (point) {
41353
- // The point itself will be updated by the
41354
- // movablePoint class, so only translate the other
41355
- // points
41356
- if (point !== currentPoint) {
41357
- point.setCoord(vector$1.add(point.coord, moveVector));
41358
- }
41359
- });
41360
- }
41361
-
41362
- // Update our shape and our currentPoint
41363
- // Without this, some shapes (circles, angles) appear
41364
- // "bouncy" as they are updated with currentPoint at the
41365
- // current mouse coordinate (oldCoord), rather than newCoord
41366
- const oldCoord = currentPoint.coord;
41367
- const newCoord = vector$1.add(currentPoint.coord, moveVector);
41368
- // Temporarily change our coordinate so that
41369
- // shape.update() sees the new coordinate
41370
- currentPoint.coord = newCoord;
41371
- shape.update();
41372
- // ...But don't break onMove, which assumes it
41373
- // is the only thing changing our coord
41374
- currentPoint.coord = oldCoord;
41375
- return newCoord;
41376
- };
41377
- const onMoveEnd = function onMoveEnd() {
41378
- // onMove isn't guaranteed to be called before onMoveEnd, so
41379
- // we have to take into account that we may not have moved and
41380
- // set previousCoord.
41381
- if (options.onMoveEnd && isMoving) {
41382
- isMoving = false;
41383
- // We don't use the supplied x and y parameters here
41384
- // because MovablePoint's onMoveEnd semantics suck.
41385
- // It returns the mouseX, mouseY without processing them
41386
- // through onMove, leaving us with weird fractional moves
41387
- const change = vector$1.subtract(currentPoint.coord, previousCoord);
41388
- options.onMoveEnd(change[0], change[1]);
41389
- }
41390
- shape.update();
41391
- };
41392
- const currentPoint = graphie.addMovablePoint({
41393
- coord: coord,
41394
- normalStyle: options.normalPointStyle,
41395
- highlightStyle: options.highlightPointStyle,
41396
- constraints: {
41397
- fixed: !options.translatable && !options.editable
41398
- },
41399
- visible: options.showPoints,
41400
- snapX: options.snap && options.snap[0] || 0,
41401
- snapY: options.snap && options.snap[1] || 0,
41402
- bounded: false,
41403
- // Don't bound it when placing it on the graph
41404
- onMove: onMove,
41405
- onMoveEnd: onMoveEnd
41406
- });
41407
-
41408
- // Bound it when moving
41409
- // We can't set this earlier, because doing so would mean any
41410
- // points outside of the graph would be moved into a moved into
41411
- // a position that doesn't preserve the shape
41412
- currentPoint.bounded = true;
41413
- return currentPoint;
41414
- });
41415
- const shape = ShapeTypes.addShape(graphie, options, points);
41416
- const removeShapeWithoutPoints = shape.remove;
41417
- shape.remove = function () {
41418
- removeShapeWithoutPoints.apply(shape);
41419
- _.invoke(points, "remove");
41420
- };
41421
- return shape;
41422
- },
41423
- addShape: function (graphie, options, points) {
41424
- points = points || options.shape.coords;
41425
- const types = ShapeTypes._typesOf(options.shape);
41426
- const typeOptions = options.shape.options || ShapeTypes.defaultOptions(types);
41427
-
41428
- // @ts-expect-error - TS2554 - Expected 4 arguments, but got 3.
41429
- const shapes = ShapeTypes._mapTypes(types, points, function (type, points, i) {
41430
- const shapeOptions = _.extend({}, options, typeOptions[i]);
41431
- return ShapeTypes._addType(graphie, type, points, shapeOptions);
41432
- });
41433
- const updateFuncs = _.filter(_.pluck(shapes, "update"), _.identity);
41434
- const update = function update() {
41435
- _.invoke(updateFuncs, "call");
41436
- };
41437
- const removeFuncs = _.filter(_.pluck(shapes, "remove"), _.identity);
41438
- const remove = function remove() {
41439
- _.invoke(removeFuncs, "call");
41440
- };
41441
- const getOptions = function getOptions() {
41442
- return _.map(shapes, function (shape) {
41443
- if (shape.getOptions) {
41444
- return shape.getOptions();
41445
- }
41446
- return {};
41447
- });
41448
- };
41449
- const toJSON = function toJSON() {
41450
- const coords = _.map(points, function (pt) {
41451
- if (_.isArray(pt)) {
41452
- return pt;
41453
- }
41454
- return pt.coord;
41455
- });
41456
- return {
41457
- type: types,
41458
- coords: coords,
41459
- options: getOptions()
41460
- };
41461
- };
41462
- return {
41463
- type: types,
41464
- points: points,
41465
- update: update,
41466
- remove: remove,
41467
- toJSON: toJSON,
41468
- getOptions: getOptions
41469
- };
41470
- },
41471
- equal: function (shape1, shape2) {
41472
- const types1 = ShapeTypes._typesOf(shape1);
41473
- const types2 = ShapeTypes._typesOf(shape2);
41474
- if (types1.length !== types2.length) {
41475
- return false;
41476
- }
41477
- // @ts-expect-error - TS2554 - Expected 4 arguments, but got 3.
41478
- const shapes1 = ShapeTypes._mapTypes(types1, shape1.coords, ShapeTypes._combine);
41479
- // @ts-expect-error - TS2554 - Expected 4 arguments, but got 3.
41480
- const shapes2 = ShapeTypes._mapTypes(types2, shape2.coords, ShapeTypes._combine);
41481
- return _.all(_.map(shapes1, function (partialShape1, i) {
41482
- const partialShape2 = shapes2[i];
41483
- if (partialShape1.type !== partialShape2.type) {
41484
- return false;
41485
- }
41486
- return ShapeTypes._forType(partialShape1.type).equal(partialShape1.coords, partialShape2.coords);
41487
- }));
41488
- },
41489
- _typesOf: function (shape) {
41490
- let types = shape.type;
41491
- if (!_.isArray(types)) {
41492
- types = [types];
41493
- }
41494
- return _.map(types, function (type) {
41495
- if (type === "polygon") {
41496
- return "polygon-3";
41497
- }
41498
- return type;
41499
- });
41500
- },
41501
- defaultOptions: function (types) {
41502
- return _.map(types, function (type) {
41503
- const typeDefaultOptions = ShapeTypes._forType(type).defaultOptions;
41504
- return _.extend({}, typeDefaultOptions);
41505
- });
41506
- },
41507
- _forType: function (type) {
41508
- const baseType = type.split("-")[0];
41509
- return ShapeTypes[baseType];
41510
- },
41511
- _mapTypes: function (types, points, func, context) {
41512
- return _.map(types, function (type, i) {
41513
- const pointCount = ShapeTypes.getPointCountForType(type);
41514
- const currentPoints = _.first(points, pointCount);
41515
- points = _.rest(points, pointCount);
41516
- return func.call(context, type, currentPoints, i);
41517
- });
41518
- },
41519
- _addType: function (graphie, type, points, options) {
41520
- const lineCoords = _.isArray(points[0]) ? {
41521
- coordA: points[0],
41522
- coordZ: points[1]
41523
- } : {
41524
- pointA: points[0],
41525
- pointZ: points[1]
41526
- };
41527
- type = type.split("-")[0];
41528
- if (type === "polygon") {
41529
- const polygon = graphie.addMovablePolygon(_.extend({}, options, {
41530
- fixed: !options.editable,
41531
- snapX: options.snap && options.snap[0] || 0,
41532
- snapY: options.snap && options.snap[1] || 0,
41533
- points: points,
41534
- constrainToGraph: false
41535
- }));
41536
- return {
41537
- update: polygon.transform.bind(polygon),
41538
- remove: polygon.remove.bind(polygon)
41539
- };
41540
- }
41541
- if (type === "line" || type === "lineSegment") {
41542
- const line = graphie.addMovableLineSegment(_.extend({}, options, lineCoords, {
41543
- movePointsWithLine: true,
41544
- fixed: true,
41545
- constraints: {
41546
- fixed: true
41547
- },
41548
- extendLine: type === "line"
41549
- }));
41550
-
41551
- // TODO(jack): Hide points on uneditable lines when translation
41552
- // is a vector.
41553
- // We can't just remove the points yet, because they are the
41554
- // translation handle for the line.
41555
- return {
41556
- update: line.transform.bind(line, true),
41557
- remove: line.remove.bind(line)
41558
- };
41559
- }
41560
- if (type === "angle") {
41561
- // If this angle is editable, we want to be able to make angles
41562
- // both larger and smaller than 180 degrees.
41563
- // If this angle is not editable, it should always maintain
41564
- // it's angle measure, even if it is reflected (causing the
41565
- // clockwise-ness of the points to change)
41566
- const shouldChangeReflexivity = options.editable ? null : false;
41567
- const angle = graphie.addMovableAngle({
41568
- angleLabel: "$deg0",
41569
- fixed: true,
41570
- points: points,
41571
- normalStyle: options.normalStyle,
41572
- reflex: options.reflex
41573
- });
41574
-
41575
- // Hide non-vertex points on uneditable angles
41576
- if (!_.isArray(points[0]) && !options.editable) {
41577
- points[0].remove();
41578
- points[2].remove();
41579
- }
41580
- return {
41581
- update: angle.update.bind(angle, shouldChangeReflexivity),
41582
- remove: angle.remove.bind(angle),
41583
- getOptions: function () {
41584
- return {
41585
- reflex: angle.isReflex()
41586
- };
41587
- }
41588
- };
41589
- }
41590
- if (type === "circle") {
41591
- let perimeter = {
41592
- // temporary object for the first removal
41593
- remove: _.identity
41594
- };
41595
- const redrawPerim = function redrawPerim() {
41596
- const coord0 = points[0].coord || points[0];
41597
- const coord1 = points[1].coord || points[1];
41598
- const radius = point.distanceToPoint(coord0, coord1);
41599
- // @ts-expect-error - TS2554 - Expected 1 arguments, but got 0.
41600
- perimeter.remove();
41601
- perimeter = graphie.circle(coord0, radius, _.extend({
41602
- stroke: KhanColors.DYNAMIC,
41603
- "stroke-width": 2
41604
- }, options.normalStyle));
41605
- };
41606
- redrawPerim();
41607
- if (points[1].remove && !options.editable) {
41608
- points[1].remove();
41609
- }
41610
- return {
41611
- update: redrawPerim,
41612
- remove: function () {
41613
- // Not _.bind because the remove function changes
41614
- // when the perimeter is redrawn
41615
- // @ts-expect-error - TS2554 - Expected 1 arguments, but got 0.
41616
- perimeter.remove();
41617
- }
41618
- };
41619
- }
41620
- if (type === "point") {
41621
- // do nothing
41622
- return {
41623
- update: null,
41624
- remove: null
41625
- };
41626
- }
41627
- throw new PerseusError("Invalid shape type " + type, Errors.InvalidInput);
41628
- },
41629
- _combine: function (type, coords) {
41630
- return {
41631
- type: type,
41632
- coords: coords
41633
- };
41634
- },
41635
- polygon: {
41636
- equal: orderInsensitiveCoordsEqual
41637
- },
41638
- line: {
41639
- equal: line.equal
41640
- },
41641
- lineSegment: {
41642
- equal: orderInsensitiveCoordsEqual
41643
- },
41644
- angle: {
41645
- equal: function (points1, points2) {
41646
- if (!point.equal(points1[1], points2[1])) {
41647
- return false;
41648
- }
41649
- const line1_0 = [points1[1], points1[0]];
41650
- const line1_2 = [points1[1], points1[2]];
41651
- const line2_0 = [points2[1], points2[0]];
41652
- const line2_2 = [points2[1], points2[2]];
41653
- const equalUnflipped =
41654
- // @ts-expect-error - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'Ray'. | TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'Ray'.
41655
- ray.equal(line1_0, line2_0) && ray.equal(line1_2, line2_2);
41656
- const equalFlipped =
41657
- // @ts-expect-error - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'Ray'. | TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'Ray'.
41658
- ray.equal(line1_0, line2_2) && ray.equal(line1_2, line2_0);
41659
- return equalUnflipped || equalFlipped;
41660
- },
41661
- defaultOptions: {
41662
- reflex: false
41663
- }
41664
- },
41665
- circle: {
41666
- equal: function (points1, points2) {
41667
- const radius1 = point.distanceToPoint(points1[0], points1[1]);
41668
- const radius2 = point.distanceToPoint(points2[0], points2[1]);
41669
- return point.equal(points1[0], points2[0]) && number.equal(radius1, radius2);
41670
- }
41671
- },
41672
- point: {
41673
- equal: point.equal
41674
- }
41675
- };
41676
- const TransformationListItem = TransformOps.ListItem;
41677
- class TransformationList extends React.Component {
41678
- constructor(...args) {
41679
- super(...args);
41680
- this._transformationRefs = () => {
41681
- return _.times(this.props.transformations.length, i => {
41682
- // eslint-disable-next-line react/no-string-refs
41683
- return this.refs["transformation" + i];
41684
- });
41685
- };
41686
- this.value = () => {
41687
- return _.invoke(this._transformationRefs(), "value");
41688
- };
41689
- this.handleChange = (changed, callback) => {
41690
- this.props.onChange(this.value(), callback);
41691
- };
41692
- this.focusLast = () => {
41693
- const transformationRefs = this._transformationRefs();
41694
- if (transformationRefs.length !== 0) {
41695
- // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
41696
- _.last(transformationRefs).focus();
41697
- }
41698
- };
41699
- }
41700
- render() {
41701
- if (this.props.mode === "static") {
41702
- return /*#__PURE__*/React.createElement("span", null); // don't render anything
41703
- }
41704
-
41705
- const transformationList = _.map(this.props.transformations, function (transform, i) {
41706
- return /*#__PURE__*/React.createElement(TransformationListItem, {
41707
- ref: "transformation" + i,
41708
- key: "transformation" + i,
41709
- transform: transform
41710
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41711
- ,
41712
- mode: this.props.mode
41713
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41714
- ,
41715
- onChange: this.handleChange
41716
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41717
- ,
41718
- onFocus: _.partial(this.props.onFocus, "" + i)
41719
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41720
- ,
41721
- onBlur: _.partial(this.props.onBlur, "" + i)
41722
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41723
- ,
41724
- keypadElement: this.props.keypadElement
41725
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41726
- ,
41727
- apiOptions: this.props.apiOptions
41728
- });
41729
- }, this);
41730
- return /*#__PURE__*/React.createElement("div", {
41731
- className: "perseus-transformation-list"
41732
- }, transformationList);
41733
- }
41734
- }
41735
- class ToolButton extends React.Component {
41736
- render() {
41737
- const classes = this.props.toggled ? "simple-button exercise-orange toggled highlighted-tool-button" : "simple-button";
41738
- return /*#__PURE__*/React.createElement("button", {
41739
- type: "button",
41740
- className: classes,
41741
- disabled: this.props.disabled,
41742
- onClick: this.props.onClick
41743
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLButtonElement>'.
41744
- ,
41745
- onTouchStart: captureScratchpadTouchStart
41746
- }, this.props.children);
41747
- }
41748
- }
41749
- class ToolsBar extends React.Component {
41750
- constructor(...args) {
41751
- super(...args);
41752
- this.state = {
41753
- selected: null
41754
- };
41755
- this.changeSelected = tool => {
41756
- this.props.removeTool(this.state.selected);
41757
- if (!tool || tool === this.state.selected) {
41758
- this.setState({
41759
- selected: null
41760
- });
41761
- } else {
41762
- this.props.addTool(tool);
41763
- this.setState({
41764
- selected: tool
41765
- });
41766
- }
41767
- };
41768
- }
41769
- render() {
41770
- const tools = _.map(Transformations, function (tool, type) {
41771
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41772
- if (this.props.enabled[type]) {
41773
- return /*#__PURE__*/React.createElement(ToolButton, {
41774
- key: type
41775
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41776
- ,
41777
- disabled: this.props.apiOptions.readOnly
41778
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41779
- ,
41780
- toggled: this.state.selected === type
41781
- // eslint-disable-next-line react/jsx-no-bind
41782
- // @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.
41783
- ,
41784
- onClick: this.changeSelected.bind(this, type)
41785
- }, tool.verbName);
41786
- }
41787
- }, this);
41788
- return /*#__PURE__*/React.createElement("div", {
41789
- className: "transformer-tools-bar"
41790
- }, /*#__PURE__*/React.createElement("span", {
41791
- className: "simple-button-group"
41792
- }, tools), /*#__PURE__*/React.createElement("button", {
41793
- className: "transformer-undo-button simple-button",
41794
- type: "button",
41795
- disabled: this.props.apiOptions.readOnly,
41796
- onClick: this.props.onUndoClick
41797
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLButtonElement>'.
41798
- ,
41799
- onTouchStart: captureScratchpadTouchStart
41800
- }, /*#__PURE__*/React.createElement(InlineIcon, iconUndo), " " + i18n._("Undo")), /*#__PURE__*/React.createElement("div", {
41801
- className: "clear"
41802
- }));
41803
- }
41804
- }
41805
- class AddTransformBar extends React.Component {
41806
- constructor(...args) {
41807
- super(...args);
41808
- this.changeSelected = tool => {
41809
- if (tool) {
41810
- this.props.addTool(tool);
41811
- }
41812
- };
41813
- }
41814
- render() {
41815
- const tools = _.map(Transformations, function (tool, type) {
41816
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41817
- if (this.props.enabled[type]) {
41818
- return /*#__PURE__*/React.createElement(ToolButton, {
41819
- key: type,
41820
- toggled: false
41821
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41822
- ,
41823
- disabled: this.props.apiOptions.readOnly
41824
- // eslint-disable-next-line react/jsx-no-bind
41825
- // @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.
41826
- ,
41827
- onClick: this.changeSelected.bind(this, type)
41828
- }, /*#__PURE__*/React.createElement(InlineIcon, iconPlus), " ", tool.nounName);
41829
- }
41830
- }, this);
41831
- return /*#__PURE__*/React.createElement("div", {
41832
- className: "transformer-tools-bar"
41833
- }, tools, /*#__PURE__*/React.createElement("button", {
41834
- className: "transformer-undo-button simple-button",
41835
- type: "button",
41836
- onClick: this.props.onUndoClick,
41837
- disabled: this.props.apiOptions.readOnly
41838
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLButtonElement>'.
41839
- ,
41840
- onTouchStart: captureScratchpadTouchStart
41841
- }, /*#__PURE__*/React.createElement(InlineIcon, iconUndo), " " + i18n._("Undo")), /*#__PURE__*/React.createElement("div", {
41842
- className: "clear"
41843
- }));
41844
- }
41845
- }
41846
-
41847
- // there's no transform in the exports
41848
-
41849
- class Transformer extends React.Component {
41850
- constructor(..._args) {
41851
- super(..._args);
41852
- // @ts-expect-error - TS2564 - Property 'tools' has no initializer and is not definitely assigned in the constructor.
41853
- this.tools = void 0;
41854
- this.currentTool = void 0;
41855
- // @ts-expect-error - TS2564 - Property 'transformations' has no initializer and is not definitely assigned in the constructor.
41856
- this.transformations = void 0;
41857
- this.shape = void 0;
41858
- // This is not the same as TransformerShape
41859
- this.dilationCircle = void 0;
41860
- this.rotatePoint = void 0;
41861
- this.rotateHandle = void 0;
41862
- this.shouldSetupGraphie = (nextProps, prevProps) => {
41863
- if (!deepEq(prevProps.starting, nextProps.starting)) {
41864
- return true;
41865
- }
41866
- if (prevProps.graphMode !== nextProps.graphMode) {
41867
- return true;
41868
- }
41869
- if (prevProps.listMode !== nextProps.listMode) {
41870
- return true;
41871
- }
41872
- if (prevProps.drawSolutionShape !== nextProps.drawSolutionShape) {
41873
- return true;
41874
- }
41875
- if (nextProps.drawSolutionShape && !deepEq(prevProps.correct.shape, nextProps.correct.shape)) {
41876
- return true;
41877
- }
41878
- if (!deepEq(this.tools, nextProps.tools)) {
41879
- return true;
41880
- }
41881
- return false;
41882
- };
41883
- this.graphie = () => {
41884
- // eslint-disable-next-line react/no-string-refs
41885
- // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
41886
- return this.refs.graph.graphie();
41887
- };
41888
- this.setupGraphie = graphie => {
41889
- // A background image of our solution:
41890
- if (this.props.drawSolutionShape && this.props.correct.shape && this.props.correct.shape.coords) {
41891
- // @ts-expect-error - TS2554 - Expected 3 arguments, but got 2.
41892
- ShapeTypes.addShape(graphie, {
41893
- fixed: true,
41894
- shape: this.props.correct.shape,
41895
- normalStyle: {
41896
- stroke: KhanColors.GRAY,
41897
- "stroke-dasharray": "",
41898
- "stroke-width": 2
41899
- }
41900
- });
41901
- }
41902
- this.currentTool = null;
41903
- // eslint-disable-next-line react/no-string-refs
41904
- // @ts-expect-error - TS2339 - Property 'changeSelected' does not exist on type 'ReactInstance'.
41905
- this.refs.toolsBar.changeSelected(null);
41906
- this.addTransformerShape(this.props.starting.shape, /* translatable */false);
41907
- this.setTransformations(this.props.transformations);
41908
-
41909
- // Save a copy of our tools so that we can check future
41910
- // this.props.tools changes against them
41911
- // This seems weird, but gives us an easy way to tell whether
41912
- // props changes were self-inflicted (for which a graphie reset
41913
- // is not required, and is in fact a bad idea right now because
41914
- // of resetting the size of the dilation tool).
41915
- // TODO (jack): A deepClone method would be nice here
41916
- this.tools = {
41917
- translation: _.clone(this.props.tools.translation),
41918
- rotation: _.clone(this.props.tools.rotation),
41919
- reflection: _.clone(this.props.tools.reflection),
41920
- dilation: _.clone(this.props.tools.dilation)
41921
- };
41922
- };
41923
- /* Applies all transformations in `transformations`
41924
- * to the starting shape, and updates this.transformations
41925
- * to reflect this
41926
- *
41927
- * Usually called with this.props.transformations
41928
- */
41929
- this.setTransformations = transformations => {
41930
- this.resetCoords();
41931
- this.transformations = _.clone(transformations);
41932
- _.each(this.transformations, this.applyTransform);
41933
- };
41934
- // the polygon that we transform
41935
- this.addTransformerShape = (shape, translatable) => {
41936
- const self = this;
41937
- const graphie = this.graphie();
41938
- this.shape = ShapeTypes.addMovableShape(graphie, {
41939
- shape: shape,
41940
- editable: false,
41941
- showPoints: this.props.graphMode !== "static",
41942
- translatable: translatable,
41943
- onMove: function (dX, dY) {
41944
- dX = KhanMath.roundToNearest(graphie.snap[0], dX);
41945
- dY = KhanMath.roundToNearest(graphie.snap[1], dY);
41946
- // NOTE(kevinb): object is missing .constraints property
41947
- // @ts-expect-error - TS2345 - Argument of type '{ type: "translation"; vector: [any, any]; }' is not assignable to parameter of type 'PerseusTransformerTransformation'.
41948
- self.addTransform({
41949
- type: "translation",
41950
- vector: [dX, dY]
41951
- });
41952
- return [dX, dY];
41953
- },
41954
- normalPointStyle: {
41955
- fill: translatable ? KhanColors.INTERACTIVE : KhanColors.DYNAMIC,
41956
- stroke: translatable ? KhanColors.INTERACTIVE : KhanColors.DYNAMIC
41957
- },
41958
- highlightPointStyle: {
41959
- fill: KhanColors.INTERACTING,
41960
- stroke: KhanColors.INTERACTING
41961
- }
41962
- });
41963
- };
41964
- this.addTool = toolId => {
41965
- const self = this;
41966
- if (this.props.graphMode === "interactive") {
41967
- if (toolId === "translation") {
41968
- this.currentTool = this.addTranslationTool();
41969
- } else if (toolId === "rotation") {
41970
- this.currentTool = this.addRotationTool();
41971
- } else if (toolId === "reflection") {
41972
- this.currentTool = this.addReflectionTool();
41973
- } else if (toolId === "dilation") {
41974
- this.currentTool = this.addDilationTool();
41975
- } else {
41976
- throw new PerseusError("Invalid tool id: " + toolId, Errors.InvalidInput);
41977
- }
41978
- } else {
41979
- let transform;
41980
- if (toolId === "translation") {
41981
- // NOTE(kevinb): object is missing .constraints property
41982
- transform = {
41983
- type: "translation",
41984
- // NOTE(kevib): these should be numbers
41985
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
41986
- vector: [null, null]
41987
- };
41988
- } else if (toolId === "rotation") {
41989
- // NOTE(kevinb): object is missing .constraints property
41990
- transform = {
41991
- type: "rotation",
41992
- // NOTE(kevib): these should be numbers
41993
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
41994
- center: [null, null],
41995
- // NOTE(kevib): this should be a number
41996
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'.
41997
- angleDeg: null
41998
- };
41999
- } else if (toolId === "reflection") {
42000
- // Reflections with nulls in them won't be applied until
42001
- // fills in the blanks
42002
- transform = {
42003
- type: "reflection",
42004
- line: [
42005
- // NOTE(kevib): these should be numbers
42006
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
42007
- [null, null],
42008
- // NOTE(kevib): these should be numbers
42009
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
42010
- [null, null]]
42011
- };
42012
- } else if (toolId === "dilation") {
42013
- // NOTE(kevinb): object is missing .constraints property
42014
- transform = {
42015
- type: "dilation",
42016
- // NOTE(kevib): these should be numbers
42017
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
42018
- center: [null, null],
42019
- // NOTE(kevib): this should be a number
42020
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'.
42021
- scale: null
42022
- };
42023
- } else {
42024
- throw new PerseusError("Invalid tool id: " + toolId, Errors.InvalidInput);
42025
- }
42026
- this.doTransform(transform, function () {
42027
- // @ts-expect-error - TS2339 - Property 'focusLast' does not exist on type 'ReactInstance'.
42028
- self.refs.transformationList.focusLast();
42029
- });
42030
- }
42031
- };
42032
- this.removeTool = toolId => {
42033
- if (this.currentTool) {
42034
- this.currentTool.remove();
42035
- }
42036
- this.currentTool = null;
42037
- };
42038
- this.addTranslationTool = () => {
42039
- var _this$shape, _this$shape2;
42040
- const self = this;
42041
- (_this$shape = this.shape) == null || _this$shape.remove();
42042
- this.addTransformerShape((_this$shape2 = this.shape) == null ? void 0 : _this$shape2.toJSON(), /* translatable */true);
42043
- return {
42044
- remove: function () {
42045
- var _self$shape, _self$shape2;
42046
- (_self$shape = self.shape) == null || _self$shape.remove();
42047
- self.addTransformerShape((_self$shape2 = self.shape) == null ? void 0 : _self$shape2.toJSON(), /* translatable */false);
42048
- }
42049
- };
42050
- };
42051
- // Snaps a coord to this.graphie()'s snap
42052
- // @ts-expect-error - TS2322 - Type '(coord: Coord) => number[]' is not assignable to type '(arg1: Coord) => Coord'.
42053
- this.snapCoord = coord => {
42054
- const graphie = this.graphie();
42055
- return _.map(coord, function (val, dim) {
42056
- return KhanMath.roundToNearest(graphie.snap[dim], val);
42057
- });
42058
- };
42059
- // Normalize the coords into something that fits the new 45 degree
42060
- // reflection line.
42061
- // @ts-expect-error - TS2322 - Type '(messyCoords: [Coord, Coord]) => any[]' is not assignable to type '(arg1: [Coord, Coord]) => [Coord, Coord]'.
42062
- this.normalizeReflectionCoords = messyCoords => {
42063
- // @ts-expect-error - TS2345 - Argument of type 'Point' is not assignable to parameter of type 'Coord'.
42064
- const midpoint = this.snapCoord(line.midpoint(messyCoords));
42065
- const origDirectionPolar = vector$1.polarDegFromCart(vector$1.subtract(messyCoords[0], messyCoords[1]));
42066
- const direction = vector$1.cartFromPolarDeg(1, KhanMath.roundToNearest(45, origDirectionPolar[1]));
42067
- const coords = _.map([-1, 1], function (directionCoefficient) {
42068
- const coord = vector$1.add(midpoint, vector$1.scale(direction, directionCoefficient *
42069
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
42070
- this.scaleToCurrentRange(REFLECT_ROTATE_HANDLE_DIST)));
42071
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
42072
- return this.snapCoord(coord);
42073
- }, this);
42074
- return coords;
42075
- };
42076
- this.addReflectionTool = () => {
42077
- const options = this.props.tools.reflection;
42078
- if (!options.enabled) {
42079
- return;
42080
- }
42081
- const self = this;
42082
- // eslint-disable-next-line react/no-string-refs
42083
- // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
42084
- const graphie = this.refs.graph.graphie();
42085
- const updateReflectionTool = function updateReflectionTool() {
42086
- self.changeTool("reflection", {
42087
- coords: _.pluck(reflectPoints, "coord")
42088
- });
42089
- };
42090
- const coords = this.normalizeReflectionCoords(options.coords);
42091
-
42092
- // The points defining the line of reflection; hidden from the
42093
- // user.
42094
- const reflectPoints = _.map(coords, function (coord) {
42095
- return graphie.addMovablePoint({
42096
- coord: coord,
42097
- visible: false
42098
- });
42099
- }, this);
42100
-
42101
- // the line of reflection
42102
- // TODO(jack): graphie.style here is a hack to prevent the dashed
42103
- // style from leaking into the rest of the shapes. Remove when
42104
- // graphie.addMovableLineSegment doesn't leak styles anymore.
42105
- let reflectLine;
42106
- const normalColor = colorForTool(options);
42107
- graphie.style({}, function () {
42108
- reflectLine = graphie.addMovableLineSegment({
42109
- fixed: options.constraints.fixed,
42110
- constraints: options.constraints,
42111
- pointA: reflectPoints[0],
42112
- pointZ: reflectPoints[1],
42113
- snapX: graphie.snap[0],
42114
- snapY: graphie.snap[1],
42115
- extendLine: true,
42116
- normalStyle: {
42117
- stroke: normalColor,
42118
- "stroke-width": 2,
42119
- "stroke-dasharray": "- "
42120
- },
42121
- highlightStyle: {
42122
- stroke: KhanColors.INTERACTING,
42123
- "stroke-width": 2,
42124
- "stroke-dasharray": "- " // TODO(jack) solid doesn't
42125
- // work here, but would be
42126
- // nicer
42127
- },
42128
-
42129
- movePointsWithLine: true,
42130
- onMoveEnd: updateReflectionTool
42131
- });
42132
- });
42133
-
42134
- // the "button" point in the center of the line of reflection
42135
- const reflectButton = graphie.addReflectButton({
42136
- fixed: options.constraints.fixed,
42137
- line: reflectLine,
42138
- size: this.scaleToCurrentRange(REFLECT_BUTTON_SIZE),
42139
- onClick: function () {
42140
- self.doTransform({
42141
- type: "reflection",
42142
- // @ts-expect-error - TS2322 - Type 'any[]' is not assignable to type '[Coord, Coord]'.
42143
- line: _.pluck(reflectPoints, "coord")
42144
- });
42145
- if (reflectRotateHandle) {
42146
- // flip the rotation handle
42147
- // @ts-expect-error - TS2339 - Property 'setCoord' does not exist on type 'never'.
42148
- reflectRotateHandle.setCoord(vector$1.add(reflectButton.coord, vector$1.subtract(reflectButton.coord,
42149
- // @ts-expect-error - TS2339 - Property 'coord' does not exist on type 'never'.
42150
- reflectRotateHandle.coord)));
42151
- // @ts-expect-error - TS2339 - Property 'update' does not exist on type 'never'.
42152
- reflectRotateHandle.update();
42153
- }
42154
- },
42155
- normalStyle: {
42156
- stroke: normalColor,
42157
- "stroke-width": 2,
42158
- fill: normalColor
42159
- },
42160
- highlightStyle: {
42161
- stroke: KhanColors.INTERACTING,
42162
- "stroke-width": 3,
42163
- fill: KhanColors.INTERACTING
42164
- },
42165
- onMoveEnd: updateReflectionTool
42166
- });
42167
- let reflectRotateHandle = null;
42168
- if (!options.constraints.fixed) {
42169
- // The rotation handle for rotating the line of reflection
42170
- const initRotateHandleAngle = vector$1.polarDegFromCart(vector$1.subtract(reflectPoints[1].coord, reflectPoints[0].coord))[1] + 90; // 90 degrees off of the line
42171
- reflectRotateHandle = graphie.addRotateHandle({
42172
- center: reflectButton,
42173
- radius: this.scaleToCurrentRange(REFLECT_ROTATE_HANDLE_DIST),
42174
- angleDeg: initRotateHandleAngle,
42175
- width: this.scaleToCurrentRange(0.24),
42176
- hoverWidth: this.scaleToCurrentRange(0.4),
42177
- lengthAngle: 17,
42178
- onMove: function (newAngle) {
42179
- return KhanMath.roundToNearest(45, newAngle);
42180
- },
42181
- onMoveEnd: updateReflectionTool
42182
- });
42183
- }
42184
-
42185
- // Move the reflectButton and reflectRotateHandle with the line
42186
- $(reflectLine).on("move", function () {
42187
- reflectButton.update();
42188
- $(reflectButton).trigger("move"); // update the rotation handle,
42189
- // which watches for this in util/interactive.js.
42190
- });
42191
-
42192
- // Update the line and reflect button when the reflectRotateHandle is
42193
- // rotated
42194
- if (reflectRotateHandle) {
42195
- $(reflectRotateHandle).on("move", function () {
42196
- const rotateHandleApprox = self.snapCoord(
42197
- // @ts-expect-error - TS2531 - Object is possibly 'null'.
42198
- reflectRotateHandle.coord);
42199
- const rotateVector = vector$1.subtract(rotateHandleApprox, reflectButton.coord);
42200
- const flipped = reflectButton.isFlipped() ? 1 : 0;
42201
- reflectPoints[flipped].setCoord(vector$1.add(reflectButton.coord, vector$1.rotateDeg(rotateVector, 90)));
42202
- reflectPoints[1 - flipped].setCoord(vector$1.add(reflectButton.coord, vector$1.rotateDeg(rotateVector, -90)));
42203
- reflectLine.transform(true);
42204
- reflectButton.update();
42205
- });
42206
- }
42207
- return {
42208
- remove: function () {
42209
- reflectButton.remove();
42210
- if (reflectRotateHandle) {
42211
- // @ts-expect-error - TS2339 - Property 'remove' does not exist on type 'never'.
42212
- reflectRotateHandle.remove();
42213
- }
42214
- reflectLine.remove();
42215
- reflectPoints[0].remove();
42216
- reflectPoints[1].remove();
42217
- }
42218
- };
42219
- };
42220
- /* Scales a distance from the default range of
42221
- * [-10, 10] to the current this.props.graph.range
42222
- *
42223
- * Used for sizing various transformation tools
42224
- * (rotation handle, dilation circle)
42225
- */
42226
- this.scaleToCurrentRange = dist => {
42227
- // eslint-disable-next-line react/no-string-refs
42228
- // @ts-expect-error - TS2339 - Property 'props' does not exist on type 'ReactInstance'.
42229
- return scaleToRange(dist, this.refs.graph.props.range);
42230
- };
42231
- this.addRotationTool = () => {
42232
- const options = this.props.tools.rotation;
42233
- if (!options.enabled) {
42234
- return;
42235
- }
42236
- const self = this;
42237
- // eslint-disable-next-line react/no-string-refs
42238
- // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
42239
- const graphie = this.refs.graph.graphie();
42240
- const pointColor = colorForTool(options);
42241
- // The center of our rotation, which can be moved to change the
42242
- // center of rotation
42243
- this.rotatePoint = graphie.addMovablePoint({
42244
- constraints: options.constraints,
42245
- coord: options.coord,
42246
- snapX: graphie.snap[0],
42247
- snapY: graphie.snap[1],
42248
- normalStyle: {
42249
- // ugh, this seems to be a global and
42250
- "stroke-dasharray": "",
42251
- // is set to dash above
42252
- stroke: pointColor,
42253
- fill: pointColor
42254
- },
42255
- highlightStyle: {
42256
- "stroke-dasharray": "",
42257
- stroke: KhanColors.INTERACTING,
42258
- fill: KhanColors.INTERACTING
42259
- }
42260
- });
42261
-
42262
- // The point that we move around the center of rotation to actually
42263
- // cause rotations
42264
- this.rotateHandle = graphie.addRotateHandle({
42265
- center: this.rotatePoint,
42266
- radius: this.scaleToCurrentRange(ROTATE_HANDLE_DIST),
42267
- width: this.scaleToCurrentRange(0.24),
42268
- hoverWidth: this.scaleToCurrentRange(0.4),
42269
- onMove: function (newAngle, oldAngle) {
42270
- const transform = self.getRotationTransformFromAngle(self.rotatePoint.coord, newAngle - oldAngle);
42271
-
42272
- // Rotate polygon with rotateHandle
42273
- self.doTransform(transform);
42274
-
42275
- // @ts-expect-error - TS2339 - Property 'angleDeg' does not exist on type 'PerseusTransformerTransformation'.
42276
- return oldAngle + transform.angleDeg;
42277
- }
42278
- });
42279
-
42280
- // Update tools.rotation.coord
42281
- this.rotatePoint.onMoveEnd = (x, y) => {
42282
- self.changeTool("rotation", {
42283
- coord: [x, y]
42284
- });
42285
- };
42286
- return {
42287
- remove: function () {
42288
- var _self$rotateHandle, _self$rotatePoint;
42289
- (_self$rotateHandle = self.rotateHandle) == null || _self$rotateHandle.remove();
42290
- (_self$rotatePoint = self.rotatePoint) == null || _self$rotatePoint.remove();
42291
- }
42292
- };
42293
- };
42294
- this.addDilationTool = () => {
42295
- const options = this.props.tools.dilation;
42296
- if (!options.enabled) {
42297
- return;
42298
- }
42299
- const self = this;
42300
- // eslint-disable-next-line react/no-string-refs
42301
- // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
42302
- const graphie = this.refs.graph.graphie();
42303
- const pointColor = colorForTool(options);
42304
- // the circle for causing dilation transforms
42305
- self.dilationCircle = graphie.addCircleGraph({
42306
- centerConstraints: options.constraints,
42307
- center: options.coord,
42308
- radius: self.scaleToCurrentRange(2),
42309
- snapX: graphie.snap[0],
42310
- snapY: graphie.snap[1],
42311
- minRadius: self.scaleToCurrentRange(1),
42312
- snapRadius: self.scaleToCurrentRange(0.5),
42313
- onResize: function (newRadius, oldRadius) {
42314
- // NOTE(kevinb): object is missing .constraints property
42315
- // @ts-expect-error - TS2345 - Argument of type '{ type: "dilation"; center: any; scale: number; }' is not assignable to parameter of type 'PerseusTransformerTransformation'.
42316
- self.doTransform({
42317
- type: "dilation",
42318
- center: self.dilationCircle.centerPoint.coord,
42319
- scale: newRadius / oldRadius
42320
- });
42321
- },
42322
- circleNormalStyle: {
42323
- stroke: pointColor,
42324
- "stroke-width": 2,
42325
- "stroke-dasharray": "- ",
42326
- "fill-opacity": 0
42327
- },
42328
- circleHighlightStyle: {
42329
- stroke: KhanColors.INTERACTING,
42330
- "stroke-width": 2,
42331
- "stroke-dasharray": "",
42332
- fill: KhanColors.INTERACTING,
42333
- "fill-opacity": 0.05
42334
- },
42335
- centerNormalStyle: {
42336
- stroke: pointColor,
42337
- fill: pointColor,
42338
- "stroke-width": 2,
42339
- "stroke-dasharray": ""
42340
- },
42341
- centerHighlightStyle: {
42342
- stroke: pointColor,
42343
- fill: pointColor,
42344
- "stroke-width": 2,
42345
- "stroke-dasharray": ""
42346
- }
42347
- });
42348
- const origOnMoveEnd = this.dilationCircle.centerPoint.onMoveEnd;
42349
- this.dilationCircle.centerPoint.onMoveEnd = (...args) => {
42350
- if (origOnMoveEnd) {
42351
- origOnMoveEnd(args);
42352
- }
42353
- self.changeTool("dilation", {
42354
- coord: self.dilationCircle.centerPoint.coord
42355
- });
42356
- };
42357
- return {
42358
- remove: function () {
42359
- var _self$dilationCircle;
42360
- (_self$dilationCircle = self.dilationCircle) == null || _self$dilationCircle.remove();
42361
- }
42362
- };
42363
- };
42364
- // returns a transformation object representing a rotation
42365
- // rounds the angle to the nearest 15 degrees
42366
- // @ts-expect-error - TS2322 - Type '(center: Coord, angleChanged: number) => { type: "rotation"; center: Coord; angleDeg: number; }' is not assignable to type '(arg1: Coord, arg2: number) => PerseusTransformerTransformation'.
42367
- this.getRotationTransformFromAngle = (center, angleChanged) => {
42368
- angleChanged = (angleChanged + 360) % 360;
42369
- if (angleChanged > 180) {
42370
- angleChanged -= 360;
42371
- }
42372
- const roundedAngle = Math.round(angleChanged / ROTATE_SNAP_DEGREES) * ROTATE_SNAP_DEGREES;
42373
-
42374
- // NOTE(kevinb): return object is missing .constraints property
42375
- return {
42376
- type: "rotation",
42377
- center: center,
42378
- angleDeg: roundedAngle
42379
- };
42380
- };
42381
- // apply and save a transform
42382
- this.doTransform = (transform, callback) => {
42383
- this.applyTransform(transform);
42384
- this.addTransform(transform, callback);
42385
- };
42386
- // apply a transform to our polygon (without modifying our transformation
42387
- // list)
42388
- this.applyTransform = transform => {
42389
- if (this.props.graphMode !== "static") {
42390
- const transformFunc = TransformOps.apply(transform);
42391
- // @ts-expect-error - TS2345 - Argument of type '(coord: any) => any' is not assignable to parameter of type 'PerseusTransformerTransformation'.
42392
- this.applyCoordTransformation(transformFunc);
42393
- }
42394
- };
42395
- // transform our polygon by transforming each point using a given function
42396
- this.applyCoordTransformation = pointTransform => {
42397
- var _this$shape3;
42398
- _.each(this.shape.points, function (point) {
42399
- // @ts-expect-error - TS2349 - This expression is not callable.
42400
- const newCoord = pointTransform(point.coord);
42401
- point.setCoord(newCoord);
42402
- });
42403
- (_this$shape3 = this.shape) == null || _this$shape3.update();
42404
- };
42405
- this.resetCoords = () => {
42406
- var _this$shape4;
42407
- const startCoords = this.props.starting.shape.coords;
42408
- _.each(this.shape.points, function (point, i) {
42409
- point.setCoord(startCoords[i]);
42410
- });
42411
- (_this$shape4 = this.shape) == null || _this$shape4.update();
42412
- };
42413
- // Remove the last transformation
42414
- this.handleUndoClick = () => {
42415
- // eslint-disable-next-line react/no-string-refs
42416
- // @ts-expect-error - TS2339 - Property 'changeSelected' does not exist on type 'ReactInstance'.
42417
- this.refs.toolsBar.changeSelected(null);
42418
- if (this.props.transformations.length) {
42419
- this.props.onChange({
42420
- transformations: _.initial(this.props.transformations)
42421
- });
42422
- }
42423
- };
42424
- this.setTransformationProps = (newTransfomationList, callback) => {
42425
- this.props.onChange({
42426
- transformations: newTransfomationList
42427
- },
42428
- // @ts-expect-error - TS2345 - Argument of type '(arg1: any) => unknown' is not assignable to parameter of type '() => unknown'.
42429
- callback);
42430
- };
42431
- // add a transformation to our props list of transformation
42432
- this.addTransform = (transform, callback) => {
42433
- this.transformations = TransformOps.append(this.transformations, transform);
42434
- this.props.onChange({
42435
- transformations: _.clone(this.transformations)
42436
- },
42437
- // @ts-expect-error - TS2345 - Argument of type '((arg1: any) => unknown) | undefined' is not assignable to parameter of type '(() => unknown) | undefined'.
42438
- callback);
42439
- };
42440
- this.changeTool = (tool, changes) => {
42441
- const newTools = _.clone(this.props.tools);
42442
- newTools[tool] = _.extend({}, this.props.tools[tool], changes);
42443
- this.tools[tool] = _.clone(newTools[tool]);
42444
- this.props.onChange({
42445
- // @ts-expect-error - TS2740 - Type '{ dilation: DilationTool; reflection: ReflectionTool; rotation: RotationTool; translation: TranslationTool; }' is missing the following properties from type 'readonly any[]': length, concat, join, slice, and 18 more.
42446
- tools: newTools
42447
- });
42448
- this.props.trackInteraction();
42449
- };
42450
- this.simpleValidate = rubric => {
42451
- return Transformer.validate(this.getUserInput(), rubric);
42452
- };
42453
- /**
42454
- * Calculate where the coordinates would be if they were
42455
- * moved, even if we're in formal mode with no movement
42456
- * (and thus the actual movablepoints may not have moved
42457
- */
42458
- // @ts-expect-error - TS2322 - Type '() => [Coord, Coord] | readonly Coord[] | [Coord, Coord, Coord]' is not assignable to type '() => Coord'.
42459
- this.getCoords = () => {
42460
- const startCoords = this.props.starting.shape.coords;
42461
- const transforms = this.props.transformations;
42462
- return _.reduce(transforms, function (coords, transform) {
42463
- return _.map(coords, TransformOps.apply(transform));
42464
- }, startCoords);
42465
- };
42466
- this.getEditorJSON = () => {
42467
- const json = _.pick(this.props, "grading", "starting", "graphMode", "listMode", "tools", "drawSolutionShape", "gradeEmpty");
42468
- // eslint-disable-next-line react/no-string-refs
42469
- // @ts-expect-error - TS2339 - Property 'graph' does not exist on type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "tools" | "starting" | "drawSolutionShape" | "gradeEmpty" | "graphMode" | "listMode">'. | TS2339 - Property 'toJSON' does not exist on type 'ReactInstance'.
42470
- json.graph = this.refs.graph.toJSON();
42471
- // @ts-expect-error - TS2339 - Property 'version' does not exist on type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "tools" | "starting" | "drawSolutionShape" | "gradeEmpty" | "graphMode" | "listMode">'.
42472
- json.version = 1.2; // Give us some safety to change the format
42473
- // when we realize that I wrote
42474
- // a horrible json spec for this widget
42475
-
42476
- // @ts-expect-error - TS2339 - Property 'answer' does not exist on type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "tools" | "starting" | "drawSolutionShape" | "gradeEmpty" | "graphMode" | "listMode">'.
42477
- json.answer = this.getUserInput();
42478
- return json;
42479
- };
42480
- this.getUserInput = () => {
42481
- var _this$shape5;
42482
- return {
42483
- transformations: this.props.transformations,
42484
- // This doesn't call this.shape.toJSON() because that doesn't
42485
- // handle coordinates in formal mode without movement, since
42486
- // the movablepoints never move
42487
- shape: {
42488
- type: this.shape.type,
42489
- coords: this.getCoords(),
42490
- options: (_this$shape5 = this.shape) == null ? void 0 : _this$shape5.getOptions()
42491
- }
42492
- };
42493
- };
42494
- /* InputPath API */
42495
- this._handleFocus = (...path) => {
42496
- this.props.onFocus(path);
42497
- };
42498
- this._handleBlur = (...path) => {
42499
- this.props.onBlur(path);
42500
- };
42501
- this._getTransformationForID = transformationID => {
42502
- // Returns the 'transformation' component corresponding to a given ID
42503
- const refPath = ["transformationList", "transformation" + transformationID];
42504
-
42505
- // Follow the path of references
42506
- let component = this;
42507
- _.each(refPath, ref => {
42508
- // @ts-expect-error - TS2322 - Type 'ReactInstance' is not assignable to type 'this'.
42509
- component = component.refs[ref];
42510
- });
42511
- return component;
42512
- };
42513
- this.getInputPaths = () => {
42514
- // If we're in static mode, then there is no transformation list, and,
42515
- // as a result, no input paths.
42516
- if (this.props.listMode === "static") {
42517
- return [];
42518
- }
42519
- let inputPaths = [];
42520
- _.each(this.props.transformations, (transformation, i) => {
42521
- // @ts-expect-error - TS2345 - Argument of type 'number' is not assignable to parameter of type 'string'.
42522
- transformation = this._getTransformationForID(i);
42523
- // @ts-expect-error - TS2339 - Property 'getInputPaths' does not exist on type 'PerseusTransformerTransformation'.
42524
- const innerPaths = transformation.getInputPaths();
42525
- const fullPaths = _.map(innerPaths, innerPath => {
42526
- return ["" + i].concat(innerPath);
42527
- });
42528
- // @ts-expect-error - TS2345 - Argument of type 'string[][]' is not assignable to parameter of type 'ConcatArray<never>'.
42529
- inputPaths = inputPaths.concat(fullPaths);
42530
- });
42531
- return inputPaths;
42532
- };
42533
- this._passToInner = function (functionName, path, ...args) {
42534
- if (!path || !path.length) {
42535
- return;
42536
- }
42537
-
42538
- // First argument tells us which transformation will receive the call;
42539
- // remaining arguments are used within that transformation to identify
42540
- // a specific input.
42541
- const innerPath = _.rest(path);
42542
-
42543
- // Pass arguments down to appropriate 'transformation' component
42544
- const transformationID = _.head(path);
42545
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
42546
- const caller = this._getTransformationForID(transformationID);
42547
- return caller[functionName](innerPath, ...args);
42548
- };
42549
- this.focus = () => {
42550
- // Just focus the first showing input
42551
- const inputs = this.getInputPaths();
42552
- if (inputs.length > 0) {
42553
- this.focusInputPath(inputs[0]);
42554
- return true;
42555
- }
42556
- return false;
42557
- };
42558
- this.focusInputPath = path => {
42559
- // Since the transformer exposes the input API, it needs to be robust
42560
- // to empty paths. We don't expect this to happen, as entire-widget
42561
- // focusing is typically done through the focus() method, which already
42562
- // handles the empty path case properly, but it's better to be safe
42563
- // here.
42564
- if (path.length === 0) {
42565
- return false;
42566
- }
42567
- assert(path.length >= 2);
42568
- return this._passToInner("focusInputPath", path);
42569
- };
42570
- this.blurInputPath = path => {
42571
- // Since the transformer exposes the input API, it needs to be robust
42572
- // to empty paths (which indicate a blurring of the entire widget,
42573
- // e.g., when switching from interacting with the transformer to
42574
- // interacting with some other widget).
42575
- if (path.length === 0) {
42576
- return false;
42577
- }
42578
- assert(path.length >= 2);
42579
- return this._passToInner("blurInputPath", path);
42580
- };
42581
- this.setInputValue = (path, value, cb) => {
42582
- assert(path.length >= 2);
42583
- return this._passToInner("setInputValue", path, value, cb);
42584
- };
42585
- this.getDOMNodeForPath = path => {
42586
- assert(path.length >= 2);
42587
- return this._passToInner("getDOMNodeForPath", path);
42588
- };
42589
- this.getGrammarTypeForPath = path => {
42590
- assert(path.length >= 2);
42591
- return this._passToInner("getGrammarTypeForPath", path);
42592
- };
42593
- }
42594
- render() {
42595
- // Fill in any missing value in this.props.graph
42596
- // this can happen because the graph json doesn't include
42597
- // box, for example
42598
- const graph = _.extend(defaultGraphProps(this.props.graph, defaultBoxSize), this.props.graph);
42599
- const interactiveToolsMode = this.props.graphMode === "interactive";
42600
- const ToolsBarClass = interactiveToolsMode ? ToolsBar : AddTransformBar;
42601
-
42602
- // This style is applied inline because it is dependent on the
42603
- // size of the graph as set by the graph.box prop, and this also
42604
- // lets us specify it in the same place the graph's width is
42605
- // specified.
42606
- const toolsBar = /*#__PURE__*/React.createElement("div", {
42607
- style: {
42608
- width: graph.box[0]
42609
- }
42610
- }, /*#__PURE__*/React.createElement(ToolsBarClass
42611
- // eslint-disable-next-line react/no-string-refs
42612
- , {
42613
- ref: "toolsBar",
42614
- enabled: pluckObject(this.props.tools, "enabled"),
42615
- apiOptions: this.props.apiOptions,
42616
- addTool: this.addTool,
42617
- removeTool: this.removeTool,
42618
- onUndoClick: this.handleUndoClick
42619
- }));
42620
- return /*#__PURE__*/React.createElement("div", {
42621
- className: "perseus-widget " + "perseus-widget-transformer"
42622
- }, /*#__PURE__*/React.createElement(Graph
42623
- // eslint-disable-next-line react/no-string-refs
42624
- , {
42625
- ref: "graph",
42626
- box: graph.box,
42627
- range: graph.range,
42628
- labels: graph.labels,
42629
- step: graph.step,
42630
- gridStep: graph.gridStep,
42631
- markings: graph.markings,
42632
- backgroundImage: graph.backgroundImage,
42633
- showProtractor: graph.showProtractor,
42634
- onGraphieUpdated: this.setupGraphie,
42635
- setDrawingAreaAvailable: this.props.apiOptions.setDrawingAreaAvailable
42636
- }), !interactiveToolsMode && i18n._("Add transformations below:"), this.props.graphMode === "static" && [/*#__PURE__*/React.createElement("br", {
42637
- key: "static-br"
42638
- }), /*#__PURE__*/React.createElement("em", {
42639
- key: "static-nomove"
42640
- }, " ", i18n._("Note: For this question, the shape will not move."), " ")], interactiveToolsMode && toolsBar, /*#__PURE__*/React.createElement(TransformationList
42641
- // eslint-disable-next-line react/no-string-refs
42642
- , {
42643
- ref: "transformationList",
42644
- mode: this.props.listMode,
42645
- transformations: this.props.transformations,
42646
- onChange: this.setTransformationProps,
42647
- onFocus: this._handleFocus,
42648
- onBlur: this._handleBlur,
42649
- keypadElement: this.props.keypadElement,
42650
- apiOptions: this.props.apiOptions
42651
- }), !interactiveToolsMode && toolsBar);
42652
- }
42653
- componentDidMount() {
42654
- this.setupGraphie(this.graphie());
42655
- }
42656
- componentDidUpdate(prevProps) {
42657
- if (this.shouldSetupGraphie(this.props, prevProps)) {
42658
- // eslint-disable-next-line react/no-string-refs
42659
- // @ts-expect-error - TS2339 - Property 'reset' does not exist on type 'ReactInstance'.
42660
- this.refs.graph.reset();
42661
- } else if (!deepEq(this.props.transformations, this.transformations)) {
42662
- this.setTransformations(this.props.transformations);
42663
- }
42664
- }
42665
- static validate(guess, rubric) {
42666
- // Check for any required transformations
42667
- for (const type in Transformations) {
42668
- if (rubric.tools[type].required) {
42669
- const isUsed = _.any(_.map(guess.transformations, function (transform) {
42670
- // Required transformations must appear in the
42671
- // transformation list, and must not be no-ops
42672
- return transform.type === type && !TransformOps.isEmpty(transform) && !TransformOps.isNoOp(transform);
42673
- }));
42674
- if (!isUsed) {
42675
- return {
42676
- type: "invalid",
42677
- message: i18n._("Your transformation must use a " + "%(type)s.", {
42678
- type: Transformations[type].lowerNounName
42679
- })
42680
- };
42681
- }
42682
- }
42683
- }
42684
-
42685
- // Compare shapes
42686
- if (ShapeTypes.equal(guess.shape, rubric.correct.shape)) {
42687
- return {
42688
- type: "points",
42689
- earned: 1,
42690
- total: 1,
42691
- message: null
42692
- };
42693
- }
42694
- if (!rubric.gradeEmpty && deepEq(guess.shape.coords, rubric.starting.shape.coords)) {
42695
- return {
42696
- type: "invalid",
42697
- message: i18n._("Use the interactive graph to define a " + "correct transformation.")
42698
- };
42699
- }
42700
- return {
42701
- type: "points",
42702
- earned: 0,
42703
- total: 1,
42704
- message: null
42705
- };
42706
- }
42707
- }
42708
- Transformer.defaultProps = _.defaults({
42709
- transformations: []
42710
- }, defaultTransformerProps);
42711
- var Transformer$1 = {
42712
- name: "transformer",
42713
- displayName: "Transformer (deprecated)",
42714
- hidden: true,
42715
- widget: Transformer
42716
- };
42717
-
42718
40309
  var hubble = {exports: {}};
42719
40310
 
42720
40311
  (function (module, exports) {
@@ -43937,7 +41528,7 @@ var Video$1 = {
43937
41528
  };
43938
41529
 
43939
41530
  /* eslint-disable import/no-named-as-default */
43940
- var extraWidgets = [Categorizer$1, CSProgram$1, Dropdown$1, Explanation$1, Definition$1, Grapher$1, GradedGroup$1, GradedGroupSet$1, Group$1, Iframe$1, Image$1, Interactive, InteractiveGraph$1, LabelImage$1, LightsPuzzle$1, Matrix$1, Matcher$1, Measurer$1, Molecule$1, NumberLine$1, Orderer$1, Passage$1, PassageRef$1, PassageRefTarget$1, Plotter$1, ReactionDiagram, Sequence$1, Simulator$1, Sorter$1, Table$1, Transformer$1, Unit, Video$1, DeprecatedStandin$1];
41531
+ var extraWidgets = [Categorizer$1, CSProgram$1, Dropdown$1, Explanation$1, Definition$1, Grapher$1, GradedGroup$1, GradedGroupSet$1, Group$1, Iframe$1, Image$1, Interactive, InteractiveGraph$1, LabelImage$1, LightsPuzzle$1, Matrix$1, Matcher$1, Measurer$1, Molecule$1, NumberLine$1, Orderer$1, Passage$1, PassageRef$1, PassageRefTarget$1, Plotter$1, ReactionDiagram, Sequence$1, Simulator$1, Sorter$1, Table$1, Unit, Video$1, DeprecatedStandin$1];
43941
41532
 
43942
41533
  /**
43943
41534
  * This should be called by all clients, specifying whether extra widgets are
@@ -43981,7 +41572,7 @@ const init = function init(options) {
43981
41572
 
43982
41573
  // This file is processed by a Rollup plugin (replace) to inject the production
43983
41574
  const libName = "@khanacademy/perseus";
43984
- const libVersion = "18.0.0";
41575
+ const libVersion = "19.0.0";
43985
41576
  addLibraryVersionToPerseusDebug(libName, libVersion);
43986
41577
 
43987
41578
  const apiVersion = {
@@ -45427,8 +43018,6 @@ const ItemExtras = [
45427
43018
 
45428
43019
  // prettier-ignore
45429
43020
 
45430
- // prettier-ignore
45431
-
45432
43021
  //prettier-ignore
45433
43022
 
45434
43023
  // A background image applied to various widgets.
@@ -46561,10 +44150,7 @@ function violatingWidgets(itemData) {
46561
44150
  */
46562
44151
  const registerAllWidgetsForTesting = () => {
46563
44152
  registerWidgets(allWidgets);
46564
-
46565
- // When it's time to replace the transformer widget
46566
- // uncomment this line
46567
- // Widgets.replaceWidget("transformer", "deprecated-standin");
44153
+ replaceWidget("transformer", "deprecated-standin");
46568
44154
  };
46569
44155
 
46570
44156
  /**
@@ -46992,5 +44578,5 @@ const EditorJsonify = {
46992
44578
  }
46993
44579
  };
46994
44580
 
46995
- export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Transformer$1 as TransformerWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, displaySigFigs, getAnswersFromWidgets, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, parsePerseusJSON, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, traverse, violatingWidgets, allWidgets as widgets };
44581
+ export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, displaySigFigs, getAnswersFromWidgets, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, parsePerseusJSON, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, traverse, violatingWidgets, allWidgets as widgets };
46996
44582
  //# sourceMappingURL=index.js.map