@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/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 (e) {
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
- callback && setTimeout(callback, 0);
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 && imgProps.onClick(e);
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 && 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
- const hash = getUrlHash(this.props.src);
17139
-
17140
- // We can't make multiple jsonp calls to the same file because their
17141
- // callbacks will collide with each other. Instead, we cache the data
17142
- // and only make the jsonp calls once.
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
- } else {
17195
- retrieveData(Util.getDataUrl(this.props.src), (x, status, error) => {
17196
- Log.error("Data load failed for svg-image", Errors.Service, {
17197
- cause: error,
17198
- loggedMetadata: {
17199
- dataUrl: Util.getDataUrl(this.props.src),
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 (e.g. used in scoreInput()). See
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 && onFocus();
21555
+ onFocus == null || onFocus();
21560
21556
  }
21561
21557
  });
21562
21558
  } else {
21563
- onFocus && 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 && 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 (err) {
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 (err) {
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
- $instructionsWrapper && $instructionsWrapper.css("opacity", invisible);
30629
+ var _$instructionsWrapper;
30630
+ (_$instructionsWrapper = $instructionsWrapper) == null || _$instructionsWrapper.css("opacity", invisible);
30642
30631
  } : undefined;
30643
30632
  const onMouseOut = $instructionsWrapper ? function () {
30644
- $instructionsWrapper && $instructionsWrapper.css("opacity", visible);
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 numPoints = props.graphState.numPoints;
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(props);
34344
+ return UnlimitedPointGraph(statefulProps);
34329
34345
  }
34330
- return LimitedPointGraph(props);
34346
+ return LimitedPointGraph(statefulProps);
34331
34347
  }
34332
- function LimitedPointGraph(props) {
34348
+ function LimitedPointGraph(statefulProps) {
34333
34349
  const {
34334
34350
  dispatch
34335
- } = props;
34336
- return /*#__PURE__*/React.createElement(React.Fragment, null, props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
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(props) {
34359
+ function UnlimitedPointGraph(statefulProps) {
34344
34360
  const {
34345
- dispatch
34346
- } = props;
34347
- const graphConfig = useGraphConfig();
34361
+ dispatch,
34362
+ graphConfig,
34363
+ pointsRef,
34364
+ top,
34365
+ left
34366
+ } = statefulProps;
34348
34367
  const {
34349
- range: [[minX, maxX], [minY, maxY]]
34368
+ coords
34369
+ } = statefulProps.graphState;
34370
+ const {
34371
+ graphDimensionsInPixels
34350
34372
  } = graphConfig;
34351
- const width = maxX - minX;
34352
- const height = maxY - minY;
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
- }), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
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
- itemsRef.current[i] = ref;
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 LimitedPolygonGraph = props => {
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
- disableKeyboardInteraction
34450
- } = useGraphConfig();
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
- const ref = React.useRef(null);
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: ref,
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
- const lastMoveTime = React.useRef(0);
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(ref.current));
34591
+ setFocusVisible(hasFocusVisible(polygonRef.current));
34520
34592
  },
34521
34593
  // Required for lines to darken on focus
34522
- onFocus: () => setFocusVisible(hasFocusVisible(ref.current)),
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(ref.current)),
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 - lastMoveTime.current > moveThresholdTime) {
34609
+ if (now - lastMoveTimeRef.current > moveThresholdTime) {
34538
34610
  dispatch(actions.polygon.movePoint(i, destination));
34539
- lastMoveTime.current = now;
34611
+ lastMoveTimeRef.current = now;
34540
34612
  }
34541
34613
  }
34542
34614
  })));
34543
34615
  };
34544
- const UnlimitedPolygonGraph = props => {
34616
+ const UnlimitedPolygonGraph = statefulProps => {
34545
34617
  const {
34546
- dispatch
34547
- } = props;
34618
+ dispatch,
34619
+ graphConfig,
34620
+ left,
34621
+ top,
34622
+ pointsRef,
34623
+ points
34624
+ } = statefulProps;
34548
34625
  const {
34549
34626
  coords,
34550
34627
  closedPolygon
34551
- } = props.graphState;
34552
- const graphConfig = useGraphConfig();
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
- const [[left, top]] = useTransformVectorsToPixels([x[0], y[1]]);
34560
- const pointRef = React.useRef([]);
34561
-
34562
- // TODO(benchristel): can the default set of points be removed here? I don't
34563
- // think coords can be null.
34564
- const points = coords != null ? coords : [[0, 0]];
34565
- React.useEffect(() => {
34566
- const focusedIndex = props.graphState.focusedPointIndex;
34567
- if (focusedIndex != null) {
34568
- var _pointRef$current$foc;
34569
- (_pointRef$current$foc = pointRef.current[focusedIndex]) == null || _pointRef$current$foc.focus();
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
- }, [props.graphState.focusedPointIndex, pointRef]);
34572
- if (closedPolygon) {
34573
- const closedPolygonProps = _extends({}, props, {
34574
- numSides: coords.length
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: "rgba(0,0,0,0)",
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
- }), props.graphState.coords.map((point, i) => /*#__PURE__*/React.createElement(MovablePoint$1, {
34604
- key: i,
34605
- point: point,
34606
- sequenceNumber: i + 1,
34607
- onMove: destination => dispatch(actions.polygon.movePoint(i, destination)),
34608
- ref: ref => {
34609
- pointRef.current[i] = ref;
34610
- },
34611
- onFocus: () => {
34612
- dispatch(actions.polygon.focusPoint(i));
34613
- },
34614
- onClick: () => {
34615
- // If the point being clicked is the first point and
34616
- // there's enough points to form a polygon (3 or more)
34617
- // Close the shape before setting focus.
34618
- if (i === 0 && props.graphState.coords.length >= 3) {
34619
- dispatch(actions.polygon.closePolygon());
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 (e) {
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
- this.props.onMeasure && this.props.onMeasure({
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 && 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 && 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 && this.props.onMouseDown(loc, this);
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
- this.props.onMouseMove && this.props.onMouseMove(loc);
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 && this.props.onMouseUp(loc);
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 = "47.0.1";
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