@khanacademy/perseus 18.0.1 → 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,
@@ -33024,7 +33018,7 @@ var Matcher$1 = {
33024
33018
  const _excluded$1 = ["onFormatChange", "checkValidity", "useArrowKeys"];
33025
33019
  const {
33026
33020
  firstNumericalParse,
33027
- captureScratchpadTouchStart: captureScratchpadTouchStart$1
33021
+ captureScratchpadTouchStart
33028
33022
  } = Util;
33029
33023
  const toNumericString = KhanMath.toNumericString;
33030
33024
  const getNumericFormat = KhanMath.getNumericFormat;
@@ -33182,7 +33176,7 @@ class NumberInput extends React.Component {
33182
33176
  onKeyDown: this._onKeyDown
33183
33177
  // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLInputElement>'.
33184
33178
  ,
33185
- onTouchStart: captureScratchpadTouchStart$1,
33179
+ onTouchStart: captureScratchpadTouchStart,
33186
33180
  defaultValue: toNumericString(this.props.value, this.state.format),
33187
33181
  value: undefined
33188
33182
  }));
@@ -33215,7 +33209,7 @@ NumberInput.defaultProps = {
33215
33209
  // Type imports
33216
33210
 
33217
33211
  const {
33218
- assert: assert$4
33212
+ assert: assert$3
33219
33213
  } = InteractiveUtil;
33220
33214
  const {
33221
33215
  stringArrayOfSize
@@ -33249,12 +33243,12 @@ const getDefaultPath$1 = function getDefaultPath() {
33249
33243
  };
33250
33244
  const getRowFromPath$1 = function getRowFromPath(path) {
33251
33245
  // 'path' should be a (row, column) pair
33252
- assert$4(_.isArray(path) && path.length === 2);
33246
+ assert$3(_.isArray(path) && path.length === 2);
33253
33247
  return +path[0];
33254
33248
  };
33255
33249
  const getColumnFromPath$1 = function getColumnFromPath(path) {
33256
33250
  // 'path' should be a (row, column) pair
33257
- assert$4(_.isArray(path) && path.length === 2);
33251
+ assert$3(_.isArray(path) && path.length === 2);
33258
33252
  return +path[1];
33259
33253
  };
33260
33254
  const getRefForPath$1 = function getRefForPath(path) {
@@ -34757,7 +34751,7 @@ const MovablePoint$1 = Graphie.MovablePoint;
34757
34751
  // @ts-expect-error - TS2339 - Property 'Line' does not exist on type 'typeof Graphie'.
34758
34752
  const Line$1 = Graphie.Line;
34759
34753
  const {
34760
- assert: assert$3
34754
+ assert: assert$2
34761
34755
  } = InteractiveUtil;
34762
34756
  const bound = (x, gt, lt) => Math.min(Math.max(x, gt), lt);
34763
34757
  const EN_DASH = "\u2013";
@@ -35015,7 +35009,7 @@ class NumberLine extends React.Component {
35015
35009
  const snapX = props.tickStep / props.snapDivisions;
35016
35010
  let x = bound(numLinePosition, left, right);
35017
35011
  x = left + number.roundTo(x - left, snapX);
35018
- assert$3(_.isFinite(x));
35012
+ assert$2(_.isFinite(x));
35019
35013
  return x;
35020
35014
  };
35021
35015
  // This function is intended to be used by the client code
@@ -38136,7 +38130,7 @@ var PassageRefTarget$1 = {
38136
38130
 
38137
38131
  /* eslint-disable react/no-unsafe */
38138
38132
  const {
38139
- deepEq: deepEq$1
38133
+ deepEq
38140
38134
  } = Util;
38141
38135
  class Plotter extends React.Component {
38142
38136
  constructor(..._args) {
@@ -38931,7 +38925,7 @@ Plotter.defaultProps = {
38931
38925
  };
38932
38926
  _.extend(Plotter, {
38933
38927
  validate: function (guess, rubric) {
38934
- if (deepEq$1(guess, rubric.starting)) {
38928
+ if (deepEq(guess, rubric.starting)) {
38935
38929
  return {
38936
38930
  type: "invalid",
38937
38931
  message: null
@@ -38939,7 +38933,7 @@ _.extend(Plotter, {
38939
38933
  }
38940
38934
  return {
38941
38935
  type: "points",
38942
- earned: deepEq$1(guess, rubric.correct) ? 1 : 0,
38936
+ earned: deepEq(guess, rubric.correct) ? 1 : 0,
38943
38937
  total: 1,
38944
38938
  message: null
38945
38939
  };
@@ -39307,7 +39301,7 @@ class InfoTip extends React.Component {
39307
39301
  }
39308
39302
 
39309
39303
  const {
39310
- assert: assert$2
39304
+ assert: assert$1
39311
39305
  } = InteractiveUtil;
39312
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'.
39313
39307
  const {
@@ -39319,7 +39313,7 @@ const {
39319
39313
  MovablePoint,
39320
39314
  MovableLine
39321
39315
  } = Graphie;
39322
- const defaultBoxSize$1 = 400;
39316
+ const defaultBoxSize = 400;
39323
39317
  const maxSampleSize = 1000;
39324
39318
  class Histogram extends React.Component {
39325
39319
  constructor(...args) {
@@ -39601,7 +39595,7 @@ Histogram.defaultProps = {
39601
39595
  data: null,
39602
39596
  xAxisLabel: "Proportion (%)",
39603
39597
  yAxisLabel: "Number of times seen",
39604
- box: [defaultBoxSize$1, defaultBoxSize$1]
39598
+ box: [defaultBoxSize, defaultBoxSize]
39605
39599
  };
39606
39600
  class Simulator extends React.Component {
39607
39601
  constructor(..._args) {
@@ -39699,7 +39693,7 @@ class Simulator extends React.Component {
39699
39693
  return true;
39700
39694
  };
39701
39695
  this.focusInputPath = path => {
39702
- assert$2(path.length > 0);
39696
+ assert$1(path.length > 0);
39703
39697
  const inputID = _.head(path);
39704
39698
  // eslint-disable-next-line react/no-string-refs
39705
39699
  const inputComponent = this.refs[inputID];
@@ -39707,7 +39701,7 @@ class Simulator extends React.Component {
39707
39701
  inputComponent.focus();
39708
39702
  };
39709
39703
  this.blurInputPath = path => {
39710
- assert$2(path.length > 0);
39704
+ assert$1(path.length > 0);
39711
39705
  const inputID = _.head(path);
39712
39706
  // eslint-disable-next-line react/no-string-refs
39713
39707
  const inputComponent = this.refs[inputID];
@@ -39715,17 +39709,17 @@ class Simulator extends React.Component {
39715
39709
  inputComponent.blur();
39716
39710
  };
39717
39711
  this.getDOMNodeForPath = path => {
39718
- assert$2(path.length > 0);
39712
+ assert$1(path.length > 0);
39719
39713
  const inputID = _.head(path);
39720
39714
  // eslint-disable-next-line react/no-string-refs
39721
39715
  return ReactDOM__default.findDOMNode(this.refs[inputID]);
39722
39716
  };
39723
39717
  this.getGrammarTypeForPath = path => {
39724
- assert$2(path.length > 0);
39718
+ assert$1(path.length > 0);
39725
39719
  return "number";
39726
39720
  };
39727
39721
  this.setInputValue = (path, newValue, cb) => {
39728
- assert$2(path.length > 0);
39722
+ assert$1(path.length > 0);
39729
39723
  const inputID = _.head(path);
39730
39724
  const capitalizedID = inputID.charAt(0).toUpperCase() + inputID.slice(1);
39731
39725
  const functionName = "handle" + capitalizedID + "Change";
@@ -40016,7 +40010,7 @@ var Sorter$1 = {
40016
40010
  };
40017
40011
 
40018
40012
  const {
40019
- assert: assert$1
40013
+ assert
40020
40014
  } = InteractiveUtil;
40021
40015
 
40022
40016
  /* Input handling: Maps a (row, column) pair to a unique ref used by React,
@@ -40030,12 +40024,12 @@ const getDefaultPath = function getDefaultPath() {
40030
40024
  };
40031
40025
  const getRowFromPath = function getRowFromPath(path) {
40032
40026
  // 'path' should be a (row, column) pair
40033
- assert$1(_.isArray(path) && path.length === 2);
40027
+ assert(_.isArray(path) && path.length === 2);
40034
40028
  return +path[0];
40035
40029
  };
40036
40030
  const getColumnFromPath = function getColumnFromPath(path) {
40037
40031
  // 'path' should be a (row, column) pair
40038
- assert$1(_.isArray(path) && path.length === 2);
40032
+ assert(_.isArray(path) && path.length === 2);
40039
40033
  return +path[1];
40040
40034
  };
40041
40035
  const getRefForPath = function getRefForPath(path) {
@@ -40312,2414 +40306,6 @@ var Table$1 = {
40312
40306
  isLintable: true
40313
40307
  };
40314
40308
 
40315
- const {
40316
- deepEq,
40317
- getGridStep,
40318
- captureScratchpadTouchStart
40319
- } = Util;
40320
- const {
40321
- assert
40322
- } = InteractiveUtil;
40323
- const ROTATE_SNAP_DEGREES = 15;
40324
- const DEGREE_SIGN = "\u00B0";
40325
- const RENDER_TRANSFORM_DELAY_IN_MS = 300;
40326
- const ROTATE_HANDLE_DIST = 1.5;
40327
- const REFLECT_ROTATE_HANDLE_DIST = 2;
40328
- const REFLECT_BUTTON_SIZE = 1;
40329
- const defaultBoxSize = 400;
40330
- const defaultBackgroundImage = {
40331
- url: null
40332
- };
40333
-
40334
- /* Does a pluck on keys inside objects in an object
40335
- *
40336
- * Ex:
40337
- * tools = {
40338
- * translation: {
40339
- * enabled: true
40340
- * },
40341
- * rotation: {
40342
- * enabled: false
40343
- * }
40344
- * };
40345
- * pluckObject(tools, "enabled") returns {
40346
- * translation: true
40347
- * rotation: false
40348
- * }
40349
- */
40350
- function pluckObject(object, subKey) {
40351
- return _.object(_.map(object, function (value, key) {
40352
- return [key, value[subKey]];
40353
- }));
40354
- }
40355
- const defaultGraphProps = function defaultGraphProps(setProps, boxSize) {
40356
- setProps = setProps || {};
40357
- const labels = setProps.labels || ["x", "y"];
40358
- const range = setProps.range || [[-10, 10], [-10, 10]];
40359
- const step = setProps.step || [1, 1];
40360
- const gridStep = setProps.gridStep || getGridStep(range, step, boxSize);
40361
- return {
40362
- box: [boxSize, boxSize],
40363
- labels: labels,
40364
- range: range,
40365
- step: step,
40366
- gridStep: gridStep,
40367
- valid: true,
40368
- backgroundImage: defaultBackgroundImage,
40369
- markings: "grid",
40370
- showProtractor: false
40371
- };
40372
- };
40373
- const defaultTransformerProps = {
40374
- apiOptions: ApiOptions.defaults,
40375
- gradeEmpty: false,
40376
- graphMode: "interactive",
40377
- listMode: "dynamic",
40378
- graph: {},
40379
- tools: {
40380
- translation: {
40381
- enabled: true,
40382
- required: false,
40383
- constraints: {}
40384
- },
40385
- rotation: {
40386
- enabled: true,
40387
- required: false,
40388
- constraints: {
40389
- fixed: false
40390
- },
40391
- coord: [1, 6]
40392
- },
40393
- reflection: {
40394
- enabled: true,
40395
- required: false,
40396
- constraints: {
40397
- fixed: false
40398
- },
40399
- coords: [[2, -4], [2, 2]]
40400
- },
40401
- dilation: {
40402
- enabled: true,
40403
- required: false,
40404
- constraints: {
40405
- fixed: false
40406
- },
40407
- coord: [6, 6]
40408
- }
40409
- },
40410
- drawSolutionShape: true,
40411
- starting: {
40412
- shape: {
40413
- type: "polygon-3",
40414
- coords: [[2, 2], [2, 6], [7, 2]]
40415
- },
40416
- transformations: []
40417
- },
40418
- correct: {
40419
- shape: {
40420
- type: "polygon-3",
40421
- coords: [[2, 2], [2, 6], [7, 2]]
40422
- },
40423
- transformations: []
40424
- }
40425
- };
40426
- function colorForTool(tool) {
40427
- return tool.constraints.fixed ? KhanColors.DYNAMIC : KhanColors.INTERACTIVE;
40428
- }
40429
-
40430
- /* Scales a distance from the default range of
40431
- * [-10, 10] to a given props.range pair
40432
- *
40433
- * Used for sizing various transformation tools
40434
- * (rotation handle, dilation circle)
40435
- */
40436
- function scaleToRange(dist, range) {
40437
- const spreadX = range[0][1] - range[0][0];
40438
- const spreadY = range[1][1] - range[1][0];
40439
- return dist * Math.max(spreadX, spreadY) / 20;
40440
- }
40441
- function dilatePointFromCenter(point, dilationCenter, scale) {
40442
- const pv = vector$1.subtract(point, dilationCenter);
40443
- const pvScaled = vector$1.scale(pv, scale);
40444
- const transformedPoint = vector$1.add(dilationCenter, pvScaled);
40445
- return transformedPoint;
40446
- }
40447
-
40448
- // TODO(jack): i18nize this
40449
- function stringFromDecimal(number) {
40450
- return String(KhanMath.roundTo(9, number));
40451
- }
40452
- function stringFromFraction(number$1) {
40453
- const frac = KhanMath.toFraction(number$1, number.DEFAULT_TOLERANCE);
40454
- if (frac[1] === 1) {
40455
- return stringFromDecimal(number$1);
40456
- }
40457
- return stringFromDecimal(frac[0]) + "/" + stringFromDecimal(frac[1]);
40458
- }
40459
- function texFromPoint(point) {
40460
- const {
40461
- TeX
40462
- } = getDependencies();
40463
- return [/*#__PURE__*/React.createElement(TeX, {
40464
- key: "("
40465
- }, "("), stringFromDecimal(point[0]), /*#__PURE__*/React.createElement(TeX, {
40466
- key: ","
40467
- }, ", {}"), stringFromDecimal(point[1]), /*#__PURE__*/React.createElement(TeX, {
40468
- key: ")"
40469
- }, ")")];
40470
- }
40471
- function texFromVector(vector) {
40472
- const {
40473
- TeX
40474
- } = getDependencies();
40475
- return [/*#__PURE__*/React.createElement(TeX, {
40476
- key: "<"
40477
- }, i18n.doNotTranslate("\\langle")), stringFromDecimal(vector[0]), /*#__PURE__*/React.createElement(TeX, {
40478
- key: ","
40479
- }, ", {}"), stringFromDecimal(vector[1]), /*#__PURE__*/React.createElement(TeX, {
40480
- key: ">"
40481
- }, i18n.doNotTranslate("\\rangle"))];
40482
- }
40483
- function texFromAngleDeg(angleDeg) {
40484
- return stringFromDecimal(angleDeg) + DEGREE_SIGN;
40485
- }
40486
- function orderInsensitiveCoordsEqual(coords1, coords2) {
40487
- coords1 = _.clone(coords1).sort(point.compare);
40488
- coords2 = _.clone(coords2).sort(point.compare);
40489
- return _.all(_.map(coords1, function (coord1, i) {
40490
- const coord2 = coords2[i];
40491
- return point.equal(coord1, coord2);
40492
- }));
40493
- }
40494
- const inputComponentForApiOptions = apiOptions => {
40495
- if (apiOptions.customKeypad) {
40496
- return SimpleKeypadInput;
40497
- }
40498
- return NumberInput;
40499
- };
40500
-
40501
- /* Perform operations on raw transform objects */
40502
- const TransformOps = {
40503
- apply: function (transform) {
40504
- // Any transformation with empty text boxes is a no-op until
40505
- // filled out (these show up as nulls in transform.vector/line/etc).
40506
- // TODO (jack): Merge this just into reflections now that other
40507
- // transforms are always valid (after merging transformation
40508
- // collapsing, which may use isValid)
40509
- // @ts-expect-error - TS2345 - Argument of type 'PerseusTransformerTransformation' is not assignable to parameter of type 'never'.
40510
- if (!Transformations[transform.type].isValid(transform)) {
40511
- return _.identity; // do not transform the coord
40512
- }
40513
- // @ts-expect-error - TS2345 - Argument of type 'PerseusTransformerTransformation' is not assignable to parameter of type 'never'.
40514
- return Transformations[transform.type].apply(transform);
40515
- },
40516
- append: function (transformList, newTransform) {
40517
- // Append newTransform to transformList, and collapse the last
40518
- // two transforms if they are collapsable
40519
- const results = TransformOps._appendAndCollapseLastTwo(transformList, newTransform);
40520
- // Collapse any no-ops at the end of the transformation list
40521
- return TransformOps._collapseFinalNoOps(results);
40522
- },
40523
- _collapseFinalNoOps: function (transforms) {
40524
- // Collapse no-op transformations at the end of the list
40525
- if (transforms.length && TransformOps.isNoOp(_.last(transforms))) {
40526
- return _.initial(transforms);
40527
- }
40528
- return transforms;
40529
- },
40530
- _appendAndCollapseLastTwo: function (transformList, newTransform) {
40531
- if (!transformList.length) {
40532
- return [newTransform];
40533
- }
40534
- const collapsed = TransformOps.collapse(_.last(transformList), newTransform);
40535
- return _.initial(transformList).concat(collapsed);
40536
- },
40537
- isNoOp: function (transform) {
40538
- return Transformations[transform.type].isNoOp(transform);
40539
- },
40540
- collapse: function (transform1, transform2) {
40541
- // We can only collapse transforms that have the same type
40542
- if (transform1.type !== transform2.type) {
40543
- return [transform1, transform2];
40544
- }
40545
-
40546
- // Clicking the button again removes empty transformations
40547
- if (TransformOps.isEmpty(transform1) && TransformOps.isEmpty(transform2)) {
40548
- return [];
40549
- }
40550
-
40551
- // Don't collapse invalid transformations otherwise
40552
- if (!TransformOps.isValid(transform1) || !TransformOps.isValid(transform2)) {
40553
- return [transform1, transform2];
40554
- }
40555
- return TransformOps._collapseValidMonotypedTransforms(transform1, transform2);
40556
- },
40557
- isValid: function (transform) {
40558
- // @ts-expect-error - TS2345 - Argument of type 'PerseusTransformerTransformation' is not assignable to parameter of type 'never'.
40559
- return Transformations[transform.type].isValid(transform);
40560
- },
40561
- isEmpty: function (transform) {
40562
- // @ts-expect-error - TS2345 - Argument of type 'PerseusTransformerTransformation' is not assignable to parameter of type 'never'.
40563
- return Transformations[transform.type].isEmpty(transform);
40564
- },
40565
- _collapseValidMonotypedTransforms: function (transform1, transform2) {
40566
- let collapsed = Transformations[transform1.type].collapse(transform1, transform2);
40567
- if (collapsed) {
40568
- // Force all answers into an array
40569
- if (!_.isArray(collapsed)) {
40570
- collapsed = [collapsed];
40571
- }
40572
- // Add types to all transforms in the answer
40573
- _.each(collapsed, function (transform) {
40574
- transform.type = transform1.type;
40575
- });
40576
- return collapsed;
40577
- }
40578
- // These transforms can't be collapsed together
40579
- return [transform1, transform2];
40580
- },
40581
- toTeX: function (transform) {
40582
- return Transformations[transform.type].toTeX(transform);
40583
- },
40584
- /* A react representation of this transform object */
40585
- ListItem: createReactClass({
40586
- displayName: "ListItem",
40587
- render: function () {
40588
- if (this.props.mode === "dynamic") {
40589
- return /*#__PURE__*/React.createElement("div", null, TransformOps.toTeX(this.props.transform));
40590
- }
40591
- if (this.props.mode === "interactive") {
40592
- const TransformClass = Transformations[this.props.transform.type].Input;
40593
- return /*#__PURE__*/React.createElement(TransformClass
40594
- // eslint-disable-next-line react/no-string-refs
40595
- , _extends({
40596
- ref: "transform",
40597
- onChange: this.handleChange,
40598
- onFocus: this.props.onFocus,
40599
- onBlur: this.props.onBlur,
40600
- keypadElement: this.props.keypadElement,
40601
- apiOptions: this.props.apiOptions
40602
- }, this.props.transform));
40603
- }
40604
- throw new PerseusError("Invalid mode: " + this.props.mode, Errors.InvalidInput);
40605
- },
40606
- value: function () {
40607
- if (this.props.mode === "interactive") {
40608
- return _.extend({
40609
- type: this.props.transform.type
40610
- },
40611
- // eslint-disable-next-line react/no-string-refs
40612
- this.refs.transform.value());
40613
- }
40614
- return this.props.transform;
40615
- },
40616
- handleChange: _.debounce(function (callback) {
40617
- // @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.
40618
- this.props.onChange(this.value(), callback);
40619
- }, RENDER_TRANSFORM_DELAY_IN_MS),
40620
- /* InputPath API: depending on the API call, this could involve simply
40621
- * navigating to the right ref and calling the function on that
40622
- * component, or threading the call down and returning the result. */
40623
- _getComponentAtPath: function (path) {
40624
- // eslint-disable-next-line react/no-string-refs
40625
- const transform = this.refs.transform;
40626
- const ref = _.head(path);
40627
- return transform.refs[ref];
40628
- },
40629
- focus: function () {
40630
- // eslint-disable-next-line react/no-string-refs
40631
- const transform = this.refs.transform;
40632
- const path = _.head(transform.getInputPaths());
40633
- if (path) {
40634
- this.focusInputPath(path);
40635
- }
40636
- },
40637
- focusInputPath: function (path) {
40638
- this._getComponentAtPath(path).focus();
40639
- },
40640
- blurInputPath: function (path) {
40641
- this._getComponentAtPath(path).blur();
40642
- },
40643
- getDOMNodeForPath: function (path) {
40644
- return ReactDOM__default.findDOMNode(this._getComponentAtPath(path));
40645
- },
40646
- getGrammarTypeForPath: function (path) {
40647
- return "number";
40648
- },
40649
- setInputValue: function (path, value, cb) {
40650
- // `value` comes in as a string on mobile, but we need a number
40651
- // We let through the empty string so that "Clear" works -- in
40652
- // that case, the transformer widget will just act as if there is
40653
- // no input, which is what we want.
40654
- // @ts-expect-error - TS2339 - Property 'length' does not exist on type 'string | number'.
40655
- if (value.length) {
40656
- // @ts-expect-error - TS2345 - Argument of type 'string | number' is not assignable to parameter of type 'string'.
40657
- value = parseFloat(value);
40658
- if (isNaN(value)) {
40659
- return;
40660
- }
40661
- }
40662
- // eslint-disable-next-line react/no-string-refs
40663
- this.refs.transform.setInputValue(path, value, cb);
40664
- },
40665
- getInputPaths: function () {
40666
- // If we're in dynamic mode, then the list items are made up of
40667
- // static text.
40668
- if (this.props.mode === "dynamic") {
40669
- return [];
40670
- }
40671
- // eslint-disable-next-line react/no-string-refs
40672
- return this.refs.transform.getInputPaths();
40673
- }
40674
- })
40675
- };
40676
- const Transformations = {
40677
- translation: {
40678
- // I18N: As in the command, "Translate the polygon"
40679
- verbName: i18n._("Translate"),
40680
- nounName: i18n._("Translation"),
40681
- lowerNounName: i18n._("translation"),
40682
- apply: function (transform) {
40683
- return function (coord) {
40684
- return vector$1.add(coord, transform.vector);
40685
- };
40686
- },
40687
- isValid: function (transform) {
40688
- return _.isFinite(transform.vector[0]) && _.isFinite(transform.vector[1]);
40689
- },
40690
- isEmpty: function (transform) {
40691
- return transform.vector[0] === null && transform.vector[1] === null;
40692
- },
40693
- isNoOp: function (transform) {
40694
- return vector$1.equal(transform.vector, [0, 0]);
40695
- },
40696
- collapse: function (transform1, transform2) {
40697
- return {
40698
- vector: vector$1.add(transform1.vector, transform2.vector)
40699
- };
40700
- },
40701
- toTeX: function (transform) {
40702
- // I18N: As in the command, "Translation by <3, 1>"
40703
- return i18n.$_("Translation by %(vector)s", {
40704
- vector: texFromVector(transform.vector)
40705
- });
40706
- },
40707
- Input: class Input extends React.Component {
40708
- constructor(...args) {
40709
- super(...args);
40710
- this.state = {
40711
- vector: this.props.vector || [null, null]
40712
- };
40713
- this.value = () => {
40714
- // eslint-disable-next-line react/no-string-refs
40715
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40716
- const x = this.refs.x.getValue();
40717
- // eslint-disable-next-line react/no-string-refs
40718
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40719
- const y = this.refs.y.getValue();
40720
- return {
40721
- vector: [x, y]
40722
- };
40723
- };
40724
- /* InputPath API */
40725
- this.setInputValue = (path, value, cb) => {
40726
- const id = _.first(path);
40727
- const vector = _.clone(this.state.vector);
40728
- if (id === "x") {
40729
- vector[0] = value;
40730
- } else if (id === "y") {
40731
- vector[1] = value;
40732
- }
40733
- this.setState({
40734
- vector: vector
40735
- }, () => {
40736
- this.props.onChange(cb);
40737
- });
40738
- };
40739
- this.getInputPaths = () => {
40740
- return [["x"], ["y"]];
40741
- };
40742
- }
40743
- componentDidUpdate(prevProps) {
40744
- if (!deepEq(this.props, prevProps)) {
40745
- this.setState({
40746
- vector: this.props.vector
40747
- });
40748
- }
40749
- }
40750
- render() {
40751
- const InputComponent = inputComponentForApiOptions(this.props.apiOptions);
40752
- const {
40753
- TeX
40754
- } = getDependencies();
40755
- const vector = [/*#__PURE__*/React.createElement(TeX, {
40756
- key: "<"
40757
- }, i18n.doNotTranslate("\\langle")), /*#__PURE__*/React.createElement(InputComponent, {
40758
- key: "vec_x"
40759
- // eslint-disable-next-line react/no-string-refs
40760
- ,
40761
- ref: "x",
40762
- placeholder: 0,
40763
- value: this.state.vector[0],
40764
- useArrowKeys: true,
40765
- onChange: val0 => {
40766
- const val1 = this.state.vector[1];
40767
- this.setState({
40768
- vector: [val0, val1]
40769
- }, () => {
40770
- this.props.onChange();
40771
- });
40772
- },
40773
- onFocus: _.partial(this.props.onFocus, "x"),
40774
- onBlur: _.partial(this.props.onBlur, "x"),
40775
- keypadElement: this.props.keypadElement
40776
- }), /*#__PURE__*/React.createElement(TeX, {
40777
- key: ","
40778
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
40779
- key: "vec_y"
40780
- // eslint-disable-next-line react/no-string-refs
40781
- ,
40782
- ref: "y",
40783
- placeholder: 0,
40784
- value: this.state.vector[1],
40785
- useArrowKeys: true,
40786
- onChange: val1 => {
40787
- const val0 = this.state.vector[0];
40788
- this.setState({
40789
- vector: [val0, val1]
40790
- }, () => {
40791
- this.props.onChange();
40792
- });
40793
- },
40794
- onFocus: _.partial(this.props.onFocus, "y"),
40795
- onBlur: _.partial(this.props.onBlur, "y"),
40796
- keypadElement: this.props.keypadElement
40797
- }), /*#__PURE__*/React.createElement(TeX, {
40798
- key: ">"
40799
- }, i18n.doNotTranslate("\\rangle"))];
40800
- return /*#__PURE__*/React.createElement("div", null, i18n.$_("Translation by %(vector)s", {
40801
- vector: vector
40802
- }));
40803
- }
40804
- }
40805
- },
40806
- rotation: {
40807
- // I18N: As in the command, "Rotate the polygon"
40808
- verbName: i18n._("Rotate"),
40809
- nounName: i18n._("Rotation"),
40810
- lowerNounName: i18n._("rotation"),
40811
- apply: function (transform) {
40812
- return function (coord) {
40813
- return point.rotateDeg(coord, transform.angleDeg, transform.center);
40814
- };
40815
- },
40816
- isValid: function (transform) {
40817
- return _.isFinite(transform.angleDeg) && _.isFinite(transform.center[0]) && _.isFinite(transform.center[1]);
40818
- },
40819
- isEmpty: function (transform) {
40820
- return transform.angleDeg === null && transform.center[0] === null && transform.center[1] === null;
40821
- },
40822
- isNoOp: function (transform) {
40823
- return number.equal(transform.angleDeg, 0);
40824
- },
40825
- collapse: function (transform1, transform2) {
40826
- if (!point.equal(transform1.center, transform2.center)) {
40827
- return false;
40828
- }
40829
- return {
40830
- center: transform1.center,
40831
- angleDeg: transform1.angleDeg + transform2.angleDeg
40832
- };
40833
- },
40834
- toTeX: function (transform) {
40835
- return i18n.$_("Rotation by %(degrees)s about %(point)s", {
40836
- degrees: texFromAngleDeg(transform.angleDeg),
40837
- point: texFromPoint(transform.center)
40838
- });
40839
- },
40840
- Input: class Input extends React.Component {
40841
- constructor(...args) {
40842
- super(...args);
40843
- this.state = {
40844
- center: this.props.center || [null, null],
40845
- angleDeg: this.props.angleDeg || null
40846
- };
40847
- this.value = () => {
40848
- // eslint-disable-next-line react/no-string-refs
40849
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40850
- const angleDeg = this.refs.angleDeg.getValue();
40851
- // eslint-disable-next-line react/no-string-refs
40852
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40853
- const centerX = this.refs.centerX.getValue();
40854
- // eslint-disable-next-line react/no-string-refs
40855
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
40856
- const centerY = this.refs.centerY.getValue();
40857
- return {
40858
- angleDeg: angleDeg,
40859
- center: [centerX, centerY]
40860
- };
40861
- };
40862
- /* InputPath API */
40863
- this.setInputValue = (path, value, cb) => {
40864
- const id = _.first(path);
40865
- let angleDeg = _.clone(this.state.angleDeg);
40866
- const center = _.clone(this.state.center);
40867
- if (id === "angleDeg") {
40868
- angleDeg = value;
40869
- } else if (id === "centerX") {
40870
- center[0] = value;
40871
- } else if (id === "centerY") {
40872
- center[1] = value;
40873
- }
40874
- this.setState({
40875
- angleDeg: angleDeg,
40876
- center: center
40877
- }, () => {
40878
- this.props.onChange(cb);
40879
- });
40880
- };
40881
- this.getInputPaths = () => {
40882
- return [["centerX"], ["centerY"], ["angleDeg"]];
40883
- };
40884
- }
40885
- componentDidUpdate(prevProps) {
40886
- if (!deepEq(this.props, prevProps)) {
40887
- this.setState({
40888
- center: this.props.center,
40889
- angleDeg: this.props.angleDeg
40890
- });
40891
- }
40892
- }
40893
- render() {
40894
- const InputComponent = inputComponentForApiOptions(this.props.apiOptions);
40895
- const {
40896
- TeX
40897
- } = getDependencies();
40898
- const point = [/*#__PURE__*/React.createElement(TeX, {
40899
- key: "("
40900
- }, "("), /*#__PURE__*/React.createElement(InputComponent, {
40901
- key: "X"
40902
- // eslint-disable-next-line react/no-string-refs
40903
- ,
40904
- ref: "centerX",
40905
- placeholder: 0,
40906
- value: this.state.center[0],
40907
- useArrowKeys: true,
40908
- onChange: val0 => {
40909
- const val1 = this.state.center[1];
40910
- this.setState({
40911
- center: [val0, val1]
40912
- }, () => {
40913
- this.props.onChange();
40914
- });
40915
- },
40916
- onFocus: _.partial(this.props.onFocus, "centerX"),
40917
- onBlur: _.partial(this.props.onBlur, "centerX"),
40918
- keypadElement: this.props.keypadElement
40919
- }), /*#__PURE__*/React.createElement(TeX, {
40920
- key: ","
40921
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
40922
- key: "Y"
40923
- // eslint-disable-next-line react/no-string-refs
40924
- ,
40925
- ref: "centerY",
40926
- placeholder: 0,
40927
- value: this.state.center[1],
40928
- useArrowKeys: true,
40929
- onChange: val1 => {
40930
- const val0 = this.state.center[0];
40931
- this.setState({
40932
- center: [val0, val1]
40933
- }, () => {
40934
- this.props.onChange();
40935
- });
40936
- },
40937
- onFocus: _.partial(this.props.onFocus, "centerY"),
40938
- onBlur: _.partial(this.props.onBlur, "centerY"),
40939
- keypadElement: this.props.keypadElement
40940
- }), /*#__PURE__*/React.createElement(TeX, {
40941
- key: ")"
40942
- }, ")")];
40943
- const degrees = [/*#__PURE__*/React.createElement(InputComponent, {
40944
- key: "deg"
40945
- // eslint-disable-next-line react/no-string-refs
40946
- ,
40947
- ref: "angleDeg",
40948
- placeholder: 0,
40949
- value: this.state.angleDeg,
40950
- useArrowKeys: true,
40951
- onChange: val => {
40952
- this.setState({
40953
- angleDeg: val
40954
- }, () => {
40955
- this.props.onChange();
40956
- });
40957
- },
40958
- onFocus: _.partial(this.props.onFocus, "angleDeg"),
40959
- onBlur: _.partial(this.props.onBlur, "angleDeg"),
40960
- keypadElement: this.props.keypadElement
40961
- }), DEGREE_SIGN];
40962
- // I18N: %(point)s must come before %(degrees)s in this phrase
40963
- const text = i18n.$_("Rotation about %(point)s by %(degrees)s", {
40964
- point,
40965
- degrees
40966
- });
40967
- return /*#__PURE__*/React.createElement("div", null, text);
40968
- }
40969
- }
40970
- },
40971
- reflection: {
40972
- // I18N: As in the command, "Reflect the polygon"
40973
- verbName: i18n._("Reflect"),
40974
- nounName: i18n._("Reflection"),
40975
- lowerNounName: i18n._("reflection"),
40976
- apply: function (transform) {
40977
- return function (coord) {
40978
- return point.reflectOverLine(coord, transform.line);
40979
- };
40980
- },
40981
- isValid: function (transform) {
40982
- // A bit hacky, but we'll also define reflecting over a
40983
- // single point as a no-op, to avoid NaN fun.
40984
- return _.all(_.flatten(transform.line), _.isFinite) && !point.equal(transform.line[0], transform.line[1]);
40985
- },
40986
- isEmpty: function (transform) {
40987
- return _.all(_.flatten(transform.line), _.isNull);
40988
- },
40989
- isNoOp: function (transform) {
40990
- // Invalid transforms are implicitly no-ops, so we don't
40991
- // have to catch that case here.
40992
- return false;
40993
- },
40994
- collapse: function (transform1, transform2) {
40995
- if (!line.equal(transform1.line, transform2.line)) {
40996
- return false;
40997
- }
40998
- return [];
40999
- },
41000
- toTeX: function (transform) {
41001
- const point1 = transform.line[0];
41002
- const point2 = transform.line[1];
41003
- return i18n.$_("Reflection over the line from %(point1)s to %(point2)s", {
41004
- point1: texFromPoint(point1),
41005
- point2: texFromPoint(point2)
41006
- });
41007
- },
41008
- Input: class Input extends React.Component {
41009
- constructor(...args) {
41010
- super(...args);
41011
- this.state = {
41012
- line: this.props.line || [[null, null], [null, null]]
41013
- };
41014
- this.changePoint = (i, j, val, cb) => {
41015
- const line = _.map(this.state.line, _.clone);
41016
- // @ts-expect-error - TS2538 - Type 'undefined' cannot be used as an index type.
41017
- line[i][j] = val;
41018
- this.setState({
41019
- line: line
41020
- }, () => {
41021
- this.props.onChange(cb);
41022
- });
41023
- };
41024
- this.value = () => {
41025
- // eslint-disable-next-line react/no-string-refs
41026
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41027
- const x1 = this.refs.x1.getValue();
41028
- // eslint-disable-next-line react/no-string-refs
41029
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41030
- const y1 = this.refs.y1.getValue();
41031
- // eslint-disable-next-line react/no-string-refs
41032
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41033
- const x2 = this.refs.x2.getValue();
41034
- // eslint-disable-next-line react/no-string-refs
41035
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41036
- const y2 = this.refs.y2.getValue();
41037
- return {
41038
- line: [[x1, y1], [x2, y2]]
41039
- };
41040
- };
41041
- /* InputPath API */
41042
- this.setInputValue = (path, value, cb) => {
41043
- const id = _.first(path);
41044
- let j;
41045
- if (id[0] === "x") {
41046
- j = 0;
41047
- } else if (id[0] === "y") {
41048
- j = 1;
41049
- }
41050
- let i;
41051
- if (id[1] === "1") {
41052
- i = 0;
41053
- } else if (id[1] === "2") {
41054
- i = 1;
41055
- }
41056
- this.changePoint(i, j, value, cb);
41057
- };
41058
- this.getInputPaths = () => {
41059
- return [["x1"], ["y1"], ["x2"], ["y2"]];
41060
- };
41061
- }
41062
- componentDidUpdate(prevProps) {
41063
- if (!deepEq(this.props, prevProps)) {
41064
- this.setState({
41065
- line: this.props.line
41066
- });
41067
- }
41068
- }
41069
- render() {
41070
- const InputComponent = inputComponentForApiOptions(this.props.apiOptions);
41071
- const {
41072
- TeX
41073
- } = getDependencies();
41074
- const point1 = [/*#__PURE__*/React.createElement(TeX, {
41075
- key: "("
41076
- }, "("), /*#__PURE__*/React.createElement(InputComponent, {
41077
- key: "x1"
41078
- // eslint-disable-next-line react/no-string-refs
41079
- ,
41080
- ref: "x1",
41081
- value: this.state.line[0][0],
41082
- useArrowKeys: true
41083
- // eslint-disable-next-line react/jsx-no-bind
41084
- ,
41085
- onChange: this.changePoint.bind(this, 0, 0),
41086
- onFocus: _.partial(this.props.onFocus, "x1"),
41087
- onBlur: _.partial(this.props.onBlur, "x1"),
41088
- keypadElement: this.props.keypadElement
41089
- }), /*#__PURE__*/React.createElement(TeX, {
41090
- key: ","
41091
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
41092
- key: "y1"
41093
- // eslint-disable-next-line react/no-string-refs
41094
- ,
41095
- ref: "y1",
41096
- value: this.state.line[0][1],
41097
- useArrowKeys: true
41098
- // eslint-disable-next-line react/jsx-no-bind
41099
- ,
41100
- onChange: this.changePoint.bind(this, 0, 1),
41101
- onFocus: _.partial(this.props.onFocus, "y1"),
41102
- onBlur: _.partial(this.props.onBlur, "y1"),
41103
- keypadElement: this.props.keypadElement
41104
- }), /*#__PURE__*/React.createElement(TeX, {
41105
- key: ")"
41106
- }, ")")];
41107
- const point2 = [/*#__PURE__*/React.createElement(TeX, {
41108
- key: "("
41109
- }, "("), /*#__PURE__*/React.createElement(InputComponent, {
41110
- key: "x2"
41111
- // eslint-disable-next-line react/no-string-refs
41112
- ,
41113
- ref: "x2",
41114
- value: this.state.line[1][0],
41115
- useArrowKeys: true
41116
- // eslint-disable-next-line react/jsx-no-bind
41117
- ,
41118
- onChange: this.changePoint.bind(this, 1, 0),
41119
- onFocus: _.partial(this.props.onFocus, "x2"),
41120
- onBlur: _.partial(this.props.onBlur, "x2"),
41121
- keypadElement: this.props.keypadElement
41122
- }), /*#__PURE__*/React.createElement(TeX, {
41123
- key: ","
41124
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
41125
- key: "y2"
41126
- // eslint-disable-next-line react/no-string-refs
41127
- ,
41128
- ref: "y2",
41129
- value: this.state.line[1][1],
41130
- useArrowKeys: true
41131
- // eslint-disable-next-line react/jsx-no-bind
41132
- ,
41133
- onChange: this.changePoint.bind(this, 1, 1),
41134
- onFocus: _.partial(this.props.onFocus, "y2"),
41135
- onBlur: _.partial(this.props.onBlur, "y2"),
41136
- keypadElement: this.props.keypadElement
41137
- }), /*#__PURE__*/React.createElement(TeX, {
41138
- key: ")"
41139
- }, ")")];
41140
- return /*#__PURE__*/React.createElement("div", null, i18n.$_("Reflection over the line from " + "%(point1)s to %(point2)s", {
41141
- point1,
41142
- point2
41143
- }));
41144
- }
41145
- }
41146
- },
41147
- dilation: {
41148
- // I18N: As in the command, "Dilate the polygon"
41149
- verbName: i18n._("Dilate"),
41150
- nounName: i18n._("Dilation"),
41151
- lowerNounName: i18n._("dilation"),
41152
- apply: function (transform) {
41153
- return function (coord) {
41154
- return dilatePointFromCenter(coord, transform.center, transform.scale);
41155
- };
41156
- },
41157
- isValid: function (transform) {
41158
- return _.isFinite(transform.scale) && _.isFinite(transform.center[0]) && _.isFinite(transform.center[1]);
41159
- },
41160
- isEmpty: function (transform) {
41161
- return transform.scale === null && transform.center[0] === null && transform.center[1] === null;
41162
- },
41163
- isNoOp: function (transform) {
41164
- return number.equal(transform.scale, 1);
41165
- },
41166
- collapse: function (transform1, transform2) {
41167
- if (!point.equal(transform1.center, transform2.center)) {
41168
- return false;
41169
- }
41170
- return {
41171
- center: transform1.center,
41172
- scale: transform1.scale * transform2.scale
41173
- };
41174
- },
41175
- toTeX: function (transform) {
41176
- const scaleString = stringFromFraction(transform.scale);
41177
- return i18n.$_("Dilation of scale %(scale)s about %(point)s", {
41178
- scale: scaleString,
41179
- point: texFromPoint(transform.center)
41180
- });
41181
- },
41182
- Input: class Input extends React.Component {
41183
- constructor(...args) {
41184
- super(...args);
41185
- this.state = {
41186
- center: this.props.center || [null, null],
41187
- scale: this.props.scale || null
41188
- };
41189
- this.value = () => {
41190
- // eslint-disable-next-line react/no-string-refs
41191
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41192
- const scale = this.refs.scale.getValue();
41193
- // eslint-disable-next-line react/no-string-refs
41194
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41195
- const x = this.refs.x.getValue();
41196
- // eslint-disable-next-line react/no-string-refs
41197
- // @ts-expect-error - TS2339 - Property 'getValue' does not exist on type 'ReactInstance'.
41198
- const y = this.refs.y.getValue();
41199
- return {
41200
- scale: scale,
41201
- center: [x, y]
41202
- };
41203
- };
41204
- /* InputPath API */
41205
- this.setInputValue = (path, value, cb) => {
41206
- const id = _.first(path);
41207
- let scale = this.state.scale;
41208
- const center = _.clone(this.state.center);
41209
- if (id === "x") {
41210
- center[0] = value;
41211
- } else if (id === "y") {
41212
- center[1] = value;
41213
- } else if (id === "scale") {
41214
- scale = value;
41215
- }
41216
- this.setState({
41217
- scale: scale,
41218
- center: center
41219
- }, () => {
41220
- this.props.onChange(cb);
41221
- });
41222
- };
41223
- this.getInputPaths = () => {
41224
- return [["x"], ["y"], ["scale"]];
41225
- };
41226
- }
41227
- componentDidUpdate(prevProps) {
41228
- if (!deepEq(this.props, prevProps)) {
41229
- this.setState({
41230
- center: this.props.center,
41231
- scale: this.props.scale
41232
- });
41233
- }
41234
- }
41235
- render() {
41236
- const InputComponent = inputComponentForApiOptions(this.props.apiOptions);
41237
- const {
41238
- TeX
41239
- } = getDependencies();
41240
- const point = [/*#__PURE__*/React.createElement(TeX, {
41241
- key: "("
41242
- }, "("), /*#__PURE__*/React.createElement(InputComponent, {
41243
- key: "point_x"
41244
- // eslint-disable-next-line react/no-string-refs
41245
- ,
41246
- ref: "x",
41247
- placeholder: 0,
41248
- value: this.state.center[0],
41249
- useArrowKeys: true,
41250
- onChange: val0 => {
41251
- const val1 = this.state.center[1];
41252
- this.setState({
41253
- center: [val0, val1]
41254
- }, () => {
41255
- this.props.onChange();
41256
- });
41257
- },
41258
- onFocus: _.partial(this.props.onFocus, "x"),
41259
- onBlur: _.partial(this.props.onBlur, "x"),
41260
- keypadElement: this.props.keypadElement
41261
- }), /*#__PURE__*/React.createElement(TeX, {
41262
- key: ","
41263
- }, ", {}"), /*#__PURE__*/React.createElement(InputComponent, {
41264
- key: "point_y"
41265
- // eslint-disable-next-line react/no-string-refs
41266
- ,
41267
- ref: "y",
41268
- placeholder: 0,
41269
- value: this.state.center[1],
41270
- useArrowKeys: true,
41271
- onChange: val1 => {
41272
- const val0 = this.state.center[0];
41273
- this.setState({
41274
- center: [val0, val1]
41275
- }, () => {
41276
- this.props.onChange();
41277
- });
41278
- },
41279
- onFocus: _.partial(this.props.onFocus, "y"),
41280
- onBlur: _.partial(this.props.onBlur, "y"),
41281
- keypadElement: this.props.keypadElement
41282
- }), /*#__PURE__*/React.createElement(TeX, {
41283
- key: ")"
41284
- }, ")")];
41285
- const scale = /*#__PURE__*/React.createElement(InputComponent
41286
- // eslint-disable-next-line react/no-string-refs
41287
- , {
41288
- ref: "scale",
41289
- placeholder: 1,
41290
- value: this.state.scale,
41291
- useArrowKeys: true,
41292
- onChange: val => {
41293
- this.setState({
41294
- scale: val
41295
- }, () => {
41296
- this.props.onChange();
41297
- });
41298
- },
41299
- onFocus: _.partial(this.props.onFocus, "scale"),
41300
- onBlur: _.partial(this.props.onBlur, "scale"),
41301
- keypadElement: this.props.keypadElement
41302
- });
41303
- return /*#__PURE__*/React.createElement("div", null, i18n.$_("Dilation about %(point)s by %(scale)s", {
41304
- point,
41305
- scale
41306
- }));
41307
- }
41308
- }
41309
- }
41310
- };
41311
-
41312
- /* Various functions to deal with different shape types */
41313
- const ShapeTypes = {
41314
- getPointCountForType: function (type) {
41315
- const splitType = type.split("-");
41316
- if (splitType[0] === "polygon") {
41317
- return splitType[1] || 3;
41318
- }
41319
- if (splitType[0] === "line" || splitType[0] === "lineSegment") {
41320
- return 2;
41321
- }
41322
- if (splitType[0] === "angle") {
41323
- return 3;
41324
- }
41325
- if (splitType[0] === "circle") {
41326
- return 2;
41327
- }
41328
- if (splitType[0] === "point") {
41329
- return 1;
41330
- }
41331
- },
41332
- addMovableShape: function (graphie, options) {
41333
- if (options.editable && options.translatable) {
41334
- 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);
41335
- }
41336
- const points = _.map(options.shape.coords, function (coord) {
41337
- let isMoving = false;
41338
- let previousCoord = coord;
41339
- const onMove = function onMove(x, y) {
41340
- if (!isMoving) {
41341
- previousCoord = currentPoint.coord;
41342
- isMoving = true;
41343
- }
41344
- let moveVector = vector$1.subtract([x, y], currentPoint.coord);
41345
-
41346
- // Translate from (x, y) semantics to (dX, dY) semantics
41347
- // This is more useful for translations on multiple points,
41348
- // where we care about how the points moved, not where any
41349
- // individual point ended up
41350
- if (options.onMove) {
41351
- moveVector = options.onMove(moveVector[0], moveVector[1]);
41352
- }
41353
-
41354
- // Perform a translation on all points in this shape when
41355
- // any point moves
41356
- if (options.translatable) {
41357
- _.each(points, function (point) {
41358
- // The point itself will be updated by the
41359
- // movablePoint class, so only translate the other
41360
- // points
41361
- if (point !== currentPoint) {
41362
- point.setCoord(vector$1.add(point.coord, moveVector));
41363
- }
41364
- });
41365
- }
41366
-
41367
- // Update our shape and our currentPoint
41368
- // Without this, some shapes (circles, angles) appear
41369
- // "bouncy" as they are updated with currentPoint at the
41370
- // current mouse coordinate (oldCoord), rather than newCoord
41371
- const oldCoord = currentPoint.coord;
41372
- const newCoord = vector$1.add(currentPoint.coord, moveVector);
41373
- // Temporarily change our coordinate so that
41374
- // shape.update() sees the new coordinate
41375
- currentPoint.coord = newCoord;
41376
- shape.update();
41377
- // ...But don't break onMove, which assumes it
41378
- // is the only thing changing our coord
41379
- currentPoint.coord = oldCoord;
41380
- return newCoord;
41381
- };
41382
- const onMoveEnd = function onMoveEnd() {
41383
- // onMove isn't guaranteed to be called before onMoveEnd, so
41384
- // we have to take into account that we may not have moved and
41385
- // set previousCoord.
41386
- if (options.onMoveEnd && isMoving) {
41387
- isMoving = false;
41388
- // We don't use the supplied x and y parameters here
41389
- // because MovablePoint's onMoveEnd semantics suck.
41390
- // It returns the mouseX, mouseY without processing them
41391
- // through onMove, leaving us with weird fractional moves
41392
- const change = vector$1.subtract(currentPoint.coord, previousCoord);
41393
- options.onMoveEnd(change[0], change[1]);
41394
- }
41395
- shape.update();
41396
- };
41397
- const currentPoint = graphie.addMovablePoint({
41398
- coord: coord,
41399
- normalStyle: options.normalPointStyle,
41400
- highlightStyle: options.highlightPointStyle,
41401
- constraints: {
41402
- fixed: !options.translatable && !options.editable
41403
- },
41404
- visible: options.showPoints,
41405
- snapX: options.snap && options.snap[0] || 0,
41406
- snapY: options.snap && options.snap[1] || 0,
41407
- bounded: false,
41408
- // Don't bound it when placing it on the graph
41409
- onMove: onMove,
41410
- onMoveEnd: onMoveEnd
41411
- });
41412
-
41413
- // Bound it when moving
41414
- // We can't set this earlier, because doing so would mean any
41415
- // points outside of the graph would be moved into a moved into
41416
- // a position that doesn't preserve the shape
41417
- currentPoint.bounded = true;
41418
- return currentPoint;
41419
- });
41420
- const shape = ShapeTypes.addShape(graphie, options, points);
41421
- const removeShapeWithoutPoints = shape.remove;
41422
- shape.remove = function () {
41423
- removeShapeWithoutPoints.apply(shape);
41424
- _.invoke(points, "remove");
41425
- };
41426
- return shape;
41427
- },
41428
- addShape: function (graphie, options, points) {
41429
- points = points || options.shape.coords;
41430
- const types = ShapeTypes._typesOf(options.shape);
41431
- const typeOptions = options.shape.options || ShapeTypes.defaultOptions(types);
41432
-
41433
- // @ts-expect-error - TS2554 - Expected 4 arguments, but got 3.
41434
- const shapes = ShapeTypes._mapTypes(types, points, function (type, points, i) {
41435
- const shapeOptions = _.extend({}, options, typeOptions[i]);
41436
- return ShapeTypes._addType(graphie, type, points, shapeOptions);
41437
- });
41438
- const updateFuncs = _.filter(_.pluck(shapes, "update"), _.identity);
41439
- const update = function update() {
41440
- _.invoke(updateFuncs, "call");
41441
- };
41442
- const removeFuncs = _.filter(_.pluck(shapes, "remove"), _.identity);
41443
- const remove = function remove() {
41444
- _.invoke(removeFuncs, "call");
41445
- };
41446
- const getOptions = function getOptions() {
41447
- return _.map(shapes, function (shape) {
41448
- if (shape.getOptions) {
41449
- return shape.getOptions();
41450
- }
41451
- return {};
41452
- });
41453
- };
41454
- const toJSON = function toJSON() {
41455
- const coords = _.map(points, function (pt) {
41456
- if (_.isArray(pt)) {
41457
- return pt;
41458
- }
41459
- return pt.coord;
41460
- });
41461
- return {
41462
- type: types,
41463
- coords: coords,
41464
- options: getOptions()
41465
- };
41466
- };
41467
- return {
41468
- type: types,
41469
- points: points,
41470
- update: update,
41471
- remove: remove,
41472
- toJSON: toJSON,
41473
- getOptions: getOptions
41474
- };
41475
- },
41476
- equal: function (shape1, shape2) {
41477
- const types1 = ShapeTypes._typesOf(shape1);
41478
- const types2 = ShapeTypes._typesOf(shape2);
41479
- if (types1.length !== types2.length) {
41480
- return false;
41481
- }
41482
- // @ts-expect-error - TS2554 - Expected 4 arguments, but got 3.
41483
- const shapes1 = ShapeTypes._mapTypes(types1, shape1.coords, ShapeTypes._combine);
41484
- // @ts-expect-error - TS2554 - Expected 4 arguments, but got 3.
41485
- const shapes2 = ShapeTypes._mapTypes(types2, shape2.coords, ShapeTypes._combine);
41486
- return _.all(_.map(shapes1, function (partialShape1, i) {
41487
- const partialShape2 = shapes2[i];
41488
- if (partialShape1.type !== partialShape2.type) {
41489
- return false;
41490
- }
41491
- return ShapeTypes._forType(partialShape1.type).equal(partialShape1.coords, partialShape2.coords);
41492
- }));
41493
- },
41494
- _typesOf: function (shape) {
41495
- let types = shape.type;
41496
- if (!_.isArray(types)) {
41497
- types = [types];
41498
- }
41499
- return _.map(types, function (type) {
41500
- if (type === "polygon") {
41501
- return "polygon-3";
41502
- }
41503
- return type;
41504
- });
41505
- },
41506
- defaultOptions: function (types) {
41507
- return _.map(types, function (type) {
41508
- const typeDefaultOptions = ShapeTypes._forType(type).defaultOptions;
41509
- return _.extend({}, typeDefaultOptions);
41510
- });
41511
- },
41512
- _forType: function (type) {
41513
- const baseType = type.split("-")[0];
41514
- return ShapeTypes[baseType];
41515
- },
41516
- _mapTypes: function (types, points, func, context) {
41517
- return _.map(types, function (type, i) {
41518
- const pointCount = ShapeTypes.getPointCountForType(type);
41519
- const currentPoints = _.first(points, pointCount);
41520
- points = _.rest(points, pointCount);
41521
- return func.call(context, type, currentPoints, i);
41522
- });
41523
- },
41524
- _addType: function (graphie, type, points, options) {
41525
- const lineCoords = _.isArray(points[0]) ? {
41526
- coordA: points[0],
41527
- coordZ: points[1]
41528
- } : {
41529
- pointA: points[0],
41530
- pointZ: points[1]
41531
- };
41532
- type = type.split("-")[0];
41533
- if (type === "polygon") {
41534
- const polygon = graphie.addMovablePolygon(_.extend({}, options, {
41535
- fixed: !options.editable,
41536
- snapX: options.snap && options.snap[0] || 0,
41537
- snapY: options.snap && options.snap[1] || 0,
41538
- points: points,
41539
- constrainToGraph: false
41540
- }));
41541
- return {
41542
- update: polygon.transform.bind(polygon),
41543
- remove: polygon.remove.bind(polygon)
41544
- };
41545
- }
41546
- if (type === "line" || type === "lineSegment") {
41547
- const line = graphie.addMovableLineSegment(_.extend({}, options, lineCoords, {
41548
- movePointsWithLine: true,
41549
- fixed: true,
41550
- constraints: {
41551
- fixed: true
41552
- },
41553
- extendLine: type === "line"
41554
- }));
41555
-
41556
- // TODO(jack): Hide points on uneditable lines when translation
41557
- // is a vector.
41558
- // We can't just remove the points yet, because they are the
41559
- // translation handle for the line.
41560
- return {
41561
- update: line.transform.bind(line, true),
41562
- remove: line.remove.bind(line)
41563
- };
41564
- }
41565
- if (type === "angle") {
41566
- // If this angle is editable, we want to be able to make angles
41567
- // both larger and smaller than 180 degrees.
41568
- // If this angle is not editable, it should always maintain
41569
- // it's angle measure, even if it is reflected (causing the
41570
- // clockwise-ness of the points to change)
41571
- const shouldChangeReflexivity = options.editable ? null : false;
41572
- const angle = graphie.addMovableAngle({
41573
- angleLabel: "$deg0",
41574
- fixed: true,
41575
- points: points,
41576
- normalStyle: options.normalStyle,
41577
- reflex: options.reflex
41578
- });
41579
-
41580
- // Hide non-vertex points on uneditable angles
41581
- if (!_.isArray(points[0]) && !options.editable) {
41582
- points[0].remove();
41583
- points[2].remove();
41584
- }
41585
- return {
41586
- update: angle.update.bind(angle, shouldChangeReflexivity),
41587
- remove: angle.remove.bind(angle),
41588
- getOptions: function () {
41589
- return {
41590
- reflex: angle.isReflex()
41591
- };
41592
- }
41593
- };
41594
- }
41595
- if (type === "circle") {
41596
- let perimeter = {
41597
- // temporary object for the first removal
41598
- remove: _.identity
41599
- };
41600
- const redrawPerim = function redrawPerim() {
41601
- const coord0 = points[0].coord || points[0];
41602
- const coord1 = points[1].coord || points[1];
41603
- const radius = point.distanceToPoint(coord0, coord1);
41604
- // @ts-expect-error - TS2554 - Expected 1 arguments, but got 0.
41605
- perimeter.remove();
41606
- perimeter = graphie.circle(coord0, radius, _.extend({
41607
- stroke: KhanColors.DYNAMIC,
41608
- "stroke-width": 2
41609
- }, options.normalStyle));
41610
- };
41611
- redrawPerim();
41612
- if (points[1].remove && !options.editable) {
41613
- points[1].remove();
41614
- }
41615
- return {
41616
- update: redrawPerim,
41617
- remove: function () {
41618
- // Not _.bind because the remove function changes
41619
- // when the perimeter is redrawn
41620
- // @ts-expect-error - TS2554 - Expected 1 arguments, but got 0.
41621
- perimeter.remove();
41622
- }
41623
- };
41624
- }
41625
- if (type === "point") {
41626
- // do nothing
41627
- return {
41628
- update: null,
41629
- remove: null
41630
- };
41631
- }
41632
- throw new PerseusError("Invalid shape type " + type, Errors.InvalidInput);
41633
- },
41634
- _combine: function (type, coords) {
41635
- return {
41636
- type: type,
41637
- coords: coords
41638
- };
41639
- },
41640
- polygon: {
41641
- equal: orderInsensitiveCoordsEqual
41642
- },
41643
- line: {
41644
- equal: line.equal
41645
- },
41646
- lineSegment: {
41647
- equal: orderInsensitiveCoordsEqual
41648
- },
41649
- angle: {
41650
- equal: function (points1, points2) {
41651
- if (!point.equal(points1[1], points2[1])) {
41652
- return false;
41653
- }
41654
- const line1_0 = [points1[1], points1[0]];
41655
- const line1_2 = [points1[1], points1[2]];
41656
- const line2_0 = [points2[1], points2[0]];
41657
- const line2_2 = [points2[1], points2[2]];
41658
- const equalUnflipped =
41659
- // @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'.
41660
- ray.equal(line1_0, line2_0) && ray.equal(line1_2, line2_2);
41661
- const equalFlipped =
41662
- // @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'.
41663
- ray.equal(line1_0, line2_2) && ray.equal(line1_2, line2_0);
41664
- return equalUnflipped || equalFlipped;
41665
- },
41666
- defaultOptions: {
41667
- reflex: false
41668
- }
41669
- },
41670
- circle: {
41671
- equal: function (points1, points2) {
41672
- const radius1 = point.distanceToPoint(points1[0], points1[1]);
41673
- const radius2 = point.distanceToPoint(points2[0], points2[1]);
41674
- return point.equal(points1[0], points2[0]) && number.equal(radius1, radius2);
41675
- }
41676
- },
41677
- point: {
41678
- equal: point.equal
41679
- }
41680
- };
41681
- const TransformationListItem = TransformOps.ListItem;
41682
- class TransformationList extends React.Component {
41683
- constructor(...args) {
41684
- super(...args);
41685
- this._transformationRefs = () => {
41686
- return _.times(this.props.transformations.length, i => {
41687
- // eslint-disable-next-line react/no-string-refs
41688
- return this.refs["transformation" + i];
41689
- });
41690
- };
41691
- this.value = () => {
41692
- return _.invoke(this._transformationRefs(), "value");
41693
- };
41694
- this.handleChange = (changed, callback) => {
41695
- this.props.onChange(this.value(), callback);
41696
- };
41697
- this.focusLast = () => {
41698
- const transformationRefs = this._transformationRefs();
41699
- if (transformationRefs.length !== 0) {
41700
- // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
41701
- _.last(transformationRefs).focus();
41702
- }
41703
- };
41704
- }
41705
- render() {
41706
- if (this.props.mode === "static") {
41707
- return /*#__PURE__*/React.createElement("span", null); // don't render anything
41708
- }
41709
-
41710
- const transformationList = _.map(this.props.transformations, function (transform, i) {
41711
- return /*#__PURE__*/React.createElement(TransformationListItem, {
41712
- ref: "transformation" + i,
41713
- key: "transformation" + i,
41714
- transform: transform
41715
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41716
- ,
41717
- mode: this.props.mode
41718
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41719
- ,
41720
- onChange: this.handleChange
41721
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41722
- ,
41723
- onFocus: _.partial(this.props.onFocus, "" + i)
41724
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41725
- ,
41726
- onBlur: _.partial(this.props.onBlur, "" + i)
41727
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41728
- ,
41729
- keypadElement: this.props.keypadElement
41730
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41731
- ,
41732
- apiOptions: this.props.apiOptions
41733
- });
41734
- }, this);
41735
- return /*#__PURE__*/React.createElement("div", {
41736
- className: "perseus-transformation-list"
41737
- }, transformationList);
41738
- }
41739
- }
41740
- class ToolButton extends React.Component {
41741
- render() {
41742
- const classes = this.props.toggled ? "simple-button exercise-orange toggled highlighted-tool-button" : "simple-button";
41743
- return /*#__PURE__*/React.createElement("button", {
41744
- type: "button",
41745
- className: classes,
41746
- disabled: this.props.disabled,
41747
- onClick: this.props.onClick
41748
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLButtonElement>'.
41749
- ,
41750
- onTouchStart: captureScratchpadTouchStart
41751
- }, this.props.children);
41752
- }
41753
- }
41754
- class ToolsBar extends React.Component {
41755
- constructor(...args) {
41756
- super(...args);
41757
- this.state = {
41758
- selected: null
41759
- };
41760
- this.changeSelected = tool => {
41761
- this.props.removeTool(this.state.selected);
41762
- if (!tool || tool === this.state.selected) {
41763
- this.setState({
41764
- selected: null
41765
- });
41766
- } else {
41767
- this.props.addTool(tool);
41768
- this.setState({
41769
- selected: tool
41770
- });
41771
- }
41772
- };
41773
- }
41774
- render() {
41775
- const tools = _.map(Transformations, function (tool, type) {
41776
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41777
- if (this.props.enabled[type]) {
41778
- return /*#__PURE__*/React.createElement(ToolButton, {
41779
- key: type
41780
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41781
- ,
41782
- disabled: this.props.apiOptions.readOnly
41783
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41784
- ,
41785
- toggled: this.state.selected === type
41786
- // eslint-disable-next-line react/jsx-no-bind
41787
- // @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.
41788
- ,
41789
- onClick: this.changeSelected.bind(this, type)
41790
- }, tool.verbName);
41791
- }
41792
- }, this);
41793
- return /*#__PURE__*/React.createElement("div", {
41794
- className: "transformer-tools-bar"
41795
- }, /*#__PURE__*/React.createElement("span", {
41796
- className: "simple-button-group"
41797
- }, tools), /*#__PURE__*/React.createElement("button", {
41798
- className: "transformer-undo-button simple-button",
41799
- type: "button",
41800
- disabled: this.props.apiOptions.readOnly,
41801
- onClick: this.props.onUndoClick
41802
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLButtonElement>'.
41803
- ,
41804
- onTouchStart: captureScratchpadTouchStart
41805
- }, /*#__PURE__*/React.createElement(InlineIcon, iconUndo), " " + i18n._("Undo")), /*#__PURE__*/React.createElement("div", {
41806
- className: "clear"
41807
- }));
41808
- }
41809
- }
41810
- class AddTransformBar extends React.Component {
41811
- constructor(...args) {
41812
- super(...args);
41813
- this.changeSelected = tool => {
41814
- if (tool) {
41815
- this.props.addTool(tool);
41816
- }
41817
- };
41818
- }
41819
- render() {
41820
- const tools = _.map(Transformations, function (tool, type) {
41821
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41822
- if (this.props.enabled[type]) {
41823
- return /*#__PURE__*/React.createElement(ToolButton, {
41824
- key: type,
41825
- toggled: false
41826
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
41827
- ,
41828
- disabled: this.props.apiOptions.readOnly
41829
- // eslint-disable-next-line react/jsx-no-bind
41830
- // @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.
41831
- ,
41832
- onClick: this.changeSelected.bind(this, type)
41833
- }, /*#__PURE__*/React.createElement(InlineIcon, iconPlus), " ", tool.nounName);
41834
- }
41835
- }, this);
41836
- return /*#__PURE__*/React.createElement("div", {
41837
- className: "transformer-tools-bar"
41838
- }, tools, /*#__PURE__*/React.createElement("button", {
41839
- className: "transformer-undo-button simple-button",
41840
- type: "button",
41841
- onClick: this.props.onUndoClick,
41842
- disabled: this.props.apiOptions.readOnly
41843
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLButtonElement>'.
41844
- ,
41845
- onTouchStart: captureScratchpadTouchStart
41846
- }, /*#__PURE__*/React.createElement(InlineIcon, iconUndo), " " + i18n._("Undo")), /*#__PURE__*/React.createElement("div", {
41847
- className: "clear"
41848
- }));
41849
- }
41850
- }
41851
-
41852
- // there's no transform in the exports
41853
-
41854
- class Transformer extends React.Component {
41855
- constructor(..._args) {
41856
- super(..._args);
41857
- // @ts-expect-error - TS2564 - Property 'tools' has no initializer and is not definitely assigned in the constructor.
41858
- this.tools = void 0;
41859
- this.currentTool = void 0;
41860
- // @ts-expect-error - TS2564 - Property 'transformations' has no initializer and is not definitely assigned in the constructor.
41861
- this.transformations = void 0;
41862
- this.shape = void 0;
41863
- // This is not the same as TransformerShape
41864
- this.dilationCircle = void 0;
41865
- this.rotatePoint = void 0;
41866
- this.rotateHandle = void 0;
41867
- this.shouldSetupGraphie = (nextProps, prevProps) => {
41868
- if (!deepEq(prevProps.starting, nextProps.starting)) {
41869
- return true;
41870
- }
41871
- if (prevProps.graphMode !== nextProps.graphMode) {
41872
- return true;
41873
- }
41874
- if (prevProps.listMode !== nextProps.listMode) {
41875
- return true;
41876
- }
41877
- if (prevProps.drawSolutionShape !== nextProps.drawSolutionShape) {
41878
- return true;
41879
- }
41880
- if (nextProps.drawSolutionShape && !deepEq(prevProps.correct.shape, nextProps.correct.shape)) {
41881
- return true;
41882
- }
41883
- if (!deepEq(this.tools, nextProps.tools)) {
41884
- return true;
41885
- }
41886
- return false;
41887
- };
41888
- this.graphie = () => {
41889
- // eslint-disable-next-line react/no-string-refs
41890
- // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
41891
- return this.refs.graph.graphie();
41892
- };
41893
- this.setupGraphie = graphie => {
41894
- // A background image of our solution:
41895
- if (this.props.drawSolutionShape && this.props.correct.shape && this.props.correct.shape.coords) {
41896
- // @ts-expect-error - TS2554 - Expected 3 arguments, but got 2.
41897
- ShapeTypes.addShape(graphie, {
41898
- fixed: true,
41899
- shape: this.props.correct.shape,
41900
- normalStyle: {
41901
- stroke: KhanColors.GRAY,
41902
- "stroke-dasharray": "",
41903
- "stroke-width": 2
41904
- }
41905
- });
41906
- }
41907
- this.currentTool = null;
41908
- // eslint-disable-next-line react/no-string-refs
41909
- // @ts-expect-error - TS2339 - Property 'changeSelected' does not exist on type 'ReactInstance'.
41910
- this.refs.toolsBar.changeSelected(null);
41911
- this.addTransformerShape(this.props.starting.shape, /* translatable */false);
41912
- this.setTransformations(this.props.transformations);
41913
-
41914
- // Save a copy of our tools so that we can check future
41915
- // this.props.tools changes against them
41916
- // This seems weird, but gives us an easy way to tell whether
41917
- // props changes were self-inflicted (for which a graphie reset
41918
- // is not required, and is in fact a bad idea right now because
41919
- // of resetting the size of the dilation tool).
41920
- // TODO (jack): A deepClone method would be nice here
41921
- this.tools = {
41922
- translation: _.clone(this.props.tools.translation),
41923
- rotation: _.clone(this.props.tools.rotation),
41924
- reflection: _.clone(this.props.tools.reflection),
41925
- dilation: _.clone(this.props.tools.dilation)
41926
- };
41927
- };
41928
- /* Applies all transformations in `transformations`
41929
- * to the starting shape, and updates this.transformations
41930
- * to reflect this
41931
- *
41932
- * Usually called with this.props.transformations
41933
- */
41934
- this.setTransformations = transformations => {
41935
- this.resetCoords();
41936
- this.transformations = _.clone(transformations);
41937
- _.each(this.transformations, this.applyTransform);
41938
- };
41939
- // the polygon that we transform
41940
- this.addTransformerShape = (shape, translatable) => {
41941
- const self = this;
41942
- const graphie = this.graphie();
41943
- this.shape = ShapeTypes.addMovableShape(graphie, {
41944
- shape: shape,
41945
- editable: false,
41946
- showPoints: this.props.graphMode !== "static",
41947
- translatable: translatable,
41948
- onMove: function (dX, dY) {
41949
- dX = KhanMath.roundToNearest(graphie.snap[0], dX);
41950
- dY = KhanMath.roundToNearest(graphie.snap[1], dY);
41951
- // NOTE(kevinb): object is missing .constraints property
41952
- // @ts-expect-error - TS2345 - Argument of type '{ type: "translation"; vector: [any, any]; }' is not assignable to parameter of type 'PerseusTransformerTransformation'.
41953
- self.addTransform({
41954
- type: "translation",
41955
- vector: [dX, dY]
41956
- });
41957
- return [dX, dY];
41958
- },
41959
- normalPointStyle: {
41960
- fill: translatable ? KhanColors.INTERACTIVE : KhanColors.DYNAMIC,
41961
- stroke: translatable ? KhanColors.INTERACTIVE : KhanColors.DYNAMIC
41962
- },
41963
- highlightPointStyle: {
41964
- fill: KhanColors.INTERACTING,
41965
- stroke: KhanColors.INTERACTING
41966
- }
41967
- });
41968
- };
41969
- this.addTool = toolId => {
41970
- const self = this;
41971
- if (this.props.graphMode === "interactive") {
41972
- if (toolId === "translation") {
41973
- this.currentTool = this.addTranslationTool();
41974
- } else if (toolId === "rotation") {
41975
- this.currentTool = this.addRotationTool();
41976
- } else if (toolId === "reflection") {
41977
- this.currentTool = this.addReflectionTool();
41978
- } else if (toolId === "dilation") {
41979
- this.currentTool = this.addDilationTool();
41980
- } else {
41981
- throw new PerseusError("Invalid tool id: " + toolId, Errors.InvalidInput);
41982
- }
41983
- } else {
41984
- let transform;
41985
- if (toolId === "translation") {
41986
- // NOTE(kevinb): object is missing .constraints property
41987
- transform = {
41988
- type: "translation",
41989
- // NOTE(kevib): these should be numbers
41990
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
41991
- vector: [null, null]
41992
- };
41993
- } else if (toolId === "rotation") {
41994
- // NOTE(kevinb): object is missing .constraints property
41995
- transform = {
41996
- type: "rotation",
41997
- // NOTE(kevib): these should be numbers
41998
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
41999
- center: [null, null],
42000
- // NOTE(kevib): this should be a number
42001
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'.
42002
- angleDeg: null
42003
- };
42004
- } else if (toolId === "reflection") {
42005
- // Reflections with nulls in them won't be applied until
42006
- // fills in the blanks
42007
- transform = {
42008
- type: "reflection",
42009
- line: [
42010
- // NOTE(kevib): these should be numbers
42011
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
42012
- [null, null],
42013
- // NOTE(kevib): these should be numbers
42014
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
42015
- [null, null]]
42016
- };
42017
- } else if (toolId === "dilation") {
42018
- // NOTE(kevinb): object is missing .constraints property
42019
- transform = {
42020
- type: "dilation",
42021
- // NOTE(kevib): these should be numbers
42022
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'. | TS2322 - Type 'null' is not assignable to type 'number'.
42023
- center: [null, null],
42024
- // NOTE(kevib): this should be a number
42025
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'number'.
42026
- scale: null
42027
- };
42028
- } else {
42029
- throw new PerseusError("Invalid tool id: " + toolId, Errors.InvalidInput);
42030
- }
42031
- this.doTransform(transform, function () {
42032
- // @ts-expect-error - TS2339 - Property 'focusLast' does not exist on type 'ReactInstance'.
42033
- self.refs.transformationList.focusLast();
42034
- });
42035
- }
42036
- };
42037
- this.removeTool = toolId => {
42038
- if (this.currentTool) {
42039
- this.currentTool.remove();
42040
- }
42041
- this.currentTool = null;
42042
- };
42043
- this.addTranslationTool = () => {
42044
- var _this$shape, _this$shape2;
42045
- const self = this;
42046
- (_this$shape = this.shape) == null || _this$shape.remove();
42047
- this.addTransformerShape((_this$shape2 = this.shape) == null ? void 0 : _this$shape2.toJSON(), /* translatable */true);
42048
- return {
42049
- remove: function () {
42050
- var _self$shape, _self$shape2;
42051
- (_self$shape = self.shape) == null || _self$shape.remove();
42052
- self.addTransformerShape((_self$shape2 = self.shape) == null ? void 0 : _self$shape2.toJSON(), /* translatable */false);
42053
- }
42054
- };
42055
- };
42056
- // Snaps a coord to this.graphie()'s snap
42057
- // @ts-expect-error - TS2322 - Type '(coord: Coord) => number[]' is not assignable to type '(arg1: Coord) => Coord'.
42058
- this.snapCoord = coord => {
42059
- const graphie = this.graphie();
42060
- return _.map(coord, function (val, dim) {
42061
- return KhanMath.roundToNearest(graphie.snap[dim], val);
42062
- });
42063
- };
42064
- // Normalize the coords into something that fits the new 45 degree
42065
- // reflection line.
42066
- // @ts-expect-error - TS2322 - Type '(messyCoords: [Coord, Coord]) => any[]' is not assignable to type '(arg1: [Coord, Coord]) => [Coord, Coord]'.
42067
- this.normalizeReflectionCoords = messyCoords => {
42068
- // @ts-expect-error - TS2345 - Argument of type 'Point' is not assignable to parameter of type 'Coord'.
42069
- const midpoint = this.snapCoord(line.midpoint(messyCoords));
42070
- const origDirectionPolar = vector$1.polarDegFromCart(vector$1.subtract(messyCoords[0], messyCoords[1]));
42071
- const direction = vector$1.cartFromPolarDeg(1, KhanMath.roundToNearest(45, origDirectionPolar[1]));
42072
- const coords = _.map([-1, 1], function (directionCoefficient) {
42073
- const coord = vector$1.add(midpoint, vector$1.scale(direction, directionCoefficient *
42074
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
42075
- this.scaleToCurrentRange(REFLECT_ROTATE_HANDLE_DIST)));
42076
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
42077
- return this.snapCoord(coord);
42078
- }, this);
42079
- return coords;
42080
- };
42081
- this.addReflectionTool = () => {
42082
- const options = this.props.tools.reflection;
42083
- if (!options.enabled) {
42084
- return;
42085
- }
42086
- const self = this;
42087
- // eslint-disable-next-line react/no-string-refs
42088
- // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
42089
- const graphie = this.refs.graph.graphie();
42090
- const updateReflectionTool = function updateReflectionTool() {
42091
- self.changeTool("reflection", {
42092
- coords: _.pluck(reflectPoints, "coord")
42093
- });
42094
- };
42095
- const coords = this.normalizeReflectionCoords(options.coords);
42096
-
42097
- // The points defining the line of reflection; hidden from the
42098
- // user.
42099
- const reflectPoints = _.map(coords, function (coord) {
42100
- return graphie.addMovablePoint({
42101
- coord: coord,
42102
- visible: false
42103
- });
42104
- }, this);
42105
-
42106
- // the line of reflection
42107
- // TODO(jack): graphie.style here is a hack to prevent the dashed
42108
- // style from leaking into the rest of the shapes. Remove when
42109
- // graphie.addMovableLineSegment doesn't leak styles anymore.
42110
- let reflectLine;
42111
- const normalColor = colorForTool(options);
42112
- graphie.style({}, function () {
42113
- reflectLine = graphie.addMovableLineSegment({
42114
- fixed: options.constraints.fixed,
42115
- constraints: options.constraints,
42116
- pointA: reflectPoints[0],
42117
- pointZ: reflectPoints[1],
42118
- snapX: graphie.snap[0],
42119
- snapY: graphie.snap[1],
42120
- extendLine: true,
42121
- normalStyle: {
42122
- stroke: normalColor,
42123
- "stroke-width": 2,
42124
- "stroke-dasharray": "- "
42125
- },
42126
- highlightStyle: {
42127
- stroke: KhanColors.INTERACTING,
42128
- "stroke-width": 2,
42129
- "stroke-dasharray": "- " // TODO(jack) solid doesn't
42130
- // work here, but would be
42131
- // nicer
42132
- },
42133
-
42134
- movePointsWithLine: true,
42135
- onMoveEnd: updateReflectionTool
42136
- });
42137
- });
42138
-
42139
- // the "button" point in the center of the line of reflection
42140
- const reflectButton = graphie.addReflectButton({
42141
- fixed: options.constraints.fixed,
42142
- line: reflectLine,
42143
- size: this.scaleToCurrentRange(REFLECT_BUTTON_SIZE),
42144
- onClick: function () {
42145
- self.doTransform({
42146
- type: "reflection",
42147
- // @ts-expect-error - TS2322 - Type 'any[]' is not assignable to type '[Coord, Coord]'.
42148
- line: _.pluck(reflectPoints, "coord")
42149
- });
42150
- if (reflectRotateHandle) {
42151
- // flip the rotation handle
42152
- // @ts-expect-error - TS2339 - Property 'setCoord' does not exist on type 'never'.
42153
- reflectRotateHandle.setCoord(vector$1.add(reflectButton.coord, vector$1.subtract(reflectButton.coord,
42154
- // @ts-expect-error - TS2339 - Property 'coord' does not exist on type 'never'.
42155
- reflectRotateHandle.coord)));
42156
- // @ts-expect-error - TS2339 - Property 'update' does not exist on type 'never'.
42157
- reflectRotateHandle.update();
42158
- }
42159
- },
42160
- normalStyle: {
42161
- stroke: normalColor,
42162
- "stroke-width": 2,
42163
- fill: normalColor
42164
- },
42165
- highlightStyle: {
42166
- stroke: KhanColors.INTERACTING,
42167
- "stroke-width": 3,
42168
- fill: KhanColors.INTERACTING
42169
- },
42170
- onMoveEnd: updateReflectionTool
42171
- });
42172
- let reflectRotateHandle = null;
42173
- if (!options.constraints.fixed) {
42174
- // The rotation handle for rotating the line of reflection
42175
- const initRotateHandleAngle = vector$1.polarDegFromCart(vector$1.subtract(reflectPoints[1].coord, reflectPoints[0].coord))[1] + 90; // 90 degrees off of the line
42176
- reflectRotateHandle = graphie.addRotateHandle({
42177
- center: reflectButton,
42178
- radius: this.scaleToCurrentRange(REFLECT_ROTATE_HANDLE_DIST),
42179
- angleDeg: initRotateHandleAngle,
42180
- width: this.scaleToCurrentRange(0.24),
42181
- hoverWidth: this.scaleToCurrentRange(0.4),
42182
- lengthAngle: 17,
42183
- onMove: function (newAngle) {
42184
- return KhanMath.roundToNearest(45, newAngle);
42185
- },
42186
- onMoveEnd: updateReflectionTool
42187
- });
42188
- }
42189
-
42190
- // Move the reflectButton and reflectRotateHandle with the line
42191
- $(reflectLine).on("move", function () {
42192
- reflectButton.update();
42193
- $(reflectButton).trigger("move"); // update the rotation handle,
42194
- // which watches for this in util/interactive.js.
42195
- });
42196
-
42197
- // Update the line and reflect button when the reflectRotateHandle is
42198
- // rotated
42199
- if (reflectRotateHandle) {
42200
- $(reflectRotateHandle).on("move", function () {
42201
- const rotateHandleApprox = self.snapCoord(
42202
- // @ts-expect-error - TS2531 - Object is possibly 'null'.
42203
- reflectRotateHandle.coord);
42204
- const rotateVector = vector$1.subtract(rotateHandleApprox, reflectButton.coord);
42205
- const flipped = reflectButton.isFlipped() ? 1 : 0;
42206
- reflectPoints[flipped].setCoord(vector$1.add(reflectButton.coord, vector$1.rotateDeg(rotateVector, 90)));
42207
- reflectPoints[1 - flipped].setCoord(vector$1.add(reflectButton.coord, vector$1.rotateDeg(rotateVector, -90)));
42208
- reflectLine.transform(true);
42209
- reflectButton.update();
42210
- });
42211
- }
42212
- return {
42213
- remove: function () {
42214
- reflectButton.remove();
42215
- if (reflectRotateHandle) {
42216
- // @ts-expect-error - TS2339 - Property 'remove' does not exist on type 'never'.
42217
- reflectRotateHandle.remove();
42218
- }
42219
- reflectLine.remove();
42220
- reflectPoints[0].remove();
42221
- reflectPoints[1].remove();
42222
- }
42223
- };
42224
- };
42225
- /* Scales a distance from the default range of
42226
- * [-10, 10] to the current this.props.graph.range
42227
- *
42228
- * Used for sizing various transformation tools
42229
- * (rotation handle, dilation circle)
42230
- */
42231
- this.scaleToCurrentRange = dist => {
42232
- // eslint-disable-next-line react/no-string-refs
42233
- // @ts-expect-error - TS2339 - Property 'props' does not exist on type 'ReactInstance'.
42234
- return scaleToRange(dist, this.refs.graph.props.range);
42235
- };
42236
- this.addRotationTool = () => {
42237
- const options = this.props.tools.rotation;
42238
- if (!options.enabled) {
42239
- return;
42240
- }
42241
- const self = this;
42242
- // eslint-disable-next-line react/no-string-refs
42243
- // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
42244
- const graphie = this.refs.graph.graphie();
42245
- const pointColor = colorForTool(options);
42246
- // The center of our rotation, which can be moved to change the
42247
- // center of rotation
42248
- this.rotatePoint = graphie.addMovablePoint({
42249
- constraints: options.constraints,
42250
- coord: options.coord,
42251
- snapX: graphie.snap[0],
42252
- snapY: graphie.snap[1],
42253
- normalStyle: {
42254
- // ugh, this seems to be a global and
42255
- "stroke-dasharray": "",
42256
- // is set to dash above
42257
- stroke: pointColor,
42258
- fill: pointColor
42259
- },
42260
- highlightStyle: {
42261
- "stroke-dasharray": "",
42262
- stroke: KhanColors.INTERACTING,
42263
- fill: KhanColors.INTERACTING
42264
- }
42265
- });
42266
-
42267
- // The point that we move around the center of rotation to actually
42268
- // cause rotations
42269
- this.rotateHandle = graphie.addRotateHandle({
42270
- center: this.rotatePoint,
42271
- radius: this.scaleToCurrentRange(ROTATE_HANDLE_DIST),
42272
- width: this.scaleToCurrentRange(0.24),
42273
- hoverWidth: this.scaleToCurrentRange(0.4),
42274
- onMove: function (newAngle, oldAngle) {
42275
- const transform = self.getRotationTransformFromAngle(self.rotatePoint.coord, newAngle - oldAngle);
42276
-
42277
- // Rotate polygon with rotateHandle
42278
- self.doTransform(transform);
42279
-
42280
- // @ts-expect-error - TS2339 - Property 'angleDeg' does not exist on type 'PerseusTransformerTransformation'.
42281
- return oldAngle + transform.angleDeg;
42282
- }
42283
- });
42284
-
42285
- // Update tools.rotation.coord
42286
- this.rotatePoint.onMoveEnd = (x, y) => {
42287
- self.changeTool("rotation", {
42288
- coord: [x, y]
42289
- });
42290
- };
42291
- return {
42292
- remove: function () {
42293
- var _self$rotateHandle, _self$rotatePoint;
42294
- (_self$rotateHandle = self.rotateHandle) == null || _self$rotateHandle.remove();
42295
- (_self$rotatePoint = self.rotatePoint) == null || _self$rotatePoint.remove();
42296
- }
42297
- };
42298
- };
42299
- this.addDilationTool = () => {
42300
- const options = this.props.tools.dilation;
42301
- if (!options.enabled) {
42302
- return;
42303
- }
42304
- const self = this;
42305
- // eslint-disable-next-line react/no-string-refs
42306
- // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
42307
- const graphie = this.refs.graph.graphie();
42308
- const pointColor = colorForTool(options);
42309
- // the circle for causing dilation transforms
42310
- self.dilationCircle = graphie.addCircleGraph({
42311
- centerConstraints: options.constraints,
42312
- center: options.coord,
42313
- radius: self.scaleToCurrentRange(2),
42314
- snapX: graphie.snap[0],
42315
- snapY: graphie.snap[1],
42316
- minRadius: self.scaleToCurrentRange(1),
42317
- snapRadius: self.scaleToCurrentRange(0.5),
42318
- onResize: function (newRadius, oldRadius) {
42319
- // NOTE(kevinb): object is missing .constraints property
42320
- // @ts-expect-error - TS2345 - Argument of type '{ type: "dilation"; center: any; scale: number; }' is not assignable to parameter of type 'PerseusTransformerTransformation'.
42321
- self.doTransform({
42322
- type: "dilation",
42323
- center: self.dilationCircle.centerPoint.coord,
42324
- scale: newRadius / oldRadius
42325
- });
42326
- },
42327
- circleNormalStyle: {
42328
- stroke: pointColor,
42329
- "stroke-width": 2,
42330
- "stroke-dasharray": "- ",
42331
- "fill-opacity": 0
42332
- },
42333
- circleHighlightStyle: {
42334
- stroke: KhanColors.INTERACTING,
42335
- "stroke-width": 2,
42336
- "stroke-dasharray": "",
42337
- fill: KhanColors.INTERACTING,
42338
- "fill-opacity": 0.05
42339
- },
42340
- centerNormalStyle: {
42341
- stroke: pointColor,
42342
- fill: pointColor,
42343
- "stroke-width": 2,
42344
- "stroke-dasharray": ""
42345
- },
42346
- centerHighlightStyle: {
42347
- stroke: pointColor,
42348
- fill: pointColor,
42349
- "stroke-width": 2,
42350
- "stroke-dasharray": ""
42351
- }
42352
- });
42353
- const origOnMoveEnd = this.dilationCircle.centerPoint.onMoveEnd;
42354
- this.dilationCircle.centerPoint.onMoveEnd = (...args) => {
42355
- if (origOnMoveEnd) {
42356
- origOnMoveEnd(args);
42357
- }
42358
- self.changeTool("dilation", {
42359
- coord: self.dilationCircle.centerPoint.coord
42360
- });
42361
- };
42362
- return {
42363
- remove: function () {
42364
- var _self$dilationCircle;
42365
- (_self$dilationCircle = self.dilationCircle) == null || _self$dilationCircle.remove();
42366
- }
42367
- };
42368
- };
42369
- // returns a transformation object representing a rotation
42370
- // rounds the angle to the nearest 15 degrees
42371
- // @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'.
42372
- this.getRotationTransformFromAngle = (center, angleChanged) => {
42373
- angleChanged = (angleChanged + 360) % 360;
42374
- if (angleChanged > 180) {
42375
- angleChanged -= 360;
42376
- }
42377
- const roundedAngle = Math.round(angleChanged / ROTATE_SNAP_DEGREES) * ROTATE_SNAP_DEGREES;
42378
-
42379
- // NOTE(kevinb): return object is missing .constraints property
42380
- return {
42381
- type: "rotation",
42382
- center: center,
42383
- angleDeg: roundedAngle
42384
- };
42385
- };
42386
- // apply and save a transform
42387
- this.doTransform = (transform, callback) => {
42388
- this.applyTransform(transform);
42389
- this.addTransform(transform, callback);
42390
- };
42391
- // apply a transform to our polygon (without modifying our transformation
42392
- // list)
42393
- this.applyTransform = transform => {
42394
- if (this.props.graphMode !== "static") {
42395
- const transformFunc = TransformOps.apply(transform);
42396
- // @ts-expect-error - TS2345 - Argument of type '(coord: any) => any' is not assignable to parameter of type 'PerseusTransformerTransformation'.
42397
- this.applyCoordTransformation(transformFunc);
42398
- }
42399
- };
42400
- // transform our polygon by transforming each point using a given function
42401
- this.applyCoordTransformation = pointTransform => {
42402
- var _this$shape3;
42403
- _.each(this.shape.points, function (point) {
42404
- // @ts-expect-error - TS2349 - This expression is not callable.
42405
- const newCoord = pointTransform(point.coord);
42406
- point.setCoord(newCoord);
42407
- });
42408
- (_this$shape3 = this.shape) == null || _this$shape3.update();
42409
- };
42410
- this.resetCoords = () => {
42411
- var _this$shape4;
42412
- const startCoords = this.props.starting.shape.coords;
42413
- _.each(this.shape.points, function (point, i) {
42414
- point.setCoord(startCoords[i]);
42415
- });
42416
- (_this$shape4 = this.shape) == null || _this$shape4.update();
42417
- };
42418
- // Remove the last transformation
42419
- this.handleUndoClick = () => {
42420
- // eslint-disable-next-line react/no-string-refs
42421
- // @ts-expect-error - TS2339 - Property 'changeSelected' does not exist on type 'ReactInstance'.
42422
- this.refs.toolsBar.changeSelected(null);
42423
- if (this.props.transformations.length) {
42424
- this.props.onChange({
42425
- transformations: _.initial(this.props.transformations)
42426
- });
42427
- }
42428
- };
42429
- this.setTransformationProps = (newTransfomationList, callback) => {
42430
- this.props.onChange({
42431
- transformations: newTransfomationList
42432
- },
42433
- // @ts-expect-error - TS2345 - Argument of type '(arg1: any) => unknown' is not assignable to parameter of type '() => unknown'.
42434
- callback);
42435
- };
42436
- // add a transformation to our props list of transformation
42437
- this.addTransform = (transform, callback) => {
42438
- this.transformations = TransformOps.append(this.transformations, transform);
42439
- this.props.onChange({
42440
- transformations: _.clone(this.transformations)
42441
- },
42442
- // @ts-expect-error - TS2345 - Argument of type '((arg1: any) => unknown) | undefined' is not assignable to parameter of type '(() => unknown) | undefined'.
42443
- callback);
42444
- };
42445
- this.changeTool = (tool, changes) => {
42446
- const newTools = _.clone(this.props.tools);
42447
- newTools[tool] = _.extend({}, this.props.tools[tool], changes);
42448
- this.tools[tool] = _.clone(newTools[tool]);
42449
- this.props.onChange({
42450
- // @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.
42451
- tools: newTools
42452
- });
42453
- this.props.trackInteraction();
42454
- };
42455
- this.simpleValidate = rubric => {
42456
- return Transformer.validate(this.getUserInput(), rubric);
42457
- };
42458
- /**
42459
- * Calculate where the coordinates would be if they were
42460
- * moved, even if we're in formal mode with no movement
42461
- * (and thus the actual movablepoints may not have moved
42462
- */
42463
- // @ts-expect-error - TS2322 - Type '() => [Coord, Coord] | readonly Coord[] | [Coord, Coord, Coord]' is not assignable to type '() => Coord'.
42464
- this.getCoords = () => {
42465
- const startCoords = this.props.starting.shape.coords;
42466
- const transforms = this.props.transformations;
42467
- return _.reduce(transforms, function (coords, transform) {
42468
- return _.map(coords, TransformOps.apply(transform));
42469
- }, startCoords);
42470
- };
42471
- this.getEditorJSON = () => {
42472
- const json = _.pick(this.props, "grading", "starting", "graphMode", "listMode", "tools", "drawSolutionShape", "gradeEmpty");
42473
- // eslint-disable-next-line react/no-string-refs
42474
- // @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'.
42475
- json.graph = this.refs.graph.toJSON();
42476
- // @ts-expect-error - TS2339 - Property 'version' does not exist on type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "tools" | "starting" | "drawSolutionShape" | "gradeEmpty" | "graphMode" | "listMode">'.
42477
- json.version = 1.2; // Give us some safety to change the format
42478
- // when we realize that I wrote
42479
- // a horrible json spec for this widget
42480
-
42481
- // @ts-expect-error - TS2339 - Property 'answer' does not exist on type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "tools" | "starting" | "drawSolutionShape" | "gradeEmpty" | "graphMode" | "listMode">'.
42482
- json.answer = this.getUserInput();
42483
- return json;
42484
- };
42485
- this.getUserInput = () => {
42486
- var _this$shape5;
42487
- return {
42488
- transformations: this.props.transformations,
42489
- // This doesn't call this.shape.toJSON() because that doesn't
42490
- // handle coordinates in formal mode without movement, since
42491
- // the movablepoints never move
42492
- shape: {
42493
- type: this.shape.type,
42494
- coords: this.getCoords(),
42495
- options: (_this$shape5 = this.shape) == null ? void 0 : _this$shape5.getOptions()
42496
- }
42497
- };
42498
- };
42499
- /* InputPath API */
42500
- this._handleFocus = (...path) => {
42501
- this.props.onFocus(path);
42502
- };
42503
- this._handleBlur = (...path) => {
42504
- this.props.onBlur(path);
42505
- };
42506
- this._getTransformationForID = transformationID => {
42507
- // Returns the 'transformation' component corresponding to a given ID
42508
- const refPath = ["transformationList", "transformation" + transformationID];
42509
-
42510
- // Follow the path of references
42511
- let component = this;
42512
- _.each(refPath, ref => {
42513
- // @ts-expect-error - TS2322 - Type 'ReactInstance' is not assignable to type 'this'.
42514
- component = component.refs[ref];
42515
- });
42516
- return component;
42517
- };
42518
- this.getInputPaths = () => {
42519
- // If we're in static mode, then there is no transformation list, and,
42520
- // as a result, no input paths.
42521
- if (this.props.listMode === "static") {
42522
- return [];
42523
- }
42524
- let inputPaths = [];
42525
- _.each(this.props.transformations, (transformation, i) => {
42526
- // @ts-expect-error - TS2345 - Argument of type 'number' is not assignable to parameter of type 'string'.
42527
- transformation = this._getTransformationForID(i);
42528
- // @ts-expect-error - TS2339 - Property 'getInputPaths' does not exist on type 'PerseusTransformerTransformation'.
42529
- const innerPaths = transformation.getInputPaths();
42530
- const fullPaths = _.map(innerPaths, innerPath => {
42531
- return ["" + i].concat(innerPath);
42532
- });
42533
- // @ts-expect-error - TS2345 - Argument of type 'string[][]' is not assignable to parameter of type 'ConcatArray<never>'.
42534
- inputPaths = inputPaths.concat(fullPaths);
42535
- });
42536
- return inputPaths;
42537
- };
42538
- this._passToInner = function (functionName, path, ...args) {
42539
- if (!path || !path.length) {
42540
- return;
42541
- }
42542
-
42543
- // First argument tells us which transformation will receive the call;
42544
- // remaining arguments are used within that transformation to identify
42545
- // a specific input.
42546
- const innerPath = _.rest(path);
42547
-
42548
- // Pass arguments down to appropriate 'transformation' component
42549
- const transformationID = _.head(path);
42550
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
42551
- const caller = this._getTransformationForID(transformationID);
42552
- return caller[functionName](innerPath, ...args);
42553
- };
42554
- this.focus = () => {
42555
- // Just focus the first showing input
42556
- const inputs = this.getInputPaths();
42557
- if (inputs.length > 0) {
42558
- this.focusInputPath(inputs[0]);
42559
- return true;
42560
- }
42561
- return false;
42562
- };
42563
- this.focusInputPath = path => {
42564
- // Since the transformer exposes the input API, it needs to be robust
42565
- // to empty paths. We don't expect this to happen, as entire-widget
42566
- // focusing is typically done through the focus() method, which already
42567
- // handles the empty path case properly, but it's better to be safe
42568
- // here.
42569
- if (path.length === 0) {
42570
- return false;
42571
- }
42572
- assert(path.length >= 2);
42573
- return this._passToInner("focusInputPath", path);
42574
- };
42575
- this.blurInputPath = path => {
42576
- // Since the transformer exposes the input API, it needs to be robust
42577
- // to empty paths (which indicate a blurring of the entire widget,
42578
- // e.g., when switching from interacting with the transformer to
42579
- // interacting with some other widget).
42580
- if (path.length === 0) {
42581
- return false;
42582
- }
42583
- assert(path.length >= 2);
42584
- return this._passToInner("blurInputPath", path);
42585
- };
42586
- this.setInputValue = (path, value, cb) => {
42587
- assert(path.length >= 2);
42588
- return this._passToInner("setInputValue", path, value, cb);
42589
- };
42590
- this.getDOMNodeForPath = path => {
42591
- assert(path.length >= 2);
42592
- return this._passToInner("getDOMNodeForPath", path);
42593
- };
42594
- this.getGrammarTypeForPath = path => {
42595
- assert(path.length >= 2);
42596
- return this._passToInner("getGrammarTypeForPath", path);
42597
- };
42598
- }
42599
- render() {
42600
- // Fill in any missing value in this.props.graph
42601
- // this can happen because the graph json doesn't include
42602
- // box, for example
42603
- const graph = _.extend(defaultGraphProps(this.props.graph, defaultBoxSize), this.props.graph);
42604
- const interactiveToolsMode = this.props.graphMode === "interactive";
42605
- const ToolsBarClass = interactiveToolsMode ? ToolsBar : AddTransformBar;
42606
-
42607
- // This style is applied inline because it is dependent on the
42608
- // size of the graph as set by the graph.box prop, and this also
42609
- // lets us specify it in the same place the graph's width is
42610
- // specified.
42611
- const toolsBar = /*#__PURE__*/React.createElement("div", {
42612
- style: {
42613
- width: graph.box[0]
42614
- }
42615
- }, /*#__PURE__*/React.createElement(ToolsBarClass
42616
- // eslint-disable-next-line react/no-string-refs
42617
- , {
42618
- ref: "toolsBar",
42619
- enabled: pluckObject(this.props.tools, "enabled"),
42620
- apiOptions: this.props.apiOptions,
42621
- addTool: this.addTool,
42622
- removeTool: this.removeTool,
42623
- onUndoClick: this.handleUndoClick
42624
- }));
42625
- return /*#__PURE__*/React.createElement("div", {
42626
- className: "perseus-widget " + "perseus-widget-transformer"
42627
- }, /*#__PURE__*/React.createElement(Graph
42628
- // eslint-disable-next-line react/no-string-refs
42629
- , {
42630
- ref: "graph",
42631
- box: graph.box,
42632
- range: graph.range,
42633
- labels: graph.labels,
42634
- step: graph.step,
42635
- gridStep: graph.gridStep,
42636
- markings: graph.markings,
42637
- backgroundImage: graph.backgroundImage,
42638
- showProtractor: graph.showProtractor,
42639
- onGraphieUpdated: this.setupGraphie,
42640
- setDrawingAreaAvailable: this.props.apiOptions.setDrawingAreaAvailable
42641
- }), !interactiveToolsMode && i18n._("Add transformations below:"), this.props.graphMode === "static" && [/*#__PURE__*/React.createElement("br", {
42642
- key: "static-br"
42643
- }), /*#__PURE__*/React.createElement("em", {
42644
- key: "static-nomove"
42645
- }, " ", i18n._("Note: For this question, the shape will not move."), " ")], interactiveToolsMode && toolsBar, /*#__PURE__*/React.createElement(TransformationList
42646
- // eslint-disable-next-line react/no-string-refs
42647
- , {
42648
- ref: "transformationList",
42649
- mode: this.props.listMode,
42650
- transformations: this.props.transformations,
42651
- onChange: this.setTransformationProps,
42652
- onFocus: this._handleFocus,
42653
- onBlur: this._handleBlur,
42654
- keypadElement: this.props.keypadElement,
42655
- apiOptions: this.props.apiOptions
42656
- }), !interactiveToolsMode && toolsBar);
42657
- }
42658
- componentDidMount() {
42659
- this.setupGraphie(this.graphie());
42660
- }
42661
- componentDidUpdate(prevProps) {
42662
- if (this.shouldSetupGraphie(this.props, prevProps)) {
42663
- // eslint-disable-next-line react/no-string-refs
42664
- // @ts-expect-error - TS2339 - Property 'reset' does not exist on type 'ReactInstance'.
42665
- this.refs.graph.reset();
42666
- } else if (!deepEq(this.props.transformations, this.transformations)) {
42667
- this.setTransformations(this.props.transformations);
42668
- }
42669
- }
42670
- static validate(guess, rubric) {
42671
- // Check for any required transformations
42672
- for (const type in Transformations) {
42673
- if (rubric.tools[type].required) {
42674
- const isUsed = _.any(_.map(guess.transformations, function (transform) {
42675
- // Required transformations must appear in the
42676
- // transformation list, and must not be no-ops
42677
- return transform.type === type && !TransformOps.isEmpty(transform) && !TransformOps.isNoOp(transform);
42678
- }));
42679
- if (!isUsed) {
42680
- return {
42681
- type: "invalid",
42682
- message: i18n._("Your transformation must use a " + "%(type)s.", {
42683
- type: Transformations[type].lowerNounName
42684
- })
42685
- };
42686
- }
42687
- }
42688
- }
42689
-
42690
- // Compare shapes
42691
- if (ShapeTypes.equal(guess.shape, rubric.correct.shape)) {
42692
- return {
42693
- type: "points",
42694
- earned: 1,
42695
- total: 1,
42696
- message: null
42697
- };
42698
- }
42699
- if (!rubric.gradeEmpty && deepEq(guess.shape.coords, rubric.starting.shape.coords)) {
42700
- return {
42701
- type: "invalid",
42702
- message: i18n._("Use the interactive graph to define a " + "correct transformation.")
42703
- };
42704
- }
42705
- return {
42706
- type: "points",
42707
- earned: 0,
42708
- total: 1,
42709
- message: null
42710
- };
42711
- }
42712
- }
42713
- Transformer.defaultProps = _.defaults({
42714
- transformations: []
42715
- }, defaultTransformerProps);
42716
- var Transformer$1 = {
42717
- name: "transformer",
42718
- displayName: "Transformer (deprecated)",
42719
- hidden: true,
42720
- widget: Transformer
42721
- };
42722
-
42723
40309
  var hubble = {exports: {}};
42724
40310
 
42725
40311
  (function (module, exports) {
@@ -43942,7 +41528,7 @@ var Video$1 = {
43942
41528
  };
43943
41529
 
43944
41530
  /* eslint-disable import/no-named-as-default */
43945
- 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];
43946
41532
 
43947
41533
  /**
43948
41534
  * This should be called by all clients, specifying whether extra widgets are
@@ -43986,7 +41572,7 @@ const init = function init(options) {
43986
41572
 
43987
41573
  // This file is processed by a Rollup plugin (replace) to inject the production
43988
41574
  const libName = "@khanacademy/perseus";
43989
- const libVersion = "18.0.1";
41575
+ const libVersion = "19.0.0";
43990
41576
  addLibraryVersionToPerseusDebug(libName, libVersion);
43991
41577
 
43992
41578
  const apiVersion = {
@@ -45432,8 +43018,6 @@ const ItemExtras = [
45432
43018
 
45433
43019
  // prettier-ignore
45434
43020
 
45435
- // prettier-ignore
45436
-
45437
43021
  //prettier-ignore
45438
43022
 
45439
43023
  // A background image applied to various widgets.
@@ -46566,10 +44150,7 @@ function violatingWidgets(itemData) {
46566
44150
  */
46567
44151
  const registerAllWidgetsForTesting = () => {
46568
44152
  registerWidgets(allWidgets);
46569
-
46570
- // When it's time to replace the transformer widget
46571
- // uncomment this line
46572
- // Widgets.replaceWidget("transformer", "deprecated-standin");
44153
+ replaceWidget("transformer", "deprecated-standin");
46573
44154
  };
46574
44155
 
46575
44156
  /**
@@ -46997,5 +44578,5 @@ const EditorJsonify = {
46997
44578
  }
46998
44579
  };
46999
44580
 
47000
- 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 };
47001
44582
  //# sourceMappingURL=index.js.map