@khanacademy/perseus 35.0.0 → 35.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.js +756 -615
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +756 -613
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +2 -0
- package/dist/perseus-types.d.ts +7 -0
- package/dist/server-item-renderer.d.ts +2 -0
- package/dist/types.d.ts +3 -4
- package/dist/validation.types.d.ts +5 -2
- package/dist/widgets/cs-program/cs-program-validator.d.ts +3 -0
- package/dist/widgets/cs-program/cs-program.d.ts +2 -2
- package/dist/widgets/expression/expression.d.ts +14 -2
- package/dist/widgets/grapher/grapher-validator.d.ts +4 -0
- package/dist/widgets/grapher/util.d.ts +0 -2
- package/dist/widgets/iframe/iframe-validator.d.ts +3 -0
- package/dist/widgets/iframe/iframe.d.ts +3 -2
- package/dist/widgets/interactive-graph.d.ts +3 -3
- package/dist/widgets/interactive-graphs/backgrounds/axis-ticks.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graph-locked-layer.d.ts +2 -0
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +7 -0
- package/dist/widgets/interactive-graphs/interactive-graph-validator.d.ts +4 -0
- package/dist/widgets/interactive-graphs/locked-figures/locked-ellipse.d.ts +5 -1
- package/dist/widgets/interactive-graphs/locked-figures/locked-function.d.ts +5 -1
- package/dist/widgets/interactive-graphs/locked-figures/locked-line.d.ts +2 -0
- package/dist/widgets/interactive-graphs/locked-figures/locked-point.d.ts +5 -1
- package/dist/widgets/interactive-graphs/locked-figures/locked-polygon.d.ts +5 -1
- package/dist/widgets/interactive-graphs/locked-figures/locked-vector.d.ts +5 -1
- package/dist/widgets/interactive-graphs/mafs-state-to-interactive-graph.d.ts +3 -0
- package/dist/widgets/label-image/label-image.d.ts +4 -0
- package/dist/widgets/matcher/matcher-validator.d.ts +4 -0
- package/dist/widgets/matcher/matcher.d.ts +3 -2
- package/dist/widgets/matrix/matrix-validator.d.ts +5 -0
- package/dist/widgets/matrix/matrix.d.ts +1 -0
- package/dist/widgets/number-line/number-line-validator.d.ts +4 -0
- package/dist/widgets/number-line/number-line.d.ts +3 -2
- package/dist/widgets/plotter/plotter-validator.d.ts +4 -0
- package/dist/widgets/plotter/plotter.d.ts +1 -0
- package/dist/widgets/table/table-validator.d.ts +5 -0
- package/dist/widgets/table/table.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3254,7 +3254,7 @@ function constrainedTickStepsFromTickSteps(tickSteps, ranges) {
|
|
|
3254
3254
|
/**
|
|
3255
3255
|
* Approximate equality on numbers and primitives.
|
|
3256
3256
|
*/
|
|
3257
|
-
function eq$
|
|
3257
|
+
function eq$4(x, y) {
|
|
3258
3258
|
if (typeof x === "number" && typeof y === "number") {
|
|
3259
3259
|
return Math.abs(x - y) < 1e-9;
|
|
3260
3260
|
}
|
|
@@ -3281,7 +3281,7 @@ function deepEq$5(x, y) {
|
|
|
3281
3281
|
return false;
|
|
3282
3282
|
}
|
|
3283
3283
|
if (typeof x === "function" && typeof y === "function") {
|
|
3284
|
-
return eq$
|
|
3284
|
+
return eq$4(x, y);
|
|
3285
3285
|
}
|
|
3286
3286
|
if (typeof x === "function" || typeof y === "function") {
|
|
3287
3287
|
return false;
|
|
@@ -3298,7 +3298,7 @@ function deepEq$5(x, y) {
|
|
|
3298
3298
|
if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
|
|
3299
3299
|
return false;
|
|
3300
3300
|
}
|
|
3301
|
-
return eq$
|
|
3301
|
+
return eq$4(x, y);
|
|
3302
3302
|
}
|
|
3303
3303
|
|
|
3304
3304
|
/**
|
|
@@ -3596,7 +3596,7 @@ const Util = {
|
|
|
3596
3596
|
gridStepFromTickStep,
|
|
3597
3597
|
tickStepFromNumTicks,
|
|
3598
3598
|
constrainedTickStepsFromTickSteps,
|
|
3599
|
-
eq: eq$
|
|
3599
|
+
eq: eq$4,
|
|
3600
3600
|
deepEq: deepEq$5,
|
|
3601
3601
|
parseQueryString,
|
|
3602
3602
|
updateQueryString: updateQueryString$2,
|
|
@@ -10921,7 +10921,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
10921
10921
|
* A collection of geomtry-related utility functions
|
|
10922
10922
|
*/
|
|
10923
10923
|
const {
|
|
10924
|
-
eq: eq$
|
|
10924
|
+
eq: eq$3,
|
|
10925
10925
|
deepEq: deepEq$4
|
|
10926
10926
|
} = Util;
|
|
10927
10927
|
|
|
@@ -10931,7 +10931,7 @@ const {
|
|
|
10931
10931
|
|
|
10932
10932
|
// Given a number, return whether it is positive (1), negative (-1), or zero (0)
|
|
10933
10933
|
function sign(val) {
|
|
10934
|
-
if (eq$
|
|
10934
|
+
if (eq$3(val, 0)) {
|
|
10935
10935
|
return 0;
|
|
10936
10936
|
}
|
|
10937
10937
|
return val > 0 ? 1 : -1;
|
|
@@ -10943,7 +10943,7 @@ function ccw(a, b, c) {
|
|
|
10943
10943
|
return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
|
|
10944
10944
|
}
|
|
10945
10945
|
function collinear(a, b, c) {
|
|
10946
|
-
return eq$
|
|
10946
|
+
return eq$3(ccw(a, b, c), 0);
|
|
10947
10947
|
}
|
|
10948
10948
|
|
|
10949
10949
|
// Given rect bounding points A and B, whether point C is inside the rect
|
|
@@ -11088,7 +11088,7 @@ function similar(coords1, coords2, tolerance) {
|
|
|
11088
11088
|
return pair[0] / pair[1];
|
|
11089
11089
|
});
|
|
11090
11090
|
const same = ___default["default"].all(factors, function (factor) {
|
|
11091
|
-
return eq$
|
|
11091
|
+
return eq$3(factors[0], factor);
|
|
11092
11092
|
});
|
|
11093
11093
|
const congruentEnough = ___default["default"].all(sidePairs, function (pair) {
|
|
11094
11094
|
return kmath.number.equal(pair[0], pair[1], tolerance);
|
|
@@ -11141,7 +11141,7 @@ function rotate(array, n) {
|
|
|
11141
11141
|
return array.slice(n).concat(array.slice(0, n));
|
|
11142
11142
|
}
|
|
11143
11143
|
function getLineEquation(first, second) {
|
|
11144
|
-
if (eq$
|
|
11144
|
+
if (eq$3(first[0], second[0])) {
|
|
11145
11145
|
return "x = " + first[0].toFixed(3);
|
|
11146
11146
|
}
|
|
11147
11147
|
const m = (second[1] - first[1]) / (second[0] - first[0]);
|
|
@@ -24914,6 +24914,31 @@ const parseUrl = url => new URL(url, location().origin);
|
|
|
24914
24914
|
*/
|
|
24915
24915
|
const toAbsoluteUrl = url => parseUrl(url).href;
|
|
24916
24916
|
|
|
24917
|
+
function csProgramValidator(state) {
|
|
24918
|
+
// The iframe can tell us whether it's correct or incorrect,
|
|
24919
|
+
// and pass an optional message
|
|
24920
|
+
if (state.status === "correct") {
|
|
24921
|
+
return {
|
|
24922
|
+
type: "points",
|
|
24923
|
+
earned: 1,
|
|
24924
|
+
total: 1,
|
|
24925
|
+
message: state.message || null
|
|
24926
|
+
};
|
|
24927
|
+
}
|
|
24928
|
+
if (state.status === "incorrect") {
|
|
24929
|
+
return {
|
|
24930
|
+
type: "points",
|
|
24931
|
+
earned: 0,
|
|
24932
|
+
total: 1,
|
|
24933
|
+
message: state.message || null
|
|
24934
|
+
};
|
|
24935
|
+
}
|
|
24936
|
+
return {
|
|
24937
|
+
type: "invalid",
|
|
24938
|
+
message: "Keep going, you're not there yet!"
|
|
24939
|
+
};
|
|
24940
|
+
}
|
|
24941
|
+
|
|
24917
24942
|
/**
|
|
24918
24943
|
* This widget is for embedding Khan Academy CS programs.
|
|
24919
24944
|
*/
|
|
@@ -24946,29 +24971,8 @@ class CSProgram extends React__namespace.Component {
|
|
|
24946
24971
|
};
|
|
24947
24972
|
|
|
24948
24973
|
// The widget's grading function
|
|
24949
|
-
static validate(state
|
|
24950
|
-
|
|
24951
|
-
// and pass an optional message
|
|
24952
|
-
if (state.status === "correct") {
|
|
24953
|
-
return {
|
|
24954
|
-
type: "points",
|
|
24955
|
-
earned: 1,
|
|
24956
|
-
total: 1,
|
|
24957
|
-
message: state.message || null
|
|
24958
|
-
};
|
|
24959
|
-
}
|
|
24960
|
-
if (state.status === "incorrect") {
|
|
24961
|
-
return {
|
|
24962
|
-
type: "points",
|
|
24963
|
-
earned: 0,
|
|
24964
|
-
total: 1,
|
|
24965
|
-
message: state.message || null
|
|
24966
|
-
};
|
|
24967
|
-
}
|
|
24968
|
-
return {
|
|
24969
|
-
type: "invalid",
|
|
24970
|
-
message: "Keep going, you're not there yet!"
|
|
24971
|
-
};
|
|
24974
|
+
static validate(state) {
|
|
24975
|
+
return csProgramValidator(state);
|
|
24972
24976
|
}
|
|
24973
24977
|
componentDidMount() {
|
|
24974
24978
|
$__default["default"](window).on("message", this.handleMessageEvent);
|
|
@@ -25005,11 +25009,11 @@ class CSProgram extends React__namespace.Component {
|
|
|
25005
25009
|
return change.apply(_this, args);
|
|
25006
25010
|
};
|
|
25007
25011
|
})();
|
|
25008
|
-
simpleValidate(
|
|
25009
|
-
return
|
|
25012
|
+
simpleValidate() {
|
|
25013
|
+
return csProgramValidator({
|
|
25010
25014
|
status: this.props.status,
|
|
25011
25015
|
message: this.props.message
|
|
25012
|
-
}
|
|
25016
|
+
});
|
|
25013
25017
|
}
|
|
25014
25018
|
render() {
|
|
25015
25019
|
if (!this.props.programID) {
|
|
@@ -26612,49 +26616,6 @@ function functionForType(
|
|
|
26612
26616
|
type) {
|
|
26613
26617
|
return functionTypeMapping[type];
|
|
26614
26618
|
}
|
|
26615
|
-
const validate$1 = (state, rubric) => {
|
|
26616
|
-
if (state.type !== rubric.correct.type) {
|
|
26617
|
-
return {
|
|
26618
|
-
type: "points",
|
|
26619
|
-
earned: 0,
|
|
26620
|
-
total: 1,
|
|
26621
|
-
message: null
|
|
26622
|
-
};
|
|
26623
|
-
}
|
|
26624
|
-
|
|
26625
|
-
// We haven't moved the coords
|
|
26626
|
-
if (state.coords == null) {
|
|
26627
|
-
return {
|
|
26628
|
-
type: "invalid",
|
|
26629
|
-
message: null
|
|
26630
|
-
};
|
|
26631
|
-
}
|
|
26632
|
-
|
|
26633
|
-
// Get new function handler for grading
|
|
26634
|
-
const grader = functionForType(state.type);
|
|
26635
|
-
const guessCoeffs = grader.getCoefficients(state.coords, state.asymptote);
|
|
26636
|
-
const correctCoeffs = grader.getCoefficients(rubric.correct.coords, rubric.correct.asymptote);
|
|
26637
|
-
if (guessCoeffs == null || correctCoeffs == null) {
|
|
26638
|
-
return {
|
|
26639
|
-
type: "invalid",
|
|
26640
|
-
message: null
|
|
26641
|
-
};
|
|
26642
|
-
}
|
|
26643
|
-
if (grader.areEqual(guessCoeffs, correctCoeffs)) {
|
|
26644
|
-
return {
|
|
26645
|
-
type: "points",
|
|
26646
|
-
earned: 1,
|
|
26647
|
-
total: 1,
|
|
26648
|
-
message: null
|
|
26649
|
-
};
|
|
26650
|
-
}
|
|
26651
|
-
return {
|
|
26652
|
-
type: "points",
|
|
26653
|
-
earned: 0,
|
|
26654
|
-
total: 1,
|
|
26655
|
-
message: null
|
|
26656
|
-
};
|
|
26657
|
-
};
|
|
26658
26619
|
const getEquationString = props => {
|
|
26659
26620
|
const plot = props.plot;
|
|
26660
26621
|
if (plot.type && plot.coords) {
|
|
@@ -26772,7 +26733,6 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
26772
26733
|
DEFAULT_BACKGROUND_IMAGE: DEFAULT_BACKGROUND_IMAGE,
|
|
26773
26734
|
allTypes: allTypes,
|
|
26774
26735
|
functionForType: functionForType,
|
|
26775
|
-
validate: validate$1,
|
|
26776
26736
|
getEquationString: getEquationString,
|
|
26777
26737
|
pointsFromNormalized: pointsFromNormalized,
|
|
26778
26738
|
maybePointsFromNormalized: maybePointsFromNormalized,
|
|
@@ -26783,6 +26743,52 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
26783
26743
|
typeToButton: typeToButton
|
|
26784
26744
|
});
|
|
26785
26745
|
|
|
26746
|
+
function grapherValidator(state, rubric) {
|
|
26747
|
+
if (state.type !== rubric.correct.type) {
|
|
26748
|
+
return {
|
|
26749
|
+
type: "points",
|
|
26750
|
+
earned: 0,
|
|
26751
|
+
total: 1,
|
|
26752
|
+
message: null
|
|
26753
|
+
};
|
|
26754
|
+
}
|
|
26755
|
+
|
|
26756
|
+
// We haven't moved the coords
|
|
26757
|
+
if (state.coords == null) {
|
|
26758
|
+
return {
|
|
26759
|
+
type: "invalid",
|
|
26760
|
+
message: null
|
|
26761
|
+
};
|
|
26762
|
+
}
|
|
26763
|
+
|
|
26764
|
+
// Get new function handler for grading
|
|
26765
|
+
const grader = functionForType(state.type);
|
|
26766
|
+
const guessCoeffs = grader.getCoefficients(state.coords, state.asymptote);
|
|
26767
|
+
const correctCoeffs = grader.getCoefficients(rubric.correct.coords,
|
|
26768
|
+
// @ts-expect-error - TS(2339) - Property 'asymptote' does not exist on type '{ type: "absolute_value"; coords: [Coord, Coord]; }'
|
|
26769
|
+
rubric.correct.asymptote);
|
|
26770
|
+
if (guessCoeffs == null || correctCoeffs == null) {
|
|
26771
|
+
return {
|
|
26772
|
+
type: "invalid",
|
|
26773
|
+
message: null
|
|
26774
|
+
};
|
|
26775
|
+
}
|
|
26776
|
+
if (grader.areEqual(guessCoeffs, correctCoeffs)) {
|
|
26777
|
+
return {
|
|
26778
|
+
type: "points",
|
|
26779
|
+
earned: 1,
|
|
26780
|
+
total: 1,
|
|
26781
|
+
message: null
|
|
26782
|
+
};
|
|
26783
|
+
}
|
|
26784
|
+
return {
|
|
26785
|
+
type: "points",
|
|
26786
|
+
earned: 0,
|
|
26787
|
+
total: 1,
|
|
26788
|
+
message: null
|
|
26789
|
+
};
|
|
26790
|
+
}
|
|
26791
|
+
|
|
26786
26792
|
// @ts-expect-error - TS2339 - Property 'MovablePoint' does not exist on type 'typeof Graphie'.
|
|
26787
26793
|
const MovablePoint$3 = Graphie.MovablePoint;
|
|
26788
26794
|
// @ts-expect-error - TS2339 - Property 'MovableLine' does not exist on type 'typeof Graphie'.
|
|
@@ -26986,7 +26992,7 @@ class Grapher extends React__namespace.Component {
|
|
|
26986
26992
|
vertHairline;
|
|
26987
26993
|
static defaultProps = DEFAULT_GRAPHER_PROPS;
|
|
26988
26994
|
static validate(state, rubric) {
|
|
26989
|
-
return
|
|
26995
|
+
return grapherValidator(state, rubric);
|
|
26990
26996
|
}
|
|
26991
26997
|
static getUserInputFromProps(props) {
|
|
26992
26998
|
return props.plot;
|
|
@@ -27091,7 +27097,7 @@ class Grapher extends React__namespace.Component {
|
|
|
27091
27097
|
}
|
|
27092
27098
|
};
|
|
27093
27099
|
simpleValidate(rubric) {
|
|
27094
|
-
return
|
|
27100
|
+
return grapherValidator(this.getUserInput(), rubric);
|
|
27095
27101
|
}
|
|
27096
27102
|
getUserInput() {
|
|
27097
27103
|
return Grapher.getUserInputFromProps(this.props);
|
|
@@ -27334,6 +27340,31 @@ var Group$1 = {
|
|
|
27334
27340
|
isLintable: true
|
|
27335
27341
|
};
|
|
27336
27342
|
|
|
27343
|
+
function iframeValidator(state) {
|
|
27344
|
+
// The iframe can tell us whether it's correct or incorrect,
|
|
27345
|
+
// and pass an optional message
|
|
27346
|
+
if (state.status === "correct") {
|
|
27347
|
+
return {
|
|
27348
|
+
type: "points",
|
|
27349
|
+
earned: 1,
|
|
27350
|
+
total: 1,
|
|
27351
|
+
message: state.message || null
|
|
27352
|
+
};
|
|
27353
|
+
}
|
|
27354
|
+
if (state.status === "incorrect") {
|
|
27355
|
+
return {
|
|
27356
|
+
type: "points",
|
|
27357
|
+
earned: 0,
|
|
27358
|
+
total: 1,
|
|
27359
|
+
message: state.message || null
|
|
27360
|
+
};
|
|
27361
|
+
}
|
|
27362
|
+
return {
|
|
27363
|
+
type: "invalid",
|
|
27364
|
+
message: "Keep going, you're not there yet!"
|
|
27365
|
+
};
|
|
27366
|
+
}
|
|
27367
|
+
|
|
27337
27368
|
/**
|
|
27338
27369
|
* This is an iframe widget. It is used for rendering an iframe that
|
|
27339
27370
|
* then communicates its state via window.postMessage
|
|
@@ -27354,6 +27385,9 @@ class Iframe extends React__namespace.Component {
|
|
|
27354
27385
|
allowFullScreen: false,
|
|
27355
27386
|
allowTopNavigation: false
|
|
27356
27387
|
};
|
|
27388
|
+
static validate(state) {
|
|
27389
|
+
return iframeValidator(state);
|
|
27390
|
+
}
|
|
27357
27391
|
componentDidMount() {
|
|
27358
27392
|
$__default["default"](window).on("message", this.handleMessageEvent);
|
|
27359
27393
|
}
|
|
@@ -27395,10 +27429,9 @@ class Iframe extends React__namespace.Component {
|
|
|
27395
27429
|
return change.apply(_this, args);
|
|
27396
27430
|
};
|
|
27397
27431
|
})();
|
|
27398
|
-
simpleValidate
|
|
27399
|
-
|
|
27400
|
-
|
|
27401
|
-
};
|
|
27432
|
+
simpleValidate() {
|
|
27433
|
+
return iframeValidator(this.getUserInput());
|
|
27434
|
+
}
|
|
27402
27435
|
render() {
|
|
27403
27436
|
const style = {
|
|
27404
27437
|
width: String(this.props.width),
|
|
@@ -27461,36 +27494,6 @@ class Iframe extends React__namespace.Component {
|
|
|
27461
27494
|
});
|
|
27462
27495
|
}
|
|
27463
27496
|
}
|
|
27464
|
-
|
|
27465
|
-
/**
|
|
27466
|
-
* This is the widget's grading function
|
|
27467
|
-
*/
|
|
27468
|
-
___default["default"].extend(Iframe, {
|
|
27469
|
-
validate: function (state, rubric) {
|
|
27470
|
-
// The iframe can tell us whether it's correct or incorrect,
|
|
27471
|
-
// and pass an optional message
|
|
27472
|
-
if (state.status === "correct") {
|
|
27473
|
-
return {
|
|
27474
|
-
type: "points",
|
|
27475
|
-
earned: 1,
|
|
27476
|
-
total: 1,
|
|
27477
|
-
message: state.message || null
|
|
27478
|
-
};
|
|
27479
|
-
}
|
|
27480
|
-
if (state.status === "incorrect") {
|
|
27481
|
-
return {
|
|
27482
|
-
type: "points",
|
|
27483
|
-
earned: 0,
|
|
27484
|
-
total: 1,
|
|
27485
|
-
message: state.message || null
|
|
27486
|
-
};
|
|
27487
|
-
}
|
|
27488
|
-
return {
|
|
27489
|
-
type: "invalid",
|
|
27490
|
-
message: "Keep going, you're not there yet!"
|
|
27491
|
-
};
|
|
27492
|
-
}
|
|
27493
|
-
});
|
|
27494
27497
|
var Iframe$1 = {
|
|
27495
27498
|
name: "iframe",
|
|
27496
27499
|
displayName: "Iframe (deprecated)",
|
|
@@ -29023,7 +29026,8 @@ const tickLabelSize = 14;
|
|
|
29023
29026
|
const YGridTick = _ref => {
|
|
29024
29027
|
let {
|
|
29025
29028
|
y,
|
|
29026
|
-
range
|
|
29029
|
+
range,
|
|
29030
|
+
tickStep
|
|
29027
29031
|
} = _ref;
|
|
29028
29032
|
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
29029
29033
|
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
@@ -29058,9 +29062,10 @@ const YGridTick = _ref => {
|
|
|
29058
29062
|
|
|
29059
29063
|
// If the graph displays both the y and x axis lines within the graph, we want
|
|
29060
29064
|
// to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
|
|
29061
|
-
const showLabel = shouldShowLabel(y, range);
|
|
29065
|
+
const showLabel = shouldShowLabel(y, range, tickStep);
|
|
29062
29066
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29063
|
-
className: "tick"
|
|
29067
|
+
className: "tick",
|
|
29068
|
+
"aria-hidden": true
|
|
29064
29069
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
29065
29070
|
x1: x1,
|
|
29066
29071
|
y1: y1,
|
|
@@ -29120,7 +29125,8 @@ const XGridTick = _ref2 => {
|
|
|
29120
29125
|
const xPositionText = xPosition + xAdjustment;
|
|
29121
29126
|
const yPositionText = yPosition + yAdjustment;
|
|
29122
29127
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29123
|
-
className: "tick"
|
|
29128
|
+
className: "tick",
|
|
29129
|
+
"aria-hidden": true
|
|
29124
29130
|
}, /*#__PURE__*/React__namespace.createElement("line", {
|
|
29125
29131
|
x1: x1,
|
|
29126
29132
|
y1: y1,
|
|
@@ -29139,13 +29145,13 @@ const XGridTick = _ref2 => {
|
|
|
29139
29145
|
};
|
|
29140
29146
|
|
|
29141
29147
|
// Determines whether to show the label for the given tick
|
|
29142
|
-
// Currently, the only condition is to hide the label at -
|
|
29143
|
-
// on the y-axis when the
|
|
29144
|
-
const shouldShowLabel = (
|
|
29148
|
+
// Currently, the only condition is to hide the label at -tickStep
|
|
29149
|
+
// on the y-axis when the y-axis is within the graph bounds
|
|
29150
|
+
const shouldShowLabel = (currentTick, range, tickStep) => {
|
|
29145
29151
|
let showLabel = true;
|
|
29146
29152
|
|
|
29147
|
-
// If the
|
|
29148
|
-
if (range[X][MIN] < -
|
|
29153
|
+
// If the y-axis is within the graph and currentTick equals -tickStep, hide the label
|
|
29154
|
+
if (range[X][MIN] < -tickStep && range[X][MAX] > 0 && currentTick === -tickStep) {
|
|
29149
29155
|
showLabel = false;
|
|
29150
29156
|
}
|
|
29151
29157
|
return showLabel;
|
|
@@ -29187,7 +29193,8 @@ const AxisTicks = () => {
|
|
|
29187
29193
|
return /*#__PURE__*/React__namespace.createElement(YGridTick, {
|
|
29188
29194
|
y: y,
|
|
29189
29195
|
key: `y-grid-tick-${y}`,
|
|
29190
|
-
range: range
|
|
29196
|
+
range: range,
|
|
29197
|
+
tickStep: tickStep[Y]
|
|
29191
29198
|
});
|
|
29192
29199
|
})), /*#__PURE__*/React__namespace.createElement("g", {
|
|
29193
29200
|
className: "x-axis-ticks"
|
|
@@ -29942,7 +29949,8 @@ function LockedLabel(props) {
|
|
|
29942
29949
|
color: lockedFigureColors[color],
|
|
29943
29950
|
fontSize: wonderBlocksTokens.font.size[size],
|
|
29944
29951
|
backgroundColor: "rgba(255, 255, 255, 0.8)"
|
|
29945
|
-
}
|
|
29952
|
+
},
|
|
29953
|
+
"aria-hidden": true
|
|
29946
29954
|
}, /*#__PURE__*/React__namespace.createElement(TeX, null, text));
|
|
29947
29955
|
}
|
|
29948
29956
|
|
|
@@ -29966,6 +29974,8 @@ function GraphLockedLabelsLayer(props) {
|
|
|
29966
29974
|
flags?.["mafs"]?.["locked-vector-labels"] && figure.type === "vector" ||
|
|
29967
29975
|
// Ellipse flag + ellipse type
|
|
29968
29976
|
flags?.["mafs"]?.["locked-ellipse-labels"] && figure.type === "ellipse" ||
|
|
29977
|
+
// Polygon flag + polygon type
|
|
29978
|
+
flags?.["mafs"]?.["locked-polygon-labels"] && figure.type === "polygon" ||
|
|
29969
29979
|
// Function flag + function type
|
|
29970
29980
|
flags?.["mafs"]?.["locked-function-labels"] && figure.type === "function") {
|
|
29971
29981
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, {
|
|
@@ -29989,9 +29999,16 @@ const LockedEllipse = props => {
|
|
|
29989
29999
|
angle,
|
|
29990
30000
|
color,
|
|
29991
30001
|
fillStyle,
|
|
29992
|
-
strokeStyle
|
|
30002
|
+
strokeStyle,
|
|
30003
|
+
ariaLabel,
|
|
30004
|
+
flags
|
|
29993
30005
|
} = props;
|
|
29994
|
-
|
|
30006
|
+
const hasAria = ariaLabel && flags?.["mafs"]?.["locked-figures-aria"];
|
|
30007
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
30008
|
+
className: "locked-ellipse",
|
|
30009
|
+
"aria-label": hasAria ? ariaLabel : undefined,
|
|
30010
|
+
"aria-hidden": !hasAria
|
|
30011
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Ellipse, {
|
|
29995
30012
|
center: center,
|
|
29996
30013
|
radius: radius,
|
|
29997
30014
|
angle: angle,
|
|
@@ -30007,7 +30024,7 @@ const LockedEllipse = props => {
|
|
|
30007
30024
|
fill: fillStyle === "white" ? wonderBlocksTokens.color.white : lockedFigureColors[color]
|
|
30008
30025
|
}
|
|
30009
30026
|
}
|
|
30010
|
-
});
|
|
30027
|
+
}));
|
|
30011
30028
|
};
|
|
30012
30029
|
|
|
30013
30030
|
const LockedFunction = props => {
|
|
@@ -30023,6 +30040,7 @@ const LockedFunction = props => {
|
|
|
30023
30040
|
style: strokeStyle,
|
|
30024
30041
|
domain
|
|
30025
30042
|
};
|
|
30043
|
+
const hasAria = props.ariaLabel && props.flags?.["mafs"]?.["locked-figures-aria"];
|
|
30026
30044
|
React.useEffect(() => {
|
|
30027
30045
|
// Parsing the equation in a "useEffect" hook saves about 2ms each frame
|
|
30028
30046
|
// when the learner is interacting with the graph (i.e. moving points).
|
|
@@ -30032,7 +30050,9 @@ const LockedFunction = props => {
|
|
|
30032
30050
|
return null;
|
|
30033
30051
|
}
|
|
30034
30052
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
30035
|
-
className: "locked-function"
|
|
30053
|
+
className: "locked-function",
|
|
30054
|
+
"aria-label": hasAria ? props.ariaLabel : undefined,
|
|
30055
|
+
"aria-hidden": !hasAria
|
|
30036
30056
|
}, directionalAxis === "x" && /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, _extends({
|
|
30037
30057
|
y: x => equation.eval({
|
|
30038
30058
|
x
|
|
@@ -30132,9 +30152,12 @@ const LockedLine = props => {
|
|
|
30132
30152
|
points,
|
|
30133
30153
|
showPoint1,
|
|
30134
30154
|
showPoint2,
|
|
30155
|
+
ariaLabel,
|
|
30156
|
+
flags,
|
|
30135
30157
|
range
|
|
30136
30158
|
} = props;
|
|
30137
30159
|
const [point1, point2] = points;
|
|
30160
|
+
const hasAria = ariaLabel && flags?.["mafs"]?.["locked-figures-aria"];
|
|
30138
30161
|
let line;
|
|
30139
30162
|
if (kind === "ray") {
|
|
30140
30163
|
// Rays extend to the end of the graph in one direction.
|
|
@@ -30174,7 +30197,9 @@ const LockedLine = props => {
|
|
|
30174
30197
|
}), endArrowHead);
|
|
30175
30198
|
}
|
|
30176
30199
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
30177
|
-
className: kind === "ray" ? "locked-ray" : "locked-line"
|
|
30200
|
+
className: kind === "ray" ? "locked-ray" : "locked-line",
|
|
30201
|
+
"aria-label": hasAria ? ariaLabel : undefined,
|
|
30202
|
+
"aria-hidden": !hasAria
|
|
30178
30203
|
}, line, showPoint1 && /*#__PURE__*/React__namespace.createElement(mafs.Point, {
|
|
30179
30204
|
x: point1.coord[X],
|
|
30180
30205
|
y: point1.coord[Y],
|
|
@@ -30200,12 +30225,19 @@ const LockedLine = props => {
|
|
|
30200
30225
|
|
|
30201
30226
|
const LockedPoint = props => {
|
|
30202
30227
|
const {
|
|
30228
|
+
flags,
|
|
30203
30229
|
color,
|
|
30204
30230
|
coord,
|
|
30205
|
-
filled
|
|
30231
|
+
filled,
|
|
30232
|
+
ariaLabel
|
|
30206
30233
|
} = props;
|
|
30207
30234
|
const [x, y] = coord;
|
|
30208
|
-
|
|
30235
|
+
const hasAria = ariaLabel && flags?.["mafs"]?.["locked-figures-aria"];
|
|
30236
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
30237
|
+
className: "locked-point",
|
|
30238
|
+
"aria-label": hasAria ? ariaLabel : undefined,
|
|
30239
|
+
"aria-hidden": !hasAria
|
|
30240
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Point, {
|
|
30209
30241
|
x: x,
|
|
30210
30242
|
y: y,
|
|
30211
30243
|
svgCircleProps: {
|
|
@@ -30215,7 +30247,7 @@ const LockedPoint = props => {
|
|
|
30215
30247
|
strokeWidth: wonderBlocksTokens.spacing.xxxxSmall_2
|
|
30216
30248
|
}
|
|
30217
30249
|
}
|
|
30218
|
-
});
|
|
30250
|
+
}));
|
|
30219
30251
|
};
|
|
30220
30252
|
|
|
30221
30253
|
const LockedPolygon = props => {
|
|
@@ -30226,8 +30258,11 @@ const LockedPolygon = props => {
|
|
|
30226
30258
|
fillStyle,
|
|
30227
30259
|
strokeStyle
|
|
30228
30260
|
} = props;
|
|
30261
|
+
const hasAria = props.ariaLabel && props.flags?.["mafs"]?.["locked-figures-aria"];
|
|
30229
30262
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
30230
|
-
className: "locked-polygon"
|
|
30263
|
+
className: "locked-polygon",
|
|
30264
|
+
"aria-label": hasAria ? props.ariaLabel : undefined,
|
|
30265
|
+
"aria-hidden": !hasAria
|
|
30231
30266
|
}, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
30232
30267
|
points: [...points],
|
|
30233
30268
|
fillOpacity: lockedFigureFillStyles[fillStyle],
|
|
@@ -30253,11 +30288,16 @@ const LockedPolygon = props => {
|
|
|
30253
30288
|
const LockedVector = props => {
|
|
30254
30289
|
const {
|
|
30255
30290
|
color,
|
|
30256
|
-
points
|
|
30291
|
+
points,
|
|
30292
|
+
ariaLabel,
|
|
30293
|
+
flags
|
|
30257
30294
|
} = props;
|
|
30258
30295
|
const [tail, tip] = points;
|
|
30296
|
+
const hasAria = ariaLabel && flags?.["mafs"]?.["locked-figures-aria"];
|
|
30259
30297
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
30260
|
-
className: "locked-vector"
|
|
30298
|
+
className: "locked-vector",
|
|
30299
|
+
"aria-label": hasAria ? ariaLabel : undefined,
|
|
30300
|
+
"aria-hidden": !hasAria
|
|
30261
30301
|
}, /*#__PURE__*/React__namespace.createElement(Vector, {
|
|
30262
30302
|
tail: tail,
|
|
30263
30303
|
tip: tip,
|
|
@@ -30267,6 +30307,7 @@ const LockedVector = props => {
|
|
|
30267
30307
|
|
|
30268
30308
|
const GraphLockedLayer = props => {
|
|
30269
30309
|
const {
|
|
30310
|
+
flags,
|
|
30270
30311
|
lockedFigures
|
|
30271
30312
|
} = props;
|
|
30272
30313
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, lockedFigures.map((figure, index) => {
|
|
@@ -30274,28 +30315,40 @@ const GraphLockedLayer = props => {
|
|
|
30274
30315
|
case "point":
|
|
30275
30316
|
return /*#__PURE__*/React__namespace.createElement(LockedPoint, _extends({
|
|
30276
30317
|
key: `point-${index}`
|
|
30277
|
-
}, figure
|
|
30318
|
+
}, figure, {
|
|
30319
|
+
flags: flags
|
|
30320
|
+
}));
|
|
30278
30321
|
case "line":
|
|
30279
30322
|
return /*#__PURE__*/React__namespace.createElement(LockedLine, _extends({
|
|
30280
30323
|
key: `line-${index}`,
|
|
30281
30324
|
range: props.range
|
|
30282
|
-
}, figure
|
|
30325
|
+
}, figure, {
|
|
30326
|
+
flags: flags
|
|
30327
|
+
}));
|
|
30283
30328
|
case "vector":
|
|
30284
30329
|
return /*#__PURE__*/React__namespace.createElement(LockedVector, _extends({
|
|
30285
30330
|
key: `vector-${index}`
|
|
30286
|
-
}, figure
|
|
30331
|
+
}, figure, {
|
|
30332
|
+
flags: flags
|
|
30333
|
+
}));
|
|
30287
30334
|
case "ellipse":
|
|
30288
30335
|
return /*#__PURE__*/React__namespace.createElement(LockedEllipse, _extends({
|
|
30289
30336
|
key: `ellipse-${index}`
|
|
30290
|
-
}, figure
|
|
30337
|
+
}, figure, {
|
|
30338
|
+
flags: flags
|
|
30339
|
+
}));
|
|
30291
30340
|
case "polygon":
|
|
30292
30341
|
return /*#__PURE__*/React__namespace.createElement(LockedPolygon, _extends({
|
|
30293
30342
|
key: `polygon-${index}`
|
|
30294
|
-
}, figure
|
|
30343
|
+
}, figure, {
|
|
30344
|
+
flags: flags
|
|
30345
|
+
}));
|
|
30295
30346
|
case "function":
|
|
30296
30347
|
return /*#__PURE__*/React__namespace.createElement(LockedFunction, _extends({
|
|
30297
30348
|
key: `function-${index}`
|
|
30298
|
-
}, figure
|
|
30349
|
+
}, figure, {
|
|
30350
|
+
flags: flags
|
|
30351
|
+
}));
|
|
30299
30352
|
case "label":
|
|
30300
30353
|
// This is rendered outside the SVG element, since
|
|
30301
30354
|
// TeX cannot be rendered inside an SVG.
|
|
@@ -31782,7 +31835,7 @@ function QuadraticGraph(props) {
|
|
|
31782
31835
|
// to content creators the currently selected "correct answer". ex: y = 0.200x^2 + 0.000x + 0.000
|
|
31783
31836
|
// While we should technically never have invalid coordinates, we want to ensure that we have a fallback
|
|
31784
31837
|
const coeffRef = React__namespace.useRef([0, 0, 0]);
|
|
31785
|
-
const coeffs = getQuadraticCoefficients(coords);
|
|
31838
|
+
const coeffs = getQuadraticCoefficients$1(coords);
|
|
31786
31839
|
if (coeffs !== undefined) {
|
|
31787
31840
|
coeffRef.current = coeffs;
|
|
31788
31841
|
}
|
|
@@ -31806,7 +31859,7 @@ function QuadraticGraph(props) {
|
|
|
31806
31859
|
// These equations were originally set up in 2013 and may require some
|
|
31807
31860
|
// additional comments to help clarify the quadratic formula manipulations
|
|
31808
31861
|
// Origin: https://phabricator.khanacademy.org/D2413
|
|
31809
|
-
const getQuadraticCoefficients = coords => {
|
|
31862
|
+
const getQuadraticCoefficients$1 = coords => {
|
|
31810
31863
|
const p1 = coords[0];
|
|
31811
31864
|
const p2 = coords[1];
|
|
31812
31865
|
const p3 = coords[2];
|
|
@@ -31846,7 +31899,7 @@ function SinusoidGraph(props) {
|
|
|
31846
31899
|
phase: 1,
|
|
31847
31900
|
verticalOffset: 0
|
|
31848
31901
|
});
|
|
31849
|
-
const coeffs = getSinusoidCoefficients(coords);
|
|
31902
|
+
const coeffs = getSinusoidCoefficients$1(coords);
|
|
31850
31903
|
|
|
31851
31904
|
// If the coefficients are valid, update the reference
|
|
31852
31905
|
if (coeffs !== undefined) {
|
|
@@ -31875,7 +31928,7 @@ sinusoidCoefficients) {
|
|
|
31875
31928
|
} = sinusoidCoefficients;
|
|
31876
31929
|
return a * Math.sin(b * x - c) + d;
|
|
31877
31930
|
};
|
|
31878
|
-
const getSinusoidCoefficients = coords => {
|
|
31931
|
+
const getSinusoidCoefficients$1 = coords => {
|
|
31879
31932
|
// It's assumed that p1 is the root and p2 is the first peak
|
|
31880
31933
|
const p1 = coords[0];
|
|
31881
31934
|
const p2 = coords[1];
|
|
@@ -32334,6 +32387,7 @@ const MafsGraph = props => {
|
|
|
32334
32387
|
})),
|
|
32335
32388
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
32336
32389
|
props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
|
|
32390
|
+
flags: props.flags,
|
|
32337
32391
|
lockedFigures: props.lockedFigures,
|
|
32338
32392
|
range: state.range
|
|
32339
32393
|
}))), props.flags?.["mafs"]?.["interactive-graph-locked-features-labels"] && props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
|
|
@@ -32589,6 +32643,95 @@ const renderGraph = props => {
|
|
|
32589
32643
|
}
|
|
32590
32644
|
};
|
|
32591
32645
|
|
|
32646
|
+
// Converts the state of a StatefulMafsGraph back to the format used to
|
|
32647
|
+
// represent graph state in the widget JSON.
|
|
32648
|
+
//
|
|
32649
|
+
// Rather than be tightly bound to how data was structured in
|
|
32650
|
+
// the legacy interactive graph, this lets us store state
|
|
32651
|
+
// however we want and we just transform it before handing it off
|
|
32652
|
+
// the the parent InteractiveGraph.
|
|
32653
|
+
//
|
|
32654
|
+
// The transformed data is used in the interactive graph widget editor, to
|
|
32655
|
+
// set the `correct` field of the graph options. In the learner-facing UI, the
|
|
32656
|
+
// data is also stored by the Renderer, and passed back down to the graph
|
|
32657
|
+
// widget via the `graph` prop. Because the data returned by this function
|
|
32658
|
+
// completely replaces the Renderer's representation of the widget, rather than
|
|
32659
|
+
// being merged into it, we take it upon ourselves to merge in the original
|
|
32660
|
+
// widget data here. If we didn't do this merging, the graph's configuration
|
|
32661
|
+
// would reset to the defaults when the learner interacted with it.
|
|
32662
|
+
function mafsStateToInteractiveGraph(state, originalGraph) {
|
|
32663
|
+
switch (state.type) {
|
|
32664
|
+
case "angle":
|
|
32665
|
+
invariant(originalGraph.type === "angle");
|
|
32666
|
+
return {
|
|
32667
|
+
...originalGraph,
|
|
32668
|
+
coords: state.coords
|
|
32669
|
+
};
|
|
32670
|
+
case "quadratic":
|
|
32671
|
+
invariant(originalGraph.type === "quadratic");
|
|
32672
|
+
return {
|
|
32673
|
+
...originalGraph,
|
|
32674
|
+
coords: state.coords
|
|
32675
|
+
};
|
|
32676
|
+
case "circle":
|
|
32677
|
+
invariant(originalGraph.type === "circle");
|
|
32678
|
+
return {
|
|
32679
|
+
...originalGraph,
|
|
32680
|
+
center: state.center,
|
|
32681
|
+
radius: getRadius(state)
|
|
32682
|
+
};
|
|
32683
|
+
case "linear":
|
|
32684
|
+
invariant(originalGraph.type === "linear");
|
|
32685
|
+
return {
|
|
32686
|
+
...originalGraph,
|
|
32687
|
+
coords: state.coords
|
|
32688
|
+
};
|
|
32689
|
+
case "ray":
|
|
32690
|
+
invariant(originalGraph.type === "ray");
|
|
32691
|
+
return {
|
|
32692
|
+
...originalGraph,
|
|
32693
|
+
coords: state.coords
|
|
32694
|
+
};
|
|
32695
|
+
case "sinusoid":
|
|
32696
|
+
invariant(originalGraph.type === "sinusoid");
|
|
32697
|
+
return {
|
|
32698
|
+
...originalGraph,
|
|
32699
|
+
coords: state.coords
|
|
32700
|
+
};
|
|
32701
|
+
case "segment":
|
|
32702
|
+
invariant(originalGraph.type === "segment");
|
|
32703
|
+
return {
|
|
32704
|
+
...originalGraph,
|
|
32705
|
+
coords: state.coords
|
|
32706
|
+
};
|
|
32707
|
+
case "linear-system":
|
|
32708
|
+
invariant(originalGraph.type === "linear-system");
|
|
32709
|
+
return {
|
|
32710
|
+
...originalGraph,
|
|
32711
|
+
coords: state.coords
|
|
32712
|
+
};
|
|
32713
|
+
case "polygon":
|
|
32714
|
+
invariant(originalGraph.type === "polygon");
|
|
32715
|
+
return {
|
|
32716
|
+
...originalGraph,
|
|
32717
|
+
coords: state.coords
|
|
32718
|
+
};
|
|
32719
|
+
case "point":
|
|
32720
|
+
invariant(originalGraph.type === "point");
|
|
32721
|
+
return {
|
|
32722
|
+
...originalGraph,
|
|
32723
|
+
coords: state.coords
|
|
32724
|
+
};
|
|
32725
|
+
case "none":
|
|
32726
|
+
invariant(originalGraph.type === "none");
|
|
32727
|
+
return {
|
|
32728
|
+
...originalGraph
|
|
32729
|
+
};
|
|
32730
|
+
default:
|
|
32731
|
+
throw new wonderStuffCore.UnreachableCaseError(state);
|
|
32732
|
+
}
|
|
32733
|
+
}
|
|
32734
|
+
|
|
32592
32735
|
function initializeGraphState(params) {
|
|
32593
32736
|
const {
|
|
32594
32737
|
graph,
|
|
@@ -33237,7 +33380,7 @@ function doMovePoint(state, action) {
|
|
|
33237
33380
|
// Bind the new destination to the graph range/snapStep and then get the quadratic coefficients
|
|
33238
33381
|
const boundDestination = boundAndSnapToGrid(action.destination, state);
|
|
33239
33382
|
newCoords[action.index] = boundDestination;
|
|
33240
|
-
const QuadraticCoefficients = getQuadraticCoefficients(newCoords);
|
|
33383
|
+
const QuadraticCoefficients = getQuadraticCoefficients$1(newCoords);
|
|
33241
33384
|
|
|
33242
33385
|
// If the new destination results in an invalid quadratic equation, we don't want to move the point
|
|
33243
33386
|
if (QuadraticCoefficients === undefined) {
|
|
@@ -33393,11 +33536,11 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
33393
33536
|
const [dx, dy] = getDeltaVertex(maxMoves, minMoves, delta);
|
|
33394
33537
|
return [dx, dy];
|
|
33395
33538
|
};
|
|
33396
|
-
const eq$
|
|
33539
|
+
const eq$2 = Util.eq;
|
|
33397
33540
|
|
|
33398
33541
|
// Less than or approximately equal
|
|
33399
33542
|
function leq$1(a, b) {
|
|
33400
|
-
return a < b || eq$
|
|
33543
|
+
return a < b || eq$2(a, b);
|
|
33401
33544
|
}
|
|
33402
33545
|
function boundAndSnapToGrid(point, _ref) {
|
|
33403
33546
|
let {
|
|
@@ -33709,24 +33852,6 @@ function setAtIndex(args) {
|
|
|
33709
33852
|
return copy;
|
|
33710
33853
|
}
|
|
33711
33854
|
|
|
33712
|
-
// Rather than be tightly bound to how data was structured in
|
|
33713
|
-
// the legacy interactive graph, this lets us store state
|
|
33714
|
-
// however we want and we just transform it before handing it off
|
|
33715
|
-
// the the parent InteractiveGraph
|
|
33716
|
-
function mafsStateToInteractiveGraph(state) {
|
|
33717
|
-
if (state.graph.type === "circle") {
|
|
33718
|
-
return {
|
|
33719
|
-
...state,
|
|
33720
|
-
graph: {
|
|
33721
|
-
...state.graph,
|
|
33722
|
-
radius: getRadius(state.graph)
|
|
33723
|
-
}
|
|
33724
|
-
};
|
|
33725
|
-
}
|
|
33726
|
-
return {
|
|
33727
|
-
...state
|
|
33728
|
-
};
|
|
33729
|
-
}
|
|
33730
33855
|
const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
33731
33856
|
const {
|
|
33732
33857
|
onChange,
|
|
@@ -33739,12 +33864,12 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
33739
33864
|
const prevState = React.useRef(state);
|
|
33740
33865
|
React.useEffect(() => {
|
|
33741
33866
|
if (prevState.current !== state) {
|
|
33742
|
-
onChange(
|
|
33743
|
-
graph: state
|
|
33744
|
-
})
|
|
33867
|
+
onChange({
|
|
33868
|
+
graph: mafsStateToInteractiveGraph(state, graph)
|
|
33869
|
+
});
|
|
33745
33870
|
}
|
|
33746
33871
|
prevState.current = state;
|
|
33747
|
-
}, [onChange, state]);
|
|
33872
|
+
}, [onChange, state, graph]);
|
|
33748
33873
|
|
|
33749
33874
|
// Destructuring first to keep useEffect from making excess calls
|
|
33750
33875
|
const [xSnap, ySnap] = props.snapStep;
|
|
@@ -33797,12 +33922,232 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
33797
33922
|
}));
|
|
33798
33923
|
});
|
|
33799
33924
|
|
|
33925
|
+
const eq$1 = Util.eq;
|
|
33926
|
+
const deepEq$1 = Util.deepEq;
|
|
33927
|
+
function interactiveGraphValidator(userInput, rubric) {
|
|
33928
|
+
// None-type graphs are not graded
|
|
33929
|
+
if (userInput.type === "none" && rubric.correct.type === "none") {
|
|
33930
|
+
return {
|
|
33931
|
+
type: "points",
|
|
33932
|
+
earned: 0,
|
|
33933
|
+
total: 0,
|
|
33934
|
+
message: null
|
|
33935
|
+
};
|
|
33936
|
+
}
|
|
33937
|
+
|
|
33938
|
+
// When nothing has moved, there will neither be coords nor the
|
|
33939
|
+
// circle's center/radius fields. When those fields are absent, skip
|
|
33940
|
+
// all these checks; just go mark the answer as empty.
|
|
33941
|
+
const hasValue = Boolean(
|
|
33942
|
+
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
33943
|
+
userInput.coords ||
|
|
33944
|
+
// @ts-expect-error - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
|
|
33945
|
+
userInput.center && userInput.radius);
|
|
33946
|
+
if (userInput.type === rubric.correct.type && hasValue) {
|
|
33947
|
+
if (userInput.type === "linear" && rubric.correct.type === "linear" && userInput.coords != null) {
|
|
33948
|
+
const guess = userInput.coords;
|
|
33949
|
+
const correct = rubric.correct.coords;
|
|
33950
|
+
|
|
33951
|
+
// If both of the guess points are on the correct line, it's
|
|
33952
|
+
// correct.
|
|
33953
|
+
if (
|
|
33954
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
33955
|
+
collinear(correct[0], correct[1], guess[0]) &&
|
|
33956
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
33957
|
+
collinear(correct[0], correct[1], guess[1])) {
|
|
33958
|
+
return {
|
|
33959
|
+
type: "points",
|
|
33960
|
+
earned: 1,
|
|
33961
|
+
total: 1,
|
|
33962
|
+
message: null
|
|
33963
|
+
};
|
|
33964
|
+
}
|
|
33965
|
+
} else if (userInput.type === "linear-system" && rubric.correct.type === "linear-system" && userInput.coords != null) {
|
|
33966
|
+
const guess = userInput.coords;
|
|
33967
|
+
const correct = rubric.correct.coords;
|
|
33968
|
+
if (collinear(correct[0][0], correct[0][1], guess[0][0]) && collinear(correct[0][0], correct[0][1], guess[0][1]) && collinear(correct[1][0], correct[1][1], guess[1][0]) && collinear(correct[1][0], correct[1][1], guess[1][1]) || collinear(correct[0][0], correct[0][1], guess[1][0]) && collinear(correct[0][0], correct[0][1], guess[1][1]) && collinear(correct[1][0], correct[1][1], guess[0][0]) && collinear(correct[1][0], correct[1][1], guess[0][1])) {
|
|
33969
|
+
return {
|
|
33970
|
+
type: "points",
|
|
33971
|
+
earned: 1,
|
|
33972
|
+
total: 1,
|
|
33973
|
+
message: null
|
|
33974
|
+
};
|
|
33975
|
+
}
|
|
33976
|
+
} else if (userInput.type === "quadratic" && rubric.correct.type === "quadratic" && userInput.coords != null) {
|
|
33977
|
+
// If the parabola coefficients match, it's correct.
|
|
33978
|
+
const guessCoeffs = getQuadraticCoefficients(userInput.coords);
|
|
33979
|
+
const correctCoeffs = getQuadraticCoefficients(
|
|
33980
|
+
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
33981
|
+
rubric.correct.coords);
|
|
33982
|
+
if (deepEq$1(guessCoeffs, correctCoeffs)) {
|
|
33983
|
+
return {
|
|
33984
|
+
type: "points",
|
|
33985
|
+
earned: 1,
|
|
33986
|
+
total: 1,
|
|
33987
|
+
message: null
|
|
33988
|
+
};
|
|
33989
|
+
}
|
|
33990
|
+
} else if (userInput.type === "sinusoid" && rubric.correct.type === "sinusoid" && userInput.coords != null) {
|
|
33991
|
+
const guessCoeffs = getSinusoidCoefficients(userInput.coords);
|
|
33992
|
+
const correctCoeffs = getSinusoidCoefficients(
|
|
33993
|
+
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
33994
|
+
rubric.correct.coords);
|
|
33995
|
+
const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
|
|
33996
|
+
const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
|
|
33997
|
+
// If the canonical coefficients match, it's correct.
|
|
33998
|
+
if (deepEq$1(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
|
|
33999
|
+
return {
|
|
34000
|
+
type: "points",
|
|
34001
|
+
earned: 1,
|
|
34002
|
+
total: 1,
|
|
34003
|
+
message: null
|
|
34004
|
+
};
|
|
34005
|
+
}
|
|
34006
|
+
} else if (userInput.type === "circle" && rubric.correct.type === "circle") {
|
|
34007
|
+
if (deepEq$1(userInput.center, rubric.correct.center) && eq$1(userInput.radius, rubric.correct.radius)) {
|
|
34008
|
+
return {
|
|
34009
|
+
type: "points",
|
|
34010
|
+
earned: 1,
|
|
34011
|
+
total: 1,
|
|
34012
|
+
message: null
|
|
34013
|
+
};
|
|
34014
|
+
}
|
|
34015
|
+
} else if (userInput.type === "point" && rubric.correct.type === "point" && userInput.coords != null) {
|
|
34016
|
+
let correct = rubric.correct.coords;
|
|
34017
|
+
if (correct == null) {
|
|
34018
|
+
throw new Error("Point graph rubric has null coords");
|
|
34019
|
+
}
|
|
34020
|
+
const guess = userInput.coords.slice();
|
|
34021
|
+
correct = correct.slice();
|
|
34022
|
+
// Everything's already rounded so we shouldn't need to do an
|
|
34023
|
+
// eq() comparison but _.isEqual(0, -0) is false, so we'll use
|
|
34024
|
+
// eq() anyway. The sort should be fine because it'll stringify
|
|
34025
|
+
// it and -0 converted to a string is "0"
|
|
34026
|
+
guess?.sort();
|
|
34027
|
+
// @ts-expect-error - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
|
|
34028
|
+
correct.sort();
|
|
34029
|
+
if (deepEq$1(guess, correct)) {
|
|
34030
|
+
return {
|
|
34031
|
+
type: "points",
|
|
34032
|
+
earned: 1,
|
|
34033
|
+
total: 1,
|
|
34034
|
+
message: null
|
|
34035
|
+
};
|
|
34036
|
+
}
|
|
34037
|
+
} else if (userInput.type === "polygon" && rubric.correct.type === "polygon" && userInput.coords != null) {
|
|
34038
|
+
const guess = userInput.coords?.slice();
|
|
34039
|
+
// @ts-expect-error - TS2322 - Type 'Coord[] | undefined' is not assignable to type 'Coord[]'.
|
|
34040
|
+
const correct = rubric.correct.coords?.slice();
|
|
34041
|
+
let match;
|
|
34042
|
+
if (rubric.correct.match === "similar") {
|
|
34043
|
+
match = similar(guess, correct, Number.POSITIVE_INFINITY);
|
|
34044
|
+
} else if (rubric.correct.match === "congruent") {
|
|
34045
|
+
match = similar(guess, correct, kmath.number.DEFAULT_TOLERANCE);
|
|
34046
|
+
} else if (rubric.correct.match === "approx") {
|
|
34047
|
+
match = similar(guess, correct, 0.1);
|
|
34048
|
+
} else {
|
|
34049
|
+
/* exact */
|
|
34050
|
+
guess.sort();
|
|
34051
|
+
correct.sort();
|
|
34052
|
+
match = deepEq$1(guess, correct);
|
|
34053
|
+
}
|
|
34054
|
+
if (match) {
|
|
34055
|
+
return {
|
|
34056
|
+
type: "points",
|
|
34057
|
+
earned: 1,
|
|
34058
|
+
total: 1,
|
|
34059
|
+
message: null
|
|
34060
|
+
};
|
|
34061
|
+
}
|
|
34062
|
+
} else if (userInput.type === "segment" && rubric.correct.type === "segment" && userInput.coords != null) {
|
|
34063
|
+
let guess = Util.deepClone(userInput.coords);
|
|
34064
|
+
let correct = Util.deepClone(rubric.correct?.coords);
|
|
34065
|
+
guess = ___default["default"].invoke(guess, "sort").sort();
|
|
34066
|
+
// @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
34067
|
+
correct = ___default["default"].invoke(correct, "sort").sort();
|
|
34068
|
+
if (deepEq$1(guess, correct)) {
|
|
34069
|
+
return {
|
|
34070
|
+
type: "points",
|
|
34071
|
+
earned: 1,
|
|
34072
|
+
total: 1,
|
|
34073
|
+
message: null
|
|
34074
|
+
};
|
|
34075
|
+
}
|
|
34076
|
+
} else if (userInput.type === "ray" && rubric.correct.type === "ray" && userInput.coords != null) {
|
|
34077
|
+
const guess = userInput.coords;
|
|
34078
|
+
const correct = rubric.correct.coords;
|
|
34079
|
+
if (
|
|
34080
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
34081
|
+
deepEq$1(guess[0], correct[0]) &&
|
|
34082
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
34083
|
+
collinear(correct[0], correct[1], guess[1])) {
|
|
34084
|
+
return {
|
|
34085
|
+
type: "points",
|
|
34086
|
+
earned: 1,
|
|
34087
|
+
total: 1,
|
|
34088
|
+
message: null
|
|
34089
|
+
};
|
|
34090
|
+
}
|
|
34091
|
+
} else if (userInput.type === "angle" && rubric.correct.type === "angle") {
|
|
34092
|
+
const guess = userInput.coords;
|
|
34093
|
+
const correct = rubric.correct.coords;
|
|
34094
|
+
let match;
|
|
34095
|
+
if (rubric.correct.match === "congruent") {
|
|
34096
|
+
const angles = ___default["default"].map([guess, correct], function (coords) {
|
|
34097
|
+
const angle = GraphUtils.findAngle(
|
|
34098
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
34099
|
+
coords[2],
|
|
34100
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
34101
|
+
coords[0],
|
|
34102
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
34103
|
+
coords[1]);
|
|
34104
|
+
return (angle + 360) % 360;
|
|
34105
|
+
});
|
|
34106
|
+
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
34107
|
+
match = eq$1(...angles);
|
|
34108
|
+
} else {
|
|
34109
|
+
/* exact */
|
|
34110
|
+
match =
|
|
34111
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
34112
|
+
deepEq$1(guess[1], correct[1]) &&
|
|
34113
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
34114
|
+
collinear(correct[1], correct[0], guess[0]) &&
|
|
34115
|
+
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
34116
|
+
collinear(correct[1], correct[2], guess[2]);
|
|
34117
|
+
}
|
|
34118
|
+
if (match) {
|
|
34119
|
+
return {
|
|
34120
|
+
type: "points",
|
|
34121
|
+
earned: 1,
|
|
34122
|
+
total: 1,
|
|
34123
|
+
message: null
|
|
34124
|
+
};
|
|
34125
|
+
}
|
|
34126
|
+
}
|
|
34127
|
+
}
|
|
34128
|
+
|
|
34129
|
+
// The input wasn't correct, so check if it's a blank input or if it's
|
|
34130
|
+
// actually just wrong
|
|
34131
|
+
if (!hasValue || ___default["default"].isEqual(userInput, rubric.graph)) {
|
|
34132
|
+
// We're where we started.
|
|
34133
|
+
return {
|
|
34134
|
+
type: "invalid",
|
|
34135
|
+
message: null
|
|
34136
|
+
};
|
|
34137
|
+
}
|
|
34138
|
+
return {
|
|
34139
|
+
type: "points",
|
|
34140
|
+
earned: 0,
|
|
34141
|
+
total: 1,
|
|
34142
|
+
message: null
|
|
34143
|
+
};
|
|
34144
|
+
}
|
|
34145
|
+
|
|
33800
34146
|
const TRASH_ICON_URI = "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
|
|
33801
34147
|
const defaultBackgroundImage = {
|
|
33802
34148
|
url: null
|
|
33803
34149
|
};
|
|
33804
34150
|
const eq = Util.eq;
|
|
33805
|
-
const deepEq$1 = Util.deepEq;
|
|
33806
34151
|
const UNLIMITED = "unlimited";
|
|
33807
34152
|
|
|
33808
34153
|
// Sample background image:
|
|
@@ -33843,6 +34188,39 @@ const makeInvalidTypeError = (functionName, graphType) => {
|
|
|
33843
34188
|
|
|
33844
34189
|
// There's no transform function in exports
|
|
33845
34190
|
|
|
34191
|
+
// TODO: there's another, very similar getSinusoidCoefficients function
|
|
34192
|
+
// they should probably be merged
|
|
34193
|
+
function getSinusoidCoefficients(coords) {
|
|
34194
|
+
// It's assumed that p1 is the root and p2 is the first peak
|
|
34195
|
+
const p1 = coords[0];
|
|
34196
|
+
const p2 = coords[1];
|
|
34197
|
+
|
|
34198
|
+
// Resulting coefficients are canonical for this sine curve
|
|
34199
|
+
const amplitude = p2[1] - p1[1];
|
|
34200
|
+
const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
|
|
34201
|
+
const phase = p1[0] * angularFrequency;
|
|
34202
|
+
const verticalOffset = p1[1];
|
|
34203
|
+
return [amplitude, angularFrequency, phase, verticalOffset];
|
|
34204
|
+
}
|
|
34205
|
+
|
|
34206
|
+
// TODO: there's another, very similar getQuadraticCoefficients function
|
|
34207
|
+
// they should probably be merged
|
|
34208
|
+
function getQuadraticCoefficients(coords) {
|
|
34209
|
+
const p1 = coords[0];
|
|
34210
|
+
const p2 = coords[1];
|
|
34211
|
+
const p3 = coords[2];
|
|
34212
|
+
const denom = (p1[0] - p2[0]) * (p1[0] - p3[0]) * (p2[0] - p3[0]);
|
|
34213
|
+
if (denom === 0) {
|
|
34214
|
+
// Many of the callers assume that the return value is always defined.
|
|
34215
|
+
// @ts-expect-error - TS2322 - Type 'undefined' is not assignable to type 'QuadraticCoefficient'.
|
|
34216
|
+
return;
|
|
34217
|
+
}
|
|
34218
|
+
const a = (p3[0] * (p2[1] - p1[1]) + p2[0] * (p1[1] - p3[1]) + p1[0] * (p3[1] - p2[1])) / denom;
|
|
34219
|
+
const b = (p3[0] * p3[0] * (p1[1] - p2[1]) + p2[0] * p2[0] * (p3[1] - p1[1]) + p1[0] * p1[0] * (p2[1] - p3[1])) / denom;
|
|
34220
|
+
const c = (p2[0] * p3[0] * (p2[0] - p3[0]) * p1[1] + p3[0] * p1[0] * (p3[0] - p1[0]) * p2[1] + p1[0] * p2[0] * (p1[0] - p2[0]) * p3[1]) / denom;
|
|
34221
|
+
return [a, b, c];
|
|
34222
|
+
}
|
|
34223
|
+
|
|
33846
34224
|
// (LEMS-2190): Move the Mafs Angle Graph coordinate reversal logic in interactive-graph-state.ts
|
|
33847
34225
|
// to this file when we remove the legacy graph. This logic allows us to support bi-directional angles
|
|
33848
34226
|
// for the new (non-reflexive) Mafs graphs, while maintaining the same scoring behaviour as the legacy graph.
|
|
@@ -34963,7 +35341,6 @@ class LegacyInteractiveGraph extends React__namespace.Component {
|
|
|
34963
35341
|
});
|
|
34964
35342
|
$__default["default"](this.angle).on("move", () => {
|
|
34965
35343
|
this.onChange({
|
|
34966
|
-
// @ts-expect-error Type '{ coords: any; type: "angle"; showAngles?: boolean | undefined; allowReflexAngles?: boolean | undefined; angleOffsetDeg?: number | undefined; snapDegrees?: number | undefined; match?: "congruent" | undefined; }' is not assignable to type 'InteractiveGraphState | undefined'.
|
|
34967
35344
|
graph: {
|
|
34968
35345
|
...graph,
|
|
34969
35346
|
coords: this.angle?.getClockwiseCoords()
|
|
@@ -34990,7 +35367,7 @@ class LegacyInteractiveGraph extends React__namespace.Component {
|
|
|
34990
35367
|
return InteractiveGraph.getUserInputFromProps(this.props);
|
|
34991
35368
|
}
|
|
34992
35369
|
simpleValidate(rubric) {
|
|
34993
|
-
return
|
|
35370
|
+
return interactiveGraphValidator(this.getUserInput(), rubric);
|
|
34994
35371
|
}
|
|
34995
35372
|
focus = $__default["default"].noop;
|
|
34996
35373
|
render() {
|
|
@@ -35065,7 +35442,7 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
35065
35442
|
throw new perseusCore.PerseusError("Cannot getUserInput from a graph that has never rendered", perseusCore.Errors.NotAllowed);
|
|
35066
35443
|
}
|
|
35067
35444
|
simpleValidate(rubric) {
|
|
35068
|
-
return
|
|
35445
|
+
return interactiveGraphValidator(this.getUserInput(), rubric);
|
|
35069
35446
|
}
|
|
35070
35447
|
render() {
|
|
35071
35448
|
// Mafs shim
|
|
@@ -35088,33 +35465,6 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
35088
35465
|
ref: this.legacyGraphRef
|
|
35089
35466
|
}, this.props));
|
|
35090
35467
|
}
|
|
35091
|
-
static getQuadraticCoefficients(coords) {
|
|
35092
|
-
const p1 = coords[0];
|
|
35093
|
-
const p2 = coords[1];
|
|
35094
|
-
const p3 = coords[2];
|
|
35095
|
-
const denom = (p1[0] - p2[0]) * (p1[0] - p3[0]) * (p2[0] - p3[0]);
|
|
35096
|
-
if (denom === 0) {
|
|
35097
|
-
// Many of the callers assume that the return value is always defined.
|
|
35098
|
-
// @ts-expect-error - TS2322 - Type 'undefined' is not assignable to type 'QuadraticCoefficient'.
|
|
35099
|
-
return;
|
|
35100
|
-
}
|
|
35101
|
-
const a = (p3[0] * (p2[1] - p1[1]) + p2[0] * (p1[1] - p3[1]) + p1[0] * (p3[1] - p2[1])) / denom;
|
|
35102
|
-
const b = (p3[0] * p3[0] * (p1[1] - p2[1]) + p2[0] * p2[0] * (p3[1] - p1[1]) + p1[0] * p1[0] * (p2[1] - p3[1])) / denom;
|
|
35103
|
-
const c = (p2[0] * p3[0] * (p2[0] - p3[0]) * p1[1] + p3[0] * p1[0] * (p3[0] - p1[0]) * p2[1] + p1[0] * p2[0] * (p1[0] - p2[0]) * p3[1]) / denom;
|
|
35104
|
-
return [a, b, c];
|
|
35105
|
-
}
|
|
35106
|
-
static getSinusoidCoefficients(coords) {
|
|
35107
|
-
// It's assumed that p1 is the root and p2 is the first peak
|
|
35108
|
-
const p1 = coords[0];
|
|
35109
|
-
const p2 = coords[1];
|
|
35110
|
-
|
|
35111
|
-
// Resulting coefficients are canonical for this sine curve
|
|
35112
|
-
const amplitude = p2[1] - p1[1];
|
|
35113
|
-
const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
|
|
35114
|
-
const phase = p1[0] * angularFrequency;
|
|
35115
|
-
const verticalOffset = p1[1];
|
|
35116
|
-
return [amplitude, angularFrequency, phase, verticalOffset];
|
|
35117
|
-
}
|
|
35118
35468
|
|
|
35119
35469
|
/**
|
|
35120
35470
|
* @param {object} graph Like props.graph or props.correct
|
|
@@ -35328,7 +35678,7 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
35328
35678
|
const coords =
|
|
35329
35679
|
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
35330
35680
|
props.graph.coords || InteractiveGraph.defaultQuadraticCoords(props);
|
|
35331
|
-
return
|
|
35681
|
+
return getQuadraticCoefficients(coords);
|
|
35332
35682
|
}
|
|
35333
35683
|
static defaultQuadraticCoords(props) {
|
|
35334
35684
|
const coords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
@@ -35343,7 +35693,7 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
35343
35693
|
const coords =
|
|
35344
35694
|
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
35345
35695
|
props.graph.coords || InteractiveGraph.defaultSinusoidCoords(props);
|
|
35346
|
-
return
|
|
35696
|
+
return getSinusoidCoefficients(coords);
|
|
35347
35697
|
}
|
|
35348
35698
|
static defaultSinusoidCoords(props) {
|
|
35349
35699
|
const coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
@@ -35423,220 +35773,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
35423
35773
|
const angle = GraphUtils.findAngle(coords[2], coords[0], coords[1]);
|
|
35424
35774
|
return angle.toFixed(0) + "\u00B0 angle" + " at (" + coords[1].join(", ") + ")";
|
|
35425
35775
|
}
|
|
35426
|
-
static validate(userInput, rubric
|
|
35427
|
-
|
|
35428
|
-
if (userInput.type === "none" && rubric.correct.type === "none") {
|
|
35429
|
-
return {
|
|
35430
|
-
type: "points",
|
|
35431
|
-
earned: 0,
|
|
35432
|
-
total: 0,
|
|
35433
|
-
message: null
|
|
35434
|
-
};
|
|
35435
|
-
}
|
|
35436
|
-
|
|
35437
|
-
// When nothing has moved, there will neither be coords nor the
|
|
35438
|
-
// circle's center/radius fields. When those fields are absent, skip
|
|
35439
|
-
// all these checks; just go mark the answer as empty.
|
|
35440
|
-
const hasValue = Boolean(
|
|
35441
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
35442
|
-
userInput.coords ||
|
|
35443
|
-
// @ts-expect-error - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
|
|
35444
|
-
userInput.center && userInput.radius);
|
|
35445
|
-
if (userInput.type === rubric.correct.type && hasValue) {
|
|
35446
|
-
if (userInput.type === "linear" && rubric.correct.type === "linear" && userInput.coords != null) {
|
|
35447
|
-
const guess = userInput.coords;
|
|
35448
|
-
const correct = rubric.correct.coords;
|
|
35449
|
-
|
|
35450
|
-
// If both of the guess points are on the correct line, it's
|
|
35451
|
-
// correct.
|
|
35452
|
-
if (
|
|
35453
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
35454
|
-
collinear(correct[0], correct[1], guess[0]) &&
|
|
35455
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
35456
|
-
collinear(correct[0], correct[1], guess[1])) {
|
|
35457
|
-
return {
|
|
35458
|
-
type: "points",
|
|
35459
|
-
earned: 1,
|
|
35460
|
-
total: 1,
|
|
35461
|
-
message: null
|
|
35462
|
-
};
|
|
35463
|
-
}
|
|
35464
|
-
} else if (userInput.type === "linear-system" && rubric.correct.type === "linear-system" && userInput.coords != null) {
|
|
35465
|
-
const guess = userInput.coords;
|
|
35466
|
-
const correct = rubric.correct.coords;
|
|
35467
|
-
if (collinear(correct[0][0], correct[0][1], guess[0][0]) && collinear(correct[0][0], correct[0][1], guess[0][1]) && collinear(correct[1][0], correct[1][1], guess[1][0]) && collinear(correct[1][0], correct[1][1], guess[1][1]) || collinear(correct[0][0], correct[0][1], guess[1][0]) && collinear(correct[0][0], correct[0][1], guess[1][1]) && collinear(correct[1][0], correct[1][1], guess[0][0]) && collinear(correct[1][0], correct[1][1], guess[0][1])) {
|
|
35468
|
-
return {
|
|
35469
|
-
type: "points",
|
|
35470
|
-
earned: 1,
|
|
35471
|
-
total: 1,
|
|
35472
|
-
message: null
|
|
35473
|
-
};
|
|
35474
|
-
}
|
|
35475
|
-
} else if (userInput.type === "quadratic" && rubric.correct.type === "quadratic" && userInput.coords != null) {
|
|
35476
|
-
// If the parabola coefficients match, it's correct.
|
|
35477
|
-
const guessCoeffs = this.getQuadraticCoefficients(userInput.coords);
|
|
35478
|
-
const correctCoeffs = this.getQuadraticCoefficients(
|
|
35479
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
35480
|
-
rubric.correct.coords);
|
|
35481
|
-
if (deepEq$1(guessCoeffs, correctCoeffs)) {
|
|
35482
|
-
return {
|
|
35483
|
-
type: "points",
|
|
35484
|
-
earned: 1,
|
|
35485
|
-
total: 1,
|
|
35486
|
-
message: null
|
|
35487
|
-
};
|
|
35488
|
-
}
|
|
35489
|
-
} else if (userInput.type === "sinusoid" && rubric.correct.type === "sinusoid" && userInput.coords != null) {
|
|
35490
|
-
const guessCoeffs = this.getSinusoidCoefficients(userInput.coords);
|
|
35491
|
-
const correctCoeffs = this.getSinusoidCoefficients(
|
|
35492
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
35493
|
-
rubric.correct.coords);
|
|
35494
|
-
const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
|
|
35495
|
-
const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
|
|
35496
|
-
// If the canonical coefficients match, it's correct.
|
|
35497
|
-
if (deepEq$1(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
|
|
35498
|
-
return {
|
|
35499
|
-
type: "points",
|
|
35500
|
-
earned: 1,
|
|
35501
|
-
total: 1,
|
|
35502
|
-
message: null
|
|
35503
|
-
};
|
|
35504
|
-
}
|
|
35505
|
-
} else if (userInput.type === "circle" && rubric.correct.type === "circle") {
|
|
35506
|
-
if (deepEq$1(userInput.center, rubric.correct.center) && eq(userInput.radius, rubric.correct.radius)) {
|
|
35507
|
-
return {
|
|
35508
|
-
type: "points",
|
|
35509
|
-
earned: 1,
|
|
35510
|
-
total: 1,
|
|
35511
|
-
message: null
|
|
35512
|
-
};
|
|
35513
|
-
}
|
|
35514
|
-
} else if (userInput.type === "point" && rubric.correct.type === "point" && userInput.coords != null) {
|
|
35515
|
-
let correct = InteractiveGraph.getPointCoords(rubric.correct, component);
|
|
35516
|
-
const guess = userInput.coords.slice();
|
|
35517
|
-
correct = correct.slice();
|
|
35518
|
-
// Everything's already rounded so we shouldn't need to do an
|
|
35519
|
-
// eq() comparison but _.isEqual(0, -0) is false, so we'll use
|
|
35520
|
-
// eq() anyway. The sort should be fine because it'll stringify
|
|
35521
|
-
// it and -0 converted to a string is "0"
|
|
35522
|
-
guess?.sort();
|
|
35523
|
-
// @ts-expect-error - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
|
|
35524
|
-
correct.sort();
|
|
35525
|
-
if (deepEq$1(guess, correct)) {
|
|
35526
|
-
return {
|
|
35527
|
-
type: "points",
|
|
35528
|
-
earned: 1,
|
|
35529
|
-
total: 1,
|
|
35530
|
-
message: null
|
|
35531
|
-
};
|
|
35532
|
-
}
|
|
35533
|
-
} else if (userInput.type === "polygon" && rubric.correct.type === "polygon" && userInput.coords != null) {
|
|
35534
|
-
const guess = userInput.coords?.slice();
|
|
35535
|
-
// @ts-expect-error - TS2322 - Type 'Coord[] | undefined' is not assignable to type 'Coord[]'.
|
|
35536
|
-
const correct = rubric.correct.coords?.slice();
|
|
35537
|
-
let match;
|
|
35538
|
-
if (rubric.correct.match === "similar") {
|
|
35539
|
-
match = similar(guess, correct, Number.POSITIVE_INFINITY);
|
|
35540
|
-
} else if (rubric.correct.match === "congruent") {
|
|
35541
|
-
match = similar(guess, correct, kmath.number.DEFAULT_TOLERANCE);
|
|
35542
|
-
} else if (rubric.correct.match === "approx") {
|
|
35543
|
-
match = similar(guess, correct, 0.1);
|
|
35544
|
-
} else {
|
|
35545
|
-
/* exact */
|
|
35546
|
-
guess.sort();
|
|
35547
|
-
correct.sort();
|
|
35548
|
-
match = deepEq$1(guess, correct);
|
|
35549
|
-
}
|
|
35550
|
-
if (match) {
|
|
35551
|
-
return {
|
|
35552
|
-
type: "points",
|
|
35553
|
-
earned: 1,
|
|
35554
|
-
total: 1,
|
|
35555
|
-
message: null
|
|
35556
|
-
};
|
|
35557
|
-
}
|
|
35558
|
-
} else if (userInput.type === "segment" && rubric.correct.type === "segment" && userInput.coords != null) {
|
|
35559
|
-
let guess = Util.deepClone(userInput.coords);
|
|
35560
|
-
let correct = Util.deepClone(rubric.correct?.coords);
|
|
35561
|
-
guess = ___default["default"].invoke(guess, "sort").sort();
|
|
35562
|
-
// @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
35563
|
-
correct = ___default["default"].invoke(correct, "sort").sort();
|
|
35564
|
-
if (deepEq$1(guess, correct)) {
|
|
35565
|
-
return {
|
|
35566
|
-
type: "points",
|
|
35567
|
-
earned: 1,
|
|
35568
|
-
total: 1,
|
|
35569
|
-
message: null
|
|
35570
|
-
};
|
|
35571
|
-
}
|
|
35572
|
-
} else if (userInput.type === "ray" && rubric.correct.type === "ray" && userInput.coords != null) {
|
|
35573
|
-
const guess = userInput.coords;
|
|
35574
|
-
const correct = rubric.correct.coords;
|
|
35575
|
-
if (
|
|
35576
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
35577
|
-
deepEq$1(guess[0], correct[0]) &&
|
|
35578
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
35579
|
-
collinear(correct[0], correct[1], guess[1])) {
|
|
35580
|
-
return {
|
|
35581
|
-
type: "points",
|
|
35582
|
-
earned: 1,
|
|
35583
|
-
total: 1,
|
|
35584
|
-
message: null
|
|
35585
|
-
};
|
|
35586
|
-
}
|
|
35587
|
-
} else if (userInput.type === "angle" && rubric.correct.type === "angle") {
|
|
35588
|
-
const guess = userInput.coords;
|
|
35589
|
-
const correct = rubric.correct.coords;
|
|
35590
|
-
let match;
|
|
35591
|
-
if (rubric.correct.match === "congruent") {
|
|
35592
|
-
const angles = ___default["default"].map([guess, correct], function (coords) {
|
|
35593
|
-
const angle = GraphUtils.findAngle(
|
|
35594
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
35595
|
-
coords[2],
|
|
35596
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
35597
|
-
coords[0],
|
|
35598
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
35599
|
-
coords[1]);
|
|
35600
|
-
return (angle + 360) % 360;
|
|
35601
|
-
});
|
|
35602
|
-
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
35603
|
-
match = eq(...angles);
|
|
35604
|
-
} else {
|
|
35605
|
-
/* exact */
|
|
35606
|
-
match =
|
|
35607
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
35608
|
-
deepEq$1(guess[1], correct[1]) &&
|
|
35609
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
35610
|
-
collinear(correct[1], correct[0], guess[0]) &&
|
|
35611
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
35612
|
-
collinear(correct[1], correct[2], guess[2]);
|
|
35613
|
-
}
|
|
35614
|
-
if (match) {
|
|
35615
|
-
return {
|
|
35616
|
-
type: "points",
|
|
35617
|
-
earned: 1,
|
|
35618
|
-
total: 1,
|
|
35619
|
-
message: null
|
|
35620
|
-
};
|
|
35621
|
-
}
|
|
35622
|
-
}
|
|
35623
|
-
}
|
|
35624
|
-
|
|
35625
|
-
// The input wasn't correct, so check if it's a blank input or if it's
|
|
35626
|
-
// actually just wrong
|
|
35627
|
-
if (!hasValue || ___default["default"].isEqual(userInput, rubric.graph)) {
|
|
35628
|
-
// We're where we started.
|
|
35629
|
-
return {
|
|
35630
|
-
type: "invalid",
|
|
35631
|
-
message: null
|
|
35632
|
-
};
|
|
35633
|
-
}
|
|
35634
|
-
return {
|
|
35635
|
-
type: "points",
|
|
35636
|
-
earned: 0,
|
|
35637
|
-
total: 1,
|
|
35638
|
-
message: null
|
|
35639
|
-
};
|
|
35776
|
+
static validate(userInput, rubric) {
|
|
35777
|
+
return interactiveGraphValidator(userInput, rubric);
|
|
35640
35778
|
}
|
|
35641
35779
|
static getUserInputFromProps(props) {
|
|
35642
35780
|
return props.graph;
|
|
@@ -38335,6 +38473,16 @@ const styles$d = aphrodite.StyleSheet.create({
|
|
|
38335
38473
|
}
|
|
38336
38474
|
});
|
|
38337
38475
|
|
|
38476
|
+
function matcherValidator(state, rubric) {
|
|
38477
|
+
const correct = ___default["default"].isEqual(state.left, rubric.left) && ___default["default"].isEqual(state.right, rubric.right);
|
|
38478
|
+
return {
|
|
38479
|
+
type: "points",
|
|
38480
|
+
earned: correct ? 1 : 0,
|
|
38481
|
+
total: 1,
|
|
38482
|
+
message: null
|
|
38483
|
+
};
|
|
38484
|
+
}
|
|
38485
|
+
|
|
38338
38486
|
const {
|
|
38339
38487
|
shuffle: shuffle$1,
|
|
38340
38488
|
seededRNG
|
|
@@ -38357,6 +38505,9 @@ class Matcher extends React__namespace.Component {
|
|
|
38357
38505
|
rightHeight: 0,
|
|
38358
38506
|
texRendererLoaded: false
|
|
38359
38507
|
};
|
|
38508
|
+
static validate(state, rubric) {
|
|
38509
|
+
return matcherValidator(state, rubric);
|
|
38510
|
+
}
|
|
38360
38511
|
changeAndTrack = e => {
|
|
38361
38512
|
this.props.onChange(e);
|
|
38362
38513
|
this.props.trackInteraction();
|
|
@@ -38409,8 +38560,7 @@ class Matcher extends React__namespace.Component {
|
|
|
38409
38560
|
this.refs.right.moveOptionToIndex(option, index);
|
|
38410
38561
|
};
|
|
38411
38562
|
simpleValidate(rubric) {
|
|
38412
|
-
|
|
38413
|
-
return Matcher.validate(this.getUserInput(), rubric);
|
|
38563
|
+
return matcherValidator(this.getUserInput(), rubric);
|
|
38414
38564
|
}
|
|
38415
38565
|
render() {
|
|
38416
38566
|
// To minimize layout shift, we display a spinner until our math
|
|
@@ -38506,17 +38656,6 @@ class Matcher extends React__namespace.Component {
|
|
|
38506
38656
|
})))));
|
|
38507
38657
|
}
|
|
38508
38658
|
}
|
|
38509
|
-
___default["default"].extend(Matcher, {
|
|
38510
|
-
validate: function (state, rubric) {
|
|
38511
|
-
const correct = ___default["default"].isEqual(state.left, rubric.left) && ___default["default"].isEqual(state.right, rubric.right);
|
|
38512
|
-
return {
|
|
38513
|
-
type: "points",
|
|
38514
|
-
earned: correct ? 1 : 0,
|
|
38515
|
-
total: 1,
|
|
38516
|
-
message: null
|
|
38517
|
-
};
|
|
38518
|
-
}
|
|
38519
|
-
});
|
|
38520
38659
|
const padding = 5;
|
|
38521
38660
|
const border = "1px solid #444";
|
|
38522
38661
|
const styles$c = aphrodite.StyleSheet.create({
|
|
@@ -38552,7 +38691,59 @@ var Matcher$1 = {
|
|
|
38552
38691
|
isLintable: true
|
|
38553
38692
|
};
|
|
38554
38693
|
|
|
38555
|
-
|
|
38694
|
+
function matrixValidator(state, rubric, strings) {
|
|
38695
|
+
const solution = rubric.answers;
|
|
38696
|
+
const supplied = state.answers;
|
|
38697
|
+
const solutionSize = getMatrixSize(solution);
|
|
38698
|
+
const suppliedSize = getMatrixSize(supplied);
|
|
38699
|
+
const incorrectSize = solutionSize[0] !== suppliedSize[0] || solutionSize[1] !== suppliedSize[1];
|
|
38700
|
+
const createValidator = KhanAnswerTypes.number.createValidatorFunctional;
|
|
38701
|
+
let message = null;
|
|
38702
|
+
let hasEmptyCell = false;
|
|
38703
|
+
let incorrect = false;
|
|
38704
|
+
___default["default"](suppliedSize[0]).times(row => {
|
|
38705
|
+
___default["default"](suppliedSize[1]).times(col => {
|
|
38706
|
+
if (supplied[row][col] == null || supplied[row][col].toString().length === 0) {
|
|
38707
|
+
hasEmptyCell = true;
|
|
38708
|
+
}
|
|
38709
|
+
if (!incorrectSize) {
|
|
38710
|
+
const validator = createValidator(
|
|
38711
|
+
// @ts-expect-error - TS2345 - Argument of type 'number' is not assignable to parameter of type 'string'.
|
|
38712
|
+
solution[row][col], {
|
|
38713
|
+
simplify: true
|
|
38714
|
+
}, strings);
|
|
38715
|
+
const result = validator(supplied[row][col]);
|
|
38716
|
+
if (result.message) {
|
|
38717
|
+
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'null'.
|
|
38718
|
+
message = result.message;
|
|
38719
|
+
}
|
|
38720
|
+
if (!result.correct) {
|
|
38721
|
+
incorrect = true;
|
|
38722
|
+
}
|
|
38723
|
+
}
|
|
38724
|
+
});
|
|
38725
|
+
});
|
|
38726
|
+
if (hasEmptyCell) {
|
|
38727
|
+
return {
|
|
38728
|
+
type: "invalid",
|
|
38729
|
+
message: strings.fillAllCells
|
|
38730
|
+
};
|
|
38731
|
+
}
|
|
38732
|
+
if (incorrectSize) {
|
|
38733
|
+
return {
|
|
38734
|
+
type: "points",
|
|
38735
|
+
earned: 0,
|
|
38736
|
+
total: 1,
|
|
38737
|
+
message: null
|
|
38738
|
+
};
|
|
38739
|
+
}
|
|
38740
|
+
return {
|
|
38741
|
+
type: "points",
|
|
38742
|
+
earned: incorrect ? 0 : 1,
|
|
38743
|
+
total: 1,
|
|
38744
|
+
message: message
|
|
38745
|
+
};
|
|
38746
|
+
}
|
|
38556
38747
|
|
|
38557
38748
|
const {
|
|
38558
38749
|
assert: assert$2
|
|
@@ -38602,7 +38793,7 @@ const getRefForPath$1 = function (path) {
|
|
|
38602
38793
|
const column = getColumnFromPath$1(path);
|
|
38603
38794
|
return "answer" + row + "," + column;
|
|
38604
38795
|
};
|
|
38605
|
-
|
|
38796
|
+
function getMatrixSize(matrix) {
|
|
38606
38797
|
const matrixSize = [1, 1];
|
|
38607
38798
|
|
|
38608
38799
|
// We need to find the widest row and tallest column to get the correct
|
|
@@ -38624,7 +38815,7 @@ const getMatrixSize = function (matrix) {
|
|
|
38624
38815
|
}
|
|
38625
38816
|
});
|
|
38626
38817
|
return matrixSize;
|
|
38627
|
-
}
|
|
38818
|
+
}
|
|
38628
38819
|
class Matrix extends React__namespace.Component {
|
|
38629
38820
|
static contextType = PerseusI18nContext;
|
|
38630
38821
|
// @ts-expect-error - TS2564 - Property 'cursorPosition' has no initializer and is not definitely assigned in the constructor.
|
|
@@ -38639,57 +38830,7 @@ class Matrix extends React__namespace.Component {
|
|
|
38639
38830
|
linterContext: PerseusLinter.linterContextDefault
|
|
38640
38831
|
};
|
|
38641
38832
|
static validate(state, rubric, strings) {
|
|
38642
|
-
|
|
38643
|
-
const supplied = state.answers;
|
|
38644
|
-
const solutionSize = getMatrixSize(solution);
|
|
38645
|
-
const suppliedSize = getMatrixSize(supplied);
|
|
38646
|
-
const incorrectSize = solutionSize[0] !== suppliedSize[0] || solutionSize[1] !== suppliedSize[1];
|
|
38647
|
-
const createValidator = KhanAnswerTypes.number.createValidatorFunctional;
|
|
38648
|
-
let message = null;
|
|
38649
|
-
let hasEmptyCell = false;
|
|
38650
|
-
let incorrect = false;
|
|
38651
|
-
___default["default"](suppliedSize[0]).times(row => {
|
|
38652
|
-
___default["default"](suppliedSize[1]).times(col => {
|
|
38653
|
-
if (supplied[row][col] == null || supplied[row][col].toString().length === 0) {
|
|
38654
|
-
hasEmptyCell = true;
|
|
38655
|
-
}
|
|
38656
|
-
if (!incorrectSize) {
|
|
38657
|
-
const validator = createValidator(
|
|
38658
|
-
// @ts-expect-error - TS2345 - Argument of type 'number' is not assignable to parameter of type 'string'.
|
|
38659
|
-
solution[row][col], {
|
|
38660
|
-
simplify: true
|
|
38661
|
-
}, strings);
|
|
38662
|
-
const result = validator(supplied[row][col]);
|
|
38663
|
-
if (result.message) {
|
|
38664
|
-
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'null'.
|
|
38665
|
-
message = result.message;
|
|
38666
|
-
}
|
|
38667
|
-
if (!result.correct) {
|
|
38668
|
-
incorrect = true;
|
|
38669
|
-
}
|
|
38670
|
-
}
|
|
38671
|
-
});
|
|
38672
|
-
});
|
|
38673
|
-
if (hasEmptyCell) {
|
|
38674
|
-
return {
|
|
38675
|
-
type: "invalid",
|
|
38676
|
-
message: strings.fillAllCells
|
|
38677
|
-
};
|
|
38678
|
-
}
|
|
38679
|
-
if (incorrectSize) {
|
|
38680
|
-
return {
|
|
38681
|
-
type: "points",
|
|
38682
|
-
earned: 0,
|
|
38683
|
-
total: 1,
|
|
38684
|
-
message: null
|
|
38685
|
-
};
|
|
38686
|
-
}
|
|
38687
|
-
return {
|
|
38688
|
-
type: "points",
|
|
38689
|
-
earned: incorrect ? 0 : 1,
|
|
38690
|
-
total: 1,
|
|
38691
|
-
message: message
|
|
38692
|
-
};
|
|
38833
|
+
return matrixValidator(state, rubric, strings);
|
|
38693
38834
|
}
|
|
38694
38835
|
state = {
|
|
38695
38836
|
enterTheMatrix: 0
|
|
@@ -38834,7 +38975,7 @@ class Matrix extends React__namespace.Component {
|
|
|
38834
38975
|
};
|
|
38835
38976
|
}
|
|
38836
38977
|
simpleValidate(rubric) {
|
|
38837
|
-
return
|
|
38978
|
+
return matrixValidator(this.getUserInput(), rubric, this.context.strings);
|
|
38838
38979
|
}
|
|
38839
38980
|
render() {
|
|
38840
38981
|
// Set the input sizes through JS so we can control the size of the
|
|
@@ -40071,6 +40212,45 @@ var Molecule$1 = {
|
|
|
40071
40212
|
widget: MoleculeWidget
|
|
40072
40213
|
};
|
|
40073
40214
|
|
|
40215
|
+
function numberLineValidator(state, rubric) {
|
|
40216
|
+
const range = rubric.range;
|
|
40217
|
+
const divisionRange = state.divisionRange;
|
|
40218
|
+
const start = rubric.initialX != null ? rubric.initialX : range[0];
|
|
40219
|
+
const startRel = rubric.isInequality ? "ge" : "eq";
|
|
40220
|
+
const correctRel = rubric.correctRel || "eq";
|
|
40221
|
+
const correctPos = kmath.number.equal(state.numLinePosition, rubric.correctX || 0);
|
|
40222
|
+
const outsideAllowedRange = state.numDivisions > divisionRange[1] || state.numDivisions < divisionRange[0];
|
|
40223
|
+
|
|
40224
|
+
// TODO: I don't think isTickCrtl is a thing anymore
|
|
40225
|
+
if (state.isTickCrtl && outsideAllowedRange) {
|
|
40226
|
+
return {
|
|
40227
|
+
type: "invalid",
|
|
40228
|
+
message: "Number of divisions is outside the allowed range."
|
|
40229
|
+
};
|
|
40230
|
+
}
|
|
40231
|
+
if (correctPos && correctRel === state.rel) {
|
|
40232
|
+
return {
|
|
40233
|
+
type: "points",
|
|
40234
|
+
earned: 1,
|
|
40235
|
+
total: 1,
|
|
40236
|
+
message: null
|
|
40237
|
+
};
|
|
40238
|
+
}
|
|
40239
|
+
if (state.numLinePosition === start && state.rel === startRel) {
|
|
40240
|
+
// We're where we started.
|
|
40241
|
+
return {
|
|
40242
|
+
type: "invalid",
|
|
40243
|
+
message: null
|
|
40244
|
+
};
|
|
40245
|
+
}
|
|
40246
|
+
return {
|
|
40247
|
+
type: "points",
|
|
40248
|
+
earned: 0,
|
|
40249
|
+
total: 1,
|
|
40250
|
+
message: null
|
|
40251
|
+
};
|
|
40252
|
+
}
|
|
40253
|
+
|
|
40074
40254
|
// @ts-expect-error - TS2339 - Property 'MovablePoint' does not exist on type 'typeof Graphie'.
|
|
40075
40255
|
const MovablePoint = Graphie.MovablePoint;
|
|
40076
40256
|
// @ts-expect-error - TS2339 - Property 'Line' does not exist on type 'typeof Graphie'.
|
|
@@ -40218,6 +40398,9 @@ class NumberLine extends React__namespace.Component {
|
|
|
40218
40398
|
state = {
|
|
40219
40399
|
numDivisionsEmpty: false
|
|
40220
40400
|
};
|
|
40401
|
+
static validate(state, rubric) {
|
|
40402
|
+
return numberLineValidator(state, rubric);
|
|
40403
|
+
}
|
|
40221
40404
|
change = (() => {
|
|
40222
40405
|
var _this = this;
|
|
40223
40406
|
return function () {
|
|
@@ -40509,8 +40692,7 @@ class NumberLine extends React__namespace.Component {
|
|
|
40509
40692
|
};
|
|
40510
40693
|
}
|
|
40511
40694
|
simpleValidate(rubric) {
|
|
40512
|
-
|
|
40513
|
-
return NumberLine.validate(this.getUserInput(), rubric);
|
|
40695
|
+
return numberLineValidator(this.getUserInput(), rubric);
|
|
40514
40696
|
}
|
|
40515
40697
|
render() {
|
|
40516
40698
|
const {
|
|
@@ -40562,46 +40744,6 @@ class NumberLine extends React__namespace.Component {
|
|
|
40562
40744
|
})) : this._renderGraphie(), !this.props.static && this.props.isInequality && inequalityControls);
|
|
40563
40745
|
}
|
|
40564
40746
|
}
|
|
40565
|
-
___default["default"].extend(NumberLine, {
|
|
40566
|
-
validate: function (state, rubric) {
|
|
40567
|
-
const range = rubric.range;
|
|
40568
|
-
const divisionRange = state.divisionRange;
|
|
40569
|
-
const start = rubric.initialX != null ? rubric.initialX : range[0];
|
|
40570
|
-
const startRel = rubric.isInequality ? "ge" : "eq";
|
|
40571
|
-
const correctRel = rubric.correctRel || "eq";
|
|
40572
|
-
const correctPos = kmath.number.equal(state.numLinePosition, rubric.correctX || 0);
|
|
40573
|
-
const outsideAllowedRange = state.numDivisions > divisionRange[1] || state.numDivisions < divisionRange[0];
|
|
40574
|
-
|
|
40575
|
-
// TODO: I don't think isTickCrtl is a thing anymore
|
|
40576
|
-
if (state.isTickCrtl && outsideAllowedRange) {
|
|
40577
|
-
return {
|
|
40578
|
-
type: "invalid",
|
|
40579
|
-
message: "Number of divisions is outside the allowed range."
|
|
40580
|
-
};
|
|
40581
|
-
}
|
|
40582
|
-
if (correctPos && correctRel === state.rel) {
|
|
40583
|
-
return {
|
|
40584
|
-
type: "points",
|
|
40585
|
-
earned: 1,
|
|
40586
|
-
total: 1,
|
|
40587
|
-
message: null
|
|
40588
|
-
};
|
|
40589
|
-
}
|
|
40590
|
-
if (state.numLinePosition === start && state.rel === startRel) {
|
|
40591
|
-
// We're where we started.
|
|
40592
|
-
return {
|
|
40593
|
-
type: "invalid",
|
|
40594
|
-
message: null
|
|
40595
|
-
};
|
|
40596
|
-
}
|
|
40597
|
-
return {
|
|
40598
|
-
type: "points",
|
|
40599
|
-
earned: 0,
|
|
40600
|
-
total: 1,
|
|
40601
|
-
message: null
|
|
40602
|
-
};
|
|
40603
|
-
}
|
|
40604
|
-
});
|
|
40605
40747
|
const numberLineTransform = editorProps => {
|
|
40606
40748
|
const props = ___default["default"].pick(editorProps, ["range", "labelRange", "labelStyle", "labelTicks", "divisionRange", "snapDivisions", "isTickCtrl", "isInequality", "showTooltips"]);
|
|
40607
40749
|
const numLinePosition = editorProps.initialX != null ? editorProps.initialX : editorProps.range[0];
|
|
@@ -43668,10 +43810,25 @@ var PhetSimulation$1 = {
|
|
|
43668
43810
|
isLintable: true
|
|
43669
43811
|
};
|
|
43670
43812
|
|
|
43671
|
-
/* eslint-disable react/no-unsafe */
|
|
43672
43813
|
const {
|
|
43673
43814
|
deepEq
|
|
43674
43815
|
} = Util;
|
|
43816
|
+
function plotterValidator(userInput, rubric) {
|
|
43817
|
+
if (deepEq(userInput, rubric.starting)) {
|
|
43818
|
+
return {
|
|
43819
|
+
type: "invalid",
|
|
43820
|
+
message: null
|
|
43821
|
+
};
|
|
43822
|
+
}
|
|
43823
|
+
return {
|
|
43824
|
+
type: "points",
|
|
43825
|
+
earned: deepEq(userInput, rubric.correct) ? 1 : 0,
|
|
43826
|
+
total: 1,
|
|
43827
|
+
message: null
|
|
43828
|
+
};
|
|
43829
|
+
}
|
|
43830
|
+
|
|
43831
|
+
/* eslint-disable react/no-unsafe */
|
|
43675
43832
|
class Plotter extends React__namespace.Component {
|
|
43676
43833
|
static contextType = PerseusI18nContext;
|
|
43677
43834
|
// @ts-expect-error - TS2564 - Property 'shouldSetupGraphie' has no initializer and is not definitely assigned in the constructor.
|
|
@@ -43700,6 +43857,9 @@ class Plotter extends React__namespace.Component {
|
|
|
43700
43857
|
// bottom label.
|
|
43701
43858
|
categoryHeights: {}
|
|
43702
43859
|
};
|
|
43860
|
+
static validate(userInput, rubric) {
|
|
43861
|
+
return plotterValidator(userInput, rubric);
|
|
43862
|
+
}
|
|
43703
43863
|
componentDidMount() {
|
|
43704
43864
|
this._isMounted = true;
|
|
43705
43865
|
this.setupGraphie(this.state);
|
|
@@ -44445,8 +44605,7 @@ class Plotter extends React__namespace.Component {
|
|
|
44445
44605
|
return this.state.values;
|
|
44446
44606
|
}
|
|
44447
44607
|
simpleValidate(rubric) {
|
|
44448
|
-
|
|
44449
|
-
return Plotter.validate(this.getUserInput(), rubric);
|
|
44608
|
+
return plotterValidator(this.getUserInput(), rubric);
|
|
44450
44609
|
}
|
|
44451
44610
|
render() {
|
|
44452
44611
|
// TODO(kevinb) actually compute the size of the graphie correctly and
|
|
@@ -44465,22 +44624,6 @@ class Plotter extends React__namespace.Component {
|
|
|
44465
44624
|
});
|
|
44466
44625
|
}
|
|
44467
44626
|
}
|
|
44468
|
-
___default["default"].extend(Plotter, {
|
|
44469
|
-
validate: function (userInput, rubric) {
|
|
44470
|
-
if (deepEq(userInput, rubric.starting)) {
|
|
44471
|
-
return {
|
|
44472
|
-
type: "invalid",
|
|
44473
|
-
message: null
|
|
44474
|
-
};
|
|
44475
|
-
}
|
|
44476
|
-
return {
|
|
44477
|
-
type: "points",
|
|
44478
|
-
earned: deepEq(userInput, rubric.correct) ? 1 : 0,
|
|
44479
|
-
total: 1,
|
|
44480
|
-
message: null
|
|
44481
|
-
};
|
|
44482
|
-
}
|
|
44483
|
-
});
|
|
44484
44627
|
|
|
44485
44628
|
// We don't need to change any of the original props for static mode
|
|
44486
44629
|
const staticTransform = ___default["default"].identity;
|
|
@@ -44649,6 +44792,66 @@ var Sorter$1 = {
|
|
|
44649
44792
|
isLintable: true
|
|
44650
44793
|
};
|
|
44651
44794
|
|
|
44795
|
+
function tableValidator(state, rubric, strings) {
|
|
44796
|
+
const filterNonEmpty = function (table) {
|
|
44797
|
+
return ___default["default"].filter(table, function (row) {
|
|
44798
|
+
// Check if row has a cell that is nonempty
|
|
44799
|
+
return ___default["default"].some(row, ___default["default"].identity);
|
|
44800
|
+
});
|
|
44801
|
+
};
|
|
44802
|
+
const solution = filterNonEmpty(rubric.answers);
|
|
44803
|
+
const supplied = filterNonEmpty(state);
|
|
44804
|
+
const hasEmptyCell = ___default["default"].some(supplied, function (row) {
|
|
44805
|
+
return ___default["default"].some(row, function (cell) {
|
|
44806
|
+
return cell === "";
|
|
44807
|
+
});
|
|
44808
|
+
});
|
|
44809
|
+
if (hasEmptyCell || !supplied.length) {
|
|
44810
|
+
return {
|
|
44811
|
+
type: "invalid",
|
|
44812
|
+
message: null
|
|
44813
|
+
};
|
|
44814
|
+
}
|
|
44815
|
+
if (supplied.length !== solution.length) {
|
|
44816
|
+
return {
|
|
44817
|
+
type: "points",
|
|
44818
|
+
earned: 0,
|
|
44819
|
+
total: 1,
|
|
44820
|
+
message: null
|
|
44821
|
+
};
|
|
44822
|
+
}
|
|
44823
|
+
const createValidator = KhanAnswerTypes.number.createValidatorFunctional;
|
|
44824
|
+
let message = null;
|
|
44825
|
+
const allCorrect = ___default["default"].every(solution, function (rowSolution) {
|
|
44826
|
+
for (let i = 0; i < supplied.length; i++) {
|
|
44827
|
+
const rowSupplied = supplied[i];
|
|
44828
|
+
const correct = ___default["default"].every(rowSupplied, function (cellSupplied, i) {
|
|
44829
|
+
const cellSolution = rowSolution[i];
|
|
44830
|
+
const validator = createValidator(cellSolution, {
|
|
44831
|
+
simplify: true
|
|
44832
|
+
}, strings);
|
|
44833
|
+
const result = validator(cellSupplied);
|
|
44834
|
+
if (result.message) {
|
|
44835
|
+
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'null'.
|
|
44836
|
+
message = result.message;
|
|
44837
|
+
}
|
|
44838
|
+
return result.correct;
|
|
44839
|
+
});
|
|
44840
|
+
if (correct) {
|
|
44841
|
+
supplied.splice(i, 1);
|
|
44842
|
+
return true;
|
|
44843
|
+
}
|
|
44844
|
+
}
|
|
44845
|
+
return false;
|
|
44846
|
+
});
|
|
44847
|
+
return {
|
|
44848
|
+
type: "points",
|
|
44849
|
+
earned: allCorrect ? 1 : 0,
|
|
44850
|
+
total: 1,
|
|
44851
|
+
message: message
|
|
44852
|
+
};
|
|
44853
|
+
}
|
|
44854
|
+
|
|
44652
44855
|
const {
|
|
44653
44856
|
assert
|
|
44654
44857
|
} = InteractiveUtil;
|
|
@@ -44694,6 +44897,9 @@ class Table extends React__namespace.Component {
|
|
|
44694
44897
|
linterContext: PerseusLinter.linterContextDefault
|
|
44695
44898
|
};
|
|
44696
44899
|
}();
|
|
44900
|
+
static validate(state, rubric, strings) {
|
|
44901
|
+
return tableValidator(state, rubric, strings);
|
|
44902
|
+
}
|
|
44697
44903
|
_getRows = () => {
|
|
44698
44904
|
return this.props.answers.length;
|
|
44699
44905
|
};
|
|
@@ -44723,8 +44929,7 @@ class Table extends React__namespace.Component {
|
|
|
44723
44929
|
});
|
|
44724
44930
|
};
|
|
44725
44931
|
simpleValidate(rubric) {
|
|
44726
|
-
|
|
44727
|
-
return Table.validate(this.getUserInput(), rubric, this.context.strings);
|
|
44932
|
+
return tableValidator(this.getUserInput(), rubric, this.context.strings);
|
|
44728
44933
|
}
|
|
44729
44934
|
_handleFocus = inputPath => {
|
|
44730
44935
|
this.props.onFocus(inputPath);
|
|
@@ -44851,68 +45056,6 @@ class Table extends React__namespace.Component {
|
|
|
44851
45056
|
})));
|
|
44852
45057
|
}
|
|
44853
45058
|
}
|
|
44854
|
-
___default["default"].extend(Table, {
|
|
44855
|
-
validate: function (state, rubric, strings) {
|
|
44856
|
-
const filterNonEmpty = function (table) {
|
|
44857
|
-
return ___default["default"].filter(table, function (row) {
|
|
44858
|
-
// Check if row has a cell that is nonempty
|
|
44859
|
-
return ___default["default"].some(row, ___default["default"].identity);
|
|
44860
|
-
});
|
|
44861
|
-
};
|
|
44862
|
-
const solution = filterNonEmpty(rubric.answers);
|
|
44863
|
-
const supplied = filterNonEmpty(state);
|
|
44864
|
-
const hasEmptyCell = ___default["default"].some(supplied, function (row) {
|
|
44865
|
-
return ___default["default"].some(row, function (cell) {
|
|
44866
|
-
return cell === "";
|
|
44867
|
-
});
|
|
44868
|
-
});
|
|
44869
|
-
if (hasEmptyCell || !supplied.length) {
|
|
44870
|
-
return {
|
|
44871
|
-
type: "invalid",
|
|
44872
|
-
message: null
|
|
44873
|
-
};
|
|
44874
|
-
}
|
|
44875
|
-
if (supplied.length !== solution.length) {
|
|
44876
|
-
return {
|
|
44877
|
-
type: "points",
|
|
44878
|
-
earned: 0,
|
|
44879
|
-
total: 1,
|
|
44880
|
-
message: null
|
|
44881
|
-
};
|
|
44882
|
-
}
|
|
44883
|
-
const createValidator = KhanAnswerTypes.number.createValidatorFunctional;
|
|
44884
|
-
let message = null;
|
|
44885
|
-
const allCorrect = ___default["default"].every(solution, function (rowSolution) {
|
|
44886
|
-
let i;
|
|
44887
|
-
for (i = 0; i < supplied.length; i++) {
|
|
44888
|
-
const rowSupplied = supplied[i];
|
|
44889
|
-
const correct = ___default["default"].every(rowSupplied, function (cellSupplied, i) {
|
|
44890
|
-
const cellSolution = rowSolution[i];
|
|
44891
|
-
const validator = createValidator(cellSolution, {
|
|
44892
|
-
simplify: true
|
|
44893
|
-
}, strings);
|
|
44894
|
-
const result = validator(cellSupplied);
|
|
44895
|
-
if (result.message) {
|
|
44896
|
-
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'null'.
|
|
44897
|
-
message = result.message;
|
|
44898
|
-
}
|
|
44899
|
-
return result.correct;
|
|
44900
|
-
});
|
|
44901
|
-
if (correct) {
|
|
44902
|
-
supplied.splice(i, 1);
|
|
44903
|
-
return true;
|
|
44904
|
-
}
|
|
44905
|
-
}
|
|
44906
|
-
return false;
|
|
44907
|
-
});
|
|
44908
|
-
return {
|
|
44909
|
-
type: "points",
|
|
44910
|
-
earned: allCorrect ? 1 : 0,
|
|
44911
|
-
total: 1,
|
|
44912
|
-
message: message
|
|
44913
|
-
};
|
|
44914
|
-
}
|
|
44915
|
-
});
|
|
44916
45059
|
const propTransform = editorProps => {
|
|
44917
45060
|
// Remove answers before passing to widget
|
|
44918
45061
|
const rows = editorProps.answers.length;
|
|
@@ -45145,7 +45288,7 @@ const init = function (options) {
|
|
|
45145
45288
|
|
|
45146
45289
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45147
45290
|
const libName = "@khanacademy/perseus";
|
|
45148
|
-
const libVersion = "35.
|
|
45291
|
+
const libVersion = "35.1.1";
|
|
45149
45292
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45150
45293
|
|
|
45151
45294
|
const apiVersion = {
|