@khanacademy/perseus 47.0.1 → 48.1.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/components/svg-image.d.ts +0 -4
- package/dist/es/index.js +389 -346
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +382 -353
- package/dist/index.js.map +1 -1
- package/dist/renderer.d.ts +0 -4
- package/dist/server-item-renderer.d.ts +0 -6
- package/dist/util/graphie-utils.d.ts +35 -0
- package/dist/util/test-utils.d.ts +7 -1
- package/package.json +9 -9
package/dist/es/index.js
CHANGED
|
@@ -2287,9 +2287,6 @@ function scoreExpression(userInput, rubric, strings, locale) {
|
|
|
2287
2287
|
// We matched a graded answer form, so we can now tell the user
|
|
2288
2288
|
// whether their input was correct or incorrect, and hand out
|
|
2289
2289
|
// points accordingly
|
|
2290
|
-
// TODO(eater): Seems silly to translate result to this
|
|
2291
|
-
// invalid/points thing and immediately translate it back in
|
|
2292
|
-
// ItemRenderer.scoreInput()
|
|
2293
2290
|
return {
|
|
2294
2291
|
type: "points",
|
|
2295
2292
|
earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
|
|
@@ -3240,7 +3237,7 @@ const supportsPassiveEvents = () => {
|
|
|
3240
3237
|
window.addEventListener("testPassive", null, opts);
|
|
3241
3238
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
3242
3239
|
window.removeEventListener("testPassive", null, opts);
|
|
3243
|
-
} catch (
|
|
3240
|
+
} catch (_unused) {
|
|
3244
3241
|
// Intentionally left empty!
|
|
3245
3242
|
}
|
|
3246
3243
|
return supportsPassive;
|
|
@@ -3376,6 +3373,162 @@ const Util = {
|
|
|
3376
3373
|
deepClone
|
|
3377
3374
|
};
|
|
3378
3375
|
|
|
3376
|
+
// For offline exercises in the mobile app, we download the graphie data
|
|
3377
|
+
// (svgs and localized data files) and serve them from the local file
|
|
3378
|
+
// system (with file://). We replace urls that start with `web+graphie`
|
|
3379
|
+
// in the perseus json with this `file+graphie` prefix to indicate that
|
|
3380
|
+
// they should have the `file://` protocol instead of `https://`.
|
|
3381
|
+
const svgLocalLabelsRegex = /^file\+graphie:/;
|
|
3382
|
+
const hashRegex = /\/([^/]+)$/;
|
|
3383
|
+
|
|
3384
|
+
/**
|
|
3385
|
+
* A Graphie axis range.
|
|
3386
|
+
*/
|
|
3387
|
+
|
|
3388
|
+
/**
|
|
3389
|
+
* A Graphie label.
|
|
3390
|
+
*/
|
|
3391
|
+
|
|
3392
|
+
/**
|
|
3393
|
+
* The Graphie data (labels, etc.)
|
|
3394
|
+
*/
|
|
3395
|
+
|
|
3396
|
+
function getLocale() {
|
|
3397
|
+
const {
|
|
3398
|
+
JIPT,
|
|
3399
|
+
kaLocale
|
|
3400
|
+
} = getDependencies();
|
|
3401
|
+
return JIPT.useJIPT ? "en-pt" : kaLocale;
|
|
3402
|
+
}
|
|
3403
|
+
function shouldUseLocalizedData() {
|
|
3404
|
+
return getLocale() !== "en";
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
// A regex to split at the last / of a URL, separating the base part from the
|
|
3408
|
+
// hash. This is used to create the localized label data URLs.
|
|
3409
|
+
const splitHashRegex = /\/(?=[^/]+$)/;
|
|
3410
|
+
function getLocalizedDataUrl(url) {
|
|
3411
|
+
// For local (cached) graphie images, they are already localized.
|
|
3412
|
+
if (svgLocalLabelsRegex.test(url)) {
|
|
3413
|
+
return Util.getDataUrl(url);
|
|
3414
|
+
}
|
|
3415
|
+
const [base, hash] = Util.getBaseUrl(url).split(splitHashRegex);
|
|
3416
|
+
return `${base}/${getLocale()}/${hash}-data.json`;
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
// Get the hash from the url, which is just the filename
|
|
3420
|
+
function getUrlHash(url) {
|
|
3421
|
+
const match = url.match(hashRegex);
|
|
3422
|
+
if (match == null) {
|
|
3423
|
+
throw new PerseusError("not a valid URL", Errors.InvalidInput);
|
|
3424
|
+
}
|
|
3425
|
+
return match && match[1];
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
/**
|
|
3429
|
+
* Parse JSONP for a web+graphie://... hash to extract Graphie data, or throw
|
|
3430
|
+
* an error if invalid JSONP.
|
|
3431
|
+
*/
|
|
3432
|
+
function parseDataFromJSONP(graphieHash, graphieJSONP, errorCallback) {
|
|
3433
|
+
// The JSONP is expected to be in the form of `svgDataHASH(...)` or `svgOtherDataHASH(...)`
|
|
3434
|
+
const match = graphieJSONP.match(new RegExp(`^(?:svgData|svgOtherData)${graphieHash}\\((.+)\\);$`));
|
|
3435
|
+
|
|
3436
|
+
// It is also possible that the JSONP is simply a JSON object,
|
|
3437
|
+
// in which case we can parse it directly.
|
|
3438
|
+
const jsonToParse = match ? match[1] : graphieJSONP;
|
|
3439
|
+
|
|
3440
|
+
// Try to parse the JSONP, and if it fails, call the error callback
|
|
3441
|
+
try {
|
|
3442
|
+
return JSON.parse(jsonToParse);
|
|
3443
|
+
} catch (error) {
|
|
3444
|
+
errorCallback(error);
|
|
3445
|
+
return null;
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
// The global cache of label data. Its format is:
|
|
3449
|
+
// {
|
|
3450
|
+
// hash (e.g. "c21435944d2cf0c8f39d9059cb35836aa701d04a"): {
|
|
3451
|
+
// loaded: a boolean of whether the data has been loaded or not
|
|
3452
|
+
// dataCallbacks: a list of callbacks to call with the data when the data
|
|
3453
|
+
// is loaded
|
|
3454
|
+
// data: the other data for this hash
|
|
3455
|
+
// },
|
|
3456
|
+
// ...
|
|
3457
|
+
// }
|
|
3458
|
+
const labelDataCache = {};
|
|
3459
|
+
function loadGraphie(url, onDataLoaded) {
|
|
3460
|
+
const hash = getUrlHash(url);
|
|
3461
|
+
|
|
3462
|
+
// We can't make multiple jsonp calls to the same file because their
|
|
3463
|
+
// callbacks will collide with each other. Instead, we cache the data
|
|
3464
|
+
// and only make the jsonp calls once.
|
|
3465
|
+
const entry = labelDataCache[hash];
|
|
3466
|
+
if (entry != null) {
|
|
3467
|
+
if (entry.loaded) {
|
|
3468
|
+
const {
|
|
3469
|
+
data,
|
|
3470
|
+
localized
|
|
3471
|
+
} = entry;
|
|
3472
|
+
onDataLoaded(data, localized);
|
|
3473
|
+
} else {
|
|
3474
|
+
entry.dataCallbacks.push(onDataLoaded);
|
|
3475
|
+
}
|
|
3476
|
+
} else {
|
|
3477
|
+
const cacheData = {
|
|
3478
|
+
loaded: false,
|
|
3479
|
+
dataCallbacks: [onDataLoaded],
|
|
3480
|
+
localized: shouldUseLocalizedData()
|
|
3481
|
+
};
|
|
3482
|
+
labelDataCache[hash] = cacheData;
|
|
3483
|
+
const retrieveData = async (url, errorCallback) => {
|
|
3484
|
+
const response = await fetch(url);
|
|
3485
|
+
if (!(response != null && response.ok)) {
|
|
3486
|
+
errorCallback();
|
|
3487
|
+
return;
|
|
3488
|
+
}
|
|
3489
|
+
const jsonp = await response.text();
|
|
3490
|
+
const data = parseDataFromJSONP(hash, jsonp, error => {
|
|
3491
|
+
Log.error("Failed to parse JSONP for svg-image", Errors.Service, {
|
|
3492
|
+
cause: error,
|
|
3493
|
+
loggedMetadata: {
|
|
3494
|
+
dataUrl: Util.getDataUrl(url),
|
|
3495
|
+
jsonp
|
|
3496
|
+
}
|
|
3497
|
+
});
|
|
3498
|
+
});
|
|
3499
|
+
|
|
3500
|
+
// If the data is null, then we failed to parse the JSONP
|
|
3501
|
+
if (!data) {
|
|
3502
|
+
return;
|
|
3503
|
+
}
|
|
3504
|
+
const newCacheEntry = labelDataCache[hash] = _extends({}, labelDataCache[hash], {
|
|
3505
|
+
loaded: true,
|
|
3506
|
+
data
|
|
3507
|
+
});
|
|
3508
|
+
newCacheEntry.dataCallbacks.forEach(callback => {
|
|
3509
|
+
callback(newCacheEntry.data, cacheData.localized);
|
|
3510
|
+
});
|
|
3511
|
+
};
|
|
3512
|
+
const dataLoadErrorHandler = (x, status, error) => {
|
|
3513
|
+
Log.error("Data load failed for svg-image", Errors.Service, {
|
|
3514
|
+
cause: error,
|
|
3515
|
+
loggedMetadata: {
|
|
3516
|
+
dataUrl: Util.getDataUrl(url),
|
|
3517
|
+
status
|
|
3518
|
+
}
|
|
3519
|
+
});
|
|
3520
|
+
};
|
|
3521
|
+
if (shouldUseLocalizedData()) {
|
|
3522
|
+
cacheData.localized = false;
|
|
3523
|
+
// If there is isn't any localized data, fall back to
|
|
3524
|
+
// the original, unlocalized data
|
|
3525
|
+
retrieveData(getLocalizedDataUrl(url), dataLoadErrorHandler);
|
|
3526
|
+
} else {
|
|
3527
|
+
retrieveData(Util.getDataUrl(url), dataLoadErrorHandler);
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
|
|
3379
3532
|
// Derived from the MIT-licensed:
|
|
3380
3533
|
|
|
3381
3534
|
/*jshint browser:true, node:true */
|
|
@@ -3483,7 +3636,9 @@ function changeViewportTag(contentString, callback) {
|
|
|
3483
3636
|
// finished resetting.
|
|
3484
3637
|
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
3485
3638
|
// eslint-disable-next-line no-restricted-syntax
|
|
3486
|
-
|
|
3639
|
+
if (callback != null) {
|
|
3640
|
+
setTimeout(callback, 0);
|
|
3641
|
+
}
|
|
3487
3642
|
}, 0);
|
|
3488
3643
|
}
|
|
3489
3644
|
|
|
@@ -8960,6 +9115,9 @@ class Graphie$1 {
|
|
|
8960
9115
|
$(el).css("position", "relative");
|
|
8961
9116
|
this.raphael = Raphael$1(el);
|
|
8962
9117
|
|
|
9118
|
+
// Hide the Raphael canvas from screen readers
|
|
9119
|
+
$(el).attr("aria-hidden", "true");
|
|
9120
|
+
|
|
8963
9121
|
// For a sometimes-reproducible IE8 bug; doesn't affect SVG browsers at all
|
|
8964
9122
|
$(el).children("div").css("position", "absolute");
|
|
8965
9123
|
}
|
|
@@ -16768,7 +16926,7 @@ class ImageLoader extends React.Component {
|
|
|
16768
16926
|
onKeyUp = e => {
|
|
16769
16927
|
// 13 is enter key, 32 is space key
|
|
16770
16928
|
if (e.keyCode === 13 || e.keyCode === 32) {
|
|
16771
|
-
imgProps.onClick
|
|
16929
|
+
imgProps.onClick == null || imgProps.onClick(e);
|
|
16772
16930
|
}
|
|
16773
16931
|
};
|
|
16774
16932
|
onKeyDown = e => {
|
|
@@ -16833,99 +16991,11 @@ class ImageLoader extends React.Component {
|
|
|
16833
16991
|
|
|
16834
16992
|
// Minimum image width to make an image appear as zoomable.
|
|
16835
16993
|
const ZOOMABLE_THRESHOLD = 700;
|
|
16836
|
-
|
|
16837
|
-
// The global cache of label data. Its format is:
|
|
16838
|
-
// {
|
|
16839
|
-
// hash (e.g. "c21435944d2cf0c8f39d9059cb35836aa701d04a"): {
|
|
16840
|
-
// loaded: a boolean of whether the data has been loaded or not
|
|
16841
|
-
// dataCallbacks: a list of callbacks to call with the data when the data
|
|
16842
|
-
// is loaded
|
|
16843
|
-
// data: the other data for this hash
|
|
16844
|
-
// },
|
|
16845
|
-
// ...
|
|
16846
|
-
// }
|
|
16847
|
-
const labelDataCache = {};
|
|
16848
|
-
|
|
16849
|
-
// Write our own JSONP handler because all the other ones don't do things we
|
|
16850
|
-
// need.
|
|
16851
|
-
const doJSONP = function doJSONP(url, options) {
|
|
16852
|
-
options = _extends({
|
|
16853
|
-
callbackName: "callback",
|
|
16854
|
-
success: $.noop,
|
|
16855
|
-
error: $.noop
|
|
16856
|
-
}, options);
|
|
16857
|
-
|
|
16858
|
-
// Create the script
|
|
16859
|
-
const script = document.createElement("script");
|
|
16860
|
-
script.setAttribute("async", "");
|
|
16861
|
-
script.setAttribute("src", url);
|
|
16862
|
-
|
|
16863
|
-
// A cleanup function to run when we're done.
|
|
16864
|
-
function cleanup() {
|
|
16865
|
-
document.head && document.head.removeChild(script);
|
|
16866
|
-
delete window[options.callbackName];
|
|
16867
|
-
}
|
|
16868
|
-
|
|
16869
|
-
// Add the global callback.
|
|
16870
|
-
// @ts-expect-error - TS2740 - Type '() => void' is missing the following properties from type 'Window': clientInformation, closed, customElements, devicePixelRatio, and 206 more.
|
|
16871
|
-
window[options.callbackName] = function (...args) {
|
|
16872
|
-
cleanup();
|
|
16873
|
-
options.success.apply(null, args);
|
|
16874
|
-
};
|
|
16875
|
-
|
|
16876
|
-
// Add the error handler.
|
|
16877
|
-
script.addEventListener("error", function (...args) {
|
|
16878
|
-
cleanup();
|
|
16879
|
-
options.error.apply(null, args);
|
|
16880
|
-
});
|
|
16881
|
-
|
|
16882
|
-
// Insert the script to start the download.
|
|
16883
|
-
document.head && document.head.appendChild(script);
|
|
16884
|
-
};
|
|
16885
|
-
|
|
16886
|
-
// For offline exercises in the mobile app, we download the graphie data
|
|
16887
|
-
// (svgs and localized data files) and serve them from the local file
|
|
16888
|
-
// system (with file://). We replace urls that start with `web+graphie`
|
|
16889
|
-
// in the perseus json with this `file+graphie` prefix to indicate that
|
|
16890
|
-
// they should have the `file://` protocol instead of `https://`.
|
|
16891
|
-
const svgLocalLabelsRegex = /^file\+graphie:/;
|
|
16892
|
-
const hashRegex = /\/([^/]+)$/;
|
|
16893
16994
|
function isImageProbablyPhotograph(imageUrl) {
|
|
16894
16995
|
// TODO(david): Do an inventory to refine this heuristic. For example, what
|
|
16895
16996
|
// % of .png images are illustrations?
|
|
16896
16997
|
return /\.(jpg|jpeg)$/i.test(imageUrl);
|
|
16897
16998
|
}
|
|
16898
|
-
function getLocale() {
|
|
16899
|
-
const {
|
|
16900
|
-
JIPT,
|
|
16901
|
-
kaLocale
|
|
16902
|
-
} = getDependencies();
|
|
16903
|
-
return JIPT.useJIPT ? "en-pt" : kaLocale;
|
|
16904
|
-
}
|
|
16905
|
-
function shouldUseLocalizedData() {
|
|
16906
|
-
return getLocale() !== "en";
|
|
16907
|
-
}
|
|
16908
|
-
|
|
16909
|
-
// A regex to split at the last / of a URL, separating the base part from the
|
|
16910
|
-
// hash. This is used to create the localized label data URLs.
|
|
16911
|
-
const splitHashRegex = /\/(?=[^/]+$)/;
|
|
16912
|
-
function getLocalizedDataUrl(url) {
|
|
16913
|
-
// For local (cached) graphie images, they are already localized.
|
|
16914
|
-
if (svgLocalLabelsRegex.test(url)) {
|
|
16915
|
-
return Util.getDataUrl(url);
|
|
16916
|
-
}
|
|
16917
|
-
const [base, hash] = Util.getBaseUrl(url).split(splitHashRegex);
|
|
16918
|
-
return `${base}/${getLocale()}/${hash}-data.json`;
|
|
16919
|
-
}
|
|
16920
|
-
|
|
16921
|
-
// Get the hash from the url, which is just the filename
|
|
16922
|
-
function getUrlHash(url) {
|
|
16923
|
-
const match = url.match(hashRegex);
|
|
16924
|
-
if (match == null) {
|
|
16925
|
-
throw new PerseusError("not a valid URL", Errors.InvalidInput);
|
|
16926
|
-
}
|
|
16927
|
-
return match && match[1];
|
|
16928
|
-
}
|
|
16929
16999
|
function defaultPreloader(dimensions) {
|
|
16930
17000
|
return /*#__PURE__*/React.createElement("span", {
|
|
16931
17001
|
style: {
|
|
@@ -16947,20 +17017,6 @@ class SvgImage extends React.Component {
|
|
|
16947
17017
|
constructor(props) {
|
|
16948
17018
|
super(props);
|
|
16949
17019
|
this._isMounted = void 0;
|
|
16950
|
-
this.onDataLoaded = (data, localized) => {
|
|
16951
|
-
if (this._isMounted && data.labels && data.range) {
|
|
16952
|
-
const labelsRendered = data.labels.reduce((dict, label) => _extends({}, dict, {
|
|
16953
|
-
[label.content]: false
|
|
16954
|
-
}), {});
|
|
16955
|
-
this.setState({
|
|
16956
|
-
dataLoaded: true,
|
|
16957
|
-
labelDataIsLocalized: localized,
|
|
16958
|
-
labelsRendered,
|
|
16959
|
-
labels: data.labels,
|
|
16960
|
-
range: data.range
|
|
16961
|
-
});
|
|
16962
|
-
}
|
|
16963
|
-
};
|
|
16964
17020
|
this.onImageLoad = () => {
|
|
16965
17021
|
// Only need to do this if rendering a Graphie
|
|
16966
17022
|
if (this.sizeProvided()) {
|
|
@@ -17050,6 +17106,7 @@ class SvgImage extends React.Component {
|
|
|
17050
17106
|
});
|
|
17051
17107
|
};
|
|
17052
17108
|
this._handleZoomClick = e => {
|
|
17109
|
+
var _this$props$trackInte, _this$props;
|
|
17053
17110
|
const $image = $(e.target);
|
|
17054
17111
|
|
|
17055
17112
|
// It's possible that the image is already displayed at its
|
|
@@ -17066,7 +17123,7 @@ class SvgImage extends React.Component {
|
|
|
17066
17123
|
$image.width() < this.props.width || this.props.zoomToFullSizeOnMobile) {
|
|
17067
17124
|
ZoomService.handleZoomClick(e, this.props.zoomToFullSizeOnMobile);
|
|
17068
17125
|
}
|
|
17069
|
-
this.props.trackInteraction
|
|
17126
|
+
(_this$props$trackInte = (_this$props = this.props).trackInteraction) == null || _this$props$trackInte.call(_this$props);
|
|
17070
17127
|
};
|
|
17071
17128
|
this.handleUpdate = status => {
|
|
17072
17129
|
this.props.onUpdate();
|
|
@@ -17135,74 +17192,21 @@ class SvgImage extends React.Component {
|
|
|
17135
17192
|
return Util.isLabeledSVG(this.props.src) ? state.imageLoaded && state.dataLoaded : state.imageLoaded;
|
|
17136
17193
|
}
|
|
17137
17194
|
loadResources() {
|
|
17138
|
-
|
|
17139
|
-
|
|
17140
|
-
|
|
17141
|
-
|
|
17142
|
-
|
|
17143
|
-
if (labelDataCache[hash]) {
|
|
17144
|
-
if (labelDataCache[hash].loaded) {
|
|
17145
|
-
const {
|
|
17146
|
-
data,
|
|
17147
|
-
localized
|
|
17148
|
-
} = labelDataCache[hash];
|
|
17149
|
-
this.onDataLoaded(data, localized);
|
|
17150
|
-
} else {
|
|
17151
|
-
labelDataCache[hash].dataCallbacks.push(this.onDataLoaded);
|
|
17152
|
-
}
|
|
17153
|
-
} else {
|
|
17154
|
-
const cacheData = {
|
|
17155
|
-
loaded: false,
|
|
17156
|
-
dataCallbacks: [this.onDataLoaded],
|
|
17157
|
-
data: null,
|
|
17158
|
-
localized: shouldUseLocalizedData()
|
|
17159
|
-
};
|
|
17160
|
-
labelDataCache[hash] = cacheData;
|
|
17161
|
-
const retrieveData = (url, errorCallback) => {
|
|
17162
|
-
doJSONP(url, {
|
|
17163
|
-
callbackName: "svgData" + hash,
|
|
17164
|
-
success: data => {
|
|
17165
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'data' because it is a read-only property.
|
|
17166
|
-
cacheData.data = data;
|
|
17167
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'loaded' because it is a read-only property.
|
|
17168
|
-
cacheData.loaded = true;
|
|
17169
|
-
_.each(cacheData.dataCallbacks, callback => {
|
|
17170
|
-
// @ts-expect-error - TS2345 - Argument of type 'null' is not assignable to parameter of type '{ labels: readonly any[]; range: readonly any[]; }'.
|
|
17171
|
-
callback(cacheData.data, cacheData.localized);
|
|
17172
|
-
});
|
|
17173
|
-
},
|
|
17174
|
-
error: errorCallback
|
|
17175
|
-
});
|
|
17176
|
-
};
|
|
17177
|
-
if (shouldUseLocalizedData()) {
|
|
17178
|
-
retrieveData(getLocalizedDataUrl(this.props.src), (x, status, error) => {
|
|
17179
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'localized' because it is a read-only property.
|
|
17180
|
-
cacheData.localized = false;
|
|
17181
|
-
|
|
17182
|
-
// If there is isn't any localized data, fall back to
|
|
17183
|
-
// the original, unlocalized data
|
|
17184
|
-
retrieveData(Util.getDataUrl(this.props.src), (x, status, error) => {
|
|
17185
|
-
Log.error("Data load failed for svg-image", Errors.Service, {
|
|
17186
|
-
cause: error,
|
|
17187
|
-
loggedMetadata: {
|
|
17188
|
-
dataUrl: Util.getDataUrl(this.props.src),
|
|
17189
|
-
status
|
|
17190
|
-
}
|
|
17191
|
-
});
|
|
17192
|
-
});
|
|
17195
|
+
loadGraphie(this.props.src, (data, localized) => {
|
|
17196
|
+
if (this._isMounted && data.labels && data.range) {
|
|
17197
|
+
const labelsRendered = {};
|
|
17198
|
+
data.labels.forEach(label => {
|
|
17199
|
+
labelsRendered[label.content] = false;
|
|
17193
17200
|
});
|
|
17194
|
-
|
|
17195
|
-
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
|
|
17199
|
-
|
|
17200
|
-
status
|
|
17201
|
-
}
|
|
17202
|
-
});
|
|
17201
|
+
this.setState({
|
|
17202
|
+
dataLoaded: true,
|
|
17203
|
+
labelDataIsLocalized: localized,
|
|
17204
|
+
labelsRendered,
|
|
17205
|
+
labels: data.labels,
|
|
17206
|
+
range: data.range
|
|
17203
17207
|
});
|
|
17204
17208
|
}
|
|
17205
|
-
}
|
|
17209
|
+
});
|
|
17206
17210
|
}
|
|
17207
17211
|
sizeProvided() {
|
|
17208
17212
|
return this.props.width != null && this.props.height != null;
|
|
@@ -19602,8 +19606,8 @@ class WidgetContainer extends React.Component {
|
|
|
19602
19606
|
});
|
|
19603
19607
|
|
|
19604
19608
|
// Note: if you add more props here, please consider whether or not
|
|
19605
|
-
// it should be auto-serialized
|
|
19606
|
-
// widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
|
|
19609
|
+
// it should be auto-serialized.
|
|
19610
|
+
// See widget-jsonify-deprecated.jsx and widget-prop-denylist.jsx
|
|
19607
19611
|
|
|
19608
19612
|
// We default to an empty object for style instead of null
|
|
19609
19613
|
// because of a strange bug where the static styles aren't applied
|
|
@@ -20582,14 +20586,6 @@ class Renderer extends React.Component {
|
|
|
20582
20586
|
this.getWidgetIds = () => {
|
|
20583
20587
|
return this.widgetIds;
|
|
20584
20588
|
};
|
|
20585
|
-
/**
|
|
20586
|
-
* @deprecated use scorePerseusItem
|
|
20587
|
-
*/
|
|
20588
|
-
this.guessAndScore = () => {
|
|
20589
|
-
const totalGuess = this.getUserInput();
|
|
20590
|
-
const totalScore = this.score();
|
|
20591
|
-
return [totalGuess, totalScore];
|
|
20592
|
-
};
|
|
20593
20589
|
// TranslationLinter callback
|
|
20594
20590
|
this.handletranslationLintErrors = lintErrors => {
|
|
20595
20591
|
if (!this._isMounted) {
|
|
@@ -21556,11 +21552,11 @@ class SimpleKeypadInput extends React.Component {
|
|
|
21556
21552
|
keypadType: KeypadType.FRACTION
|
|
21557
21553
|
}, () => {
|
|
21558
21554
|
if (_this._isMounted) {
|
|
21559
|
-
onFocus
|
|
21555
|
+
onFocus == null || onFocus();
|
|
21560
21556
|
}
|
|
21561
21557
|
});
|
|
21562
21558
|
} else {
|
|
21563
|
-
onFocus
|
|
21559
|
+
onFocus == null || onFocus();
|
|
21564
21560
|
}
|
|
21565
21561
|
},
|
|
21566
21562
|
value: value == null ? "" : "" + value
|
|
@@ -21799,9 +21795,6 @@ function scoreInputNumber(userInput, rubric, strings) {
|
|
|
21799
21795
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
21800
21796
|
const currentValue = ParseTex$1(userInput.currentValue);
|
|
21801
21797
|
const result = val(currentValue);
|
|
21802
|
-
|
|
21803
|
-
// TODO(eater): Seems silly to translate result to this invalid/points
|
|
21804
|
-
// thing and immediately translate it back in ItemRenderer.scoreInput()
|
|
21805
21798
|
if (result.empty) {
|
|
21806
21799
|
return {
|
|
21807
21800
|
type: "invalid",
|
|
@@ -22168,10 +22161,6 @@ function scoreNumericInput(userInput, rubric, strings) {
|
|
|
22168
22161
|
message: (_matchedAnswer$messag = matchedAnswer == null ? void 0 : matchedAnswer.message) != null ? _matchedAnswer$messag : null,
|
|
22169
22162
|
guess: localValue
|
|
22170
22163
|
};
|
|
22171
|
-
|
|
22172
|
-
// TODO(eater): Seems silly to translate result to this
|
|
22173
|
-
// invalid/points thing and immediately translate it
|
|
22174
|
-
// back in ItemRenderer.scoreInput()
|
|
22175
22164
|
if (result.empty) {
|
|
22176
22165
|
return {
|
|
22177
22166
|
type: "invalid",
|
|
@@ -25024,7 +25013,7 @@ const scrollElementToOffset = (elementOrWindow, offset, duration = 0, callback)
|
|
|
25024
25013
|
// TODO(jeff, WEB-1378): Use Wonder Blocks Timing API
|
|
25025
25014
|
// eslint-disable-next-line no-restricted-syntax
|
|
25026
25015
|
clearInterval(scrollInterval);
|
|
25027
|
-
callback
|
|
25016
|
+
callback == null || callback();
|
|
25028
25017
|
}
|
|
25029
25018
|
}, tickLength);
|
|
25030
25019
|
};
|
|
@@ -27048,7 +27037,7 @@ class CSProgram extends React.Component {
|
|
|
27048
27037
|
let data = {};
|
|
27049
27038
|
try {
|
|
27050
27039
|
data = JSON.parse(e.originalEvent.data);
|
|
27051
|
-
} catch (
|
|
27040
|
+
} catch (_unused) {
|
|
27052
27041
|
return;
|
|
27053
27042
|
}
|
|
27054
27043
|
if (_.isUndefined(data.testsPassed)) {
|
|
@@ -28104,7 +28093,6 @@ class GradedGroupSet extends React.Component {
|
|
|
28104
28093
|
};
|
|
28105
28094
|
}
|
|
28106
28095
|
shouldComponentUpdate(nextProps, nextState) {
|
|
28107
|
-
nextProps.gradedGroups;
|
|
28108
28096
|
return nextProps !== this.props || nextState !== this.state;
|
|
28109
28097
|
}
|
|
28110
28098
|
getPromptJSON() {
|
|
@@ -29573,7 +29561,7 @@ class Iframe extends React.Component {
|
|
|
29573
29561
|
let data = {};
|
|
29574
29562
|
try {
|
|
29575
29563
|
data = JSON.parse(e.originalEvent.data);
|
|
29576
|
-
} catch (
|
|
29564
|
+
} catch (_unused) {
|
|
29577
29565
|
return;
|
|
29578
29566
|
}
|
|
29579
29567
|
if (_.isUndefined(data.testsPassed)) {
|
|
@@ -30638,10 +30626,12 @@ class Graph extends React.Component {
|
|
|
30638
30626
|
this.props.onMouseDown(coord);
|
|
30639
30627
|
}, this) : undefined;
|
|
30640
30628
|
const onMouseOver = $instructionsWrapper ? function () {
|
|
30641
|
-
|
|
30629
|
+
var _$instructionsWrapper;
|
|
30630
|
+
(_$instructionsWrapper = $instructionsWrapper) == null || _$instructionsWrapper.css("opacity", invisible);
|
|
30642
30631
|
} : undefined;
|
|
30643
30632
|
const onMouseOut = $instructionsWrapper ? function () {
|
|
30644
|
-
|
|
30633
|
+
var _$instructionsWrapper2;
|
|
30634
|
+
(_$instructionsWrapper2 = $instructionsWrapper) == null || _$instructionsWrapper2.css("opacity", visible);
|
|
30645
30635
|
} : undefined;
|
|
30646
30636
|
/* eslint-enable indent */
|
|
30647
30637
|
|
|
@@ -31373,6 +31363,7 @@ function replaceOutsideTeX(mathString) {
|
|
|
31373
31363
|
const parsed = mathOnlyParser(mathString);
|
|
31374
31364
|
let result = "";
|
|
31375
31365
|
for (const piece of parsed) {
|
|
31366
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
31376
31367
|
piece.type === "math" ? result += "$" + piece.content + "$" : piece.type === "specialCharacter" ? result += escapeIfUnescaped(piece.content) : result += piece.content;
|
|
31377
31368
|
}
|
|
31378
31369
|
return `\\text{${result}}`;
|
|
@@ -34323,43 +34314,64 @@ function renderPointGraph(state, dispatch) {
|
|
|
34323
34314
|
};
|
|
34324
34315
|
}
|
|
34325
34316
|
function PointGraph(props) {
|
|
34326
|
-
const
|
|
34317
|
+
const {
|
|
34318
|
+
numPoints
|
|
34319
|
+
} = props.graphState;
|
|
34320
|
+
const graphConfig = useGraphConfig();
|
|
34321
|
+
const pointsRef = React.useRef([]);
|
|
34322
|
+
|
|
34323
|
+
// Dimensions to build the graph overlay for Unlimited Point.
|
|
34324
|
+
const {
|
|
34325
|
+
range: [x, y]
|
|
34326
|
+
} = graphConfig;
|
|
34327
|
+
const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
|
|
34328
|
+
|
|
34329
|
+
// This useEffect is to handle the focus snapping for Unlimited Point.
|
|
34330
|
+
React.useEffect(() => {
|
|
34331
|
+
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34332
|
+
if (focusedIndex != null) {
|
|
34333
|
+
var _pointsRef$current$fo;
|
|
34334
|
+
(_pointsRef$current$fo = pointsRef.current[focusedIndex]) == null || _pointsRef$current$fo.focus();
|
|
34335
|
+
}
|
|
34336
|
+
}, [props.graphState.focusedPointIndex, pointsRef]);
|
|
34337
|
+
const statefulProps = _extends({}, props, {
|
|
34338
|
+
graphConfig,
|
|
34339
|
+
pointsRef,
|
|
34340
|
+
top,
|
|
34341
|
+
left
|
|
34342
|
+
});
|
|
34327
34343
|
if (numPoints === "unlimited") {
|
|
34328
|
-
return UnlimitedPointGraph(
|
|
34344
|
+
return UnlimitedPointGraph(statefulProps);
|
|
34329
34345
|
}
|
|
34330
|
-
return LimitedPointGraph(
|
|
34346
|
+
return LimitedPointGraph(statefulProps);
|
|
34331
34347
|
}
|
|
34332
|
-
function LimitedPointGraph(
|
|
34348
|
+
function LimitedPointGraph(statefulProps) {
|
|
34333
34349
|
const {
|
|
34334
34350
|
dispatch
|
|
34335
|
-
} =
|
|
34336
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null,
|
|
34351
|
+
} = statefulProps;
|
|
34352
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, statefulProps.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34337
34353
|
key: i,
|
|
34338
34354
|
point: point,
|
|
34339
34355
|
sequenceNumber: i + 1,
|
|
34340
34356
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination))
|
|
34341
34357
|
})));
|
|
34342
34358
|
}
|
|
34343
|
-
function UnlimitedPointGraph(
|
|
34359
|
+
function UnlimitedPointGraph(statefulProps) {
|
|
34344
34360
|
const {
|
|
34345
|
-
dispatch
|
|
34346
|
-
|
|
34347
|
-
|
|
34361
|
+
dispatch,
|
|
34362
|
+
graphConfig,
|
|
34363
|
+
pointsRef,
|
|
34364
|
+
top,
|
|
34365
|
+
left
|
|
34366
|
+
} = statefulProps;
|
|
34348
34367
|
const {
|
|
34349
|
-
|
|
34368
|
+
coords
|
|
34369
|
+
} = statefulProps.graphState;
|
|
34370
|
+
const {
|
|
34371
|
+
graphDimensionsInPixels
|
|
34350
34372
|
} = graphConfig;
|
|
34351
|
-
const
|
|
34352
|
-
const
|
|
34353
|
-
const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
|
|
34354
|
-
const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
|
|
34355
|
-
const itemsRef = React.useRef([]);
|
|
34356
|
-
React.useEffect(() => {
|
|
34357
|
-
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34358
|
-
if (focusedIndex != null) {
|
|
34359
|
-
var _itemsRef$current$foc;
|
|
34360
|
-
(_itemsRef$current$foc = itemsRef.current[focusedIndex]) == null || _itemsRef$current$foc.focus();
|
|
34361
|
-
}
|
|
34362
|
-
}, [props.graphState.focusedPointIndex, itemsRef]);
|
|
34373
|
+
const widthPx = graphDimensionsInPixels[0];
|
|
34374
|
+
const heightPx = graphDimensionsInPixels[1];
|
|
34363
34375
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
|
|
34364
34376
|
style: {
|
|
34365
34377
|
fill: "rgba(0,0,0,0)",
|
|
@@ -34376,13 +34388,13 @@ function UnlimitedPointGraph(props) {
|
|
|
34376
34388
|
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
34377
34389
|
dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));
|
|
34378
34390
|
}
|
|
34379
|
-
}),
|
|
34391
|
+
}), coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34380
34392
|
key: i,
|
|
34381
34393
|
point: point,
|
|
34382
34394
|
sequenceNumber: i + 1,
|
|
34383
34395
|
onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
|
|
34384
34396
|
ref: ref => {
|
|
34385
|
-
|
|
34397
|
+
pointsRef.current[i] = ref;
|
|
34386
34398
|
},
|
|
34387
34399
|
onFocus: () => {
|
|
34388
34400
|
dispatch(actions.pointGraph.focusPoint(i));
|
|
@@ -34429,36 +34441,42 @@ function renderPolygonGraph(state, dispatch) {
|
|
|
34429
34441
|
interactiveElementsDescription: null
|
|
34430
34442
|
};
|
|
34431
34443
|
}
|
|
34432
|
-
const
|
|
34433
|
-
const [hovered, setHovered] = React.useState(false);
|
|
34434
|
-
// This is more so required for the re-rendering that occurs when state
|
|
34435
|
-
// updates; specifically with regard to line weighting and polygon focus.
|
|
34436
|
-
const [focusVisible, setFocusVisible] = React.useState(false);
|
|
34444
|
+
const PolygonGraph = props => {
|
|
34437
34445
|
const {
|
|
34438
34446
|
dispatch
|
|
34439
34447
|
} = props;
|
|
34440
34448
|
const {
|
|
34449
|
+
numSides,
|
|
34441
34450
|
coords,
|
|
34442
|
-
showAngles,
|
|
34443
|
-
showSides,
|
|
34444
|
-
range,
|
|
34445
34451
|
snapStep,
|
|
34446
34452
|
snapTo = "grid"
|
|
34447
34453
|
} = props.graphState;
|
|
34454
|
+
const graphConfig = useGraphConfig();
|
|
34455
|
+
|
|
34456
|
+
// Ref to implement the dragging behavior on a Limited/Closed Polygon.
|
|
34457
|
+
const polygonRef = React.useRef(null);
|
|
34458
|
+
// Ref to manage dynamic focus for the points in Unlimited Polygon
|
|
34459
|
+
const pointsRef = React.useRef([]);
|
|
34460
|
+
// Ref to manage the last move time for a Limited Polygon.
|
|
34461
|
+
const lastMoveTimeRef = React.useRef(0);
|
|
34462
|
+
|
|
34463
|
+
// Dimensions to build the graph overlay for Unlimited Polgyon.
|
|
34448
34464
|
const {
|
|
34449
|
-
|
|
34450
|
-
} =
|
|
34465
|
+
range: [x, y]
|
|
34466
|
+
} = graphConfig;
|
|
34467
|
+
const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
|
|
34451
34468
|
|
|
34452
34469
|
// TODO(benchristel): can the default set of points be removed here? I don't
|
|
34453
34470
|
// think coords can be null.
|
|
34454
34471
|
const points = coords != null ? coords : [[0, 0]];
|
|
34455
|
-
|
|
34472
|
+
|
|
34473
|
+
// Logic to build the dragging experience. Primarily used by Limited Polygon.
|
|
34456
34474
|
const dragReferencePoint = points[0];
|
|
34457
34475
|
const constrain = ["angles", "sides"].includes(snapTo) ? p => p : p => snap(snapStep, p);
|
|
34458
34476
|
const {
|
|
34459
34477
|
dragging
|
|
34460
34478
|
} = useDraggable({
|
|
34461
|
-
gestureTarget:
|
|
34479
|
+
gestureTarget: polygonRef,
|
|
34462
34480
|
point: dragReferencePoint,
|
|
34463
34481
|
onMove: newPoint => {
|
|
34464
34482
|
const delta = vec.sub(newPoint, dragReferencePoint);
|
|
@@ -34466,7 +34484,61 @@ const LimitedPolygonGraph = props => {
|
|
|
34466
34484
|
},
|
|
34467
34485
|
constrainKeyboardMovement: constrain
|
|
34468
34486
|
});
|
|
34469
|
-
|
|
34487
|
+
|
|
34488
|
+
// Hover/focus state for Limited Polgyon effects.
|
|
34489
|
+
const [hovered, setHovered] = React.useState(false);
|
|
34490
|
+
// This is more so required for the re-rendering that occurs when state
|
|
34491
|
+
// updates; specifically with regard to line weighting and polygon focus.
|
|
34492
|
+
const [focusVisible, setFocusVisible] = React.useState(false);
|
|
34493
|
+
|
|
34494
|
+
// This useEffect is to handle the focus snapping for Unlimited Polygon.
|
|
34495
|
+
React.useEffect(() => {
|
|
34496
|
+
const focusedIndex = props.graphState.focusedPointIndex;
|
|
34497
|
+
if (focusedIndex != null) {
|
|
34498
|
+
var _pointsRef$current$fo;
|
|
34499
|
+
(_pointsRef$current$fo = pointsRef.current[focusedIndex]) == null || _pointsRef$current$fo.focus();
|
|
34500
|
+
}
|
|
34501
|
+
}, [props.graphState.focusedPointIndex, pointsRef]);
|
|
34502
|
+
const statefulProps = _extends({}, props, {
|
|
34503
|
+
graphConfig,
|
|
34504
|
+
polygonRef,
|
|
34505
|
+
pointsRef,
|
|
34506
|
+
lastMoveTimeRef,
|
|
34507
|
+
left,
|
|
34508
|
+
top,
|
|
34509
|
+
dragging,
|
|
34510
|
+
points,
|
|
34511
|
+
constrain,
|
|
34512
|
+
hovered,
|
|
34513
|
+
setHovered,
|
|
34514
|
+
focusVisible,
|
|
34515
|
+
setFocusVisible
|
|
34516
|
+
});
|
|
34517
|
+
return numSides === "unlimited" ? UnlimitedPolygonGraph(statefulProps) : LimitedPolygonGraph(statefulProps);
|
|
34518
|
+
};
|
|
34519
|
+
const LimitedPolygonGraph = statefulProps => {
|
|
34520
|
+
const {
|
|
34521
|
+
dispatch,
|
|
34522
|
+
hovered,
|
|
34523
|
+
setHovered,
|
|
34524
|
+
focusVisible,
|
|
34525
|
+
setFocusVisible,
|
|
34526
|
+
graphConfig,
|
|
34527
|
+
polygonRef,
|
|
34528
|
+
lastMoveTimeRef,
|
|
34529
|
+
dragging,
|
|
34530
|
+
points,
|
|
34531
|
+
constrain
|
|
34532
|
+
} = statefulProps;
|
|
34533
|
+
const {
|
|
34534
|
+
showAngles,
|
|
34535
|
+
showSides,
|
|
34536
|
+
range,
|
|
34537
|
+
snapTo = "grid"
|
|
34538
|
+
} = statefulProps.graphState;
|
|
34539
|
+
const {
|
|
34540
|
+
disableKeyboardInteraction
|
|
34541
|
+
} = graphConfig;
|
|
34470
34542
|
const lines = getLines(points);
|
|
34471
34543
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Polygon, {
|
|
34472
34544
|
points: [...points],
|
|
@@ -34504,7 +34576,7 @@ const LimitedPolygonGraph = props => {
|
|
|
34504
34576
|
points: [...points],
|
|
34505
34577
|
color: "transparent",
|
|
34506
34578
|
svgPolygonProps: {
|
|
34507
|
-
ref,
|
|
34579
|
+
ref: polygonRef,
|
|
34508
34580
|
tabIndex: disableKeyboardInteraction ? -1 : 0,
|
|
34509
34581
|
strokeWidth: TARGET_SIZE,
|
|
34510
34582
|
style: {
|
|
@@ -34516,13 +34588,13 @@ const LimitedPolygonGraph = props => {
|
|
|
34516
34588
|
// Required to remove line weighting when user clicks away
|
|
34517
34589
|
// from the focused polygon
|
|
34518
34590
|
onKeyDownCapture: () => {
|
|
34519
|
-
setFocusVisible(hasFocusVisible(
|
|
34591
|
+
setFocusVisible(hasFocusVisible(polygonRef.current));
|
|
34520
34592
|
},
|
|
34521
34593
|
// Required for lines to darken on focus
|
|
34522
|
-
onFocus: () => setFocusVisible(hasFocusVisible(
|
|
34594
|
+
onFocus: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
|
|
34523
34595
|
// Required for line weighting to update on blur. Without this,
|
|
34524
34596
|
// the user has to hover over the shape for it to update
|
|
34525
|
-
onBlur: () => setFocusVisible(hasFocusVisible(
|
|
34597
|
+
onBlur: () => setFocusVisible(hasFocusVisible(polygonRef.current)),
|
|
34526
34598
|
className: "movable-polygon"
|
|
34527
34599
|
}
|
|
34528
34600
|
}), points.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
@@ -34534,94 +34606,85 @@ const LimitedPolygonGraph = props => {
|
|
|
34534
34606
|
const now = Date.now();
|
|
34535
34607
|
const targetFPS = 40;
|
|
34536
34608
|
const moveThresholdTime = 1000 / targetFPS;
|
|
34537
|
-
if (now -
|
|
34609
|
+
if (now - lastMoveTimeRef.current > moveThresholdTime) {
|
|
34538
34610
|
dispatch(actions.polygon.movePoint(i, destination));
|
|
34539
|
-
|
|
34611
|
+
lastMoveTimeRef.current = now;
|
|
34540
34612
|
}
|
|
34541
34613
|
}
|
|
34542
34614
|
})));
|
|
34543
34615
|
};
|
|
34544
|
-
const UnlimitedPolygonGraph =
|
|
34616
|
+
const UnlimitedPolygonGraph = statefulProps => {
|
|
34545
34617
|
const {
|
|
34546
|
-
dispatch
|
|
34547
|
-
|
|
34618
|
+
dispatch,
|
|
34619
|
+
graphConfig,
|
|
34620
|
+
left,
|
|
34621
|
+
top,
|
|
34622
|
+
pointsRef,
|
|
34623
|
+
points
|
|
34624
|
+
} = statefulProps;
|
|
34548
34625
|
const {
|
|
34549
34626
|
coords,
|
|
34550
34627
|
closedPolygon
|
|
34551
|
-
} =
|
|
34552
|
-
|
|
34628
|
+
} = statefulProps.graphState;
|
|
34629
|
+
|
|
34630
|
+
// If the polygon is closed, return a LimitedPolygon component.
|
|
34631
|
+
if (closedPolygon) {
|
|
34632
|
+
const closedPolygonProps = _extends({}, statefulProps, {
|
|
34633
|
+
numSides: coords.length
|
|
34634
|
+
});
|
|
34635
|
+
return /*#__PURE__*/React.createElement(LimitedPolygonGraph, closedPolygonProps);
|
|
34636
|
+
}
|
|
34553
34637
|
const {
|
|
34554
|
-
range: [x, y],
|
|
34555
34638
|
graphDimensionsInPixels
|
|
34556
34639
|
} = graphConfig;
|
|
34557
34640
|
const widthPx = graphDimensionsInPixels[0];
|
|
34558
34641
|
const heightPx = graphDimensionsInPixels[1];
|
|
34559
|
-
|
|
34560
|
-
|
|
34561
|
-
|
|
34562
|
-
|
|
34563
|
-
|
|
34564
|
-
|
|
34565
|
-
|
|
34566
|
-
|
|
34567
|
-
|
|
34568
|
-
|
|
34569
|
-
|
|
34642
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
|
|
34643
|
+
style: {
|
|
34644
|
+
fill: "rgba(0,0,0,0)",
|
|
34645
|
+
cursor: "crosshair"
|
|
34646
|
+
},
|
|
34647
|
+
width: widthPx,
|
|
34648
|
+
height: heightPx,
|
|
34649
|
+
x: left,
|
|
34650
|
+
y: top,
|
|
34651
|
+
onClick: event => {
|
|
34652
|
+
const elementRect = event.currentTarget.getBoundingClientRect();
|
|
34653
|
+
const x = event.clientX - elementRect.x;
|
|
34654
|
+
const y = event.clientY - elementRect.y;
|
|
34655
|
+
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
34656
|
+
dispatch(actions.polygon.addPoint(graphCoordinates[0]));
|
|
34570
34657
|
}
|
|
34571
|
-
},
|
|
34572
|
-
|
|
34573
|
-
|
|
34574
|
-
|
|
34575
|
-
|
|
34576
|
-
return /*#__PURE__*/React.createElement(LimitedPolygonGraph, closedPolygonProps);
|
|
34577
|
-
} else {
|
|
34578
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
|
|
34658
|
+
}), /*#__PURE__*/React.createElement(Polyline, {
|
|
34659
|
+
points: [...points],
|
|
34660
|
+
color: "var(--movable-line-stroke-color)",
|
|
34661
|
+
svgPolylineProps: {
|
|
34662
|
+
strokeWidth: "var(--movable-line-stroke-weight)",
|
|
34579
34663
|
style: {
|
|
34580
|
-
fill: "
|
|
34581
|
-
cursor: "crosshair"
|
|
34582
|
-
},
|
|
34583
|
-
width: widthPx,
|
|
34584
|
-
height: heightPx,
|
|
34585
|
-
x: left,
|
|
34586
|
-
y: top,
|
|
34587
|
-
onClick: event => {
|
|
34588
|
-
const elementRect = event.currentTarget.getBoundingClientRect();
|
|
34589
|
-
const x = event.clientX - elementRect.x;
|
|
34590
|
-
const y = event.clientY - elementRect.y;
|
|
34591
|
-
const graphCoordinates = pixelsToVectors([[x, y]], graphConfig);
|
|
34592
|
-
dispatch(actions.polygon.addPoint(graphCoordinates[0]));
|
|
34593
|
-
}
|
|
34594
|
-
}), /*#__PURE__*/React.createElement(Polyline, {
|
|
34595
|
-
points: [...points],
|
|
34596
|
-
color: "var(--movable-line-stroke-color)",
|
|
34597
|
-
svgPolylineProps: {
|
|
34598
|
-
strokeWidth: "var(--movable-line-stroke-weight)",
|
|
34599
|
-
style: {
|
|
34600
|
-
fill: "transparent"
|
|
34601
|
-
}
|
|
34664
|
+
fill: "transparent"
|
|
34602
34665
|
}
|
|
34603
|
-
}
|
|
34604
|
-
|
|
34605
|
-
|
|
34606
|
-
|
|
34607
|
-
|
|
34608
|
-
|
|
34609
|
-
|
|
34610
|
-
|
|
34611
|
-
|
|
34612
|
-
|
|
34613
|
-
|
|
34614
|
-
|
|
34615
|
-
|
|
34616
|
-
|
|
34617
|
-
|
|
34618
|
-
|
|
34619
|
-
|
|
34620
|
-
|
|
34621
|
-
dispatch(actions.polygon.clickPoint(i));
|
|
34666
|
+
}
|
|
34667
|
+
}), coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
|
|
34668
|
+
key: i,
|
|
34669
|
+
point: point,
|
|
34670
|
+
sequenceNumber: i + 1,
|
|
34671
|
+
onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
|
|
34672
|
+
ref: ref => {
|
|
34673
|
+
pointsRef.current[i] = ref;
|
|
34674
|
+
},
|
|
34675
|
+
onFocus: () => {
|
|
34676
|
+
dispatch(actions.polygon.focusPoint(i));
|
|
34677
|
+
},
|
|
34678
|
+
onClick: () => {
|
|
34679
|
+
// If the point being clicked is the first point and
|
|
34680
|
+
// there's enough points to form a polygon (3 or more)
|
|
34681
|
+
// Close the shape before setting focus.
|
|
34682
|
+
if (i === 0 && coords.length >= 3) {
|
|
34683
|
+
dispatch(actions.polygon.closePolygon());
|
|
34622
34684
|
}
|
|
34623
|
-
|
|
34624
|
-
|
|
34685
|
+
dispatch(actions.polygon.clickPoint(i));
|
|
34686
|
+
}
|
|
34687
|
+
})));
|
|
34625
34688
|
};
|
|
34626
34689
|
function getLines(points) {
|
|
34627
34690
|
return points.map((point, i) => {
|
|
@@ -34636,17 +34699,13 @@ const hasFocusVisible = element => {
|
|
|
34636
34699
|
};
|
|
34637
34700
|
try {
|
|
34638
34701
|
return matches(":focus-visible");
|
|
34639
|
-
} catch (
|
|
34702
|
+
} catch (_unused) {
|
|
34640
34703
|
// jsdom doesn't support :focus-visible
|
|
34641
34704
|
// (see https://github.com/jsdom/jsdom/issues/3426),
|
|
34642
34705
|
// so the call to matches(":focus-visible") will fail in tests.
|
|
34643
34706
|
return matches(":focus");
|
|
34644
34707
|
}
|
|
34645
34708
|
};
|
|
34646
|
-
const PolygonGraph = props => {
|
|
34647
|
-
const numSides = props.graphState.numSides;
|
|
34648
|
-
return numSides === "unlimited" ? UnlimitedPolygonGraph(props) : LimitedPolygonGraph(props);
|
|
34649
|
-
};
|
|
34650
34709
|
|
|
34651
34710
|
function renderQuadraticGraph(state, dispatch) {
|
|
34652
34711
|
return {
|
|
@@ -40012,7 +40071,8 @@ class Sortable extends React.Component {
|
|
|
40012
40071
|
this.setState({
|
|
40013
40072
|
items
|
|
40014
40073
|
}, () => {
|
|
40015
|
-
|
|
40074
|
+
var _this$props$onMeasure, _this$props;
|
|
40075
|
+
(_this$props$onMeasure = (_this$props = this.props).onMeasure) == null || _this$props$onMeasure.call(_this$props, {
|
|
40016
40076
|
widths: widths,
|
|
40017
40077
|
heights: heights
|
|
40018
40078
|
});
|
|
@@ -40038,6 +40098,7 @@ class Sortable extends React.Component {
|
|
|
40038
40098
|
// for testing and other cases where dragging and dropping may
|
|
40039
40099
|
// not be available
|
|
40040
40100
|
moveOptionToIndex(option, index) {
|
|
40101
|
+
var _this$props$onChange, _this$props2;
|
|
40041
40102
|
const {
|
|
40042
40103
|
items
|
|
40043
40104
|
} = this.state;
|
|
@@ -40062,7 +40123,7 @@ class Sortable extends React.Component {
|
|
|
40062
40123
|
this.setState({
|
|
40063
40124
|
items: nextItems
|
|
40064
40125
|
});
|
|
40065
|
-
this.props.onChange
|
|
40126
|
+
(_this$props$onChange = (_this$props2 = this.props).onChange) == null || _this$props$onChange.call(_this$props2, {});
|
|
40066
40127
|
}
|
|
40067
40128
|
onMouseMove(key) {
|
|
40068
40129
|
// Dragging: Rearrange items based on draggable's position
|
|
@@ -40120,6 +40181,7 @@ class Sortable extends React.Component {
|
|
|
40120
40181
|
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
40121
40182
|
// eslint-disable-next-line no-restricted-syntax
|
|
40122
40183
|
const nextAnimationFrame = requestAnimationFrame(() => {
|
|
40184
|
+
var _this$props$onChange2, _this$props3;
|
|
40123
40185
|
const items = _.map(this.state.items, function (item) {
|
|
40124
40186
|
if (item.key === key) {
|
|
40125
40187
|
item.state = ItemState.ANIMATING;
|
|
@@ -40142,7 +40204,7 @@ class Sortable extends React.Component {
|
|
|
40142
40204
|
// HACK: We need to know *that* the widget changed, but currently it's
|
|
40143
40205
|
// not set up in a nice way to tell us *how* it changed, since the
|
|
40144
40206
|
// permutation of the items is stored in state.
|
|
40145
|
-
this.props.onChange
|
|
40207
|
+
(_this$props$onChange2 = (_this$props3 = this.props).onChange) == null || _this$props$onChange2.call(_this$props3, {});
|
|
40146
40208
|
});
|
|
40147
40209
|
|
|
40148
40210
|
// @ts-expect-error - TS2339 - Property 'animationFrameRequest' does not exist on type 'Sortable'.
|
|
@@ -42820,11 +42882,12 @@ class Card extends React.Component {
|
|
|
42820
42882
|
event.preventDefault();
|
|
42821
42883
|
const loc = Util.extractPointerLocation(event);
|
|
42822
42884
|
if (loc) {
|
|
42885
|
+
var _this$props$onMouseDo, _this$props;
|
|
42823
42886
|
this.setState({
|
|
42824
42887
|
dragging: true
|
|
42825
42888
|
});
|
|
42826
42889
|
this.bindMouseMoveUp();
|
|
42827
|
-
this.props.onMouseDown
|
|
42890
|
+
(_this$props$onMouseDo = (_this$props = this.props).onMouseDown) == null || _this$props$onMouseDo.call(_this$props, loc, this);
|
|
42828
42891
|
}
|
|
42829
42892
|
};
|
|
42830
42893
|
this.onMouseMove = event => {
|
|
@@ -42834,18 +42897,20 @@ class Card extends React.Component {
|
|
|
42834
42897
|
event.preventDefault();
|
|
42835
42898
|
const loc = Util.extractPointerLocation(event);
|
|
42836
42899
|
if (loc) {
|
|
42837
|
-
|
|
42900
|
+
var _this$props$onMouseMo, _this$props2;
|
|
42901
|
+
(_this$props$onMouseMo = (_this$props2 = this.props).onMouseMove) == null || _this$props$onMouseMo.call(_this$props2, loc);
|
|
42838
42902
|
}
|
|
42839
42903
|
};
|
|
42840
42904
|
this.onMouseUp = event => {
|
|
42841
42905
|
event.preventDefault();
|
|
42842
42906
|
const loc = Util.extractPointerLocation(event);
|
|
42843
42907
|
if (loc) {
|
|
42908
|
+
var _this$props$onMouseUp, _this$props3;
|
|
42844
42909
|
this.setState({
|
|
42845
42910
|
dragging: false
|
|
42846
42911
|
});
|
|
42847
42912
|
this.unbindMouseMoveUp();
|
|
42848
|
-
this.props.onMouseUp
|
|
42913
|
+
(_this$props$onMouseUp = (_this$props3 = this.props).onMouseUp) == null || _this$props$onMouseUp.call(_this$props3, loc);
|
|
42849
42914
|
}
|
|
42850
42915
|
};
|
|
42851
42916
|
}
|
|
@@ -45107,7 +45172,7 @@ const init = function init() {
|
|
|
45107
45172
|
|
|
45108
45173
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45109
45174
|
const libName = "@khanacademy/perseus";
|
|
45110
|
-
const libVersion = "
|
|
45175
|
+
const libVersion = "48.1.0";
|
|
45111
45176
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45112
45177
|
|
|
45113
45178
|
const apiVersion = {
|
|
@@ -45869,28 +45934,6 @@ class ServerItemRenderer extends React.Component {
|
|
|
45869
45934
|
return this.questionRenderer.getUserInputMap();
|
|
45870
45935
|
}
|
|
45871
45936
|
|
|
45872
|
-
/**
|
|
45873
|
-
* Grades the item.
|
|
45874
|
-
*
|
|
45875
|
-
* @deprecated use scorePerseusItem
|
|
45876
|
-
*/
|
|
45877
|
-
scoreInput() {
|
|
45878
|
-
const guess = this.getUserInput();
|
|
45879
|
-
const score = scorePerseusItem(this.props.item.question, guess, this.context.strings, this.context.locale);
|
|
45880
|
-
|
|
45881
|
-
// Continue to include an empty guess for the now defunct answer area.
|
|
45882
|
-
// TODO(alex): Check whether we rely on the format here for
|
|
45883
|
-
// analyzing ProblemLogs. If not, remove this layer.
|
|
45884
|
-
const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
|
|
45885
|
-
const keScore = keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
|
|
45886
|
-
const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
|
|
45887
|
-
this.setState({
|
|
45888
|
-
questionCompleted: keScore.correct,
|
|
45889
|
-
questionHighlightedWidgets: emptyQuestionAreaWidgets
|
|
45890
|
-
});
|
|
45891
|
-
return keScore;
|
|
45892
|
-
}
|
|
45893
|
-
|
|
45894
45937
|
/**
|
|
45895
45938
|
* Returns an array of all widget IDs in the order they occur in
|
|
45896
45939
|
* the question content.
|
|
@@ -48681,5 +48724,5 @@ const ScoringUtil = {
|
|
|
48681
48724
|
* Types
|
|
48682
48725
|
*/
|
|
48683
48726
|
|
|
48684
|
-
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mathOnlyParser, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, scorePerseusItem, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
48727
|
+
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mathOnlyParser, mediaQueries, parseDataFromJSONP, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, scorePerseusItem, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
48685
48728
|
//# sourceMappingURL=index.js.map
|