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