@khanacademy/perseus 36.1.1 → 37.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/all-widgets.d.ts +1 -0
- package/dist/basic-widgets.d.ts +1 -0
- package/dist/components/math-input.d.ts +1 -1
- package/dist/es/index.js +523 -660
- package/dist/es/index.js.map +1 -1
- package/dist/extra-widgets.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +489 -635
- package/dist/index.js.map +1 -1
- package/dist/renderer-util.d.ts +8 -0
- package/dist/renderer.d.ts +19 -20
- package/dist/server-item-renderer.d.ts +3 -1
- package/dist/types.d.ts +11 -3
- package/dist/util/graphie.d.ts +1 -1
- package/dist/util.d.ts +4 -0
- package/dist/validation.types.d.ts +19 -9
- package/dist/widgets/categorizer/categorizer.d.ts +3 -5
- package/dist/widgets/cs-program/cs-program.d.ts +4 -8
- package/dist/widgets/definition/definition.d.ts +3 -4
- package/dist/widgets/deprecated-standin/deprecated-standin.d.ts +35 -0
- package/dist/widgets/deprecated-standin/index.d.ts +1 -37
- package/dist/widgets/dropdown/dropdown.d.ts +2 -3
- package/dist/widgets/explanation/explanation.d.ts +4 -5
- package/dist/widgets/expression/expression.d.ts +4 -8
- package/dist/widgets/graded-group/graded-group.d.ts +1 -0
- package/dist/widgets/graded-group-set/graded-group-set.d.ts +1 -0
- package/dist/widgets/grapher/grapher.d.ts +2 -3
- package/dist/widgets/group/group.d.ts +8 -5
- package/dist/widgets/iframe/iframe.d.ts +2 -3
- package/dist/widgets/image/image.d.ts +3 -4
- package/dist/widgets/input-number/input-number.d.ts +2 -5
- package/dist/widgets/interaction/interaction.d.ts +3 -4
- package/dist/widgets/interactive-graph.d.ts +3 -5
- package/dist/widgets/interactive-graphs/math/angles.d.ts +3 -1
- package/dist/widgets/interactive-graphs/math/index.d.ts +1 -1
- package/dist/widgets/label-image/label-image.d.ts +3 -4
- package/dist/widgets/matcher/matcher.d.ts +2 -3
- package/dist/widgets/matrix/matrix.d.ts +3 -5
- package/dist/widgets/measurer/measurer.d.ts +2 -4
- package/dist/widgets/molecule/molecule.d.ts +2 -4
- package/dist/widgets/number-line/number-line.d.ts +3 -4
- package/dist/widgets/numeric-input/numeric-input.d.ts +2 -3
- package/dist/widgets/orderer/orderer.d.ts +2 -3
- package/dist/widgets/passage/passage.d.ts +3 -9
- package/dist/widgets/passage-ref/passage-ref.d.ts +3 -4
- package/dist/widgets/passage-ref-target/passage-ref-target.d.ts +2 -4
- package/dist/widgets/phet-simulation/phet-simulation.d.ts +2 -2
- package/dist/widgets/plotter/plotter.d.ts +2 -3
- package/dist/widgets/python-program/python-program.d.ts +1 -0
- package/dist/widgets/radio/radio-component.d.ts +2 -5
- package/dist/widgets/radio/radio.d.ts +1 -0
- package/dist/widgets/sorter/sorter.d.ts +2 -3
- package/dist/widgets/table/table.d.ts +3 -5
- package/dist/widgets/video/video.d.ts +2 -8
- package/dist/widgets.d.ts +2 -1
- package/package.json +9 -9
- package/dist/widgets/expression/expression.types.d.ts +0 -2
package/dist/index.js
CHANGED
|
@@ -2386,23 +2386,11 @@ const anglicizeOperators = tex => {
|
|
|
2386
2386
|
const normalizeTex = tex => {
|
|
2387
2387
|
return anglicizeOperators(tex);
|
|
2388
2388
|
};
|
|
2389
|
-
const deriveKeypadVersion = apiOptions => {
|
|
2390
|
-
// We can derive which version of the keypad is in use. This is
|
|
2391
|
-
// a bit tricky, but this code will be relatively short-lived
|
|
2392
|
-
// as we coalesce onto the new, v2 Keypad, at which point we
|
|
2393
|
-
// can remove this `virtualKeypadVersion` field entirely.
|
|
2394
|
-
return apiOptions.nativeKeypadProxy != null ? "REACT_NATIVE_KEYPAD" : "MATH_INPUT_KEYPAD_V2";
|
|
2395
|
-
};
|
|
2396
2389
|
// The new, MathQuill input expression widget
|
|
2397
2390
|
class Expression extends React__namespace.Component {
|
|
2398
2391
|
static contextType = PerseusI18nContext;
|
|
2399
2392
|
_textareaId = `expression_textarea_${Date.now()}`;
|
|
2400
2393
|
_isMounted = false;
|
|
2401
|
-
|
|
2402
|
-
// TODO remove this in favor of just using expressionValidator
|
|
2403
|
-
static validate(userInput, rubric, strings, locale) {
|
|
2404
|
-
return expressionValidator(userInput, rubric, strings, locale);
|
|
2405
|
-
}
|
|
2406
2394
|
static getUserInputFromProps(props) {
|
|
2407
2395
|
return normalizeTex(props.value);
|
|
2408
2396
|
}
|
|
@@ -2478,26 +2466,6 @@ class Expression extends React__namespace.Component {
|
|
|
2478
2466
|
});
|
|
2479
2467
|
}
|
|
2480
2468
|
};
|
|
2481
|
-
simpleValidate = _ref => {
|
|
2482
|
-
let {
|
|
2483
|
-
scoring,
|
|
2484
|
-
...rubric
|
|
2485
|
-
} = _ref;
|
|
2486
|
-
const score = expressionValidator(this.getUserInput(), rubric, this.context.strings, this.context.locale);
|
|
2487
|
-
|
|
2488
|
-
// "scoring" is a flag that indicates when we are checking answers.
|
|
2489
|
-
// otherwise, we may just be checking validity after changes.
|
|
2490
|
-
if (scoring && score.type !== "invalid") {
|
|
2491
|
-
this.props.analytics?.onAnalyticsEvent({
|
|
2492
|
-
type: "perseus:expression-evaluated",
|
|
2493
|
-
payload: {
|
|
2494
|
-
result: score.earned === score.total ? "correct" : "incorrect",
|
|
2495
|
-
virtualKeypadVersion: deriveKeypadVersion(this.props.apiOptions)
|
|
2496
|
-
}
|
|
2497
|
-
});
|
|
2498
|
-
}
|
|
2499
|
-
return score;
|
|
2500
|
-
};
|
|
2501
2469
|
getUserInput() {
|
|
2502
2470
|
return Expression.getUserInputFromProps(this.props);
|
|
2503
2471
|
}
|
|
@@ -2796,7 +2764,8 @@ var Expression$1 = {
|
|
|
2796
2764
|
},
|
|
2797
2765
|
propUpgrades: propUpgrades$2,
|
|
2798
2766
|
// For use by the editor
|
|
2799
|
-
isLintable: true
|
|
2767
|
+
isLintable: true,
|
|
2768
|
+
validator: expressionValidator
|
|
2800
2769
|
};
|
|
2801
2770
|
|
|
2802
2771
|
/**
|
|
@@ -3026,6 +2995,9 @@ function combineScores(scoreA, scoreB) {
|
|
|
3026
2995
|
}
|
|
3027
2996
|
});
|
|
3028
2997
|
}
|
|
2998
|
+
function flattenScores(widgetScoreMap) {
|
|
2999
|
+
return Object.values(widgetScoreMap).reduce(combineScores, noScore);
|
|
3000
|
+
}
|
|
3029
3001
|
function isCorrect(score) {
|
|
3030
3002
|
return score.type === "points" && score.earned >= score.total;
|
|
3031
3003
|
}
|
|
@@ -3556,6 +3528,7 @@ const Util = {
|
|
|
3556
3528
|
shuffle: shuffle$3,
|
|
3557
3529
|
split,
|
|
3558
3530
|
combineScores,
|
|
3531
|
+
flattenScores,
|
|
3559
3532
|
keScoreFromPerseusScore,
|
|
3560
3533
|
firstNumericalParse: firstNumericalParse$1,
|
|
3561
3534
|
stringArrayOfSize: stringArrayOfSize$1,
|
|
@@ -10094,6 +10067,8 @@ const setLabelMargins = function (span, size) {
|
|
|
10094
10067
|
marginTop: -height / 2 - y * scale
|
|
10095
10068
|
});
|
|
10096
10069
|
} else {
|
|
10070
|
+
const currentHeightMatchesProps = span.scrollHeight === height;
|
|
10071
|
+
|
|
10097
10072
|
// We are using jQuery to collect information and calculate a scale
|
|
10098
10073
|
// since we don't have a way to pass it to this function.
|
|
10099
10074
|
// We need the width of the container in order to calculate the scale to apply to the label.
|
|
@@ -10115,6 +10090,17 @@ const setLabelMargins = function (span, size) {
|
|
|
10115
10090
|
// Inherited line-height values can really mess up placement.
|
|
10116
10091
|
$container.css("line-height", "normal");
|
|
10117
10092
|
|
|
10093
|
+
// TODO: Verify that this change doesn't impact other graphies
|
|
10094
|
+
// Check the full list of consuming widgets.
|
|
10095
|
+
|
|
10096
|
+
// If the change in line-height affected the height of the element,
|
|
10097
|
+
// then the height used for calculations should be updated.
|
|
10098
|
+
// This can happen when the first label in the container calls this method,
|
|
10099
|
+
// and the line-height was different when the height measurement was originally referenced.
|
|
10100
|
+
if (currentHeightMatchesProps && span.scrollHeight !== height) {
|
|
10101
|
+
height = span.scrollHeight;
|
|
10102
|
+
}
|
|
10103
|
+
|
|
10118
10104
|
// The expected width of the graphie is found in the "max-width" property on ".svg-image" containers,
|
|
10119
10105
|
// and is found in the "width" property on ".graphie" containers.
|
|
10120
10106
|
const widthValues = $container.css(["max-width", "width"]) ?? {
|
|
@@ -10176,7 +10162,10 @@ const GraphUtils = {
|
|
|
10176
10162
|
return kmath.point.distanceToPoint(point1, point2);
|
|
10177
10163
|
},
|
|
10178
10164
|
// Find the angle in degrees between two or three points
|
|
10179
|
-
|
|
10165
|
+
// This function is deprecated as it has several issues calculating
|
|
10166
|
+
// correctly when dealing with reflex angles or the position of point2
|
|
10167
|
+
// (LEMS-2202) Remove this function while removing the Legacy Interactive Graph.
|
|
10168
|
+
findAngleDeprecated: function (point1, point2, vertex) {
|
|
10180
10169
|
if (vertex === undefined) {
|
|
10181
10170
|
const x = point1[0] - point2[0];
|
|
10182
10171
|
const y = point1[1] - point2[1];
|
|
@@ -10185,7 +10174,7 @@ const GraphUtils = {
|
|
|
10185
10174
|
}
|
|
10186
10175
|
return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
|
|
10187
10176
|
}
|
|
10188
|
-
return GraphUtils.
|
|
10177
|
+
return GraphUtils.findAngleDeprecated(point1, vertex) - GraphUtils.findAngleDeprecated(point2, vertex);
|
|
10189
10178
|
},
|
|
10190
10179
|
graphs: {}
|
|
10191
10180
|
};
|
|
@@ -11202,8 +11191,8 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11202
11191
|
p1 = options.point3;
|
|
11203
11192
|
p3 = options.point1;
|
|
11204
11193
|
}
|
|
11205
|
-
const startAngle = GraphUtils.
|
|
11206
|
-
const endAngle = GraphUtils.
|
|
11194
|
+
const startAngle = GraphUtils.findAngleDeprecated(p1, vertex);
|
|
11195
|
+
const endAngle = GraphUtils.findAngleDeprecated(p3, vertex);
|
|
11207
11196
|
const angle = (endAngle + 360 - startAngle) % 360;
|
|
11208
11197
|
const halfAngle = (startAngle + angle / 2) % 360;
|
|
11209
11198
|
const sPadding = 5 * options.pushOut;
|
|
@@ -11370,15 +11359,15 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11370
11359
|
let angle = 135;
|
|
11371
11360
|
let halfAngle;
|
|
11372
11361
|
if (p1 && p3) {
|
|
11373
|
-
const startAngle = GraphUtils.
|
|
11374
|
-
const endAngle = GraphUtils.
|
|
11362
|
+
const startAngle = GraphUtils.findAngleDeprecated(p1, vertex);
|
|
11363
|
+
const endAngle = GraphUtils.findAngleDeprecated(p3, vertex);
|
|
11375
11364
|
angle = (endAngle + 360 - startAngle) % 360;
|
|
11376
11365
|
halfAngle = (startAngle + angle / 2 + 180) % 360;
|
|
11377
11366
|
} else if (p1) {
|
|
11378
|
-
const parallelAngle = GraphUtils.
|
|
11367
|
+
const parallelAngle = GraphUtils.findAngleDeprecated(vertex, p1);
|
|
11379
11368
|
halfAngle = parallelAngle + 90;
|
|
11380
11369
|
} else if (p3) {
|
|
11381
|
-
const parallelAngle = GraphUtils.
|
|
11370
|
+
const parallelAngle = GraphUtils.findAngleDeprecated(p3, vertex);
|
|
11382
11371
|
halfAngle = parallelAngle + 90;
|
|
11383
11372
|
} else {
|
|
11384
11373
|
// Standalone point
|
|
@@ -11584,7 +11573,7 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11584
11573
|
const vertex = constraints.fixedAngle.vertex.coord || constraints.fixedAngle.vertex;
|
|
11585
11574
|
const ref = constraints.fixedAngle.ref.coord || constraints.fixedAngle.ref;
|
|
11586
11575
|
const distPoint = constraints.fixedDistance.point.coord || constraints.fixedDistance.point;
|
|
11587
|
-
const constrainedAngle = (constraints.fixedAngle.angle + GraphUtils.
|
|
11576
|
+
const constrainedAngle = (constraints.fixedAngle.angle + GraphUtils.findAngleDeprecated(ref, vertex)) * Math.PI / 180;
|
|
11588
11577
|
const length = constraints.fixedDistance.dist;
|
|
11589
11578
|
newCoord[0] = length * Math.cos(constrainedAngle) + distPoint[0];
|
|
11590
11579
|
newCoord[1] = length * Math.sin(constrainedAngle) + distPoint[1];
|
|
@@ -11593,8 +11582,8 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11593
11582
|
} else if (typeof constraints.fixedAngle.angle === "number") {
|
|
11594
11583
|
const vertex = constraints.fixedAngle.vertex.coord || constraints.fixedAngle.vertex;
|
|
11595
11584
|
const ref = constraints.fixedAngle.ref.coord || constraints.fixedAngle.ref;
|
|
11596
|
-
const constrainedAngle = (constraints.fixedAngle.angle + GraphUtils.
|
|
11597
|
-
const angle = GraphUtils.
|
|
11585
|
+
const constrainedAngle = (constraints.fixedAngle.angle + GraphUtils.findAngleDeprecated(ref, vertex)) * Math.PI / 180;
|
|
11586
|
+
const angle = GraphUtils.findAngleDeprecated(coord, vertex) * Math.PI / 180;
|
|
11598
11587
|
const distance = GraphUtils.getDistance(coord, vertex);
|
|
11599
11588
|
let length = distance * Math.cos(constrainedAngle - angle);
|
|
11600
11589
|
length = length < 1.0 ? 1.0 : length;
|
|
@@ -11604,7 +11593,7 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11604
11593
|
// distance is constrained
|
|
11605
11594
|
} else if (typeof constraints.fixedDistance.dist === "number") {
|
|
11606
11595
|
const distPoint = constraints.fixedDistance.point.coord || constraints.fixedDistance.point;
|
|
11607
|
-
let angle = GraphUtils.
|
|
11596
|
+
let angle = GraphUtils.findAngleDeprecated(coord, distPoint);
|
|
11608
11597
|
const length = constraints.fixedDistance.dist;
|
|
11609
11598
|
angle = angle * Math.PI / 180;
|
|
11610
11599
|
newCoord[0] = length * Math.cos(angle) + distPoint[0];
|
|
@@ -13804,7 +13793,7 @@ ___default["default"].extend(MovableAngle.prototype, {
|
|
|
13804
13793
|
___default["default"].each([0, 2], function (i) {
|
|
13805
13794
|
const oldPoint = points[i].coord;
|
|
13806
13795
|
let newPoint = kmath.vector.add(oldPoint, delta);
|
|
13807
|
-
let angle = GraphUtils.
|
|
13796
|
+
let angle = GraphUtils.findAngleDeprecated(newVertex, newPoint);
|
|
13808
13797
|
angle *= Math.PI / 180;
|
|
13809
13798
|
newPoint = graphie.constrainToBoundsOnAngle(newPoint, 10, angle);
|
|
13810
13799
|
newPoints[i] = newPoint;
|
|
@@ -13837,7 +13826,7 @@ ___default["default"].extend(MovableAngle.prototype, {
|
|
|
13837
13826
|
return false;
|
|
13838
13827
|
}
|
|
13839
13828
|
if (snap) {
|
|
13840
|
-
let angle = GraphUtils.
|
|
13829
|
+
let angle = GraphUtils.findAngleDeprecated(newPoint, vertex);
|
|
13841
13830
|
angle = Math.round((angle - snapOffset) / snap) * snap + snapOffset;
|
|
13842
13831
|
const distance = GraphUtils.getDistance(newPoint, vertex);
|
|
13843
13832
|
return kmath.vector.add(vertex, polar$1(distance, angle));
|
|
@@ -13925,7 +13914,7 @@ ___default["default"].extend(MovableAngle.prototype, {
|
|
|
13925
13914
|
* coords when interpreted in a clockwise direction.
|
|
13926
13915
|
*/
|
|
13927
13916
|
_getClockwiseAngle: function (coords) {
|
|
13928
|
-
const rawAngle = GraphUtils.
|
|
13917
|
+
const rawAngle = GraphUtils.findAngleDeprecated(
|
|
13929
13918
|
// The order of these is "weird" to match what a clockwise
|
|
13930
13919
|
// order is in graphie.labelAngle
|
|
13931
13920
|
coords[2],
|
|
@@ -14774,7 +14763,7 @@ const mapObject$1 = function (obj, lambda) {
|
|
|
14774
14763
|
* });
|
|
14775
14764
|
* {a: 'aa', b: 'bb'}
|
|
14776
14765
|
*/
|
|
14777
|
-
const mapObjectFromArray
|
|
14766
|
+
const mapObjectFromArray = function (arr, lambda) {
|
|
14778
14767
|
const result = {};
|
|
14779
14768
|
___default["default"].each(arr, function (elem) {
|
|
14780
14769
|
result[elem] = lambda(elem);
|
|
@@ -14784,7 +14773,7 @@ const mapObjectFromArray$1 = function (arr, lambda) {
|
|
|
14784
14773
|
var objective_ = {
|
|
14785
14774
|
pluck: pluck,
|
|
14786
14775
|
mapObject: mapObject$1,
|
|
14787
|
-
mapObjectFromArray: mapObjectFromArray
|
|
14776
|
+
mapObjectFromArray: mapObjectFromArray
|
|
14788
14777
|
};
|
|
14789
14778
|
|
|
14790
14779
|
/**
|
|
@@ -19000,169 +18989,6 @@ class QuestionParagraph extends React__namespace.Component {
|
|
|
19000
18989
|
}
|
|
19001
18990
|
}
|
|
19002
18991
|
|
|
19003
|
-
/**
|
|
19004
|
-
* TranslationLinter asynchronously calls Khan Academy's poentry linter.
|
|
19005
|
-
*
|
|
19006
|
-
* The poentry linter checks for issues that prevent content from being
|
|
19007
|
-
* translated. This linter is KA-specific and so TranslationLinter does not do
|
|
19008
|
-
* anything in non-KA environments.
|
|
19009
|
-
*/
|
|
19010
|
-
|
|
19011
|
-
// A LintCB is called once the linter has been run.
|
|
19012
|
-
|
|
19013
|
-
// msec to wait before we actually call the linter after receiving the
|
|
19014
|
-
// last call. The timer is reset if the lint is called during the wait.
|
|
19015
|
-
const DEBOUNCE_TIMEOUT = 1000;
|
|
19016
|
-
class TranslationLinter {
|
|
19017
|
-
// To avoid linting when nothing has changed.
|
|
19018
|
-
previousContent = null;
|
|
19019
|
-
|
|
19020
|
-
/**
|
|
19021
|
-
* Runs the poentry linter on the given perseus string asynchronously.
|
|
19022
|
-
*
|
|
19023
|
-
* It doesn't do anything unless we're in a KA environment.
|
|
19024
|
-
*
|
|
19025
|
-
* onLintErrorsGenerated is called even if there are no errors. It is not
|
|
19026
|
-
* called if perseusStr is unchanged from the last call.
|
|
19027
|
-
*
|
|
19028
|
-
* Linting is relatively expensive -- so we debounce linting each instance
|
|
19029
|
-
* for some amount of time.
|
|
19030
|
-
*/
|
|
19031
|
-
runLinter = _.debounce((perseusStr, onLintErrorsGenerated) => {
|
|
19032
|
-
// TODO(FEI-5003): Do we still need to check for 'KA'?
|
|
19033
|
-
if (typeof KA === "undefined") {
|
|
19034
|
-
return;
|
|
19035
|
-
}
|
|
19036
|
-
if (perseusStr === this.previousContent) {
|
|
19037
|
-
return;
|
|
19038
|
-
}
|
|
19039
|
-
this.previousContent = perseusStr;
|
|
19040
|
-
if (perseusStr === "") {
|
|
19041
|
-
onLintErrorsGenerated([]);
|
|
19042
|
-
return;
|
|
19043
|
-
}
|
|
19044
|
-
fetch("/api/internal/translations/lint_poentry?preview=1&lang=en", {
|
|
19045
|
-
headers: {
|
|
19046
|
-
"Content-Type": "application/json"
|
|
19047
|
-
},
|
|
19048
|
-
body: JSON.stringify({
|
|
19049
|
-
// The poentry linter verifies that the translation from
|
|
19050
|
-
// the original text to the translated text is sane. We
|
|
19051
|
-
// want to ensure that the translation from English to
|
|
19052
|
-
// English is sane, so msgid === mstr.
|
|
19053
|
-
msgid: perseusStr,
|
|
19054
|
-
// msgid is the original text
|
|
19055
|
-
msgstr: perseusStr,
|
|
19056
|
-
// msgstr is the translated text
|
|
19057
|
-
format: "perseus_text",
|
|
19058
|
-
filename: ""
|
|
19059
|
-
}),
|
|
19060
|
-
method: "POST"
|
|
19061
|
-
}).then(response => {
|
|
19062
|
-
if (response.status >= 400) {
|
|
19063
|
-
return {
|
|
19064
|
-
status: "error",
|
|
19065
|
-
message: "Could not run i18n linter."
|
|
19066
|
-
};
|
|
19067
|
-
}
|
|
19068
|
-
return response.json();
|
|
19069
|
-
}, rejection => {
|
|
19070
|
-
return {
|
|
19071
|
-
status: "error",
|
|
19072
|
-
message: "Could not run i18n linter."
|
|
19073
|
-
};
|
|
19074
|
-
}).then(json => {
|
|
19075
|
-
if (json.status === "error") {
|
|
19076
|
-
onLintErrorsGenerated(["Some part of this text makes it untranslatable. " + "The specific message from the i18n linter was: " + json.message.replace(/\n/g, " ")]);
|
|
19077
|
-
} else {
|
|
19078
|
-
onLintErrorsGenerated([]);
|
|
19079
|
-
}
|
|
19080
|
-
});
|
|
19081
|
-
}, DEBOUNCE_TIMEOUT);
|
|
19082
|
-
|
|
19083
|
-
/**
|
|
19084
|
-
* Applies an array of errors generated by linters without position
|
|
19085
|
-
* information (TranslationLinter and the legacy getSaveWarnings()) to the
|
|
19086
|
-
* top of a Perseus tree.
|
|
19087
|
-
*/
|
|
19088
|
-
applyLintErrors(parsedMarkdown, translationLintErrors) {
|
|
19089
|
-
// These lint errors do not have position data associated with
|
|
19090
|
-
// them, so we just plop them at the top.
|
|
19091
|
-
if (translationLintErrors.length) {
|
|
19092
|
-
const errorText = translationLintErrors.join("\n\n");
|
|
19093
|
-
parsedMarkdown.unshift({
|
|
19094
|
-
content: {
|
|
19095
|
-
type: "text",
|
|
19096
|
-
content: ""
|
|
19097
|
-
},
|
|
19098
|
-
insideTable: false,
|
|
19099
|
-
message: errorText,
|
|
19100
|
-
ruleName: "legacy-error",
|
|
19101
|
-
severity: PerseusLinter.Rule.Severity.ERROR,
|
|
19102
|
-
type: "lint"
|
|
19103
|
-
});
|
|
19104
|
-
}
|
|
19105
|
-
}
|
|
19106
|
-
}
|
|
19107
|
-
|
|
19108
|
-
/**
|
|
19109
|
-
* Preprocess TeX code to convert things that MathJax doesn't know how to handle
|
|
19110
|
-
* to things is does.
|
|
19111
|
-
*/
|
|
19112
|
-
var preprocessTex = (texCode => texCode
|
|
19113
|
-
// Replace uses of \begin{align}...\end{align} which KaTeX doesn't
|
|
19114
|
-
// support (yet) with \begin{aligned}...\end{aligned} which renders
|
|
19115
|
-
// the same is supported by KaTeX. It does the same for align*.
|
|
19116
|
-
// TODO(kevinb) update content to use aligned instead of align.
|
|
19117
|
-
// TODO(LEMS-1608) Remove this replacement as MathJax supports the
|
|
19118
|
-
// "align" macro correctly (and, in fact, it is not synonymous with
|
|
19119
|
-
// "aligned").
|
|
19120
|
-
.replace(/\{align[*]?\}/g, "{aligned}")
|
|
19121
|
-
// Replace non-breaking spaces with regular spaces.
|
|
19122
|
-
.replace(/[\u00a0]/g, " "));
|
|
19123
|
-
|
|
19124
|
-
const {
|
|
19125
|
-
interactiveSizes
|
|
19126
|
-
} = constants$1;
|
|
19127
|
-
|
|
19128
|
-
// Note: these size cutoffs represent content-width cutoffs as specified in
|
|
19129
|
-
// http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
|
|
19130
|
-
// TODO(benkomalo): these values aren't used in JS outside of this file, but
|
|
19131
|
-
// are coupled to the values in styles/styles.less - DRY it up at some point
|
|
19132
|
-
const smMax = articleMaxWidthTableInPx;
|
|
19133
|
-
const mdMax = articleMaxWidthInPx;
|
|
19134
|
-
const containerSizeClass = {
|
|
19135
|
-
SMALL: "small",
|
|
19136
|
-
MEDIUM: "medium",
|
|
19137
|
-
LARGE: "large",
|
|
19138
|
-
XLARGE: "xlarge"
|
|
19139
|
-
};
|
|
19140
|
-
const getClassFromWidth = width => {
|
|
19141
|
-
if (!width) {
|
|
19142
|
-
return containerSizeClass.MEDIUM;
|
|
19143
|
-
}
|
|
19144
|
-
if (width <= smMax) {
|
|
19145
|
-
return containerSizeClass.SMALL;
|
|
19146
|
-
}
|
|
19147
|
-
if (width <= mdMax) {
|
|
19148
|
-
return containerSizeClass.MEDIUM;
|
|
19149
|
-
}
|
|
19150
|
-
return containerSizeClass.LARGE;
|
|
19151
|
-
};
|
|
19152
|
-
const getInteractiveBoxFromSizeClass = sizeClass => {
|
|
19153
|
-
if (sizeClass === containerSizeClass.SMALL) {
|
|
19154
|
-
return [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall];
|
|
19155
|
-
}
|
|
19156
|
-
return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
|
|
19157
|
-
};
|
|
19158
|
-
|
|
19159
|
-
var sizingUtils = /*#__PURE__*/Object.freeze({
|
|
19160
|
-
__proto__: null,
|
|
19161
|
-
containerSizeClass: containerSizeClass,
|
|
19162
|
-
getClassFromWidth: getClassFromWidth,
|
|
19163
|
-
getInteractiveBoxFromSizeClass: getInteractiveBoxFromSizeClass
|
|
19164
|
-
});
|
|
19165
|
-
|
|
19166
18992
|
const DEFAULT_ALIGNMENT$1 = "block";
|
|
19167
18993
|
// NOTE(kevinb): "default" is not one in `validAlignments`.
|
|
19168
18994
|
const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
|
|
@@ -19261,6 +19087,9 @@ const getWidget = name => {
|
|
|
19261
19087
|
}
|
|
19262
19088
|
return widgets[name].widget;
|
|
19263
19089
|
};
|
|
19090
|
+
const getWidgetValidator = name => {
|
|
19091
|
+
return widgets[name]?.validator ?? null;
|
|
19092
|
+
};
|
|
19264
19093
|
const getEditor = name => {
|
|
19265
19094
|
return ___default["default"].has(editors, name) ? editors[name] : null;
|
|
19266
19095
|
};
|
|
@@ -19566,6 +19395,7 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
19566
19395
|
replaceEditor: replaceEditor,
|
|
19567
19396
|
replaceDeprecatedEditors: replaceDeprecatedEditors,
|
|
19568
19397
|
getWidget: getWidget,
|
|
19398
|
+
getWidgetValidator: getWidgetValidator,
|
|
19569
19399
|
getEditor: getEditor,
|
|
19570
19400
|
getTransform: getTransform,
|
|
19571
19401
|
getVersion: getVersion,
|
|
@@ -19585,6 +19415,222 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
19585
19415
|
isLintable: isLintable
|
|
19586
19416
|
});
|
|
19587
19417
|
|
|
19418
|
+
function emptyWidgetsFunctional(widgets,
|
|
19419
|
+
// This is a port of old code, I'm not sure why
|
|
19420
|
+
// we need widgetIds vs the keys of the widgets object
|
|
19421
|
+
widgetIds, userInputMap, strings, locale) {
|
|
19422
|
+
return widgetIds.filter(id => {
|
|
19423
|
+
const widget = widgets[id];
|
|
19424
|
+
if (!widget || widget.static) {
|
|
19425
|
+
// Static widgets shouldn't count as empty
|
|
19426
|
+
return false;
|
|
19427
|
+
}
|
|
19428
|
+
let score = null;
|
|
19429
|
+
const userInput = userInputMap[id];
|
|
19430
|
+
const validator = getWidgetValidator(widget.type);
|
|
19431
|
+
if (widget.type === "group") {
|
|
19432
|
+
const scores = scoreWidgetsFunctional(widget.options.widgets, Object.keys(widget.options.widgets), userInputMap[id], strings, locale);
|
|
19433
|
+
score = Util.flattenScores(scores);
|
|
19434
|
+
} else if (validator) {
|
|
19435
|
+
score = validator(userInput, widget.options, strings, locale);
|
|
19436
|
+
}
|
|
19437
|
+
if (score) {
|
|
19438
|
+
return Util.scoreIsEmpty(score);
|
|
19439
|
+
}
|
|
19440
|
+
});
|
|
19441
|
+
}
|
|
19442
|
+
function scoreWidgetsFunctional(widgets,
|
|
19443
|
+
// This is a port of old code, I'm not sure why
|
|
19444
|
+
// we need widgetIds vs the keys of the widgets object
|
|
19445
|
+
widgetIds, userInputMap, strings, locale) {
|
|
19446
|
+
const gradedWidgetIds = widgetIds.filter(id => {
|
|
19447
|
+
const props = widgets[id];
|
|
19448
|
+
const widgetIsGraded = props?.graded == null || props.graded;
|
|
19449
|
+
const widgetIsStatic = !!props?.static;
|
|
19450
|
+
// Ungraded widgets or widgets set to static shouldn't be graded.
|
|
19451
|
+
return widgetIsGraded && !widgetIsStatic;
|
|
19452
|
+
});
|
|
19453
|
+
const widgetScores = {};
|
|
19454
|
+
gradedWidgetIds.forEach(id => {
|
|
19455
|
+
const widget = widgets[id];
|
|
19456
|
+
if (!widget) {
|
|
19457
|
+
return;
|
|
19458
|
+
}
|
|
19459
|
+
const userInput = userInputMap[id];
|
|
19460
|
+
const validator = getWidgetValidator(widget.type);
|
|
19461
|
+
if (widget.type === "group") {
|
|
19462
|
+
const scores = scoreWidgetsFunctional(widget.options.widgets, Object.keys(widget.options.widgets), userInputMap[id], strings, locale);
|
|
19463
|
+
widgetScores[id] = Util.flattenScores(scores);
|
|
19464
|
+
} else if (validator) {
|
|
19465
|
+
widgetScores[id] = validator(userInput, widget.options, strings, locale);
|
|
19466
|
+
}
|
|
19467
|
+
});
|
|
19468
|
+
return widgetScores;
|
|
19469
|
+
}
|
|
19470
|
+
|
|
19471
|
+
/**
|
|
19472
|
+
* TranslationLinter asynchronously calls Khan Academy's poentry linter.
|
|
19473
|
+
*
|
|
19474
|
+
* The poentry linter checks for issues that prevent content from being
|
|
19475
|
+
* translated. This linter is KA-specific and so TranslationLinter does not do
|
|
19476
|
+
* anything in non-KA environments.
|
|
19477
|
+
*/
|
|
19478
|
+
|
|
19479
|
+
// A LintCB is called once the linter has been run.
|
|
19480
|
+
|
|
19481
|
+
// msec to wait before we actually call the linter after receiving the
|
|
19482
|
+
// last call. The timer is reset if the lint is called during the wait.
|
|
19483
|
+
const DEBOUNCE_TIMEOUT = 1000;
|
|
19484
|
+
class TranslationLinter {
|
|
19485
|
+
// To avoid linting when nothing has changed.
|
|
19486
|
+
previousContent = null;
|
|
19487
|
+
|
|
19488
|
+
/**
|
|
19489
|
+
* Runs the poentry linter on the given perseus string asynchronously.
|
|
19490
|
+
*
|
|
19491
|
+
* It doesn't do anything unless we're in a KA environment.
|
|
19492
|
+
*
|
|
19493
|
+
* onLintErrorsGenerated is called even if there are no errors. It is not
|
|
19494
|
+
* called if perseusStr is unchanged from the last call.
|
|
19495
|
+
*
|
|
19496
|
+
* Linting is relatively expensive -- so we debounce linting each instance
|
|
19497
|
+
* for some amount of time.
|
|
19498
|
+
*/
|
|
19499
|
+
runLinter = _.debounce((perseusStr, onLintErrorsGenerated) => {
|
|
19500
|
+
// TODO(FEI-5003): Do we still need to check for 'KA'?
|
|
19501
|
+
if (typeof KA === "undefined") {
|
|
19502
|
+
return;
|
|
19503
|
+
}
|
|
19504
|
+
if (perseusStr === this.previousContent) {
|
|
19505
|
+
return;
|
|
19506
|
+
}
|
|
19507
|
+
this.previousContent = perseusStr;
|
|
19508
|
+
if (perseusStr === "") {
|
|
19509
|
+
onLintErrorsGenerated([]);
|
|
19510
|
+
return;
|
|
19511
|
+
}
|
|
19512
|
+
fetch("/api/internal/translations/lint_poentry?preview=1&lang=en", {
|
|
19513
|
+
headers: {
|
|
19514
|
+
"Content-Type": "application/json"
|
|
19515
|
+
},
|
|
19516
|
+
body: JSON.stringify({
|
|
19517
|
+
// The poentry linter verifies that the translation from
|
|
19518
|
+
// the original text to the translated text is sane. We
|
|
19519
|
+
// want to ensure that the translation from English to
|
|
19520
|
+
// English is sane, so msgid === mstr.
|
|
19521
|
+
msgid: perseusStr,
|
|
19522
|
+
// msgid is the original text
|
|
19523
|
+
msgstr: perseusStr,
|
|
19524
|
+
// msgstr is the translated text
|
|
19525
|
+
format: "perseus_text",
|
|
19526
|
+
filename: ""
|
|
19527
|
+
}),
|
|
19528
|
+
method: "POST"
|
|
19529
|
+
}).then(response => {
|
|
19530
|
+
if (response.status >= 400) {
|
|
19531
|
+
return {
|
|
19532
|
+
status: "error",
|
|
19533
|
+
message: "Could not run i18n linter."
|
|
19534
|
+
};
|
|
19535
|
+
}
|
|
19536
|
+
return response.json();
|
|
19537
|
+
}, rejection => {
|
|
19538
|
+
return {
|
|
19539
|
+
status: "error",
|
|
19540
|
+
message: "Could not run i18n linter."
|
|
19541
|
+
};
|
|
19542
|
+
}).then(json => {
|
|
19543
|
+
if (json.status === "error") {
|
|
19544
|
+
onLintErrorsGenerated(["Some part of this text makes it untranslatable. " + "The specific message from the i18n linter was: " + json.message.replace(/\n/g, " ")]);
|
|
19545
|
+
} else {
|
|
19546
|
+
onLintErrorsGenerated([]);
|
|
19547
|
+
}
|
|
19548
|
+
});
|
|
19549
|
+
}, DEBOUNCE_TIMEOUT);
|
|
19550
|
+
|
|
19551
|
+
/**
|
|
19552
|
+
* Applies an array of errors generated by linters without position
|
|
19553
|
+
* information (TranslationLinter and the legacy getSaveWarnings()) to the
|
|
19554
|
+
* top of a Perseus tree.
|
|
19555
|
+
*/
|
|
19556
|
+
applyLintErrors(parsedMarkdown, translationLintErrors) {
|
|
19557
|
+
// These lint errors do not have position data associated with
|
|
19558
|
+
// them, so we just plop them at the top.
|
|
19559
|
+
if (translationLintErrors.length) {
|
|
19560
|
+
const errorText = translationLintErrors.join("\n\n");
|
|
19561
|
+
parsedMarkdown.unshift({
|
|
19562
|
+
content: {
|
|
19563
|
+
type: "text",
|
|
19564
|
+
content: ""
|
|
19565
|
+
},
|
|
19566
|
+
insideTable: false,
|
|
19567
|
+
message: errorText,
|
|
19568
|
+
ruleName: "legacy-error",
|
|
19569
|
+
severity: PerseusLinter.Rule.Severity.ERROR,
|
|
19570
|
+
type: "lint"
|
|
19571
|
+
});
|
|
19572
|
+
}
|
|
19573
|
+
}
|
|
19574
|
+
}
|
|
19575
|
+
|
|
19576
|
+
/**
|
|
19577
|
+
* Preprocess TeX code to convert things that MathJax doesn't know how to handle
|
|
19578
|
+
* to things is does.
|
|
19579
|
+
*/
|
|
19580
|
+
var preprocessTex = (texCode => texCode
|
|
19581
|
+
// Replace uses of \begin{align}...\end{align} which KaTeX doesn't
|
|
19582
|
+
// support (yet) with \begin{aligned}...\end{aligned} which renders
|
|
19583
|
+
// the same is supported by KaTeX. It does the same for align*.
|
|
19584
|
+
// TODO(kevinb) update content to use aligned instead of align.
|
|
19585
|
+
// TODO(LEMS-1608) Remove this replacement as MathJax supports the
|
|
19586
|
+
// "align" macro correctly (and, in fact, it is not synonymous with
|
|
19587
|
+
// "aligned").
|
|
19588
|
+
.replace(/\{align[*]?\}/g, "{aligned}")
|
|
19589
|
+
// Replace non-breaking spaces with regular spaces.
|
|
19590
|
+
.replace(/[\u00a0]/g, " "));
|
|
19591
|
+
|
|
19592
|
+
const {
|
|
19593
|
+
interactiveSizes
|
|
19594
|
+
} = constants$1;
|
|
19595
|
+
|
|
19596
|
+
// Note: these size cutoffs represent content-width cutoffs as specified in
|
|
19597
|
+
// http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
|
|
19598
|
+
// TODO(benkomalo): these values aren't used in JS outside of this file, but
|
|
19599
|
+
// are coupled to the values in styles/styles.less - DRY it up at some point
|
|
19600
|
+
const smMax = articleMaxWidthTableInPx;
|
|
19601
|
+
const mdMax = articleMaxWidthInPx;
|
|
19602
|
+
const containerSizeClass = {
|
|
19603
|
+
SMALL: "small",
|
|
19604
|
+
MEDIUM: "medium",
|
|
19605
|
+
LARGE: "large",
|
|
19606
|
+
XLARGE: "xlarge"
|
|
19607
|
+
};
|
|
19608
|
+
const getClassFromWidth = width => {
|
|
19609
|
+
if (!width) {
|
|
19610
|
+
return containerSizeClass.MEDIUM;
|
|
19611
|
+
}
|
|
19612
|
+
if (width <= smMax) {
|
|
19613
|
+
return containerSizeClass.SMALL;
|
|
19614
|
+
}
|
|
19615
|
+
if (width <= mdMax) {
|
|
19616
|
+
return containerSizeClass.MEDIUM;
|
|
19617
|
+
}
|
|
19618
|
+
return containerSizeClass.LARGE;
|
|
19619
|
+
};
|
|
19620
|
+
const getInteractiveBoxFromSizeClass = sizeClass => {
|
|
19621
|
+
if (sizeClass === containerSizeClass.SMALL) {
|
|
19622
|
+
return [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall];
|
|
19623
|
+
}
|
|
19624
|
+
return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
|
|
19625
|
+
};
|
|
19626
|
+
|
|
19627
|
+
var sizingUtils = /*#__PURE__*/Object.freeze({
|
|
19628
|
+
__proto__: null,
|
|
19629
|
+
containerSizeClass: containerSizeClass,
|
|
19630
|
+
getClassFromWidth: getClassFromWidth,
|
|
19631
|
+
getInteractiveBoxFromSizeClass: getInteractiveBoxFromSizeClass
|
|
19632
|
+
});
|
|
19633
|
+
|
|
19588
19634
|
class WidgetContainer extends React__namespace.Component {
|
|
19589
19635
|
widgetRef = /*#__PURE__*/React__namespace.createRef();
|
|
19590
19636
|
static defaultProps = {
|
|
@@ -19731,8 +19777,7 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
19731
19777
|
}
|
|
19732
19778
|
|
|
19733
19779
|
const {
|
|
19734
|
-
mapObject
|
|
19735
|
-
mapObjectFromArray
|
|
19780
|
+
mapObject
|
|
19736
19781
|
} = objective_;
|
|
19737
19782
|
const rContainsNonWhitespace = /\S/;
|
|
19738
19783
|
const rImageURL = /(web\+graphie|https):\/\/[^\s]*/;
|
|
@@ -19759,6 +19804,7 @@ const isIdPathPrefix = function (prefixArray, wholeArray) {
|
|
|
19759
19804
|
});
|
|
19760
19805
|
};
|
|
19761
19806
|
class Renderer extends React__namespace.Component {
|
|
19807
|
+
static contextType = PerseusI18nContext;
|
|
19762
19808
|
_currentFocus;
|
|
19763
19809
|
// @ts-expect-error - TS2564 - Property '_foundTextNodes' has no initializer and is not definitely assigned in the constructor.
|
|
19764
19810
|
_foundTextNodes;
|
|
@@ -20062,11 +20108,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20062
20108
|
}
|
|
20063
20109
|
let interactionTracker = this._interactionTrackers[id];
|
|
20064
20110
|
if (!interactionTracker) {
|
|
20065
|
-
interactionTracker = this._interactionTrackers[id] = new InteractionTracker(apiOptions.trackInteraction,
|
|
20066
|
-
// @ts-expect-error - TS2345 - Argument of type 'string | null | undefined' is not assignable to parameter of type 'string'.
|
|
20067
|
-
widgetInfo && widgetInfo.type, id,
|
|
20068
|
-
// @ts-expect-error - TS2345 - Argument of type 'string | null | undefined' is not assignable to parameter of type 'string'.
|
|
20069
|
-
getTracking(widgetInfo && widgetInfo.type));
|
|
20111
|
+
interactionTracker = this._interactionTrackers[id] = new InteractionTracker(apiOptions.trackInteraction, widgetInfo && widgetInfo.type, id, getTracking(widgetInfo && widgetInfo.type));
|
|
20070
20112
|
}
|
|
20071
20113
|
return {
|
|
20072
20114
|
...widgetProps,
|
|
@@ -20880,23 +20922,9 @@ class Renderer extends React__namespace.Component {
|
|
|
20880
20922
|
}, this);
|
|
20881
20923
|
return state;
|
|
20882
20924
|
};
|
|
20883
|
-
emptyWidgets
|
|
20884
|
-
|
|
20885
|
-
|
|
20886
|
-
const widgetInfo = this._getWidgetInfo(id);
|
|
20887
|
-
if (widgetInfo.static) {
|
|
20888
|
-
// Static widgets shouldn't count as empty
|
|
20889
|
-
return false;
|
|
20890
|
-
}
|
|
20891
|
-
const widget = this.getWidgetInstance(id);
|
|
20892
|
-
if (widget && widget.simpleValidate) {
|
|
20893
|
-
const score = widget.simpleValidate(widgetInfo.options,
|
|
20894
|
-
// @ts-expect-error - TS2345 - Argument of type 'null' is not assignable to parameter of type '((widgetId: any, value: string, message?: string | null | undefined) => unknown) | undefined'.
|
|
20895
|
-
null);
|
|
20896
|
-
return Util.scoreIsEmpty(score);
|
|
20897
|
-
}
|
|
20898
|
-
});
|
|
20899
|
-
};
|
|
20925
|
+
emptyWidgets() {
|
|
20926
|
+
return emptyWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
|
|
20927
|
+
}
|
|
20900
20928
|
_setWidgetProps = (id, newProps, cb, silent) => {
|
|
20901
20929
|
this.setState(prevState => {
|
|
20902
20930
|
const widgetProps = {
|
|
@@ -20960,8 +20988,11 @@ class Renderer extends React__namespace.Component {
|
|
|
20960
20988
|
|
|
20961
20989
|
/**
|
|
20962
20990
|
* Returns an array of the widget `.getUserInput()` results
|
|
20991
|
+
*
|
|
20992
|
+
* TODO: can we remove this?
|
|
20993
|
+
* @deprecated use getUserInputMap
|
|
20963
20994
|
*/
|
|
20964
|
-
getUserInput
|
|
20995
|
+
getUserInput() {
|
|
20965
20996
|
return this.widgetIds.map(id => {
|
|
20966
20997
|
const widget = this.getWidgetInstance(id);
|
|
20967
20998
|
if (widget && widget.getUserInput) {
|
|
@@ -20970,7 +21001,24 @@ class Renderer extends React__namespace.Component {
|
|
|
20970
21001
|
return widget.getUserInput();
|
|
20971
21002
|
}
|
|
20972
21003
|
});
|
|
20973
|
-
}
|
|
21004
|
+
}
|
|
21005
|
+
|
|
21006
|
+
/**
|
|
21007
|
+
* Returns an object of the widget `.getUserInput()` results
|
|
21008
|
+
*/
|
|
21009
|
+
getUserInputMap() {
|
|
21010
|
+
const userInputMap = {};
|
|
21011
|
+
this.widgetIds.forEach(id => {
|
|
21012
|
+
const widget = this.getWidgetInstance(id);
|
|
21013
|
+
// Handle Groups, which have their own sets of widgets
|
|
21014
|
+
if (widget?.getUserInputMap) {
|
|
21015
|
+
userInputMap[id] = widget.getUserInputMap();
|
|
21016
|
+
} else if (widget?.getUserInput) {
|
|
21017
|
+
userInputMap[id] = widget.getUserInput();
|
|
21018
|
+
}
|
|
21019
|
+
});
|
|
21020
|
+
return userInputMap;
|
|
21021
|
+
}
|
|
20974
21022
|
|
|
20975
21023
|
/**
|
|
20976
21024
|
* Returns an array of all widget IDs in the order they occur in
|
|
@@ -20980,59 +21028,23 @@ class Renderer extends React__namespace.Component {
|
|
|
20980
21028
|
return this.widgetIds;
|
|
20981
21029
|
};
|
|
20982
21030
|
|
|
20983
|
-
/**
|
|
20984
|
-
* Returns the result of `.getUserInput()` for each widget, in
|
|
20985
|
-
* a map from widgetId to userInput.
|
|
20986
|
-
* NOTE(jeremy): This function is hauntingly similar to `getUserInput` with
|
|
20987
|
-
* the major difference being that this function returns a map of
|
|
20988
|
-
* `widgetID` => UserInput and `getUserInput` simply returns an array. It
|
|
20989
|
-
* would be trivial to map between the results of each of these functions,
|
|
20990
|
-
* so we should aim to remove one of these functions.
|
|
20991
|
-
*/
|
|
20992
|
-
getUserInputForWidgets = () => {
|
|
20993
|
-
return mapObjectFromArray(this.widgetIds, id => {
|
|
20994
|
-
const widget = this.getWidgetInstance(id);
|
|
20995
|
-
if (widget && widget.getUserInput) {
|
|
20996
|
-
return widget.getUserInput();
|
|
20997
|
-
}
|
|
20998
|
-
});
|
|
20999
|
-
};
|
|
21000
|
-
|
|
21001
21031
|
/**
|
|
21002
21032
|
* Returns an object mapping from widget ID to perseus-style score.
|
|
21003
21033
|
* The keys of this object are the values of the array returned
|
|
21004
21034
|
* from `getWidgetIds`.
|
|
21005
21035
|
*/
|
|
21006
|
-
scoreWidgets
|
|
21007
|
-
|
|
21008
|
-
|
|
21009
|
-
const props = widgetProps[id];
|
|
21010
|
-
const widgetIsGraded = props?.graded == null || props.graded;
|
|
21011
|
-
const widgetIsStatic = !!props?.static;
|
|
21012
|
-
// Ungraded widgets or widgets set to static shouldn't be graded.
|
|
21013
|
-
return widgetIsGraded && !widgetIsStatic;
|
|
21014
|
-
});
|
|
21015
|
-
const widgetScores = {};
|
|
21016
|
-
___default["default"].each(gradedWidgetIds, id => {
|
|
21017
|
-
const props = widgetProps[id];
|
|
21018
|
-
const widget = this.getWidgetInstance(id);
|
|
21019
|
-
// widget can be undefined if it hasn't yet been rendered
|
|
21020
|
-
if (widget && widget.simpleValidate) {
|
|
21021
|
-
widgetScores[id] = widget.simpleValidate({
|
|
21022
|
-
...props?.options,
|
|
21023
|
-
scoring: true
|
|
21024
|
-
});
|
|
21025
|
-
}
|
|
21026
|
-
});
|
|
21027
|
-
return widgetScores;
|
|
21028
|
-
};
|
|
21036
|
+
scoreWidgets() {
|
|
21037
|
+
return scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
|
|
21038
|
+
}
|
|
21029
21039
|
|
|
21030
21040
|
/**
|
|
21031
21041
|
* Grades the content.
|
|
21032
21042
|
*/
|
|
21033
|
-
score
|
|
21034
|
-
|
|
21035
|
-
|
|
21043
|
+
score() {
|
|
21044
|
+
const scores = this.scoreWidgets();
|
|
21045
|
+
const combinedScore = Util.flattenScores(scores);
|
|
21046
|
+
return combinedScore;
|
|
21047
|
+
}
|
|
21036
21048
|
guessAndScore = () => {
|
|
21037
21049
|
const totalGuess = this.getUserInput();
|
|
21038
21050
|
const totalScore = this.score();
|
|
@@ -21144,12 +21156,12 @@ class Renderer extends React__namespace.Component {
|
|
|
21144
21156
|
// If highlightLint is true and lint is detected, this call
|
|
21145
21157
|
// will modify the parse tree by adding lint nodes that will
|
|
21146
21158
|
// serve to highlight the lint when rendered
|
|
21147
|
-
const
|
|
21159
|
+
const fullLinterContext = {
|
|
21148
21160
|
content: this.props.content,
|
|
21149
21161
|
widgets: this.props.widgets,
|
|
21150
21162
|
...this.props.linterContext
|
|
21151
21163
|
};
|
|
21152
|
-
PerseusLinter__namespace.runLinter(parsedMarkdown,
|
|
21164
|
+
PerseusLinter__namespace.runLinter(parsedMarkdown, fullLinterContext, true);
|
|
21153
21165
|
|
|
21154
21166
|
// Apply the lint errors from the last TranslationLinter run.
|
|
21155
21167
|
// TODO(joshuan): Support overlapping dots.
|
|
@@ -21971,9 +21983,6 @@ class InputNumber extends React__namespace.Component {
|
|
|
21971
21983
|
apiOptions: ApiOptions.defaults,
|
|
21972
21984
|
linterContext: PerseusLinter.linterContextDefault
|
|
21973
21985
|
};
|
|
21974
|
-
static validate(state, rubric, strings) {
|
|
21975
|
-
return inputNumberValidator(state, rubric, strings);
|
|
21976
|
-
}
|
|
21977
21986
|
static getUserInputFromProps(props) {
|
|
21978
21987
|
return {
|
|
21979
21988
|
currentValue: props.currentValue
|
|
@@ -22044,9 +22053,6 @@ class InputNumber extends React__namespace.Component {
|
|
|
22044
22053
|
getUserInput() {
|
|
22045
22054
|
return InputNumber.getUserInputFromProps(this.props);
|
|
22046
22055
|
}
|
|
22047
|
-
simpleValidate(rubric) {
|
|
22048
|
-
return inputNumberValidator(this.getUserInput(), rubric, this.context.strings);
|
|
22049
|
-
}
|
|
22050
22056
|
examples = () => {
|
|
22051
22057
|
const {
|
|
22052
22058
|
strings
|
|
@@ -22149,7 +22155,8 @@ var InputNumber$1 = {
|
|
|
22149
22155
|
hidden: true,
|
|
22150
22156
|
widget: InputNumber,
|
|
22151
22157
|
transform: propTransform$3,
|
|
22152
|
-
isLintable: true
|
|
22158
|
+
isLintable: true,
|
|
22159
|
+
validator: inputNumberValidator
|
|
22153
22160
|
};
|
|
22154
22161
|
|
|
22155
22162
|
const ParseTex = TexWrangler.parseTex;
|
|
@@ -22356,9 +22363,6 @@ class NumericInput extends React__namespace.Component {
|
|
|
22356
22363
|
}
|
|
22357
22364
|
return answerStrings[0];
|
|
22358
22365
|
}
|
|
22359
|
-
static validate(userInput, rubric, strings) {
|
|
22360
|
-
return numericInputValidator(userInput, rubric, strings);
|
|
22361
|
-
}
|
|
22362
22366
|
state = {
|
|
22363
22367
|
// keeps track of the other set of values when switching
|
|
22364
22368
|
// between 0 and finite solutions
|
|
@@ -22391,9 +22395,6 @@ class NumericInput extends React__namespace.Component {
|
|
|
22391
22395
|
const allFormsAccepted = answerFormNames.length >= Object.keys(formExamples).length;
|
|
22392
22396
|
return !noFormsAccepted && !allFormsAccepted;
|
|
22393
22397
|
};
|
|
22394
|
-
simpleValidate(rubric) {
|
|
22395
|
-
return numericInputValidator(this.getUserInput(), rubric, this.context.strings);
|
|
22396
|
-
}
|
|
22397
22398
|
focus = () => {
|
|
22398
22399
|
this.inputRef?.focus();
|
|
22399
22400
|
return true;
|
|
@@ -22547,7 +22548,8 @@ var NumericInput$1 = {
|
|
|
22547
22548
|
accessible: true,
|
|
22548
22549
|
widget: NumericInput,
|
|
22549
22550
|
transform: propsTransform,
|
|
22550
|
-
isLintable: true
|
|
22551
|
+
isLintable: true,
|
|
22552
|
+
validator: numericInputValidator
|
|
22551
22553
|
};
|
|
22552
22554
|
|
|
22553
22555
|
/**
|
|
@@ -24368,16 +24370,15 @@ class Passage extends React__namespace.Component {
|
|
|
24368
24370
|
highlights: {},
|
|
24369
24371
|
linterContext: PerseusLinter.linterContextDefault
|
|
24370
24372
|
};
|
|
24373
|
+
|
|
24374
|
+
// this just helps with TS weak typing when a Widget
|
|
24375
|
+
// doesn't implement any Widget methods
|
|
24376
|
+
isWidget = true;
|
|
24371
24377
|
state = {
|
|
24372
24378
|
nLines: null,
|
|
24373
24379
|
startLineNumbersAfter: 0,
|
|
24374
24380
|
stylesAreApplied: false
|
|
24375
24381
|
};
|
|
24376
|
-
|
|
24377
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
24378
|
-
static validate() {
|
|
24379
|
-
return noopValidator();
|
|
24380
|
-
}
|
|
24381
24382
|
componentDidMount() {
|
|
24382
24383
|
this._updateState();
|
|
24383
24384
|
|
|
@@ -24586,21 +24587,6 @@ class Passage extends React__namespace.Component {
|
|
|
24586
24587
|
};
|
|
24587
24588
|
}
|
|
24588
24589
|
|
|
24589
|
-
/**
|
|
24590
|
-
* Misc functions
|
|
24591
|
-
*
|
|
24592
|
-
* These are misc widget functions used for the widget API
|
|
24593
|
-
*/
|
|
24594
|
-
|
|
24595
|
-
getUserInput() {
|
|
24596
|
-
return null;
|
|
24597
|
-
}
|
|
24598
|
-
|
|
24599
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
24600
|
-
simpleValidate() {
|
|
24601
|
-
return noopValidator();
|
|
24602
|
-
}
|
|
24603
|
-
|
|
24604
24590
|
/**
|
|
24605
24591
|
* Rendering
|
|
24606
24592
|
*
|
|
@@ -24744,7 +24730,8 @@ var Passage$1 = {
|
|
|
24744
24730
|
transform: editorProps => {
|
|
24745
24731
|
return ___default["default"].pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
|
|
24746
24732
|
},
|
|
24747
|
-
isLintable: true
|
|
24733
|
+
isLintable: true,
|
|
24734
|
+
validator: () => noopValidator()
|
|
24748
24735
|
};
|
|
24749
24736
|
|
|
24750
24737
|
/**
|
|
@@ -24774,15 +24761,14 @@ class PassageRef extends React__namespace.Component {
|
|
|
24774
24761
|
referenceNumber: 1,
|
|
24775
24762
|
summaryText: ""
|
|
24776
24763
|
};
|
|
24764
|
+
|
|
24765
|
+
// this just helps with TS weak typing when a Widget
|
|
24766
|
+
// doesn't implement any Widget methods
|
|
24767
|
+
isWidget = true;
|
|
24777
24768
|
state = {
|
|
24778
24769
|
lineRange: null,
|
|
24779
24770
|
content: null
|
|
24780
24771
|
};
|
|
24781
|
-
|
|
24782
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
24783
|
-
static validate() {
|
|
24784
|
-
return noopValidator();
|
|
24785
|
-
}
|
|
24786
24772
|
componentDidMount() {
|
|
24787
24773
|
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
24788
24774
|
// this.isMounted() but is still considered an anti-pattern.
|
|
@@ -24801,11 +24787,6 @@ class PassageRef extends React__namespace.Component {
|
|
|
24801
24787
|
window.removeEventListener("resize", this._throttledUpdateRange);
|
|
24802
24788
|
this._isMounted = false;
|
|
24803
24789
|
}
|
|
24804
|
-
|
|
24805
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
24806
|
-
getUserInput() {
|
|
24807
|
-
return null;
|
|
24808
|
-
}
|
|
24809
24790
|
change = (() => {
|
|
24810
24791
|
var _this = this;
|
|
24811
24792
|
return function () {
|
|
@@ -24835,11 +24816,6 @@ class PassageRef extends React__namespace.Component {
|
|
|
24835
24816
|
}
|
|
24836
24817
|
}
|
|
24837
24818
|
};
|
|
24838
|
-
|
|
24839
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
24840
|
-
simpleValidate() {
|
|
24841
|
-
return noopValidator();
|
|
24842
|
-
}
|
|
24843
24819
|
render() {
|
|
24844
24820
|
const {
|
|
24845
24821
|
strings
|
|
@@ -24888,7 +24864,9 @@ var PassageRef$1 = {
|
|
|
24888
24864
|
version: {
|
|
24889
24865
|
major: 0,
|
|
24890
24866
|
minor: 1
|
|
24891
|
-
}
|
|
24867
|
+
},
|
|
24868
|
+
// TODO: things that aren't interactive shouldn't need validators
|
|
24869
|
+
validator: () => noopValidator()
|
|
24892
24870
|
};
|
|
24893
24871
|
|
|
24894
24872
|
/**
|
|
@@ -26307,9 +26285,6 @@ class Radio$1 extends React__namespace.Component {
|
|
|
26307
26285
|
linterContext: PerseusLinter.linterContextDefault,
|
|
26308
26286
|
showSolutions: "none"
|
|
26309
26287
|
};
|
|
26310
|
-
static validate(userInput, rubric, strings) {
|
|
26311
|
-
return radioValidator(userInput, rubric, strings);
|
|
26312
|
-
}
|
|
26313
26288
|
static getUserInputFromProps(props) {
|
|
26314
26289
|
// Return checked inputs in the form {choicesSelected: [bool]}. (Dear
|
|
26315
26290
|
// future timeline implementers: this used to be {value: i} before
|
|
@@ -26496,9 +26471,6 @@ class Radio$1 extends React__namespace.Component {
|
|
|
26496
26471
|
getUserInput() {
|
|
26497
26472
|
return Radio$1.getUserInputFromProps(this.props);
|
|
26498
26473
|
}
|
|
26499
|
-
simpleValidate(rubric) {
|
|
26500
|
-
return radioValidator(this.getUserInput(), rubric, this.context.strings);
|
|
26501
|
-
}
|
|
26502
26474
|
|
|
26503
26475
|
/**
|
|
26504
26476
|
* Turn on rationale display for the currently selected choices. Note that
|
|
@@ -26511,7 +26483,7 @@ class Radio$1 extends React__namespace.Component {
|
|
|
26511
26483
|
choiceStates
|
|
26512
26484
|
} = this.props;
|
|
26513
26485
|
if (choiceStates) {
|
|
26514
|
-
const score = this.
|
|
26486
|
+
const score = radioValidator(this.getUserInput(), rubric, this.context.strings);
|
|
26515
26487
|
const widgetCorrect = score.type === "points" && score.total === score.earned;
|
|
26516
26488
|
const newStates = choiceStates.map(state => ({
|
|
26517
26489
|
...state,
|
|
@@ -26791,7 +26763,8 @@ var Radio = {
|
|
|
26791
26763
|
minor: 0
|
|
26792
26764
|
},
|
|
26793
26765
|
propUpgrades: propUpgrades$1,
|
|
26794
|
-
isLintable: true
|
|
26766
|
+
isLintable: true,
|
|
26767
|
+
validator: radioValidator
|
|
26795
26768
|
};
|
|
26796
26769
|
|
|
26797
26770
|
/* eslint-disable import/no-named-as-default */
|
|
@@ -26834,9 +26807,6 @@ class Categorizer extends React__namespace.Component {
|
|
|
26834
26807
|
state = {
|
|
26835
26808
|
uniqueId: ___default["default"].uniqueId("perseus_radio_")
|
|
26836
26809
|
};
|
|
26837
|
-
static validate(userInput, rubric, strings) {
|
|
26838
|
-
return categorizerValidator(userInput, rubric, strings);
|
|
26839
|
-
}
|
|
26840
26810
|
static getUserInputFromProps(props) {
|
|
26841
26811
|
return {
|
|
26842
26812
|
values: props.values
|
|
@@ -26862,9 +26832,6 @@ class Categorizer extends React__namespace.Component {
|
|
|
26862
26832
|
this.change("values", values);
|
|
26863
26833
|
this.props.trackInteraction();
|
|
26864
26834
|
}
|
|
26865
|
-
simpleValidate(rubric) {
|
|
26866
|
-
return categorizerValidator(this.getUserInput(), rubric, this.context.strings);
|
|
26867
|
-
}
|
|
26868
26835
|
render() {
|
|
26869
26836
|
const self = this;
|
|
26870
26837
|
|
|
@@ -27003,7 +26970,8 @@ var Categorizer$1 = {
|
|
|
27003
26970
|
staticTransform: editorProps => {
|
|
27004
26971
|
return ___default["default"].pick(editorProps, "items", "categories", "values", "randomizeItems");
|
|
27005
26972
|
},
|
|
27006
|
-
isLintable: true
|
|
26973
|
+
isLintable: true,
|
|
26974
|
+
validator: categorizerValidator
|
|
27007
26975
|
};
|
|
27008
26976
|
|
|
27009
26977
|
/**
|
|
@@ -27108,11 +27076,6 @@ class CSProgram extends React__namespace.Component {
|
|
|
27108
27076
|
// optional message
|
|
27109
27077
|
message: null
|
|
27110
27078
|
};
|
|
27111
|
-
|
|
27112
|
-
// The widget's grading function
|
|
27113
|
-
static validate(state) {
|
|
27114
|
-
return csProgramValidator(state);
|
|
27115
|
-
}
|
|
27116
27079
|
componentDidMount() {
|
|
27117
27080
|
$__default["default"](window).on("message", this.handleMessageEvent);
|
|
27118
27081
|
}
|
|
@@ -27154,9 +27117,6 @@ class CSProgram extends React__namespace.Component {
|
|
|
27154
27117
|
message: this.props.message
|
|
27155
27118
|
};
|
|
27156
27119
|
}
|
|
27157
|
-
simpleValidate() {
|
|
27158
|
-
return csProgramValidator(this.getUserInput());
|
|
27159
|
-
}
|
|
27160
27120
|
render() {
|
|
27161
27121
|
if (!this.props.programID) {
|
|
27162
27122
|
return /*#__PURE__*/React__namespace.createElement("div", null);
|
|
@@ -27231,7 +27191,8 @@ var CSProgram$1 = {
|
|
|
27231
27191
|
displayName: "CS Program",
|
|
27232
27192
|
supportedAlignments: ["block", "full-width"],
|
|
27233
27193
|
widget: CSProgram,
|
|
27234
|
-
hidden: true
|
|
27194
|
+
hidden: true,
|
|
27195
|
+
validator: csProgramValidator
|
|
27235
27196
|
};
|
|
27236
27197
|
|
|
27237
27198
|
class Definition extends React__namespace.Component {
|
|
@@ -27241,18 +27202,9 @@ class Definition extends React__namespace.Component {
|
|
|
27241
27202
|
definition: "definition goes here"
|
|
27242
27203
|
};
|
|
27243
27204
|
|
|
27244
|
-
//
|
|
27245
|
-
|
|
27246
|
-
|
|
27247
|
-
}
|
|
27248
|
-
getUserInput() {
|
|
27249
|
-
return {};
|
|
27250
|
-
}
|
|
27251
|
-
|
|
27252
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27253
|
-
simpleValidate() {
|
|
27254
|
-
return noopValidator();
|
|
27255
|
-
}
|
|
27205
|
+
// this just helps with TS weak typing when a Widget
|
|
27206
|
+
// doesn't implement any Widget methods
|
|
27207
|
+
isWidget = true;
|
|
27256
27208
|
render() {
|
|
27257
27209
|
return /*#__PURE__*/React__namespace.createElement(DefinitionConsumer, null, _ref => {
|
|
27258
27210
|
let {
|
|
@@ -27308,24 +27260,18 @@ var Definition$1 = {
|
|
|
27308
27260
|
accessible: true,
|
|
27309
27261
|
defaultAlignment: "inline",
|
|
27310
27262
|
widget: Definition,
|
|
27311
|
-
transform: x => x
|
|
27263
|
+
transform: x => x,
|
|
27264
|
+
// TODO: things that aren't interactive shouldn't need validators
|
|
27265
|
+
validator: () => noopValidator()
|
|
27312
27266
|
};
|
|
27313
27267
|
|
|
27314
27268
|
// The props are type `any` on purpose so that this can receive props
|
|
27315
27269
|
// from any deprecated widget
|
|
27316
27270
|
class DeprecatedStandin extends React__default["default"].Component {
|
|
27317
27271
|
static contextType = PerseusI18nContext;
|
|
27318
|
-
|
|
27319
|
-
|
|
27320
|
-
|
|
27321
|
-
return noopValidator(1);
|
|
27322
|
-
}
|
|
27323
|
-
getUserInput() {
|
|
27324
|
-
return {};
|
|
27325
|
-
}
|
|
27326
|
-
simpleValidate() {
|
|
27327
|
-
return noopValidator(1);
|
|
27328
|
-
}
|
|
27272
|
+
// this just helps with TS weak typing when a Widget
|
|
27273
|
+
// doesn't implement any Widget methods
|
|
27274
|
+
isWidget = true;
|
|
27329
27275
|
render() {
|
|
27330
27276
|
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
27331
27277
|
style: {
|
|
@@ -27343,7 +27289,9 @@ var DeprecatedStandin$1 = {
|
|
|
27343
27289
|
name: "deprecated-standin",
|
|
27344
27290
|
displayName: "Deprecated Standin",
|
|
27345
27291
|
widget: DeprecatedStandin,
|
|
27346
|
-
hidden: true
|
|
27292
|
+
hidden: true,
|
|
27293
|
+
// TODO: things that aren't interactive shouldn't need validators
|
|
27294
|
+
validator: () => noopValidator(1)
|
|
27347
27295
|
};
|
|
27348
27296
|
|
|
27349
27297
|
function dropdownValidator(userInput, rubric) {
|
|
@@ -27370,9 +27318,6 @@ class Dropdown extends React__namespace.Component {
|
|
|
27370
27318
|
placeholder: "",
|
|
27371
27319
|
apiOptions: ApiOptions.defaults
|
|
27372
27320
|
};
|
|
27373
|
-
static validate(userInput, rubric) {
|
|
27374
|
-
return dropdownValidator(userInput, rubric);
|
|
27375
|
-
}
|
|
27376
27321
|
focus = () => {
|
|
27377
27322
|
// TODO(LP-10797): This focus() call doesn't do anything because our
|
|
27378
27323
|
// root element is a <div> and that cannot be focused without a
|
|
@@ -27395,9 +27340,6 @@ class Dropdown extends React__namespace.Component {
|
|
|
27395
27340
|
value: this.props.selected
|
|
27396
27341
|
};
|
|
27397
27342
|
}
|
|
27398
|
-
simpleValidate(rubric) {
|
|
27399
|
-
return dropdownValidator(this.getUserInput(), rubric);
|
|
27400
|
-
}
|
|
27401
27343
|
render() {
|
|
27402
27344
|
const children = [/*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
|
|
27403
27345
|
key: "placeholder",
|
|
@@ -27438,7 +27380,8 @@ var Dropdown$1 = {
|
|
|
27438
27380
|
defaultAlignment: "inline-block",
|
|
27439
27381
|
accessible: true,
|
|
27440
27382
|
widget: Dropdown,
|
|
27441
|
-
transform: optionsTransform
|
|
27383
|
+
transform: optionsTransform,
|
|
27384
|
+
validator: dropdownValidator
|
|
27442
27385
|
};
|
|
27443
27386
|
|
|
27444
27387
|
// transform = _.identity
|
|
@@ -27451,10 +27394,6 @@ function mediaQueryIsMatched(mediaQuery) {
|
|
|
27451
27394
|
}
|
|
27452
27395
|
class Explanation extends React__namespace.Component {
|
|
27453
27396
|
static contextType = PerseusI18nContext;
|
|
27454
|
-
state = {
|
|
27455
|
-
expanded: false
|
|
27456
|
-
};
|
|
27457
|
-
_mounted = false;
|
|
27458
27397
|
static defaultProps = {
|
|
27459
27398
|
showPrompt: "Explain",
|
|
27460
27399
|
hidePrompt: "Hide explanation",
|
|
@@ -27463,10 +27402,13 @@ class Explanation extends React__namespace.Component {
|
|
|
27463
27402
|
linterContext: PerseusLinter.linterContextDefault
|
|
27464
27403
|
};
|
|
27465
27404
|
|
|
27466
|
-
//
|
|
27467
|
-
|
|
27468
|
-
|
|
27469
|
-
|
|
27405
|
+
// this just helps with TS weak typing when a Widget
|
|
27406
|
+
// doesn't implement any Widget methods
|
|
27407
|
+
isWidget = true;
|
|
27408
|
+
state = {
|
|
27409
|
+
expanded: false
|
|
27410
|
+
};
|
|
27411
|
+
_mounted = false;
|
|
27470
27412
|
componentDidMount() {
|
|
27471
27413
|
this._mounted = true;
|
|
27472
27414
|
}
|
|
@@ -27488,14 +27430,6 @@ class Explanation extends React__namespace.Component {
|
|
|
27488
27430
|
});
|
|
27489
27431
|
this.props.trackInteraction();
|
|
27490
27432
|
};
|
|
27491
|
-
getUserInput() {
|
|
27492
|
-
return {};
|
|
27493
|
-
}
|
|
27494
|
-
|
|
27495
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27496
|
-
simpleValidate() {
|
|
27497
|
-
return noopValidator();
|
|
27498
|
-
}
|
|
27499
27433
|
render() {
|
|
27500
27434
|
const promptText = this.state.expanded ? this.props.hidePrompt : this.props.showPrompt;
|
|
27501
27435
|
const caretIcon = this.state.expanded ? caretUp__default["default"] : caretDown__default["default"];
|
|
@@ -27601,7 +27535,9 @@ var Explanation$1 = {
|
|
|
27601
27535
|
defaultAlignment: "inline",
|
|
27602
27536
|
widget: Explanation,
|
|
27603
27537
|
transform: ___default["default"].identity,
|
|
27604
|
-
isLintable: true
|
|
27538
|
+
isLintable: true,
|
|
27539
|
+
// TODO: things that aren't interactive shouldn't need validators
|
|
27540
|
+
validator: () => noopValidator()
|
|
27605
27541
|
};
|
|
27606
27542
|
|
|
27607
27543
|
class GradedGroupAnswerBar extends React__namespace.Component {
|
|
@@ -29133,9 +29069,6 @@ class Grapher extends React__namespace.Component {
|
|
|
29133
29069
|
horizHairline;
|
|
29134
29070
|
vertHairline;
|
|
29135
29071
|
static defaultProps = DEFAULT_GRAPHER_PROPS;
|
|
29136
|
-
static validate(state, rubric) {
|
|
29137
|
-
return grapherValidator(state, rubric);
|
|
29138
|
-
}
|
|
29139
29072
|
static getUserInputFromProps(props) {
|
|
29140
29073
|
return props.plot;
|
|
29141
29074
|
}
|
|
@@ -29238,9 +29171,6 @@ class Grapher extends React__namespace.Component {
|
|
|
29238
29171
|
this.vertHairline.hide();
|
|
29239
29172
|
}
|
|
29240
29173
|
};
|
|
29241
|
-
simpleValidate(rubric) {
|
|
29242
|
-
return grapherValidator(this.getUserInput(), rubric);
|
|
29243
|
-
}
|
|
29244
29174
|
getUserInput() {
|
|
29245
29175
|
return Grapher.getUserInputFromProps(this.props);
|
|
29246
29176
|
}
|
|
@@ -29332,7 +29262,8 @@ var Grapher$1 = {
|
|
|
29332
29262
|
hidden: true,
|
|
29333
29263
|
widget: Grapher,
|
|
29334
29264
|
transform: propTransform$2,
|
|
29335
|
-
staticTransform: staticTransform$4
|
|
29265
|
+
staticTransform: staticTransform$4,
|
|
29266
|
+
validator: grapherValidator
|
|
29336
29267
|
};
|
|
29337
29268
|
|
|
29338
29269
|
// exports has no 'transform'
|
|
@@ -29361,6 +29292,13 @@ class Group extends React__namespace.Component {
|
|
|
29361
29292
|
return change.apply(_this, args);
|
|
29362
29293
|
};
|
|
29363
29294
|
})();
|
|
29295
|
+
getUserInputMap() {
|
|
29296
|
+
return this.rendererRef?.getUserInputMap();
|
|
29297
|
+
}
|
|
29298
|
+
|
|
29299
|
+
/**
|
|
29300
|
+
* @deprecated getUserInputMap should be used for Groups
|
|
29301
|
+
*/
|
|
29364
29302
|
getUserInput() {
|
|
29365
29303
|
return this.rendererRef?.getUserInput();
|
|
29366
29304
|
}
|
|
@@ -29373,11 +29311,6 @@ class Group extends React__namespace.Component {
|
|
|
29373
29311
|
// (all our changes were in state):
|
|
29374
29312
|
return null;
|
|
29375
29313
|
};
|
|
29376
|
-
simpleValidate() {
|
|
29377
|
-
return this.rendererRef?.score() ?? {
|
|
29378
|
-
type: "invalid"
|
|
29379
|
-
};
|
|
29380
|
-
}
|
|
29381
29314
|
|
|
29382
29315
|
// Mobile API:
|
|
29383
29316
|
getInputPaths() {
|
|
@@ -29527,9 +29460,6 @@ class Iframe extends React__namespace.Component {
|
|
|
29527
29460
|
allowFullScreen: false,
|
|
29528
29461
|
allowTopNavigation: false
|
|
29529
29462
|
};
|
|
29530
|
-
static validate(state) {
|
|
29531
|
-
return iframeValidator(state);
|
|
29532
|
-
}
|
|
29533
29463
|
componentDidMount() {
|
|
29534
29464
|
$__default["default"](window).on("message", this.handleMessageEvent);
|
|
29535
29465
|
}
|
|
@@ -29571,9 +29501,6 @@ class Iframe extends React__namespace.Component {
|
|
|
29571
29501
|
return change.apply(_this, args);
|
|
29572
29502
|
};
|
|
29573
29503
|
})();
|
|
29574
|
-
simpleValidate() {
|
|
29575
|
-
return iframeValidator(this.getUserInput());
|
|
29576
|
-
}
|
|
29577
29504
|
render() {
|
|
29578
29505
|
const style = {
|
|
29579
29506
|
width: String(this.props.width),
|
|
@@ -29641,7 +29568,8 @@ var Iframe$1 = {
|
|
|
29641
29568
|
displayName: "Iframe (deprecated)",
|
|
29642
29569
|
widget: Iframe,
|
|
29643
29570
|
// Let's not expose it to all content creators yet
|
|
29644
|
-
hidden: true
|
|
29571
|
+
hidden: true,
|
|
29572
|
+
validator: iframeValidator
|
|
29645
29573
|
};
|
|
29646
29574
|
|
|
29647
29575
|
const defaultBoxSize = 400;
|
|
@@ -29670,10 +29598,9 @@ class ImageWidget extends React__namespace.Component {
|
|
|
29670
29598
|
linterContext: PerseusLinter.linterContextDefault
|
|
29671
29599
|
};
|
|
29672
29600
|
|
|
29673
|
-
//
|
|
29674
|
-
|
|
29675
|
-
|
|
29676
|
-
}
|
|
29601
|
+
// this just helps with TS weak typing when a Widget
|
|
29602
|
+
// doesn't implement any Widget methods
|
|
29603
|
+
isWidget = true;
|
|
29677
29604
|
change = (() => {
|
|
29678
29605
|
var _this = this;
|
|
29679
29606
|
return function () {
|
|
@@ -29683,14 +29610,6 @@ class ImageWidget extends React__namespace.Component {
|
|
|
29683
29610
|
return change.apply(_this, args);
|
|
29684
29611
|
};
|
|
29685
29612
|
})();
|
|
29686
|
-
getUserInput() {
|
|
29687
|
-
return null;
|
|
29688
|
-
}
|
|
29689
|
-
|
|
29690
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
29691
|
-
simpleValidate() {
|
|
29692
|
-
return noopValidator();
|
|
29693
|
-
}
|
|
29694
29613
|
render() {
|
|
29695
29614
|
let image;
|
|
29696
29615
|
const alt = this.props.caption === this.props.alt ? "" : this.props.alt;
|
|
@@ -29836,7 +29755,9 @@ var Image$1 = {
|
|
|
29836
29755
|
supportedAlignments: editorAlignments,
|
|
29837
29756
|
displayName: "Image",
|
|
29838
29757
|
widget: ImageWidget,
|
|
29839
|
-
isLintable: true
|
|
29758
|
+
isLintable: true,
|
|
29759
|
+
// TODO: things that aren't interactive shouldn't need validators
|
|
29760
|
+
validator: () => noopValidator()
|
|
29840
29761
|
};
|
|
29841
29762
|
|
|
29842
29763
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -29915,10 +29836,9 @@ class Interaction extends React__namespace.Component {
|
|
|
29915
29836
|
elements: []
|
|
29916
29837
|
};
|
|
29917
29838
|
|
|
29918
|
-
//
|
|
29919
|
-
|
|
29920
|
-
|
|
29921
|
-
}
|
|
29839
|
+
// this just helps with TS weak typing when a Widget
|
|
29840
|
+
// doesn't implement any Widget methods
|
|
29841
|
+
isWidget = true;
|
|
29922
29842
|
state = {
|
|
29923
29843
|
variables: _getInitialVariables(this.props.elements),
|
|
29924
29844
|
functions: _getInitialFunctions(this.props.elements)
|
|
@@ -30026,18 +29946,6 @@ class Interaction extends React__namespace.Component {
|
|
|
30026
29946
|
return change.apply(_this, args);
|
|
30027
29947
|
};
|
|
30028
29948
|
})();
|
|
30029
|
-
getUserInput() {
|
|
30030
|
-
// TODO(eater): Perhaps we want to be able to record the state of the
|
|
30031
|
-
// user's interaction. Unfortunately sending all the props will
|
|
30032
|
-
// probably make the attempt payload too large. So for now, don't send
|
|
30033
|
-
// anything.
|
|
30034
|
-
return {};
|
|
30035
|
-
}
|
|
30036
|
-
|
|
30037
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
30038
|
-
simpleValidate() {
|
|
30039
|
-
return noopValidator();
|
|
30040
|
-
}
|
|
30041
29949
|
render() {
|
|
30042
29950
|
const range = this.props.graph.range;
|
|
30043
29951
|
let labels = this.props.graph.labels;
|
|
@@ -30445,7 +30353,9 @@ var Interactive = {
|
|
|
30445
30353
|
displayName: "Interaction",
|
|
30446
30354
|
widget: Interaction,
|
|
30447
30355
|
transform: ___default["default"].identity,
|
|
30448
|
-
hidden: true
|
|
30356
|
+
hidden: true,
|
|
30357
|
+
// TODO: things that aren't interactive shouldn't need validators
|
|
30358
|
+
validator: () => noopValidator()
|
|
30449
30359
|
};
|
|
30450
30360
|
|
|
30451
30361
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -30975,16 +30885,40 @@ function polar(r, th) {
|
|
|
30975
30885
|
th = th * Math.PI / 180;
|
|
30976
30886
|
return [r[0] * Math.cos(th), r[1] * Math.sin(th)];
|
|
30977
30887
|
}
|
|
30978
|
-
|
|
30979
|
-
|
|
30980
|
-
|
|
30981
|
-
|
|
30982
|
-
|
|
30983
|
-
|
|
30984
|
-
|
|
30985
|
-
return
|
|
30888
|
+
// This function calculates the angle between two points and an optional vertex.
|
|
30889
|
+
// If the vertex is not provided, the angle is measured between the two points.
|
|
30890
|
+
// This does not account for reflex angles or clockwise position.
|
|
30891
|
+
const getAngleFromVertex = (point, vertex) => {
|
|
30892
|
+
const x = point[0] - vertex[0];
|
|
30893
|
+
const y = point[1] - vertex[1];
|
|
30894
|
+
if (!x && !y) {
|
|
30895
|
+
return 0;
|
|
30986
30896
|
}
|
|
30987
|
-
return
|
|
30897
|
+
return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
|
|
30898
|
+
};
|
|
30899
|
+
|
|
30900
|
+
// This function calculates the clockwise angle between three points,
|
|
30901
|
+
// and is used to generate the labels and equation strings of the
|
|
30902
|
+
// current angle for the interactive graph.
|
|
30903
|
+
const getClockwiseAngle = function (coords) {
|
|
30904
|
+
let allowReflexAngles = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
30905
|
+
const coordsCopy = [...coords];
|
|
30906
|
+
// The coords are saved as [point1, vertex, point2] in the interactive graph
|
|
30907
|
+
const areClockwise = clockwise$1([coordsCopy[0], coordsCopy[2], coordsCopy[1]]);
|
|
30908
|
+
|
|
30909
|
+
// We may need to reverse the coordinates if we allow
|
|
30910
|
+
// reflex angles and the points are not in clockwise order.
|
|
30911
|
+
const shouldReverseCoords = !areClockwise || allowReflexAngles;
|
|
30912
|
+
|
|
30913
|
+
// Reverse the coordinates accordingly to ensure the angle is calculated correctly
|
|
30914
|
+
const clockwiseCoords = shouldReverseCoords ? coordsCopy.reverse() : coordsCopy;
|
|
30915
|
+
|
|
30916
|
+
// Calculate the angles between the two points and get the difference
|
|
30917
|
+
// between the two angles to get the clockwise angle.
|
|
30918
|
+
const startAngle = getAngleFromVertex(clockwiseCoords[0], clockwiseCoords[1]);
|
|
30919
|
+
const endAngle = getAngleFromVertex(clockwiseCoords[2], clockwiseCoords[1]);
|
|
30920
|
+
const angle = (startAngle + 360 - endAngle) % 360;
|
|
30921
|
+
return angle;
|
|
30988
30922
|
};
|
|
30989
30923
|
|
|
30990
30924
|
const defaultGraphConfig = {
|
|
@@ -33416,8 +33350,8 @@ const Angle = _ref2 => {
|
|
|
33416
33350
|
const clockwiseCoords = shouldReverseCoords ? coords : coords.reverse();
|
|
33417
33351
|
|
|
33418
33352
|
// Calculate the angles between the two points
|
|
33419
|
-
const startAngle =
|
|
33420
|
-
const endAngle =
|
|
33353
|
+
const startAngle = getAngleFromVertex(clockwiseCoords[0], vertex);
|
|
33354
|
+
const endAngle = getAngleFromVertex(clockwiseCoords[1], vertex);
|
|
33421
33355
|
const angle = (startAngle + 360 - endAngle) % 360;
|
|
33422
33356
|
|
|
33423
33357
|
// Check if the angle is reflexive
|
|
@@ -34506,7 +34440,9 @@ const MafsGraph = props => {
|
|
|
34506
34440
|
analytics.onAnalyticsEvent({
|
|
34507
34441
|
type: "perseus:interactive-graph-widget:rendered",
|
|
34508
34442
|
payload: {
|
|
34509
|
-
type
|
|
34443
|
+
type,
|
|
34444
|
+
widgetType: "INTERACTIVE_GRAPH",
|
|
34445
|
+
widgetId: "interactive-graph"
|
|
34510
34446
|
}
|
|
34511
34447
|
});
|
|
34512
34448
|
});
|
|
@@ -35793,7 +35729,7 @@ function boundAndSnapAngleVertex(_ref2, _ref3) {
|
|
|
35793
35729
|
for (const i of [0, 2]) {
|
|
35794
35730
|
const oldPoint = coordsCopy[i];
|
|
35795
35731
|
let newPoint = mafs.vec.add(oldPoint, delta);
|
|
35796
|
-
let angle =
|
|
35732
|
+
let angle = getAngleFromVertex(newVertex, newPoint);
|
|
35797
35733
|
angle *= Math.PI / 180;
|
|
35798
35734
|
newPoint = constrainToBoundsOnAngle(newPoint, angle, range, snapStep);
|
|
35799
35735
|
newPoints[i] = newPoint;
|
|
@@ -35866,7 +35802,7 @@ function boundAndSnapAngleEndPoints(destinationPoint, _ref5, index) {
|
|
|
35866
35802
|
const vertex = coords[1];
|
|
35867
35803
|
|
|
35868
35804
|
// Gets the angle between the coords and the vertex
|
|
35869
|
-
let angle =
|
|
35805
|
+
let angle = getAngleFromVertex(coordsCopy[index], vertex);
|
|
35870
35806
|
|
|
35871
35807
|
// Snap the angle to the nearest multiple of snapDegrees (if provided)
|
|
35872
35808
|
angle = Math.round((angle - offsetDegrees) / snap) * snap + offsetDegrees;
|
|
@@ -35913,8 +35849,8 @@ function boundAndSnapToPolygonAngle(destinationPoint, _ref6, index) {
|
|
|
35913
35849
|
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
35914
35850
|
});
|
|
35915
35851
|
const getAngle = function (a, vertex, b) {
|
|
35916
|
-
const angle =
|
|
35917
|
-
return
|
|
35852
|
+
const angle = getClockwiseAngle([coordsCopy[rel(a)], coordsCopy[rel(vertex)], coordsCopy[rel(b)]]);
|
|
35853
|
+
return angle;
|
|
35918
35854
|
};
|
|
35919
35855
|
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
35920
35856
|
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
@@ -35941,7 +35877,7 @@ function boundAndSnapToPolygonAngle(destinationPoint, _ref6, index) {
|
|
|
35941
35877
|
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
35942
35878
|
|
|
35943
35879
|
// Angle at the second vertex of the polygon
|
|
35944
|
-
const outerAngle =
|
|
35880
|
+
const outerAngle = getAngleFromVertex(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
35945
35881
|
|
|
35946
35882
|
// Uses the length of the side of the polygon (radial coordinate)
|
|
35947
35883
|
// and the angle between the first and second sides of the
|
|
@@ -35992,7 +35928,7 @@ function boundAndSnapToSides(destinationPoint, _ref7, index) {
|
|
|
35992
35928
|
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
35993
35929
|
|
|
35994
35930
|
// Angle at the second vertex of the polygon
|
|
35995
|
-
const outerAngle =
|
|
35931
|
+
const outerAngle = getAngleFromVertex(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
35996
35932
|
|
|
35997
35933
|
// Returns true if the points form a counter-clockwise turn;
|
|
35998
35934
|
// a.k.a if the point is on the left or right of the polygon.
|
|
@@ -36101,7 +36037,8 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
36101
36037
|
const numPoints = graph.type === "point" ? graph.numPoints : null;
|
|
36102
36038
|
const numSides = graph.type === "polygon" ? graph.numSides : null;
|
|
36103
36039
|
const snapTo = graph.type === "polygon" ? graph.snapTo : null;
|
|
36104
|
-
const showAngles = graph.type === "polygon" ? graph.showAngles : null;
|
|
36040
|
+
const showAngles = graph.type === "polygon" || graph.type === "angle" ? graph.showAngles : null;
|
|
36041
|
+
const allowReflexAngles = graph.type === "angle" ? graph.allowReflexAngles : null;
|
|
36105
36042
|
const showSides = graph.type === "polygon" ? graph.showSides : null;
|
|
36106
36043
|
const startCoords = "startCoords" in graph ? graph.startCoords : undefined;
|
|
36107
36044
|
const originalPropsRef = React.useRef(props);
|
|
@@ -36114,7 +36051,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
36114
36051
|
if (latestPropsRef.current !== originalPropsRef.current) {
|
|
36115
36052
|
dispatch(reinitialize(latestPropsRef.current));
|
|
36116
36053
|
}
|
|
36117
|
-
}, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, startCoords]);
|
|
36054
|
+
}, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, startCoords, allowReflexAngles]);
|
|
36118
36055
|
|
|
36119
36056
|
// If the graph is static, it always displays the correct answer. This is
|
|
36120
36057
|
// standard behavior for Perseus widgets (e.g. compare the Radio widget).
|
|
@@ -36302,17 +36239,15 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36302
36239
|
} else if (userInput.type === "angle" && rubric.correct.type === "angle") {
|
|
36303
36240
|
const guess = userInput.coords;
|
|
36304
36241
|
const correct = rubric.correct.coords;
|
|
36242
|
+
const allowReflexAngles = rubric.correct.allowReflexAngles;
|
|
36305
36243
|
let match;
|
|
36306
36244
|
if (rubric.correct.match === "congruent") {
|
|
36307
36245
|
const angles = ___default["default"].map([guess, correct], function (coords) {
|
|
36308
|
-
|
|
36309
|
-
|
|
36310
|
-
|
|
36311
|
-
|
|
36312
|
-
|
|
36313
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
36314
|
-
coords[1]);
|
|
36315
|
-
return (angle + 360) % 360;
|
|
36246
|
+
if (!coords) {
|
|
36247
|
+
return false;
|
|
36248
|
+
}
|
|
36249
|
+
const angle = getClockwiseAngle(coords, allowReflexAngles);
|
|
36250
|
+
return angle;
|
|
36316
36251
|
});
|
|
36317
36252
|
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
36318
36253
|
match = eq$1(...angles);
|
|
@@ -37215,7 +37150,7 @@ class LegacyInteractiveGraph extends React__namespace.Component {
|
|
|
37215
37150
|
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
37216
37151
|
});
|
|
37217
37152
|
const getAngle = function (a, vertex, b) {
|
|
37218
|
-
const angle = GraphUtils.
|
|
37153
|
+
const angle = GraphUtils.findAngleDeprecated(coords[rel(a)], coords[rel(b)], coords[rel(vertex)]);
|
|
37219
37154
|
return (angle + 360) % 360;
|
|
37220
37155
|
};
|
|
37221
37156
|
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
@@ -37234,7 +37169,7 @@ class LegacyInteractiveGraph extends React__namespace.Component {
|
|
|
37234
37169
|
|
|
37235
37170
|
// Solve for side by using the law of sines
|
|
37236
37171
|
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
37237
|
-
const outerAngle = GraphUtils.
|
|
37172
|
+
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
37238
37173
|
const offset = polar$1(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
37239
37174
|
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
37240
37175
|
}
|
|
@@ -37257,7 +37192,7 @@ class LegacyInteractiveGraph extends React__namespace.Component {
|
|
|
37257
37192
|
|
|
37258
37193
|
// Solve for angle by using the law of cosines
|
|
37259
37194
|
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
37260
|
-
const outerAngle = GraphUtils.
|
|
37195
|
+
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
37261
37196
|
const onLeft = sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
37262
37197
|
const offset = polar$1(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
37263
37198
|
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
@@ -37577,9 +37512,6 @@ class LegacyInteractiveGraph extends React__namespace.Component {
|
|
|
37577
37512
|
getUserInput() {
|
|
37578
37513
|
return InteractiveGraph.getUserInputFromProps(this.props);
|
|
37579
37514
|
}
|
|
37580
|
-
simpleValidate(rubric) {
|
|
37581
|
-
return interactiveGraphValidator(this.getUserInput(), rubric);
|
|
37582
|
-
}
|
|
37583
37515
|
focus = $__default["default"].noop;
|
|
37584
37516
|
render() {
|
|
37585
37517
|
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
@@ -37652,9 +37584,6 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
37652
37584
|
}
|
|
37653
37585
|
throw new perseusCore.PerseusError("Cannot getUserInput from a graph that has never rendered", perseusCore.Errors.NotAllowed);
|
|
37654
37586
|
}
|
|
37655
|
-
simpleValidate(rubric) {
|
|
37656
|
-
return interactiveGraphValidator(this.getUserInput(), rubric);
|
|
37657
|
-
}
|
|
37658
37587
|
render() {
|
|
37659
37588
|
// Mafs shim
|
|
37660
37589
|
const mafsFlags = this.props.apiOptions?.flags?.["mafs"];
|
|
@@ -37981,12 +37910,10 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
37981
37910
|
throw makeInvalidTypeError("getAngleEquationString", "angle");
|
|
37982
37911
|
}
|
|
37983
37912
|
const coords = InteractiveGraph.getAngleCoords(props.graph, props);
|
|
37984
|
-
const
|
|
37913
|
+
const allowReflexAngles = props.graph.allowReflexAngles;
|
|
37914
|
+
const angle = getClockwiseAngle(coords, allowReflexAngles);
|
|
37985
37915
|
return angle.toFixed(0) + "\u00B0 angle" + " at (" + coords[1].join(", ") + ")";
|
|
37986
37916
|
}
|
|
37987
|
-
static validate(userInput, rubric) {
|
|
37988
|
-
return interactiveGraphValidator(userInput, rubric);
|
|
37989
|
-
}
|
|
37990
37917
|
static getUserInputFromProps(props) {
|
|
37991
37918
|
return props.graph;
|
|
37992
37919
|
}
|
|
@@ -38023,7 +37950,8 @@ var InteractiveGraph$1 = {
|
|
|
38023
37950
|
name: "interactive-graph",
|
|
38024
37951
|
displayName: "Interactive graph (Assessments only)",
|
|
38025
37952
|
widget: InteractiveGraph,
|
|
38026
|
-
staticTransform: staticTransform$3
|
|
37953
|
+
staticTransform: staticTransform$3,
|
|
37954
|
+
validator: interactiveGraphValidator
|
|
38027
37955
|
};
|
|
38028
37956
|
|
|
38029
37957
|
const black = "#000000";
|
|
@@ -39399,9 +39327,6 @@ class LabelImage extends React__namespace.Component {
|
|
|
39399
39327
|
// The rendered markers on the question image for labeling.
|
|
39400
39328
|
_markers;
|
|
39401
39329
|
_mounted = false;
|
|
39402
|
-
static validate(state, rubric) {
|
|
39403
|
-
return labelImageValidator(state);
|
|
39404
|
-
}
|
|
39405
39330
|
|
|
39406
39331
|
/**
|
|
39407
39332
|
* Test whether point is contained within triangle.
|
|
@@ -39594,9 +39519,6 @@ class LabelImage extends React__namespace.Component {
|
|
|
39594
39519
|
componentWillUnmount() {
|
|
39595
39520
|
this._mounted = false;
|
|
39596
39521
|
}
|
|
39597
|
-
simpleValidate(rubric) {
|
|
39598
|
-
return labelImageValidator(this.getUserInput());
|
|
39599
|
-
}
|
|
39600
39522
|
getUserInput() {
|
|
39601
39523
|
const {
|
|
39602
39524
|
markers
|
|
@@ -39962,19 +39884,13 @@ const LabelImageWithDependencies = /*#__PURE__*/React__namespace.forwardRef((pro
|
|
|
39962
39884
|
analytics: deps.analytics
|
|
39963
39885
|
}, props));
|
|
39964
39886
|
});
|
|
39965
|
-
|
|
39966
|
-
// HACK: Propogate "static" methods onto our wrapper component.
|
|
39967
|
-
// In the future we should adjust client apps to not depend on these static
|
|
39968
|
-
// methods and instead adjust Peresus to provide these facilities through
|
|
39969
|
-
// instance methods on our Renderers.
|
|
39970
|
-
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
39971
|
-
LabelImageWithDependencies.validate = labelImageValidator;
|
|
39972
39887
|
var LabelImage$1 = {
|
|
39973
39888
|
name: "label-image",
|
|
39974
39889
|
displayName: "Label Image",
|
|
39975
39890
|
widget: LabelImageWithDependencies,
|
|
39976
39891
|
accessible: true,
|
|
39977
|
-
isLintable: true
|
|
39892
|
+
isLintable: true,
|
|
39893
|
+
validator: labelImageValidator
|
|
39978
39894
|
};
|
|
39979
39895
|
|
|
39980
39896
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -40716,9 +40632,6 @@ class Matcher extends React__namespace.Component {
|
|
|
40716
40632
|
rightHeight: 0,
|
|
40717
40633
|
texRendererLoaded: false
|
|
40718
40634
|
};
|
|
40719
|
-
static validate(state, rubric) {
|
|
40720
|
-
return matcherValidator(state, rubric);
|
|
40721
|
-
}
|
|
40722
40635
|
changeAndTrack = e => {
|
|
40723
40636
|
this.props.onChange(e);
|
|
40724
40637
|
this.props.trackInteraction();
|
|
@@ -40770,9 +40683,6 @@ class Matcher extends React__namespace.Component {
|
|
|
40770
40683
|
// @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
|
|
40771
40684
|
this.refs.right.moveOptionToIndex(option, index);
|
|
40772
40685
|
};
|
|
40773
|
-
simpleValidate(rubric) {
|
|
40774
|
-
return matcherValidator(this.getUserInput(), rubric);
|
|
40775
|
-
}
|
|
40776
40686
|
render() {
|
|
40777
40687
|
// To minimize layout shift, we display a spinner until our math
|
|
40778
40688
|
// renderer is ready to render the math inside the matcher. To
|
|
@@ -40899,7 +40809,8 @@ var Matcher$1 = {
|
|
|
40899
40809
|
name: "matcher",
|
|
40900
40810
|
displayName: "Matcher (two column)",
|
|
40901
40811
|
widget: Matcher,
|
|
40902
|
-
isLintable: true
|
|
40812
|
+
isLintable: true,
|
|
40813
|
+
validator: matcherValidator
|
|
40903
40814
|
};
|
|
40904
40815
|
|
|
40905
40816
|
function matrixValidator(state, rubric, strings) {
|
|
@@ -41040,9 +40951,6 @@ class Matrix extends React__namespace.Component {
|
|
|
41040
40951
|
apiOptions: ApiOptions.defaults,
|
|
41041
40952
|
linterContext: PerseusLinter.linterContextDefault
|
|
41042
40953
|
};
|
|
41043
|
-
static validate(state, rubric, strings) {
|
|
41044
|
-
return matrixValidator(state, rubric, strings);
|
|
41045
|
-
}
|
|
41046
40954
|
state = {
|
|
41047
40955
|
enterTheMatrix: 0
|
|
41048
40956
|
};
|
|
@@ -41185,9 +41093,6 @@ class Matrix extends React__namespace.Component {
|
|
|
41185
41093
|
answers: this.props.answers
|
|
41186
41094
|
};
|
|
41187
41095
|
}
|
|
41188
|
-
simpleValidate(rubric) {
|
|
41189
|
-
return matrixValidator(this.getUserInput(), rubric, this.context.strings);
|
|
41190
|
-
}
|
|
41191
41096
|
render() {
|
|
41192
41097
|
// Set the input sizes through JS so we can control the size of the
|
|
41193
41098
|
// brackets. (If we set them in CSS we won't know values until the
|
|
@@ -41372,7 +41277,8 @@ var Matrix$1 = {
|
|
|
41372
41277
|
widget: Matrix,
|
|
41373
41278
|
transform: propTransform$1,
|
|
41374
41279
|
staticTransform: staticTransform$2,
|
|
41375
|
-
isLintable: true
|
|
41280
|
+
isLintable: true,
|
|
41281
|
+
validator: matrixValidator
|
|
41376
41282
|
};
|
|
41377
41283
|
|
|
41378
41284
|
const defaultImage = {
|
|
@@ -41381,7 +41287,6 @@ const defaultImage = {
|
|
|
41381
41287
|
left: 0
|
|
41382
41288
|
};
|
|
41383
41289
|
class Measurer extends React__namespace.Component {
|
|
41384
|
-
static validate = noopValidator;
|
|
41385
41290
|
static defaultProps = {
|
|
41386
41291
|
box: [480, 480],
|
|
41387
41292
|
image: {},
|
|
@@ -41394,6 +41299,10 @@ class Measurer extends React__namespace.Component {
|
|
|
41394
41299
|
rulerPixels: 40,
|
|
41395
41300
|
rulerLength: 10
|
|
41396
41301
|
};
|
|
41302
|
+
|
|
41303
|
+
// this just helps with TS weak typing when a Widget
|
|
41304
|
+
// doesn't implement any Widget methods
|
|
41305
|
+
isWidget = true;
|
|
41397
41306
|
state = {};
|
|
41398
41307
|
ruler;
|
|
41399
41308
|
protractor;
|
|
@@ -41446,12 +41355,6 @@ class Measurer extends React__namespace.Component {
|
|
|
41446
41355
|
});
|
|
41447
41356
|
}
|
|
41448
41357
|
}
|
|
41449
|
-
getUserInput() {
|
|
41450
|
-
return {};
|
|
41451
|
-
}
|
|
41452
|
-
simpleValidate() {
|
|
41453
|
-
return noopValidator(1);
|
|
41454
|
-
}
|
|
41455
41358
|
render() {
|
|
41456
41359
|
const image = ___default["default"].extend({}, defaultImage, this.props.image);
|
|
41457
41360
|
|
|
@@ -41498,7 +41401,9 @@ var Measurer$1 = {
|
|
|
41498
41401
|
major: 1,
|
|
41499
41402
|
minor: 0
|
|
41500
41403
|
},
|
|
41501
|
-
propUpgrades: propUpgrades
|
|
41404
|
+
propUpgrades: propUpgrades,
|
|
41405
|
+
// TODO: things that aren't interactive shouldn't need validators
|
|
41406
|
+
validator: () => noopValidator(1)
|
|
41502
41407
|
};
|
|
41503
41408
|
|
|
41504
41409
|
/**
|
|
@@ -42371,18 +42276,9 @@ class MoleculeWidget extends React__namespace.Component {
|
|
|
42371
42276
|
rotationAngle: 0
|
|
42372
42277
|
};
|
|
42373
42278
|
|
|
42374
|
-
//
|
|
42375
|
-
|
|
42376
|
-
|
|
42377
|
-
}
|
|
42378
|
-
getUserInput() {
|
|
42379
|
-
return [];
|
|
42380
|
-
}
|
|
42381
|
-
|
|
42382
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
42383
|
-
validate() {
|
|
42384
|
-
return noopValidator();
|
|
42385
|
-
}
|
|
42279
|
+
// this just helps with TS weak typing when a Widget
|
|
42280
|
+
// doesn't implement any Widget methods
|
|
42281
|
+
isWidget = true;
|
|
42386
42282
|
render() {
|
|
42387
42283
|
return /*#__PURE__*/React__namespace.createElement(Molecule, {
|
|
42388
42284
|
widgetId: this.props.widgetId,
|
|
@@ -42395,7 +42291,8 @@ var Molecule$1 = {
|
|
|
42395
42291
|
name: "molecule-renderer",
|
|
42396
42292
|
displayName: "Molecule renderer",
|
|
42397
42293
|
hidden: true,
|
|
42398
|
-
widget: MoleculeWidget
|
|
42294
|
+
widget: MoleculeWidget,
|
|
42295
|
+
validator: () => noopValidator()
|
|
42399
42296
|
};
|
|
42400
42297
|
|
|
42401
42298
|
function numberLineValidator(state, rubric) {
|
|
@@ -42540,18 +42437,16 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
42540
42437
|
results.push(graphie.line([x, -0.2], [x, 0.2]));
|
|
42541
42438
|
const labelTicks = props.labelTicks;
|
|
42542
42439
|
if (labelTicks || props.labelStyle === "decimal ticks") {
|
|
42543
|
-
|
|
42440
|
+
if (x === leftLabel || x === rightLabel) {
|
|
42441
|
+
results.push(graphie.style({
|
|
42442
|
+
color: KhanColors.BLUE
|
|
42443
|
+
}, () => _label(graphie, props.labelStyle, x, x, base)));
|
|
42444
|
+
} else {
|
|
42445
|
+
results.push(_label(graphie, props.labelStyle, x, x, base));
|
|
42446
|
+
}
|
|
42544
42447
|
}
|
|
42545
42448
|
}
|
|
42546
42449
|
|
|
42547
|
-
// Render the text labels
|
|
42548
|
-
results.push(graphie.style(props.isMobile ? {
|
|
42549
|
-
color: KhanColors.BLUE
|
|
42550
|
-
} : {}, () => _label(graphie, props.labelStyle, leftLabel, leftLabel, base)));
|
|
42551
|
-
results.push(graphie.style(props.isMobile ? {
|
|
42552
|
-
color: KhanColors.BLUE
|
|
42553
|
-
} : {}, () => _label(graphie, props.labelStyle, rightLabel, rightLabel, base)));
|
|
42554
|
-
|
|
42555
42450
|
// Render the labels' lines
|
|
42556
42451
|
graphie.style({
|
|
42557
42452
|
stroke: KhanColors.BLUE,
|
|
@@ -42584,9 +42479,6 @@ class NumberLine extends React__namespace.Component {
|
|
|
42584
42479
|
state = {
|
|
42585
42480
|
numDivisionsEmpty: false
|
|
42586
42481
|
};
|
|
42587
|
-
static validate(state, rubric) {
|
|
42588
|
-
return numberLineValidator(state, rubric);
|
|
42589
|
-
}
|
|
42590
42482
|
change = (() => {
|
|
42591
42483
|
var _this = this;
|
|
42592
42484
|
return function () {
|
|
@@ -42879,9 +42771,6 @@ class NumberLine extends React__namespace.Component {
|
|
|
42879
42771
|
divisionRange: this.props.divisionRange
|
|
42880
42772
|
};
|
|
42881
42773
|
}
|
|
42882
|
-
simpleValidate(rubric) {
|
|
42883
|
-
return numberLineValidator(this.getUserInput(), rubric);
|
|
42884
|
-
}
|
|
42885
42774
|
render() {
|
|
42886
42775
|
const {
|
|
42887
42776
|
strings
|
|
@@ -42985,7 +42874,8 @@ var NumberLine$1 = {
|
|
|
42985
42874
|
displayName: "Number line",
|
|
42986
42875
|
widget: NumberLine,
|
|
42987
42876
|
transform: numberLineTransform,
|
|
42988
|
-
staticTransform: staticTransform$1
|
|
42877
|
+
staticTransform: staticTransform$1,
|
|
42878
|
+
validator: numberLineValidator
|
|
42989
42879
|
};
|
|
42990
42880
|
|
|
42991
42881
|
function ordererValidator(userInput, rubric) {
|
|
@@ -43195,9 +43085,6 @@ class Orderer extends React__namespace.Component {
|
|
|
43195
43085
|
animateTo: null,
|
|
43196
43086
|
grabPos: null
|
|
43197
43087
|
};
|
|
43198
|
-
static validate(userInput, rubric) {
|
|
43199
|
-
return ordererValidator(userInput, rubric);
|
|
43200
|
-
}
|
|
43201
43088
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
43202
43089
|
if (!___default["default"].isEqual(this.props.current, nextProps.current)) {
|
|
43203
43090
|
this.setState({
|
|
@@ -43437,9 +43324,6 @@ class Orderer extends React__namespace.Component {
|
|
|
43437
43324
|
})
|
|
43438
43325
|
};
|
|
43439
43326
|
}
|
|
43440
|
-
simpleValidate(rubric) {
|
|
43441
|
-
return ordererValidator(this.getUserInput(), rubric);
|
|
43442
|
-
}
|
|
43443
43327
|
render() {
|
|
43444
43328
|
// This is the card we are currently dragging
|
|
43445
43329
|
const dragging = this.state.dragging && /*#__PURE__*/React__namespace.createElement(Card
|
|
@@ -43549,7 +43433,8 @@ var Orderer$1 = {
|
|
|
43549
43433
|
displayName: "Orderer",
|
|
43550
43434
|
hidden: true,
|
|
43551
43435
|
widget: Orderer,
|
|
43552
|
-
isLintable: true
|
|
43436
|
+
isLintable: true,
|
|
43437
|
+
validator: ordererValidator
|
|
43553
43438
|
};
|
|
43554
43439
|
|
|
43555
43440
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -43560,15 +43445,9 @@ class PassageRefTarget extends React__namespace.Component {
|
|
|
43560
43445
|
linterContext: PerseusLinter.linterContextDefault
|
|
43561
43446
|
};
|
|
43562
43447
|
|
|
43563
|
-
//
|
|
43564
|
-
|
|
43565
|
-
|
|
43566
|
-
}
|
|
43567
|
-
|
|
43568
|
-
// TODO passage-ref-target isn't interactive; remove
|
|
43569
|
-
getUserInput() {
|
|
43570
|
-
return null;
|
|
43571
|
-
}
|
|
43448
|
+
// this just helps with TS weak typing when a Widget
|
|
43449
|
+
// doesn't implement any Widget methods
|
|
43450
|
+
isWidget = true;
|
|
43572
43451
|
|
|
43573
43452
|
// TODO passage-ref-target isn't interactive; remove
|
|
43574
43453
|
change = (() => {
|
|
@@ -43580,11 +43459,6 @@ class PassageRefTarget extends React__namespace.Component {
|
|
|
43580
43459
|
return change.apply(_this, args);
|
|
43581
43460
|
};
|
|
43582
43461
|
})();
|
|
43583
|
-
|
|
43584
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43585
|
-
simpleValidate() {
|
|
43586
|
-
return noopValidator();
|
|
43587
|
-
}
|
|
43588
43462
|
render() {
|
|
43589
43463
|
return /*#__PURE__*/React__namespace.createElement(Renderer, {
|
|
43590
43464
|
content: this.props.content
|
|
@@ -43613,7 +43487,9 @@ var PassageRefTarget$1 = {
|
|
|
43613
43487
|
major: 0,
|
|
43614
43488
|
minor: 0
|
|
43615
43489
|
},
|
|
43616
|
-
isLintable: true
|
|
43490
|
+
isLintable: true,
|
|
43491
|
+
// TODO: things that aren't interactive shouldn't need validators
|
|
43492
|
+
validator: () => noopValidator()
|
|
43617
43493
|
};
|
|
43618
43494
|
|
|
43619
43495
|
/**
|
|
@@ -43625,6 +43501,10 @@ class PhetSimulation extends React__namespace.Component {
|
|
|
43625
43501
|
static contextType = PerseusI18nContext;
|
|
43626
43502
|
iframeRef = /*#__PURE__*/React__namespace.createRef();
|
|
43627
43503
|
locale;
|
|
43504
|
+
|
|
43505
|
+
// this just helps with TS weak typing when a Widget
|
|
43506
|
+
// doesn't implement any Widget methods
|
|
43507
|
+
isWidget = true;
|
|
43628
43508
|
state = {
|
|
43629
43509
|
url: null,
|
|
43630
43510
|
banner: null
|
|
@@ -43643,11 +43523,6 @@ class PhetSimulation extends React__namespace.Component {
|
|
|
43643
43523
|
}
|
|
43644
43524
|
}
|
|
43645
43525
|
|
|
43646
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43647
|
-
getUserInput() {
|
|
43648
|
-
return null;
|
|
43649
|
-
}
|
|
43650
|
-
|
|
43651
43526
|
// kaLocales and PhET locales use different formats and abbreviations.
|
|
43652
43527
|
// PhET accepts different formats, i.e. kaLocale's hyphens, but it does not accept
|
|
43653
43528
|
// different abbreviations, so in points of divergence of abbreviations, we need to
|
|
@@ -43867,9 +43742,6 @@ class Plotter extends React__namespace.Component {
|
|
|
43867
43742
|
// bottom label.
|
|
43868
43743
|
categoryHeights: {}
|
|
43869
43744
|
};
|
|
43870
|
-
static validate(userInput, rubric) {
|
|
43871
|
-
return plotterValidator(userInput, rubric);
|
|
43872
|
-
}
|
|
43873
43745
|
componentDidMount() {
|
|
43874
43746
|
this._isMounted = true;
|
|
43875
43747
|
this.setupGraphie(this.state);
|
|
@@ -44614,9 +44486,6 @@ class Plotter extends React__namespace.Component {
|
|
|
44614
44486
|
getUserInput() {
|
|
44615
44487
|
return this.state.values;
|
|
44616
44488
|
}
|
|
44617
|
-
simpleValidate(rubric) {
|
|
44618
|
-
return plotterValidator(this.getUserInput(), rubric);
|
|
44619
|
-
}
|
|
44620
44489
|
render() {
|
|
44621
44490
|
// TODO(kevinb) actually compute the size of the graphie correctly and
|
|
44622
44491
|
// make it that size so we don't have to add extra padding. The value
|
|
@@ -44642,7 +44511,8 @@ var Plotter$1 = {
|
|
|
44642
44511
|
displayName: "Plotter",
|
|
44643
44512
|
hidden: true,
|
|
44644
44513
|
widget: Plotter,
|
|
44645
|
-
staticTransform: staticTransform
|
|
44514
|
+
staticTransform: staticTransform,
|
|
44515
|
+
validator: plotterValidator
|
|
44646
44516
|
};
|
|
44647
44517
|
|
|
44648
44518
|
/**
|
|
@@ -44737,9 +44607,6 @@ class Sorter extends React__namespace.Component {
|
|
|
44737
44607
|
onChange: function () {},
|
|
44738
44608
|
linterContext: PerseusLinter.linterContextDefault
|
|
44739
44609
|
};
|
|
44740
|
-
static validate(userInput, rubric) {
|
|
44741
|
-
return sorterValidator(userInput, rubric);
|
|
44742
|
-
}
|
|
44743
44610
|
state = {
|
|
44744
44611
|
changed: false
|
|
44745
44612
|
};
|
|
@@ -44782,9 +44649,6 @@ class Sorter extends React__namespace.Component {
|
|
|
44782
44649
|
// @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
|
|
44783
44650
|
this.refs.sortable.moveOptionToIndex(option, index);
|
|
44784
44651
|
};
|
|
44785
|
-
simpleValidate(rubric) {
|
|
44786
|
-
return sorterValidator(this.getUserInput(), rubric);
|
|
44787
|
-
}
|
|
44788
44652
|
render() {
|
|
44789
44653
|
const options = shuffle(this.props.correct, this.props.problemNum, /* ensurePermuted */true);
|
|
44790
44654
|
const {
|
|
@@ -44810,7 +44674,8 @@ var Sorter$1 = {
|
|
|
44810
44674
|
name: "sorter",
|
|
44811
44675
|
displayName: "Sorter",
|
|
44812
44676
|
widget: Sorter,
|
|
44813
|
-
isLintable: true
|
|
44677
|
+
isLintable: true,
|
|
44678
|
+
validator: sorterValidator
|
|
44814
44679
|
};
|
|
44815
44680
|
|
|
44816
44681
|
function tableValidator(state, rubric, strings) {
|
|
@@ -44918,9 +44783,6 @@ class Table extends React__namespace.Component {
|
|
|
44918
44783
|
linterContext: PerseusLinter.linterContextDefault
|
|
44919
44784
|
};
|
|
44920
44785
|
}();
|
|
44921
|
-
static validate(state, rubric, strings) {
|
|
44922
|
-
return tableValidator(state, rubric, strings);
|
|
44923
|
-
}
|
|
44924
44786
|
_getRows = () => {
|
|
44925
44787
|
return this.props.answers.length;
|
|
44926
44788
|
};
|
|
@@ -44949,9 +44811,6 @@ class Table extends React__namespace.Component {
|
|
|
44949
44811
|
headers: headers
|
|
44950
44812
|
});
|
|
44951
44813
|
};
|
|
44952
|
-
simpleValidate(rubric) {
|
|
44953
|
-
return tableValidator(this.getUserInput(), rubric, this.context.strings);
|
|
44954
|
-
}
|
|
44955
44814
|
_handleFocus = inputPath => {
|
|
44956
44815
|
this.props.onFocus(inputPath);
|
|
44957
44816
|
};
|
|
@@ -45095,7 +44954,8 @@ var Table$1 = {
|
|
|
45095
44954
|
widget: Table,
|
|
45096
44955
|
transform: propTransform,
|
|
45097
44956
|
hidden: true,
|
|
45098
|
-
isLintable: true
|
|
44957
|
+
isLintable: true,
|
|
44958
|
+
validator: tableValidator
|
|
45099
44959
|
};
|
|
45100
44960
|
|
|
45101
44961
|
/**
|
|
@@ -45181,22 +45041,9 @@ const IS_VIMEO = /(vimeo\.com)/;
|
|
|
45181
45041
|
*/
|
|
45182
45042
|
class Video extends React__namespace.Component {
|
|
45183
45043
|
static contextType = PerseusI18nContext;
|
|
45184
|
-
|
|
45185
|
-
|
|
45186
|
-
|
|
45187
|
-
* of Khan Academy videos.
|
|
45188
|
-
*/ // TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
45189
|
-
static validate() {
|
|
45190
|
-
return noopValidator();
|
|
45191
|
-
}
|
|
45192
|
-
getUserInput = () => {
|
|
45193
|
-
return null;
|
|
45194
|
-
};
|
|
45195
|
-
|
|
45196
|
-
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
45197
|
-
simpleValidate() {
|
|
45198
|
-
return noopValidator();
|
|
45199
|
-
}
|
|
45044
|
+
// this just helps with TS weak typing when a Widget
|
|
45045
|
+
// doesn't implement any Widget methods
|
|
45046
|
+
isWidget = true;
|
|
45200
45047
|
change = (() => {
|
|
45201
45048
|
var _this = this;
|
|
45202
45049
|
return function () {
|
|
@@ -45265,7 +45112,9 @@ var Video$1 = {
|
|
|
45265
45112
|
displayName: "Video",
|
|
45266
45113
|
defaultAlignment: "block",
|
|
45267
45114
|
supportedAlignments: ["block", "float-left", "float-right", "full-width"],
|
|
45268
|
-
widget: Video
|
|
45115
|
+
widget: Video,
|
|
45116
|
+
// TODO: things that aren't interactive shouldn't need validators
|
|
45117
|
+
validator: () => noopValidator()
|
|
45269
45118
|
};
|
|
45270
45119
|
|
|
45271
45120
|
/* eslint-disable import/no-named-as-default */
|
|
@@ -45309,7 +45158,7 @@ const init = function (options) {
|
|
|
45309
45158
|
|
|
45310
45159
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45311
45160
|
const libName = "@khanacademy/perseus";
|
|
45312
|
-
const libVersion = "
|
|
45161
|
+
const libVersion = "37.0.0";
|
|
45313
45162
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45314
45163
|
|
|
45315
45164
|
const apiVersion = {
|
|
@@ -46035,7 +45884,7 @@ class ServerItemRenderer extends React__namespace.Component {
|
|
|
46035
45884
|
});
|
|
46036
45885
|
|
|
46037
45886
|
// Call the interactionCallback, if it exists, with the current user input data
|
|
46038
|
-
this.props.apiOptions?.interactionCallback?.(this.questionRenderer.
|
|
45887
|
+
this.props.apiOptions?.interactionCallback?.(this.questionRenderer.getUserInputMap());
|
|
46039
45888
|
};
|
|
46040
45889
|
focus() {
|
|
46041
45890
|
return this.questionRenderer.focus();
|
|
@@ -48661,11 +48510,12 @@ function parsePerseusItem(json) {
|
|
|
48661
48510
|
throw new Error("Something went wrong.");
|
|
48662
48511
|
}
|
|
48663
48512
|
function buildRandomString() {
|
|
48513
|
+
let capitalize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
|
48664
48514
|
let randomString = "";
|
|
48665
48515
|
const randomLength = Math.floor(Math.random() * 8) + 3;
|
|
48666
48516
|
for (let i = 0; i < randomLength; i++) {
|
|
48667
48517
|
const randomLetter = String.fromCharCode(97 + Math.floor(Math.random() * 26));
|
|
48668
|
-
randomString += randomLetter;
|
|
48518
|
+
randomString += capitalize && i === 0 ? randomLetter.toUpperCase() : randomLetter;
|
|
48669
48519
|
}
|
|
48670
48520
|
return randomString;
|
|
48671
48521
|
}
|
|
@@ -48673,9 +48523,12 @@ function buildRandomPhrase() {
|
|
|
48673
48523
|
const phrases = [];
|
|
48674
48524
|
const randomLength = Math.floor(Math.random() * 10) + 5;
|
|
48675
48525
|
for (let i = 0; i < randomLength; i++) {
|
|
48676
|
-
phrases.push(buildRandomString());
|
|
48526
|
+
phrases.push(buildRandomString(i === 0));
|
|
48677
48527
|
}
|
|
48678
|
-
|
|
48528
|
+
const modifierStart = ["**", "$"];
|
|
48529
|
+
const modifierEnd = ["**", "$"];
|
|
48530
|
+
const modifierIndex = Math.floor(Math.random() * modifierStart.length);
|
|
48531
|
+
return `${modifierStart[modifierIndex]}${phrases.join(" ")}${modifierEnd[modifierIndex]}`;
|
|
48679
48532
|
}
|
|
48680
48533
|
function buildTestData(testObject) {
|
|
48681
48534
|
return `{"data":{"assessmentItem":{"__typename":"AssessmentItemOrError","error":null,"item":{"__typename":"AssessmentItem","id":"x890b3c70f3e8f4a6","itemData":"${testObject}","problemType":"Type 1","sha":"c7284a3ad65214b4e62bccce236d92f7f5d35941"}}}}`;
|
|
@@ -48942,6 +48795,7 @@ exports.getAngleCoords = getAngleCoords;
|
|
|
48942
48795
|
exports.getAnswerFromUserInput = getAnswerFromUserInput;
|
|
48943
48796
|
exports.getAnswersFromWidgets = getAnswersFromWidgets;
|
|
48944
48797
|
exports.getCircleCoords = getCircleCoords;
|
|
48798
|
+
exports.getClockwiseAngle = getClockwiseAngle;
|
|
48945
48799
|
exports.getCorrectAnswerForWidgetId = getCorrectAnswerForWidgetId;
|
|
48946
48800
|
exports.getImagesWithoutAltData = getImagesWithoutAltData;
|
|
48947
48801
|
exports.getLineCoords = getLineCoords;
|