@khanacademy/perseus 52.0.1 → 53.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/components/number-input.d.ts +2 -0
  2. package/dist/components/range-input.d.ts +1 -0
  3. package/dist/components/svg-image.d.ts +2 -2
  4. package/dist/es/index.css +0 -30
  5. package/dist/es/index.js +385 -865
  6. package/dist/es/index.js.map +1 -1
  7. package/dist/index.css +0 -30
  8. package/dist/index.d.ts +1 -2
  9. package/dist/index.js +383 -868
  10. package/dist/index.js.map +1 -1
  11. package/dist/perseus-api.d.ts +0 -1
  12. package/dist/renderer.d.ts +1 -1
  13. package/dist/styles/constants.d.ts +0 -2
  14. package/dist/types.d.ts +3 -20
  15. package/dist/util/math-utils.d.ts +1 -0
  16. package/dist/util/scoring.d.ts +0 -15
  17. package/dist/util/test-utils.d.ts +1 -1
  18. package/dist/widget-ai-utils/mock-widget/prompt-utils.d.ts +1 -1
  19. package/dist/widget-container.d.ts +1 -1
  20. package/dist/widgets/categorizer/categorizer.d.ts +0 -3
  21. package/dist/widgets/cs-program/cs-program.d.ts +2 -3
  22. package/dist/widgets/definition/definition.d.ts +0 -2
  23. package/dist/widgets/deprecated-standin/deprecated-standin.d.ts +0 -1
  24. package/dist/widgets/dropdown/dropdown.d.ts +2 -4
  25. package/dist/widgets/explanation/explanation.d.ts +0 -2
  26. package/dist/widgets/expression/expression.d.ts +1 -5
  27. package/dist/widgets/grapher/grapher.d.ts +0 -2
  28. package/dist/widgets/group/group.d.ts +0 -4
  29. package/dist/widgets/iframe/iframe.d.ts +0 -2
  30. package/dist/widgets/image/image.d.ts +0 -3
  31. package/dist/widgets/input-number/input-number.d.ts +0 -3
  32. package/dist/widgets/interaction/interaction.d.ts +0 -1
  33. package/dist/widgets/interactive-graph.d.ts +0 -2
  34. package/dist/widgets/interactive-graphs/backgrounds/axis-ticks.d.ts +1 -0
  35. package/dist/widgets/label-image/label-image.d.ts +2 -2
  36. package/dist/widgets/matcher/matcher.d.ts +0 -2
  37. package/dist/widgets/matrix/matrix.d.ts +0 -3
  38. package/dist/widgets/measurer/measurer.d.ts +0 -1
  39. package/dist/widgets/mock-widgets/mock-widget-types.d.ts +0 -6
  40. package/dist/widgets/mock-widgets/mock-widget.d.ts +2 -5
  41. package/dist/widgets/molecule/molecule.d.ts +0 -1
  42. package/dist/widgets/number-line/number-line.d.ts +2 -3
  43. package/dist/widgets/numeric-input/numeric-input.d.ts +2 -3
  44. package/dist/widgets/orderer/orderer.d.ts +0 -3
  45. package/dist/widgets/passage/passage.d.ts +0 -1
  46. package/dist/widgets/passage-ref/passage-ref.d.ts +0 -2
  47. package/dist/widgets/passage-ref-target/passage-ref-target.d.ts +0 -2
  48. package/dist/widgets/plotter/plotter.d.ts +0 -3
  49. package/dist/widgets/radio/radio.d.ts +0 -3
  50. package/dist/widgets/sorter/sorter.d.ts +2 -4
  51. package/dist/widgets/table/table.d.ts +0 -3
  52. package/dist/widgets/video/video.d.ts +0 -3
  53. package/dist/widgets.d.ts +1 -32
  54. package/package.json +10 -10
  55. package/dist/renderer-util.d.ts +0 -14
  56. package/dist/widgets/__shared__/score-noop.d.ts +0 -11
  57. package/dist/widgets/group/score-group.d.ts +0 -4
  58. package/dist/widgets/group/validate-group.d.ts +0 -4
  59. package/dist/widgets/mock-widgets/score-mock-widget.d.ts +0 -5
  60. package/dist/widgets/mock-widgets/validate-mock-widget.d.ts +0 -4
package/dist/es/index.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import * as KAS from '@khanacademy/kas';
2
2
  import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
3
- import { PerseusError, Errors, expressionLogic, getExpressionPublicWidgetOptions, getDecimalSeparator, approximateDeepEqual, pluck, getWidgetIdsFromContent, mapObject, passageRefLogic, radioLogic, getCategorizerPublicWidgetOptions, GrapherUtil as GrapherUtil$1, lockedFigureColors, lockedFigureFillStyles, approximateEqual, getMatrixSize, measurerLogic, getOrdererPublicWidgetOptions, addLibraryVersionToPerseusDebug, getWidgetIdsFromContentByType } from '@khanacademy/perseus-core';
3
+ import { PerseusError, Errors, expressionLogic, getExpressionPublicWidgetOptions, getDecimalSeparator, approximateDeepEqual, pluck, CoreWidgetRegistry, getUpgradedWidgetOptions, mapObject, getNumericInputPublicWidgetOptions, passageRefLogic, radioLogic, getCategorizerPublicWidgetOptions, getCSProgramPublicWidgetOptions, getDropdownPublicWidgetOptions, GrapherUtil as GrapherUtil$1, lockedFigureColors, lockedFigureFillStyles, approximateEqual, getLabelImagePublicWidgetOptions, getMatrixSize, measurerLogic, getNumberLinePublicWidgetOptions, getOrdererPublicWidgetOptions, getSorterPublicWidgetOptions, addLibraryVersionToPerseusDebug, upgradeWidgetInfoToLatestVersion, getWidgetIdsFromContentByType } from '@khanacademy/perseus-core';
4
4
  export { isFailure, isSuccess, parseAndMigratePerseusArticle, parseAndMigratePerseusItem, parsePerseusItem } from '@khanacademy/perseus-core';
5
5
  import * as PerseusLinter from '@khanacademy/perseus-linter';
6
6
  import { linterContextDefault, Rule } from '@khanacademy/perseus-linter';
7
- import { scoreExpression, validateExpression, KhanAnswerTypes, scoreInputNumber, inputNumberAnswerTypes, scoreNumericInput, scoreRadio, validateRadio, scoreCategorizer, validateCategorizer, scoreCSProgram, scoreDropdown, validateDropdown, scoreGrapher, scoreIframe, scoreInteractiveGraph, scoreLabelImage, scoreLabelImageMarker, scoreMatcher, scoreMatrix, validateMatrix, scoreNumberLine, validateNumberLine, scoreOrderer, validateOrderer, scorePlotter, validatePlotter, scoreSorter, validateSorter, scoreTable, validateTable } from '@khanacademy/perseus-score';
8
7
  import { View, RenderStateRoot, Id, useOnMountEffect, useLatestRef, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
9
8
  import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
10
9
  import { HeadingMedium, LabelSmall, LabelLarge, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
@@ -21,6 +20,7 @@ import { color, spacing, font } from '@khanacademy/wonder-blocks-tokens';
21
20
  import $ from 'jquery';
22
21
  import { v4 } from 'uuid';
23
22
  import PropTypes from 'prop-types';
23
+ import { KhanAnswerTypes, emptyWidgetsFunctional, scoreWidgetsFunctional, flattenScores, inputNumberAnswerTypes, scoreRadio, scoreLabelImageMarker } from '@khanacademy/perseus-score';
24
24
  import { entries, UnreachableCaseError, keys } from '@khanacademy/wonder-stuff-core';
25
25
  import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
26
26
  import { point, KhanMath, number, vector as vector$3, line, geometry, angles, coefficients } from '@khanacademy/kmath';
@@ -1389,7 +1389,6 @@ const ClassNames = {
1389
1389
  SELECTED: "perseus-radio-selected",
1390
1390
  OPTION_CONTENT: "perseus-radio-option-content"
1391
1391
  },
1392
- INTERACTIVE: "perseus-interactive",
1393
1392
  CORRECT: "perseus-correct",
1394
1393
  INCORRECT: "perseus-incorrect",
1395
1394
  UNANSWERED: "perseus-unanswered",
@@ -1637,7 +1636,6 @@ class Expression extends React.Component {
1637
1636
  // eslint-disable-next-line react/no-string-refs
1638
1637
  , {
1639
1638
  ref: "input",
1640
- className: ClassNames.INTERACTIVE,
1641
1639
  value: this.props.value,
1642
1640
  onChange: this.changeAndTrack,
1643
1641
  convertDotToTimes: this.props.times,
@@ -1750,7 +1748,6 @@ var Expression$1 = {
1750
1748
  name: "expression",
1751
1749
  displayName: "Expression / Equation",
1752
1750
  accessible: true,
1753
- defaultAlignment: "inline-block",
1754
1751
  widget: ExpressionWithDependencies,
1755
1752
  transform: widgetOptions => {
1756
1753
  const {
@@ -1775,12 +1772,6 @@ var Expression$1 = {
1775
1772
  propUpgrades: expressionLogic.widgetOptionsUpgrades,
1776
1773
  // For use by the editor
1777
1774
  isLintable: true,
1778
- // TODO(LEMS-2656): remove TS suppression
1779
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
1780
- scorer: scoreExpression,
1781
- // TODO(LEMS-2656): remove TS suppression
1782
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
1783
- validator: validateExpression,
1784
1775
  getPublicWidgetOptions: getExpressionPublicWidgetOptions,
1785
1776
  // TODO(LEMS-2656): remove TS suppression
1786
1777
  // @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusExpressionRubric'.
@@ -3061,10 +3052,6 @@ const pureMdMax$1 = "1023px";
3061
3052
  const pureLgMax$1 = "1279px";
3062
3053
  // @tableBackgroundAccent: #f9f9f9; // for striping
3063
3054
  const tableBackgroundAccent = "#F9F9F9";
3064
- // @zIndexAboveScratchpad: @zIndexScratchPad + 1;
3065
- const zIndexAboveScratchpad$1 = 2;
3066
- // @zIndexInteractiveComponent: @zIndexAboveScratchpad + 1;
3067
- const zIndexInteractiveComponent$1 = 3;
3068
3055
  // @phoneMargin: 16px;
3069
3056
  const phoneMargin = 16;
3070
3057
  const negativePhoneMargin = -16;
@@ -3108,8 +3095,6 @@ var constants = /*#__PURE__*/Object.freeze({
3108
3095
  pureMdMax: pureMdMax$1,
3109
3096
  pureLgMax: pureLgMax$1,
3110
3097
  tableBackgroundAccent: tableBackgroundAccent,
3111
- zIndexAboveScratchpad: zIndexAboveScratchpad$1,
3112
- zIndexInteractiveComponent: zIndexInteractiveComponent$1,
3113
3098
  phoneMargin: phoneMargin,
3114
3099
  negativePhoneMargin: negativePhoneMargin,
3115
3100
  hintBorderWidth: hintBorderWidth,
@@ -17334,130 +17319,162 @@ class QuestionParagraph extends React.Component {
17334
17319
  }
17335
17320
  }
17336
17321
 
17337
- const noScore = {
17338
- type: "points",
17339
- earned: 0,
17340
- total: 0,
17341
- message: null
17342
- };
17343
-
17344
- /**
17345
- * If a widget says that it is empty once it is graded.
17346
- * Trying to encapsulate references to the score format.
17347
- */
17348
- function scoreIsEmpty(score) {
17349
- // HACK(benkomalo): ugh. this isn't great; the Perseus score objects
17350
- // overload the type "invalid" for what should probably be three
17351
- // distinct cases:
17352
- // - truly empty or not fully filled out
17353
- // - invalid or malformed inputs
17354
- // - "almost correct" like inputs where the widget wants to give
17355
- // feedback (e.g. a fraction needs to be reduced, or `pi` should
17356
- // be used instead of 3.14)
17357
- //
17358
- // Unfortunately the coercion happens all over the place, as these
17359
- // Perseus style score objects are created *everywhere* (basically
17360
- // in every widget), so it's hard to change now. We assume that
17361
- // anything with a "message" is not truly empty, and one of the
17362
- // latter two cases for now.
17363
- return score.type === "invalid" && (!score.message || score.message.length === 0);
17364
- }
17365
-
17366
17322
  /**
17367
- * Combine two score objects.
17323
+ * TranslationLinter asynchronously calls Khan Academy's poentry linter.
17368
17324
  *
17369
- * Given two score objects for two different widgets, combine them so that
17370
- * if one is wrong, the total score is wrong, etc.
17325
+ * The poentry linter checks for issues that prevent content from being
17326
+ * translated. This linter is KA-specific and so TranslationLinter does not do
17327
+ * anything in non-KA environments.
17371
17328
  */
17372
- function combineScores(scoreA, scoreB) {
17373
- let message;
17374
- if (scoreA.type === "points" && scoreB.type === "points") {
17375
- if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
17376
- // TODO(alpert): Figure out how to combine messages usefully
17377
- message = null;
17378
- } else {
17379
- message = scoreA.message || scoreB.message;
17380
- }
17381
- return {
17382
- type: "points",
17383
- earned: scoreA.earned + scoreB.earned,
17384
- total: scoreA.total + scoreB.total,
17385
- message: message
17386
- };
17387
- }
17388
- if (scoreA.type === "points" && scoreB.type === "invalid") {
17389
- return scoreB;
17390
- }
17391
- if (scoreA.type === "invalid" && scoreB.type === "points") {
17392
- return scoreA;
17393
- }
17394
- if (scoreA.type === "invalid" && scoreB.type === "invalid") {
17395
- if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
17396
- // TODO(alpert): Figure out how to combine messages usefully
17397
- message = null;
17398
- } else {
17399
- message = scoreA.message || scoreB.message;
17400
- }
17401
- return {
17402
- type: "invalid",
17403
- message: message
17404
- };
17405
- }
17406
17329
 
17330
+ // A LintCB is called once the linter has been run.
17331
+
17332
+ // msec to wait before we actually call the linter after receiving the
17333
+ // last call. The timer is reset if the lint is called during the wait.
17334
+ const DEBOUNCE_TIMEOUT = 1000;
17335
+ class TranslationLinter {
17336
+ constructor() {
17337
+ // To avoid linting when nothing has changed.
17338
+ this.previousContent = null;
17339
+ /**
17340
+ * Runs the poentry linter on the given perseus string asynchronously.
17341
+ *
17342
+ * It doesn't do anything unless we're in a KA environment.
17343
+ *
17344
+ * onLintErrorsGenerated is called even if there are no errors. It is not
17345
+ * called if perseusStr is unchanged from the last call.
17346
+ *
17347
+ * Linting is relatively expensive -- so we debounce linting each instance
17348
+ * for some amount of time.
17349
+ */
17350
+ this.runLinter = debounce$1((perseusStr, onLintErrorsGenerated) => {
17351
+ // TODO(FEI-5003): Do we still need to check for 'KA'?
17352
+ if (typeof KA === "undefined") {
17353
+ return;
17354
+ }
17355
+ if (perseusStr === this.previousContent) {
17356
+ return;
17357
+ }
17358
+ this.previousContent = perseusStr;
17359
+ if (perseusStr === "") {
17360
+ onLintErrorsGenerated([]);
17361
+ return;
17362
+ }
17363
+ fetch("/api/internal/translations/lint_poentry?preview=1&lang=en", {
17364
+ headers: {
17365
+ "Content-Type": "application/json"
17366
+ },
17367
+ body: JSON.stringify({
17368
+ // The poentry linter verifies that the translation from
17369
+ // the original text to the translated text is sane. We
17370
+ // want to ensure that the translation from English to
17371
+ // English is sane, so msgid === mstr.
17372
+ msgid: perseusStr,
17373
+ // msgid is the original text
17374
+ msgstr: perseusStr,
17375
+ // msgstr is the translated text
17376
+ format: "perseus_text",
17377
+ filename: ""
17378
+ }),
17379
+ method: "POST"
17380
+ }).then(response => {
17381
+ if (response.status >= 400) {
17382
+ return {
17383
+ status: "error",
17384
+ message: "Could not run i18n linter."
17385
+ };
17386
+ }
17387
+ return response.json();
17388
+ }, rejection => {
17389
+ return {
17390
+ status: "error",
17391
+ message: "Could not run i18n linter."
17392
+ };
17393
+ }).then(json => {
17394
+ if (json.status === "error") {
17395
+ onLintErrorsGenerated(["Some part of this text makes it untranslatable. " + "The specific message from the i18n linter was: " + json.message.replace(/\n/g, " ")]);
17396
+ } else {
17397
+ onLintErrorsGenerated([]);
17398
+ }
17399
+ });
17400
+ }, DEBOUNCE_TIMEOUT);
17401
+ }
17407
17402
  /**
17408
- * The above checks cover all combinations of score type, so if we get here
17409
- * then something is amiss with our inputs.
17403
+ * Applies an array of errors generated by linters without position
17404
+ * information (TranslationLinter and the legacy getSaveWarnings()) to the
17405
+ * top of a Perseus tree.
17410
17406
  */
17411
- throw new PerseusError("PerseusScore with unknown type encountered", Errors.InvalidInput, {
17412
- metadata: {
17413
- scoreA: JSON.stringify(scoreA),
17414
- scoreB: JSON.stringify(scoreB)
17407
+ applyLintErrors(parsedMarkdown, translationLintErrors) {
17408
+ // These lint errors do not have position data associated with
17409
+ // them, so we just plop them at the top.
17410
+ if (translationLintErrors.length) {
17411
+ const errorText = translationLintErrors.join("\n\n");
17412
+ parsedMarkdown.unshift({
17413
+ content: {
17414
+ type: "text",
17415
+ content: ""
17416
+ },
17417
+ insideTable: false,
17418
+ message: errorText,
17419
+ ruleName: "legacy-error",
17420
+ severity: Rule.Severity.ERROR,
17421
+ type: "lint"
17422
+ });
17415
17423
  }
17416
- });
17417
- }
17418
- function flattenScores(widgetScoreMap) {
17419
- return Object.values(widgetScoreMap).reduce(combineScores, noScore);
17420
- }
17421
- function isCorrect(score) {
17422
- return score.type === "points" && score.earned >= score.total;
17424
+ }
17423
17425
  }
17424
- function keScoreFromPerseusScore(score,
17425
- // It's weird, but this is what we're passing it
17426
- guess, state) {
17427
- if (score.type === "points") {
17428
- return {
17429
- empty: false,
17430
- correct: isCorrect(score),
17431
- message: score.message,
17432
- guess: guess,
17433
- state: state
17434
- };
17426
+
17427
+ /**
17428
+ * Preprocess TeX code to convert things that MathJax doesn't know how to handle
17429
+ * to things is does.
17430
+ */
17431
+ var preprocessTex = (texCode => texCode
17432
+ // Replace uses of \begin{align}...\end{align} which KaTeX doesn't
17433
+ // support (yet) with \begin{aligned}...\end{aligned} which renders
17434
+ // the same is supported by KaTeX. It does the same for align*.
17435
+ // TODO(kevinb) update content to use aligned instead of align.
17436
+ // TODO(LEMS-1608) Remove this replacement as MathJax supports the
17437
+ // "align" macro correctly (and, in fact, it is not synonymous with
17438
+ // "aligned").
17439
+ .replace(/\{align[*]?\}/g, "{aligned}")
17440
+ // Replace non-breaking spaces with regular spaces.
17441
+ .replace(/[\u00a0]/g, " "));
17442
+
17443
+ const {
17444
+ interactiveSizes
17445
+ } = constants;
17446
+
17447
+ // Note: these size cutoffs represent content-width cutoffs as specified in
17448
+ // http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
17449
+ // TODO(benkomalo): these values aren't used in JS outside of this file, but
17450
+ // are coupled to the values in styles/styles.less - DRY it up at some point
17451
+ const smMax = articleMaxWidthTableInPx;
17452
+ const mdMax = articleMaxWidthInPx;
17453
+ const containerSizeClass = {
17454
+ SMALL: "small",
17455
+ MEDIUM: "medium",
17456
+ LARGE: "large",
17457
+ XLARGE: "xlarge"
17458
+ };
17459
+ const getClassFromWidth = width => {
17460
+ if (!width) {
17461
+ return containerSizeClass.MEDIUM;
17435
17462
  }
17436
- if (score.type === "invalid") {
17437
- return {
17438
- empty: true,
17439
- correct: false,
17440
- message: score.message,
17441
- suppressAlmostThere: score.suppressAlmostThere,
17442
- guess: guess,
17443
- state: state
17444
- };
17463
+ if (width <= smMax) {
17464
+ return containerSizeClass.SMALL;
17445
17465
  }
17446
- throw new PerseusError(
17447
- // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
17448
- "Invalid score type: " + score.type, Errors.InvalidInput, {
17449
- metadata: {
17450
- score: JSON.stringify(score),
17451
- guess: JSON.stringify(guess),
17452
- state: JSON.stringify(state)
17453
- }
17454
- });
17455
- }
17466
+ if (width <= mdMax) {
17467
+ return containerSizeClass.MEDIUM;
17468
+ }
17469
+ return containerSizeClass.LARGE;
17470
+ };
17471
+ const getInteractiveBoxFromSizeClass = sizeClass => {
17472
+ if (sizeClass === containerSizeClass.SMALL) {
17473
+ return [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall];
17474
+ }
17475
+ return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
17476
+ };
17456
17477
 
17457
- const DEFAULT_ALIGNMENT$1 = "block";
17458
- // NOTE(kevinb): "default" is not one in `validAlignments`.
17459
- const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
17460
- const DEFAULT_STATIC = false;
17461
17478
  const DEFAULT_TRACKING = "";
17462
17479
  const DEFAULT_LINTABLE = false;
17463
17480
  const widgets = {};
@@ -17474,7 +17491,6 @@ const registerWidgets = widgets => {
17474
17491
  widgets.forEach(widget => {
17475
17492
  registerWidget(widget.name, widget);
17476
17493
  });
17477
- validateAlignments();
17478
17494
  };
17479
17495
 
17480
17496
  /**
@@ -17558,17 +17574,9 @@ const getWidgetExport = name => {
17558
17574
  var _widgets$name3;
17559
17575
  return (_widgets$name3 = widgets[name]) != null ? _widgets$name3 : null;
17560
17576
  };
17561
- const getWidgetValidator = name => {
17562
- var _widgets$name$validat, _widgets$name4;
17563
- return (_widgets$name$validat = (_widgets$name4 = widgets[name]) == null ? void 0 : _widgets$name4.validator) != null ? _widgets$name$validat : null;
17564
- };
17565
- const getWidgetScorer = name => {
17566
- var _widgets$name$scorer, _widgets$name5;
17567
- return (_widgets$name$scorer = (_widgets$name5 = widgets[name]) == null ? void 0 : _widgets$name5.scorer) != null ? _widgets$name$scorer : null;
17568
- };
17569
17577
  const getPublicWidgetOptionsFunction = name => {
17570
- var _widgets$name$getPubl, _widgets$name6;
17571
- return (_widgets$name$getPubl = (_widgets$name6 = widgets[name]) == null ? void 0 : _widgets$name6.getPublicWidgetOptions) != null ? _widgets$name$getPubl : i => i;
17578
+ var _widgets$name$getPubl, _widgets$name4;
17579
+ return (_widgets$name$getPubl = (_widgets$name4 = widgets[name]) == null ? void 0 : _widgets$name4.getPublicWidgetOptions) != null ? _widgets$name$getPubl : i => i;
17572
17580
  };
17573
17581
  const getEditor = name => {
17574
17582
  return _.has(editors, name) ? editors[name] : null;
@@ -17612,109 +17620,6 @@ const isAccessible = widgetInfo => {
17612
17620
  const getAllWidgetTypes = () => {
17613
17621
  return _.keys(widgets);
17614
17622
  };
17615
- const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
17616
- const type = oldWidgetInfo.type;
17617
- // NOTE(jeremy): This looks like it could be replaced by fixing types so
17618
- // that `type` is non-optional. But we're seeing this in Sentry today so I
17619
- // suspect we have legacy data (potentially unpublished) and we should
17620
- // figure that out before depending solely on types.
17621
- if (!_.isString(type)) {
17622
- throw new PerseusError("widget type must be a string, but was: " + type, Errors.Internal);
17623
- }
17624
- const widgetExports = widgets[type];
17625
- if (widgetExports == null) {
17626
- // If we have a widget that isn't registered, we can't upgrade it
17627
- // TODO(aria): Figure out what the best thing to do here would be
17628
- return oldWidgetInfo;
17629
- }
17630
-
17631
- // Unversioned widgets (pre-July 2014) are all implicitly 0.0
17632
- const initialVersion = oldWidgetInfo.version || {
17633
- major: 0,
17634
- minor: 0
17635
- };
17636
- const latestVersion = widgetExports.version || {
17637
- major: 0,
17638
- minor: 0
17639
- };
17640
-
17641
- // If the widget version is later than what we understand (major
17642
- // version is higher than latest, or major versions are equal and minor
17643
- // version is higher than latest), don't perform any upgrades.
17644
- if (initialVersion.major > latestVersion.major || initialVersion.major === latestVersion.major && initialVersion.minor > latestVersion.minor) {
17645
- return oldWidgetInfo;
17646
- }
17647
-
17648
- // We do a clone here so that it's safe to mutate the input parameter
17649
- // in propUpgrades functions (which I will probably accidentally do at
17650
- // some point, and we would like to not break when that happens).
17651
- let newEditorProps = _.clone(oldWidgetInfo.options) || {};
17652
- const upgradePropsMap = widgetExports.propUpgrades || {};
17653
-
17654
- // Empty props usually mean a newly created widget by the editor,
17655
- // and are always considerered up-to-date.
17656
- // Mostly, we'd rather not run upgrade functions on props that are
17657
- // not complete.
17658
- if (_.keys(newEditorProps).length !== 0) {
17659
- // We loop through all the versions after the current version of
17660
- // the loaded widget, up to and including the latest version of the
17661
- // loaded widget, and run the upgrade function to bring our loaded
17662
- // widget's props up to that version.
17663
- // There is a little subtlety here in that we call
17664
- // upgradePropsMap[1] to upgrade *to* version 1,
17665
- // (not from version 1).
17666
- for (let nextVersion = initialVersion.major + 1; nextVersion <= latestVersion.major; nextVersion++) {
17667
- if (upgradePropsMap[String(nextVersion)]) {
17668
- newEditorProps = upgradePropsMap[String(nextVersion)](newEditorProps);
17669
- } else {
17670
- // This is a Log.error because it is unlikely to be hit in
17671
- // local testing, and a Log.error is slightly less scary in
17672
- // prod than a `throw new Error`
17673
- Log.error("No upgrade found for widget. Cannot render.", Errors.Internal, {
17674
- loggedMetadata: {
17675
- type,
17676
- fromMajorVersion: nextVersion - 1,
17677
- toMajorVersion: nextVersion
17678
- }
17679
- });
17680
- // But try to keep going anyways (yolo!)
17681
- // (Throwing an error here would just break the page
17682
- // silently anyways, so that doesn't seem much better
17683
- // than a halfhearted attempt to continue, however
17684
- // shallow...)
17685
- }
17686
- }
17687
- }
17688
-
17689
- // Minor version upgrades (eg. new optional props) don't have
17690
- // transform functions. Instead, we fill in the new props with their
17691
- // defaults.
17692
- const defaultProps = type in editors ? editors[type].defaultProps : {};
17693
- newEditorProps = _extends({}, defaultProps, newEditorProps);
17694
- let alignment = oldWidgetInfo.alignment;
17695
-
17696
- // Widgets that support multiple alignments will "lock in" the
17697
- // alignment to the alignment that would be listed first in the
17698
- // select box. If the widget only supports one alignment, the
17699
- // alignment value will likely just end up as "default".
17700
- if (alignment == null || alignment === "default") {
17701
- alignment = getSupportedAlignments(type)[0];
17702
- }
17703
- let widgetStatic = oldWidgetInfo.static;
17704
- if (widgetStatic == null) {
17705
- widgetStatic = DEFAULT_STATIC;
17706
- }
17707
- return _.extend({}, oldWidgetInfo, {
17708
- // maintain other info, like type
17709
- // After upgrading we guarantee that the version is up-to-date
17710
- version: latestVersion,
17711
- // Default graded to true (so null/undefined becomes true):
17712
- graded: oldWidgetInfo.graded != null ? oldWidgetInfo.graded : true,
17713
- alignment: alignment,
17714
- static: widgetStatic,
17715
- options: newEditorProps
17716
- });
17717
- };
17718
17623
  const getRendererPropsForWidgetInfo = (widgetInfo, strings, problemNum) => {
17719
17624
  const type = widgetInfo.type;
17720
17625
  const widgetExports = widgets[type];
@@ -17756,70 +17661,6 @@ const traverseChildWidgets$3 = (widgetInfo, traverseRenderer) => {
17756
17661
  return widgetInfo;
17757
17662
  };
17758
17663
 
17759
- /**
17760
- * Handling for the optional alignments for widgets
17761
- * See widget-container.jsx for details on how alignments are implemented.
17762
- */
17763
-
17764
- /**
17765
- * Returns the list of supported alignments for the given (string) widget
17766
- * type. This is used primarily at editing time to display the choices
17767
- * for the user.
17768
- *
17769
- * Supported alignments are given as an array of strings in the exports of
17770
- * a widget's module.
17771
- */
17772
- const getSupportedAlignments = type => {
17773
- const widgetExport = widgets[type];
17774
- // @ts-expect-error - TS2322 - Type 'string[] | readonly Alignment[]' is not assignable to type 'readonly Alignment[]'.
17775
- return widgetExport && widgetExport.supportedAlignments || DEFAULT_SUPPORTED_ALIGNMENTS;
17776
- };
17777
-
17778
- /**
17779
- * For the given (string) widget type, determine the default alignment for
17780
- * the widget. This is used at rendering time to go from "default" alignment
17781
- * to the actual alignment displayed on the screen.
17782
- *
17783
- * The default alignment is given either as a string (called
17784
- * `defaultAlignment`) or a function (called `getDefaultAlignment`) on
17785
- * the exports of a widget's module.
17786
- */
17787
- const getDefaultAlignment = type => {
17788
- const widgetExports = widgets[type];
17789
- let alignment;
17790
- if (!widgetExports) {
17791
- return DEFAULT_ALIGNMENT$1;
17792
- }
17793
- if (widgetExports.getDefaultAlignment) {
17794
- alignment = widgetExports.getDefaultAlignment();
17795
- } else {
17796
- alignment = widgetExports.defaultAlignment;
17797
- }
17798
- return alignment || DEFAULT_ALIGNMENT$1;
17799
- };
17800
- const validAlignments = ["block", "inline-block", "inline", "float-left", "float-right", "full-width"];
17801
-
17802
- /**
17803
- * Used at startup to fail fast if an alignment given by a widget is
17804
- * invalid.
17805
- */
17806
- // TODO(alex): Change this to run as a testcase (vs. being run at runtime)
17807
- // TODO(LP-10707): I think this can be completely removed because our TypeScript types
17808
- // enforce this!
17809
- const validateAlignments = () => {
17810
- _.each(widgets, function (widgetInfo) {
17811
- if (widgetInfo.defaultAlignment && !_.contains(validAlignments, widgetInfo.defaultAlignment)) {
17812
- throw new PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid defaultAlignment value: " + widgetInfo.defaultAlignment, Errors.InvalidInput);
17813
- }
17814
- if (widgetInfo.supportedAlignments) {
17815
- const unknownAlignments = _.difference(widgetInfo.supportedAlignments, validAlignments);
17816
- if (unknownAlignments.length) {
17817
- throw new PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid value for supportedAlignments: " + unknownAlignments.join(" "), Errors.InvalidInput);
17818
- }
17819
- }
17820
- });
17821
- };
17822
-
17823
17664
  /**
17824
17665
  * Handling for static mode for widgets that support it.
17825
17666
  */
@@ -17873,8 +17714,6 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
17873
17714
  replaceDeprecatedEditors: replaceDeprecatedEditors,
17874
17715
  getWidget: getWidget,
17875
17716
  getWidgetExport: getWidgetExport,
17876
- getWidgetValidator: getWidgetValidator,
17877
- getWidgetScorer: getWidgetScorer,
17878
17717
  getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
17879
17718
  getEditor: getEditor,
17880
17719
  getTransform: getTransform,
@@ -17883,265 +17722,14 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
17883
17722
  getPublicWidgets: getPublicWidgets,
17884
17723
  isAccessible: isAccessible,
17885
17724
  getAllWidgetTypes: getAllWidgetTypes,
17886
- upgradeWidgetInfoToLatestVersion: upgradeWidgetInfoToLatestVersion,
17887
17725
  getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
17888
17726
  traverseChildWidgets: traverseChildWidgets$3,
17889
- getSupportedAlignments: getSupportedAlignments,
17890
- getDefaultAlignment: getDefaultAlignment,
17891
- validateAlignments: validateAlignments,
17892
17727
  supportsStaticMode: supportsStaticMode,
17893
17728
  getStaticTransform: getStaticTransform,
17894
17729
  getTracking: getTracking,
17895
17730
  isLintable: isLintable
17896
17731
  });
17897
17732
 
17898
- function getUpgradedWidgetOptions(oldWidgetOptions) {
17899
- return mapObject(oldWidgetOptions, (widgetInfo, widgetId) => {
17900
- if (!widgetInfo.type || !widgetInfo.alignment) {
17901
- const newValues = {};
17902
- if (!widgetInfo.type) {
17903
- // TODO: why does widget have no type?
17904
- // We don't want to derive type from widget ID
17905
- // see: LEMS-1845
17906
- newValues.type = widgetId.split(" ")[0];
17907
- }
17908
- if (!widgetInfo.alignment) {
17909
- newValues.alignment = "default";
17910
- }
17911
- widgetInfo = _extends({}, widgetInfo, newValues);
17912
- }
17913
- // TODO(LEMS-2656): remove TS suppression
17914
- return upgradeWidgetInfoToLatestVersion(widgetInfo);
17915
- });
17916
- }
17917
-
17918
- /**
17919
- * Checks the given user input to see if any answerable widgets have not been
17920
- * "filled in" (ie. if they're empty). Another way to think about this
17921
- * function is that its a check to see if we can score the provided input.
17922
- */
17923
- function emptyWidgetsFunctional(widgets,
17924
- // This is a port of old code, I'm not sure why
17925
- // we need widgetIds vs the keys of the widgets object
17926
- widgetIds, userInputMap, strings, locale) {
17927
- return widgetIds.filter(id => {
17928
- const widget = widgets[id];
17929
- if (!widget || widget.static === true) {
17930
- // Static widgets shouldn't count as empty
17931
- return false;
17932
- }
17933
- const validator = getWidgetValidator(widget.type);
17934
- const userInput = userInputMap[id];
17935
- const validationData = widget.options;
17936
- const score = validator == null ? void 0 : validator(userInput, validationData, strings, locale);
17937
- if (score) {
17938
- return scoreIsEmpty(score);
17939
- }
17940
- });
17941
- }
17942
-
17943
- // TODO: combine scorePerseusItem with scoreWidgetsFunctional
17944
- // once scorePerseusItem is the only one calling scoreWidgetsFunctional
17945
- function scorePerseusItem(perseusRenderData, userInputMap,
17946
- // TODO(LEMS-2461,LEMS-2391): these probably
17947
- // need to be removed before we move this to the server
17948
- strings, locale) {
17949
- // There seems to be a chance that PerseusRenderer.widgets might include
17950
- // widget data for widgets that are not in PerseusRenderer.content,
17951
- // so this checks that the widgets are being used before scoring them
17952
- const usedWidgetIds = getWidgetIdsFromContent(perseusRenderData.content);
17953
- const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
17954
- return flattenScores(scores);
17955
- }
17956
- function scoreWidgetsFunctional(widgets,
17957
- // This is a port of old code, I'm not sure why
17958
- // we need widgetIds vs the keys of the widgets object
17959
- widgetIds, userInputMap, strings, locale) {
17960
- const upgradedWidgets = getUpgradedWidgetOptions(widgets);
17961
- const gradedWidgetIds = widgetIds.filter(id => {
17962
- const props = upgradedWidgets[id];
17963
- const widgetIsGraded = (props == null ? void 0 : props.graded) == null || props.graded;
17964
- const widgetIsStatic = !!(props != null && props.static);
17965
- // Ungraded widgets or widgets set to static shouldn't be graded.
17966
- return widgetIsGraded && !widgetIsStatic;
17967
- });
17968
- const widgetScores = {};
17969
- gradedWidgetIds.forEach(id => {
17970
- var _validator;
17971
- const widget = upgradedWidgets[id];
17972
- if (!widget) {
17973
- return;
17974
- }
17975
- const userInput = userInputMap[id];
17976
- const validator = getWidgetValidator(widget.type);
17977
- const scorer = getWidgetScorer(widget.type);
17978
-
17979
- // We do validation (empty checks) first and then scoring. If
17980
- // validation fails, it's result is itself a PerseusScore.
17981
- const score = (_validator = validator == null ? void 0 : validator(userInput, widget.options, strings, locale)) != null ? _validator : scorer == null ? void 0 : scorer(userInput, widget.options, strings, locale);
17982
- if (score != null) {
17983
- widgetScores[id] = score;
17984
- }
17985
- });
17986
- return widgetScores;
17987
- }
17988
-
17989
- /**
17990
- * TranslationLinter asynchronously calls Khan Academy's poentry linter.
17991
- *
17992
- * The poentry linter checks for issues that prevent content from being
17993
- * translated. This linter is KA-specific and so TranslationLinter does not do
17994
- * anything in non-KA environments.
17995
- */
17996
-
17997
- // A LintCB is called once the linter has been run.
17998
-
17999
- // msec to wait before we actually call the linter after receiving the
18000
- // last call. The timer is reset if the lint is called during the wait.
18001
- const DEBOUNCE_TIMEOUT = 1000;
18002
- class TranslationLinter {
18003
- constructor() {
18004
- // To avoid linting when nothing has changed.
18005
- this.previousContent = null;
18006
- /**
18007
- * Runs the poentry linter on the given perseus string asynchronously.
18008
- *
18009
- * It doesn't do anything unless we're in a KA environment.
18010
- *
18011
- * onLintErrorsGenerated is called even if there are no errors. It is not
18012
- * called if perseusStr is unchanged from the last call.
18013
- *
18014
- * Linting is relatively expensive -- so we debounce linting each instance
18015
- * for some amount of time.
18016
- */
18017
- this.runLinter = debounce$1((perseusStr, onLintErrorsGenerated) => {
18018
- // TODO(FEI-5003): Do we still need to check for 'KA'?
18019
- if (typeof KA === "undefined") {
18020
- return;
18021
- }
18022
- if (perseusStr === this.previousContent) {
18023
- return;
18024
- }
18025
- this.previousContent = perseusStr;
18026
- if (perseusStr === "") {
18027
- onLintErrorsGenerated([]);
18028
- return;
18029
- }
18030
- fetch("/api/internal/translations/lint_poentry?preview=1&lang=en", {
18031
- headers: {
18032
- "Content-Type": "application/json"
18033
- },
18034
- body: JSON.stringify({
18035
- // The poentry linter verifies that the translation from
18036
- // the original text to the translated text is sane. We
18037
- // want to ensure that the translation from English to
18038
- // English is sane, so msgid === mstr.
18039
- msgid: perseusStr,
18040
- // msgid is the original text
18041
- msgstr: perseusStr,
18042
- // msgstr is the translated text
18043
- format: "perseus_text",
18044
- filename: ""
18045
- }),
18046
- method: "POST"
18047
- }).then(response => {
18048
- if (response.status >= 400) {
18049
- return {
18050
- status: "error",
18051
- message: "Could not run i18n linter."
18052
- };
18053
- }
18054
- return response.json();
18055
- }, rejection => {
18056
- return {
18057
- status: "error",
18058
- message: "Could not run i18n linter."
18059
- };
18060
- }).then(json => {
18061
- if (json.status === "error") {
18062
- onLintErrorsGenerated(["Some part of this text makes it untranslatable. " + "The specific message from the i18n linter was: " + json.message.replace(/\n/g, " ")]);
18063
- } else {
18064
- onLintErrorsGenerated([]);
18065
- }
18066
- });
18067
- }, DEBOUNCE_TIMEOUT);
18068
- }
18069
- /**
18070
- * Applies an array of errors generated by linters without position
18071
- * information (TranslationLinter and the legacy getSaveWarnings()) to the
18072
- * top of a Perseus tree.
18073
- */
18074
- applyLintErrors(parsedMarkdown, translationLintErrors) {
18075
- // These lint errors do not have position data associated with
18076
- // them, so we just plop them at the top.
18077
- if (translationLintErrors.length) {
18078
- const errorText = translationLintErrors.join("\n\n");
18079
- parsedMarkdown.unshift({
18080
- content: {
18081
- type: "text",
18082
- content: ""
18083
- },
18084
- insideTable: false,
18085
- message: errorText,
18086
- ruleName: "legacy-error",
18087
- severity: Rule.Severity.ERROR,
18088
- type: "lint"
18089
- });
18090
- }
18091
- }
18092
- }
18093
-
18094
- /**
18095
- * Preprocess TeX code to convert things that MathJax doesn't know how to handle
18096
- * to things is does.
18097
- */
18098
- var preprocessTex = (texCode => texCode
18099
- // Replace uses of \begin{align}...\end{align} which KaTeX doesn't
18100
- // support (yet) with \begin{aligned}...\end{aligned} which renders
18101
- // the same is supported by KaTeX. It does the same for align*.
18102
- // TODO(kevinb) update content to use aligned instead of align.
18103
- // TODO(LEMS-1608) Remove this replacement as MathJax supports the
18104
- // "align" macro correctly (and, in fact, it is not synonymous with
18105
- // "aligned").
18106
- .replace(/\{align[*]?\}/g, "{aligned}")
18107
- // Replace non-breaking spaces with regular spaces.
18108
- .replace(/[\u00a0]/g, " "));
18109
-
18110
- const {
18111
- interactiveSizes
18112
- } = constants;
18113
-
18114
- // Note: these size cutoffs represent content-width cutoffs as specified in
18115
- // http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
18116
- // TODO(benkomalo): these values aren't used in JS outside of this file, but
18117
- // are coupled to the values in styles/styles.less - DRY it up at some point
18118
- const smMax = articleMaxWidthTableInPx;
18119
- const mdMax = articleMaxWidthInPx;
18120
- const containerSizeClass = {
18121
- SMALL: "small",
18122
- MEDIUM: "medium",
18123
- LARGE: "large",
18124
- XLARGE: "xlarge"
18125
- };
18126
- const getClassFromWidth = width => {
18127
- if (!width) {
18128
- return containerSizeClass.MEDIUM;
18129
- }
18130
- if (width <= smMax) {
18131
- return containerSizeClass.SMALL;
18132
- }
18133
- if (width <= mdMax) {
18134
- return containerSizeClass.MEDIUM;
18135
- }
18136
- return containerSizeClass.LARGE;
18137
- };
18138
- const getInteractiveBoxFromSizeClass = sizeClass => {
18139
- if (sizeClass === containerSizeClass.SMALL) {
18140
- return [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall];
18141
- }
18142
- return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
18143
- };
18144
-
18145
17733
  class WidgetContainer extends React.Component {
18146
17734
  constructor(...args) {
18147
17735
  super(...args);
@@ -18214,7 +17802,7 @@ class WidgetContainer extends React.Component {
18214
17802
  }
18215
17803
  let alignment = this.state.widgetProps.alignment;
18216
17804
  if (alignment === "default") {
18217
- alignment = getDefaultAlignment(type);
17805
+ alignment = CoreWidgetRegistry.getDefaultAlignment(type);
18218
17806
  }
18219
17807
  className += " widget-" + alignment;
18220
17808
  const apiOptions = this.state.widgetProps.apiOptions;
@@ -18222,6 +17810,7 @@ class WidgetContainer extends React.Component {
18222
17810
  // Hack to prevent interaction with static widgets: we overlay a big
18223
17811
  // div on top of the widget and overflow: hidden the container.
18224
17812
  // Ideally widgets themselves should know how to prevent interaction.
17813
+ // UPDATE HTML5: `inert` on the underlying div would be better
18225
17814
  const isStatic = this.state.widgetProps.static || apiOptions.readOnly;
18226
17815
  const staticContainerStyles = {
18227
17816
  position: "relative",
@@ -18233,7 +17822,7 @@ class WidgetContainer extends React.Component {
18233
17822
  position: "absolute",
18234
17823
  top: 0,
18235
17824
  left: 0,
18236
- zIndex: zIndexInteractiveComponent$1
17825
+ zIndex: 3
18237
17826
  };
18238
17827
 
18239
17828
  // Some widgets may include strings of markdown that we may
@@ -19417,7 +19006,7 @@ class Renderer extends React.Component {
19417
19006
  * empty if the graph is in the starting state.
19418
19007
  */
19419
19008
  emptyWidgets() {
19420
- return emptyWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
19009
+ return emptyWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.context.locale);
19421
19010
  }
19422
19011
  /**
19423
19012
  * Returns an array of the widget `.getUserInput()` results
@@ -19477,7 +19066,7 @@ class Renderer extends React.Component {
19477
19066
  * @deprecated use scorePerseusItem
19478
19067
  */
19479
19068
  score() {
19480
- const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
19069
+ const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.context.locale);
19481
19070
  const combinedScore = flattenScores(scores);
19482
19071
  return combinedScore;
19483
19072
  }
@@ -20017,7 +19606,7 @@ class InputWithExamples extends React.Component {
20017
19606
  };
20018
19607
  this._getInputClassName = () => {
20019
19608
  // Otherwise, we need to add these INPUT and FOCUSED tags here.
20020
- let className = ClassNames.INPUT + " " + ClassNames.INTERACTIVE;
19609
+ let className = ClassNames.INPUT;
20021
19610
  if (this.state.focused) {
20022
19611
  className += " " + ClassNames.FOCUSED;
20023
19612
  }
@@ -20429,14 +20018,10 @@ const propTransform$3 = widgetOptions => {
20429
20018
  var InputNumber$1 = {
20430
20019
  name: "input-number",
20431
20020
  displayName: "Input number (deprecated - use numeric input instead)",
20432
- defaultAlignment: "inline-block",
20433
20021
  hidden: true,
20434
20022
  widget: InputNumber,
20435
20023
  transform: propTransform$3,
20436
20024
  isLintable: true,
20437
- // TODO(LEMS-2656): remove TS suppression
20438
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInputNumberUserInput'.
20439
- scorer: scoreInputNumber,
20440
20025
  getOneCorrectAnswerFromRubric(rubric) {
20441
20026
  if (rubric.value == null) {
20442
20027
  return;
@@ -20693,14 +20278,11 @@ const propsTransform = function propsTransform(widgetOptions) {
20693
20278
  var NumericInput$1 = {
20694
20279
  name: "numeric-input",
20695
20280
  displayName: "Numeric input",
20696
- defaultAlignment: "inline-block",
20697
20281
  accessible: true,
20698
20282
  widget: NumericInput,
20699
20283
  transform: propsTransform,
20700
20284
  isLintable: true,
20701
- // TODO(LEMS-2656): remove TS suppression
20702
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumericInputUserInput'.
20703
- scorer: scoreNumericInput,
20285
+ getPublicWidgetOptions: getNumericInputPublicWidgetOptions,
20704
20286
  // TODO(LEMS-2656): remove TS suppression
20705
20287
  // @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusNumericInputRubric'
20706
20288
  getOneCorrectAnswerFromRubric(rubric) {
@@ -20761,23 +20343,6 @@ const getPromptJSON$p = renderProps => {
20761
20343
  };
20762
20344
  };
20763
20345
 
20764
- /**
20765
- * Several widgets don't have "right"/"wrong" scoring logic,
20766
- * so this just says to move on past those widgets
20767
- *
20768
- * TODO(LEMS-2543) widgets that use this probably shouldn't have any
20769
- * scoring logic and the thing scoring an exercise
20770
- * should just know to skip these
20771
- */
20772
- function scoreNoop(points = 0) {
20773
- return {
20774
- type: "points",
20775
- earned: points,
20776
- total: points,
20777
- message: null
20778
- };
20779
- }
20780
-
20781
20346
  /**
20782
20347
  * Utility functions for manipulating ranges of highlightable content.
20783
20348
  */
@@ -22953,8 +22518,7 @@ var Passage$1 = {
22953
22518
  transform: editorProps => {
22954
22519
  return _.pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
22955
22520
  },
22956
- isLintable: true,
22957
- scorer: () => scoreNoop()
22521
+ isLintable: true
22958
22522
  };
22959
22523
 
22960
22524
  /**
@@ -23076,16 +22640,13 @@ var PassageRef$1 = {
23076
22640
  name: "passage-ref",
23077
22641
  displayName: "PassageRef (SAT only)",
23078
22642
  hidden: true,
23079
- defaultAlignment: "inline",
23080
22643
  widget: PassageRef,
23081
22644
  transform: widgetOptions => ({
23082
22645
  passageNumber: widgetOptions.passageNumber,
23083
22646
  referenceNumber: widgetOptions.referenceNumber,
23084
22647
  summaryText: widgetOptions.summaryText
23085
22648
  }),
23086
- version: passageRefLogic.version,
23087
- // TODO: things that aren't interactive shouldn't need scoring functions
23088
- scorer: () => scoreNoop()
22649
+ version: passageRefLogic.version
23089
22650
  };
23090
22651
 
23091
22652
  /**
@@ -23127,109 +22688,6 @@ var mediaQueries = {
23127
22688
  xlOrLarger: `@media screen and (min-width: ${pureXlMin})`
23128
22689
  };
23129
22690
 
23130
- const {
23131
- zIndexAboveScratchpad,
23132
- zIndexInteractiveComponent,
23133
- radioBorderColor,
23134
- checkedColor,
23135
- circleSize,
23136
- radioMarginWidth
23137
- } = constants;
23138
- var sharedStyles = StyleSheet.create({
23139
- perseusInteractive: {
23140
- zIndex: zIndexInteractiveComponent,
23141
- position: "relative"
23142
- },
23143
- aboveScratchpad: {
23144
- position: "relative",
23145
- zIndex: zIndexAboveScratchpad
23146
- },
23147
- blankBackground: {
23148
- // TODO(emily): Use WB colors?
23149
- backgroundColor: "#FDFDFD"
23150
- },
23151
- perseusSrOnly: {
23152
- border: 0,
23153
- clip: "rect(0,0,0,0)",
23154
- height: 1,
23155
- margin: -1,
23156
- overflow: "hidden",
23157
- padding: 0,
23158
- position: "absolute",
23159
- width: 1
23160
- },
23161
- responsiveLabel: {
23162
- // NOTE(charlie): The values used here should be kept in sync with the
23163
- // caption text sizing in Perseus' styles.less.
23164
- // TODO(charlie): Migrate the captions over to using this style.
23165
- [mediaQueries.smOrSmaller]: {
23166
- fontSize: 14,
23167
- lineHeight: 1.3
23168
- },
23169
- [mediaQueries.md]: {
23170
- fontSize: 17,
23171
- lineHeight: 1.4
23172
- },
23173
- [mediaQueries.lgOrLarger]: {
23174
- fontSize: 20,
23175
- lineHeight: 1.4
23176
- }
23177
- },
23178
- responsiveInput: {
23179
- display: "inline-block",
23180
- WebkitAppearance: "none",
23181
- appearance: "none",
23182
- "::-ms-check": {
23183
- display: "none"
23184
- },
23185
- backgroundColor: "#fff",
23186
- border: "2px solid #fff",
23187
- boxShadow: `0 0px 0px 1px ${radioBorderColor}`,
23188
- outline: "none",
23189
- boxSizing: "border-box",
23190
- flexShrink: 0,
23191
- marginBottom: 1,
23192
- marginLeft: 1,
23193
- marginRight: 1,
23194
- marginTop: 1,
23195
- height: circleSize - 2,
23196
- width: circleSize - 2
23197
- },
23198
- responsiveRadioInput: {
23199
- borderRadius: "50%",
23200
- ":checked": {
23201
- backgroundColor: checkedColor,
23202
- border: "none",
23203
- borderRadius: "50%",
23204
- boxShadow: `inset 0px 0px 0px 2px white, ` + `0 0px 0px 2px ${checkedColor}`,
23205
- marginTop: radioMarginWidth,
23206
- marginBottom: radioMarginWidth,
23207
- marginLeft: radioMarginWidth,
23208
- marginRight: radioMarginWidth,
23209
- height: circleSize - 2 * radioMarginWidth,
23210
- width: circleSize - 2 * radioMarginWidth
23211
- }
23212
- },
23213
- responsiveRadioInputActive: {
23214
- backgroundColor: "#fff",
23215
- border: "2px solid #fff",
23216
- borderRadius: "50%",
23217
- boxShadow: `0 0px 0px 2px ${checkedColor}`,
23218
- marginTop: radioMarginWidth,
23219
- marginBottom: radioMarginWidth,
23220
- marginLeft: radioMarginWidth,
23221
- marginRight: radioMarginWidth,
23222
- height: circleSize - 2 * radioMarginWidth,
23223
- width: circleSize - 2 * radioMarginWidth,
23224
- ":checked": {
23225
- backgroundColor: "#fff"
23226
- }
23227
- },
23228
- disableTextSelection: {
23229
- userSelect: "none"
23230
- }
23231
- });
23232
-
23233
22691
  /**
23234
22692
  * A small collection of useful scrolling utility functions.
23235
22693
  */
@@ -24288,7 +23746,7 @@ const BaseRadio = function BaseRadio({
24288
23746
  const aphroditeClassName = checked => {
24289
23747
  // Whether or not to show correctness borders
24290
23748
  // for this choice and the next choice.
24291
- return css(sharedStyles.aboveScratchpad, styles$o.item, styles$o.responsiveItem, checked && styles$o.selectedItem, checked && choice.highlighted && styles$o.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$o.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$o.nextHighlighted);
23749
+ return css(styles$o.item, styles$o.responsiveItem, checked && styles$o.selectedItem, checked && choice.highlighted && styles$o.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$o.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$o.nextHighlighted);
24292
23750
  };
24293
23751
 
24294
23752
  // HACK(abdulrahman): Preloads the selection-state
@@ -24877,18 +24335,105 @@ var Radio = {
24877
24335
  staticTransform: transform,
24878
24336
  version: radioLogic.version,
24879
24337
  propUpgrades: radioLogic.widgetOptionsUpgrades,
24880
- isLintable: true,
24881
- // TODO(LEMS-2656): remove TS suppression
24882
- // @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
24883
- scorer: scoreRadio,
24884
- // TODO(LEMS-2656): remove TS suppression
24885
- // @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
24886
- validator: validateRadio
24338
+ isLintable: true
24887
24339
  };
24888
24340
 
24889
24341
  /* eslint-disable import/no-named-as-default */
24890
24342
  var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
24891
24343
 
24344
+ const {
24345
+ radioBorderColor,
24346
+ checkedColor,
24347
+ circleSize,
24348
+ radioMarginWidth
24349
+ } = constants;
24350
+ var sharedStyles = StyleSheet.create({
24351
+ blankBackground: {
24352
+ // TODO(emily): Use WB colors?
24353
+ backgroundColor: "#FDFDFD"
24354
+ },
24355
+ perseusSrOnly: {
24356
+ border: 0,
24357
+ clip: "rect(0,0,0,0)",
24358
+ height: 1,
24359
+ margin: -1,
24360
+ overflow: "hidden",
24361
+ padding: 0,
24362
+ position: "absolute",
24363
+ width: 1
24364
+ },
24365
+ responsiveLabel: {
24366
+ // NOTE(charlie): The values used here should be kept in sync with the
24367
+ // caption text sizing in Perseus' styles.less.
24368
+ // TODO(charlie): Migrate the captions over to using this style.
24369
+ [mediaQueries.smOrSmaller]: {
24370
+ fontSize: 14,
24371
+ lineHeight: 1.3
24372
+ },
24373
+ [mediaQueries.md]: {
24374
+ fontSize: 17,
24375
+ lineHeight: 1.4
24376
+ },
24377
+ [mediaQueries.lgOrLarger]: {
24378
+ fontSize: 20,
24379
+ lineHeight: 1.4
24380
+ }
24381
+ },
24382
+ responsiveInput: {
24383
+ display: "inline-block",
24384
+ WebkitAppearance: "none",
24385
+ appearance: "none",
24386
+ "::-ms-check": {
24387
+ display: "none"
24388
+ },
24389
+ backgroundColor: "#fff",
24390
+ border: "2px solid #fff",
24391
+ boxShadow: `0 0px 0px 1px ${radioBorderColor}`,
24392
+ outline: "none",
24393
+ boxSizing: "border-box",
24394
+ flexShrink: 0,
24395
+ marginBottom: 1,
24396
+ marginLeft: 1,
24397
+ marginRight: 1,
24398
+ marginTop: 1,
24399
+ height: circleSize - 2,
24400
+ width: circleSize - 2
24401
+ },
24402
+ responsiveRadioInput: {
24403
+ borderRadius: "50%",
24404
+ ":checked": {
24405
+ backgroundColor: checkedColor,
24406
+ border: "none",
24407
+ borderRadius: "50%",
24408
+ boxShadow: `inset 0px 0px 0px 2px white, ` + `0 0px 0px 2px ${checkedColor}`,
24409
+ marginTop: radioMarginWidth,
24410
+ marginBottom: radioMarginWidth,
24411
+ marginLeft: radioMarginWidth,
24412
+ marginRight: radioMarginWidth,
24413
+ height: circleSize - 2 * radioMarginWidth,
24414
+ width: circleSize - 2 * radioMarginWidth
24415
+ }
24416
+ },
24417
+ responsiveRadioInputActive: {
24418
+ backgroundColor: "#fff",
24419
+ border: "2px solid #fff",
24420
+ borderRadius: "50%",
24421
+ boxShadow: `0 0px 0px 2px ${checkedColor}`,
24422
+ marginTop: radioMarginWidth,
24423
+ marginBottom: radioMarginWidth,
24424
+ marginLeft: radioMarginWidth,
24425
+ marginRight: radioMarginWidth,
24426
+ height: circleSize - 2 * radioMarginWidth,
24427
+ width: circleSize - 2 * radioMarginWidth,
24428
+ ":checked": {
24429
+ backgroundColor: "#fff"
24430
+ }
24431
+ },
24432
+ disableTextSelection: {
24433
+ userSelect: "none"
24434
+ }
24435
+ });
24436
+
24892
24437
  const getPromptJSON$n = (renderProps, userInput) => {
24893
24438
  return {
24894
24439
  type: "categorizer",
@@ -24979,7 +24524,6 @@ class Categorizer extends React.Component {
24979
24524
  className: "category " + css(styles$n.cell),
24980
24525
  key: catNum
24981
24526
  }, /*#__PURE__*/React.createElement("div", {
24982
- className: ClassNames.INTERACTIVE,
24983
24527
  role: "button",
24984
24528
  "aria-label": catName,
24985
24529
  onClick: () => this.onChange(itemNum, catNum)
@@ -25078,12 +24622,6 @@ var Categorizer$1 = {
25078
24622
  return _.pick(editorProps, "items", "categories", "values", "randomizeItems");
25079
24623
  },
25080
24624
  isLintable: true,
25081
- // TODO(LEMS-2656): remove TS suppression
25082
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
25083
- scorer: scoreCategorizer,
25084
- // TODO(LEMS-2656): remove TS suppression
25085
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
25086
- validator: validateCategorizer,
25087
24625
  getPublicWidgetOptions: getCategorizerPublicWidgetOptions
25088
24626
  };
25089
24627
 
@@ -25289,12 +24827,9 @@ const styles$m = StyleSheet.create({
25289
24827
  var CSProgram$1 = {
25290
24828
  name: "cs-program",
25291
24829
  displayName: "CS Program",
25292
- supportedAlignments: ["block", "full-width"],
25293
24830
  widget: CSProgram,
25294
24831
  hidden: true,
25295
- // TODO(LEMS-2656): remove TS suppression
25296
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
25297
- scorer: scoreCSProgram
24832
+ getPublicWidgetOptions: getCSProgramPublicWidgetOptions
25298
24833
  };
25299
24834
 
25300
24835
  const getPromptJSON$l = renderProps => {
@@ -25367,11 +24902,8 @@ var Definition$1 = {
25367
24902
  name: "definition",
25368
24903
  displayName: "Definition",
25369
24904
  accessible: true,
25370
- defaultAlignment: "inline",
25371
24905
  widget: Definition,
25372
- transform: x => x,
25373
- // TODO: things that aren't interactive shouldn't need scoring functions
25374
- scorer: () => scoreNoop()
24906
+ transform: x => x
25375
24907
  };
25376
24908
 
25377
24909
  // There are no required props, but this component can receive props from any
@@ -25401,9 +24933,7 @@ var DeprecatedStandin$1 = {
25401
24933
  name: "deprecated-standin",
25402
24934
  displayName: "Deprecated Standin",
25403
24935
  widget: DeprecatedStandin,
25404
- hidden: true,
25405
- // TODO: things that aren't interactive shouldn't need scoring functions
25406
- scorer: () => scoreNoop(1)
24936
+ hidden: true
25407
24937
  };
25408
24938
 
25409
24939
  const getPromptJSON$k = (renderProps, userInput) => {
@@ -25515,16 +25045,10 @@ const optionsTransform = widgetOptions => {
25515
25045
  var Dropdown$1 = {
25516
25046
  name: "dropdown",
25517
25047
  displayName: "Drop down",
25518
- defaultAlignment: "inline-block",
25519
25048
  accessible: true,
25520
25049
  widget: Dropdown,
25521
25050
  transform: optionsTransform,
25522
- // TODO(LEMS-2656): remove TS suppression
25523
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
25524
- scorer: scoreDropdown,
25525
- // TODO(LEMS-2656): remove TS suppression
25526
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
25527
- validator: validateDropdown
25051
+ getPublicWidgetOptions: getDropdownPublicWidgetOptions
25528
25052
  };
25529
25053
 
25530
25054
  const getPromptJSON$j = renderProps => {
@@ -25679,12 +25203,9 @@ var Explanation$1 = {
25679
25203
  name: "explanation",
25680
25204
  displayName: "Explanation",
25681
25205
  accessible: true,
25682
- defaultAlignment: "inline",
25683
25206
  widget: Explanation,
25684
25207
  transform: _.identity,
25685
- isLintable: true,
25686
- // TODO: things that aren't interactive shouldn't need scoring functions
25687
- scorer: () => scoreNoop()
25208
+ isLintable: true
25688
25209
  };
25689
25210
 
25690
25211
  const getPromptJSON$i = (title, rendererJSON, hintRendererJSON) => {
@@ -26784,7 +26305,7 @@ class FunctionGrapher extends React.Component {
26784
26305
  boxSizing: "initial"
26785
26306
  }
26786
26307
  }, /*#__PURE__*/React.createElement("div", {
26787
- className: "graphie-container above-scratchpad",
26308
+ className: "graphie-container blank-background",
26788
26309
  style: {
26789
26310
  width: box[0],
26790
26311
  height: box[1]
@@ -26923,8 +26444,7 @@ class Grapher extends React.Component {
26923
26444
  const coords = this.props.plot.coords;
26924
26445
  const asymptote = this.props.plot.asymptote;
26925
26446
  const typeSelector = /*#__PURE__*/React.createElement("div", {
26926
- style: typeSelectorStyle,
26927
- className: "above-scratchpad"
26447
+ style: typeSelectorStyle
26928
26448
  }, /*#__PURE__*/React.createElement(ButtonGroup, {
26929
26449
  value: type,
26930
26450
  allowEmpty: true,
@@ -27002,10 +26522,7 @@ var Grapher$1 = {
27002
26522
  hidden: true,
27003
26523
  widget: Grapher,
27004
26524
  transform: propTransform$2,
27005
- staticTransform: staticTransform$4,
27006
- // TODO(LEMS-2656): remove TS suppression
27007
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGrapherUserInput'.
27008
- scorer: scoreGrapher
26525
+ staticTransform: staticTransform$4
27009
26526
  };
27010
26527
 
27011
26528
  const getPromptJSON$f = rendererJSON => {
@@ -27021,24 +26538,6 @@ const getPromptJSON$f = rendererJSON => {
27021
26538
  });
27022
26539
  };
27023
26540
 
27024
- // The `group` widget is basically a widget hosting a full Perseus system in
27025
- // it. As such, scoring a group means scoring all widgets it contains.
27026
- function scoreGroup(userInput, rubric, strings, locale) {
27027
- const scores = scoreWidgetsFunctional(rubric.widgets, Object.keys(rubric.widgets), userInput, strings, locale);
27028
- return flattenScores(scores);
27029
- }
27030
-
27031
- function validateGroup(userInput, validationData, strings, locale) {
27032
- const emptyWidgets = emptyWidgetsFunctional(validationData.widgets, Object.keys(validationData.widgets), userInput, strings, locale);
27033
- if (emptyWidgets.length === 0) {
27034
- return null;
27035
- }
27036
- return {
27037
- type: "invalid",
27038
- message: null
27039
- };
27040
- }
27041
-
27042
26541
  // exports has no 'transform'
27043
26542
 
27044
26543
  class Group extends React.Component {
@@ -27193,12 +26692,6 @@ var Group$1 = {
27193
26692
  displayName: "Group (SAT only)",
27194
26693
  widget: Group,
27195
26694
  traverseChildWidgets: traverseChildWidgets,
27196
- // TODO(LEMS-2656): remove TS suppression
27197
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
27198
- scorer: scoreGroup,
27199
- // TODO(LEMS-2656): remove TS suppression
27200
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
27201
- validator: validateGroup,
27202
26695
  hidden: true,
27203
26696
  isLintable: true
27204
26697
  };
@@ -27334,10 +26827,7 @@ var Iframe$1 = {
27334
26827
  displayName: "Iframe (deprecated)",
27335
26828
  widget: Iframe,
27336
26829
  // Let's not expose it to all content creators yet
27337
- hidden: true,
27338
- // TODO(LEMS-2656): remove TS suppression
27339
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusIframeUserInput'.
27340
- scorer: scoreIframe
26830
+ hidden: true
27341
26831
  };
27342
26832
 
27343
26833
  const getPromptJSON$d = renderProps => {
@@ -27359,8 +26849,6 @@ const defaultBackgroundImage$2 = {
27359
26849
  width: 0,
27360
26850
  height: 0
27361
26851
  };
27362
- const editorAlignments = ["block", "full-width"];
27363
- const DEFAULT_ALIGNMENT = "block";
27364
26852
 
27365
26853
  // there is no transform as part of exports
27366
26854
 
@@ -27509,7 +26997,7 @@ class ImageWidget extends React.Component {
27509
26997
  }
27510
26998
  ImageWidget.contextType = PerseusI18nContext;
27511
26999
  ImageWidget.defaultProps = {
27512
- alignment: DEFAULT_ALIGNMENT,
27000
+ alignment: "block",
27513
27001
  title: "",
27514
27002
  range: [defaultRange, defaultRange],
27515
27003
  box: [defaultBoxSize, defaultBoxSize],
@@ -27527,13 +27015,9 @@ var Image$1 = {
27527
27015
  const bgImage = widgetOptions.backgroundImage;
27528
27016
  return !(bgImage && bgImage.url && !widgetOptions.alt);
27529
27017
  },
27530
- defaultAlignment: DEFAULT_ALIGNMENT,
27531
- supportedAlignments: editorAlignments,
27532
27018
  displayName: "Image",
27533
27019
  widget: ImageWidget,
27534
- isLintable: true,
27535
- // TODO: things that aren't interactive shouldn't need scoring functions
27536
- scorer: () => scoreNoop()
27020
+ isLintable: true
27537
27021
  };
27538
27022
 
27539
27023
  const getPromptJSON$c = () => {
@@ -28131,9 +27615,7 @@ var Interactive = {
28131
27615
  displayName: "Interaction",
28132
27616
  widget: Interaction,
28133
27617
  transform: _.identity,
28134
- hidden: true,
28135
- // TODO: things that aren't interactive shouldn't need scoring functions
28136
- scorer: () => scoreNoop()
27618
+ hidden: true
28137
27619
  };
28138
27620
 
28139
27621
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
@@ -28402,7 +27884,7 @@ class Graph extends React.Component {
28402
27884
  image = null;
28403
27885
  }
28404
27886
  return /*#__PURE__*/React.createElement("div", {
28405
- className: "graphie-container above-scratchpad",
27887
+ className: "graphie-container blank-background",
28406
27888
  style: {
28407
27889
  width: this.props.box[0],
28408
27890
  height: this.props.box[1]
@@ -29037,7 +28519,8 @@ const tickLabelSize = 14;
29037
28519
  const YGridTick = ({
29038
28520
  y,
29039
28521
  range,
29040
- tickStep
28522
+ tickStep,
28523
+ showPi
29041
28524
  }) => {
29042
28525
  // If the graph requires out-of-bounds labels, we want to make sure to set the
29043
28526
  // coordinates to the edge of the visible range of the graph. Otherwise,
@@ -29073,6 +28556,7 @@ const YGridTick = ({
29073
28556
  // If the graph displays both the y and x axis lines within the graph, we want
29074
28557
  // to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
29075
28558
  const showLabel = shouldShowLabel(y, range, tickStep);
28559
+ const yLabel = showPi ? divideByAndShowPi(y) : y.toString();
29076
28560
  return /*#__PURE__*/React.createElement("g", {
29077
28561
  className: "tick",
29078
28562
  "aria-hidden": true
@@ -29090,11 +28574,12 @@ const YGridTick = ({
29090
28574
  textAnchor: "end",
29091
28575
  x: xPositionText,
29092
28576
  y: yPositionText
29093
- }, y.toString()));
28577
+ }, yLabel));
29094
28578
  };
29095
28579
  const XGridTick = ({
29096
28580
  x,
29097
- range
28581
+ range,
28582
+ showPi
29098
28583
  }) => {
29099
28584
  // If the graph requires out-of-bounds labels, we want to make sure to set the
29100
28585
  // coordinates to the edge of the visible range of the graph. Otherwise,
@@ -29133,6 +28618,7 @@ const XGridTick = ({
29133
28618
  // Apply the adjustments to the x and y positions for the text
29134
28619
  const xPositionText = xPosition + xAdjustment;
29135
28620
  const yPositionText = yPosition + yAdjustment;
28621
+ const xLabel = showPi ? divideByAndShowPi(x) : x.toString();
29136
28622
  return /*#__PURE__*/React.createElement("g", {
29137
28623
  className: "tick",
29138
28624
  "aria-hidden": true
@@ -29150,7 +28636,7 @@ const XGridTick = ({
29150
28636
  textAnchor: "middle",
29151
28637
  x: xPositionText,
29152
28638
  y: yPositionText
29153
- }, x.toString()));
28639
+ }, xLabel));
29154
28640
  };
29155
28641
 
29156
28642
  // Determines whether to show the label for the given tick
@@ -29197,6 +28683,22 @@ const countSignificantDecimals = number => {
29197
28683
  }
29198
28684
  return numStr.split(".")[1].length;
29199
28685
  };
28686
+
28687
+ // Show the given value as a multiple of pi (already assumed to be
28688
+ // a multiple of pi). Exported for testing
28689
+ function divideByAndShowPi(value) {
28690
+ const dividedValue = value / Math.PI;
28691
+ switch (dividedValue) {
28692
+ case 1:
28693
+ return "π";
28694
+ case -1:
28695
+ return "-π";
28696
+ case 0:
28697
+ return "0";
28698
+ default:
28699
+ return dividedValue + "π";
28700
+ }
28701
+ }
29200
28702
  const AxisTicks = () => {
29201
28703
  const {
29202
28704
  tickStep,
@@ -29219,6 +28721,10 @@ const AxisTicks = () => {
29219
28721
  key: `y-grid-tick-${y}`,
29220
28722
  range: range,
29221
28723
  tickStep: tickStep[Y]
28724
+ // Show the tick labels as multiples of pi
28725
+ // if the tick step is a multiple of pi.
28726
+ ,
28727
+ showPi: tickStep[Y] % Math.PI === 0
29222
28728
  });
29223
28729
  })), /*#__PURE__*/React.createElement("g", {
29224
28730
  className: "x-axis-ticks"
@@ -29227,6 +28733,10 @@ const AxisTicks = () => {
29227
28733
  x: x,
29228
28734
  key: `x-grid-tick-${x}`,
29229
28735
  range: range
28736
+ // Show the tick labels as multiples of pi
28737
+ // if the tick step is a multiple of pi.
28738
+ ,
28739
+ showPi: tickStep[X] % Math.PI === 0
29230
28740
  });
29231
28741
  })));
29232
28742
  };
@@ -29534,6 +29044,15 @@ const styles$d = StyleSheet.create({
29534
29044
  }
29535
29045
  });
29536
29046
 
29047
+ function isPiMultiple(value) {
29048
+ // Early return for integers that clearly aren't multiples
29049
+ // of pi to save some computation.
29050
+ if (Number.isInteger(value)) {
29051
+ return false;
29052
+ }
29053
+ return value % Math.PI === 0 || value % (Math.PI / 2) === 0 || value % (Math.PI / 3) === 0 || value % (Math.PI / 4) === 0 || value % (Math.PI / 6) === 0;
29054
+ }
29055
+
29537
29056
  const _excluded$1 = ["onFormatChange", "checkValidity", "useArrowKeys"];
29538
29057
  const {
29539
29058
  firstNumericalParse,
@@ -29671,6 +29190,19 @@ class NumberInput extends React.Component {
29671
29190
  this._getInput().value = toNumericString(val, format);
29672
29191
  };
29673
29192
  }
29193
+ componentDidMount() {
29194
+ // If the value is a multiple of pi, but it is not in the pi format,
29195
+ // then convert it to the pi format and show it as a multiple of pi.
29196
+ const value = this.getValue();
29197
+ if (this.props.allowPiTruncation && value !== null && value !== 0) {
29198
+ if (this.state.format !== "pi" && isPiMultiple(value)) {
29199
+ this._setValue(value / Math.PI, "pi");
29200
+ this.setState({
29201
+ format: "pi"
29202
+ });
29203
+ }
29204
+ }
29205
+ }
29674
29206
  componentDidUpdate(prevProps) {
29675
29207
  if (!number.equal(this.getValue(), this.props.value)) {
29676
29208
  this._setValue(this.props.value, this.state.format);
@@ -29713,7 +29245,8 @@ NumberInput.propTypes = {
29713
29245
  onFormatChange: PropTypes.func,
29714
29246
  checkValidity: PropTypes.func,
29715
29247
  size: PropTypes.oneOf(["mini", "small", "normal"]),
29716
- label: PropTypes.oneOf(["put your labels outside your inputs!"])
29248
+ label: PropTypes.oneOf(["put your labels outside your inputs!"]),
29249
+ allowPiTruncation: PropTypes.bool
29717
29250
  };
29718
29251
  NumberInput.defaultProps = {
29719
29252
  value: null,
@@ -29752,14 +29285,16 @@ class RangeInput extends React.Component {
29752
29285
  // eslint-disable-next-line react/jsx-no-bind
29753
29286
  ,
29754
29287
  onChange: this.onChange.bind(this, 0),
29755
- placeholder: this.props.placeholder[0]
29288
+ placeholder: this.props.placeholder[0],
29289
+ allowPiTruncation: this.props.allowPiTruncation
29756
29290
  })), /*#__PURE__*/React.createElement(NumberInput, _extends({}, this.props, {
29757
29291
  value: value[1],
29758
29292
  checkValidity: val => checkValidity([value[0], val])
29759
29293
  // eslint-disable-next-line react/jsx-no-bind
29760
29294
  ,
29761
29295
  onChange: this.onChange.bind(this, 1),
29762
- placeholder: this.props.placeholder[1]
29296
+ placeholder: this.props.placeholder[1],
29297
+ allowPiTruncation: this.props.allowPiTruncation
29763
29298
  })));
29764
29299
  }
29765
29300
  }
@@ -29767,7 +29302,8 @@ RangeInput.propTypes = {
29767
29302
  value: PropTypes.array.isRequired,
29768
29303
  onChange: PropTypes.func.isRequired,
29769
29304
  placeholder: PropTypes.array,
29770
- checkValidity: PropTypes.func
29305
+ checkValidity: PropTypes.func,
29306
+ allowPiTruncation: PropTypes.bool
29771
29307
  };
29772
29308
  RangeInput.defaultProps = {
29773
29309
  placeholder: [null, null]
@@ -36750,10 +36286,7 @@ var InteractiveGraph$1 = {
36750
36286
  name: "interactive-graph",
36751
36287
  displayName: "Interactive graph (Assessments only)",
36752
36288
  widget: InteractiveGraph,
36753
- staticTransform: staticTransform$3,
36754
- // TODO(LEMS-2656): remove TS suppression
36755
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInteractiveGraphUserInput'.
36756
- scorer: scoreInteractiveGraph
36289
+ staticTransform: staticTransform$3
36757
36290
  };
36758
36291
 
36759
36292
  // TODO: can we use WB typography?
@@ -37695,9 +37228,7 @@ var LabelImage$1 = {
37695
37228
  widget: LabelImageWithDependencies,
37696
37229
  accessible: true,
37697
37230
  isLintable: true,
37698
- // TODO(LEMS-2656): remove TS suppression
37699
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusLabelImageUserInput'.
37700
- scorer: scoreLabelImage
37231
+ getPublicWidgetOptions: getLabelImagePublicWidgetOptions
37701
37232
  };
37702
37233
 
37703
37234
  var ItemState = /*#__PURE__*/function (ItemState) {
@@ -37930,7 +37461,7 @@ class Draggable extends React.Component {
37930
37461
  // We need to keep backwards compatbility with rules specified directly
37931
37462
  // in CSS. Hence the hacky tacking on of manual classNames.
37932
37463
  // See sortable.less for details.
37933
- let className = css(styles$8.card, styles$8.draggable, layout === "horizontal" && styles$8.horizontalCard, layout === "vertical" && styles$8.verticalCard, type === ItemState.DRAGGING && styles$8.dragging, type === ItemState.DISABLED && styles$8.disabled, !includePadding && styles$8.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
37464
+ let className = css(styles$8.card, styles$8.draggable, layout === "horizontal" && styles$8.horizontalCard, layout === "vertical" && styles$8.verticalCard, type === ItemState.DRAGGING && styles$8.dragging, type === ItemState.DISABLED && styles$8.disabled, !includePadding && styles$8.unpaddedCard) + " " + "perseus-sortable-draggable";
37934
37465
  if (!includePadding) {
37935
37466
  className += " perseus-sortable-draggable-unpadded";
37936
37467
  }
@@ -38631,10 +38162,7 @@ var Matcher$1 = {
38631
38162
  name: "matcher",
38632
38163
  displayName: "Matcher (two column)",
38633
38164
  widget: Matcher,
38634
- isLintable: true,
38635
- // TODO(LEMS-2656): remove TS suppression
38636
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatcherUserInput'.
38637
- scorer: scoreMatcher
38165
+ isLintable: true
38638
38166
  };
38639
38167
 
38640
38168
  const getPromptJSON$8 = (renderProps, userInput) => {
@@ -39038,13 +38566,7 @@ var Matrix$1 = {
39038
38566
  widget: Matrix,
39039
38567
  transform: propTransform$1,
39040
38568
  staticTransform: staticTransform$2,
39041
- isLintable: true,
39042
- // TODO(LEMS-2656): remove TS suppression
39043
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
39044
- scorer: scoreMatrix,
39045
- // TODO(LEMS-2656): remove TS suppression
39046
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
39047
- validator: validateMatrix
38569
+ isLintable: true
39048
38570
  };
39049
38571
 
39050
38572
  const getPromptJSON$7 = () => {
@@ -39124,7 +38646,7 @@ class Measurer extends React.Component {
39124
38646
  // TODO(scottgrant): This isn't a11y-friendly! We should insist on
39125
38647
  // finding some valid alt text when this widget is used.
39126
38648
  return /*#__PURE__*/React.createElement("div", {
39127
- className: "perseus-widget perseus-widget-measurer " + "graphie-container above-scratchpad",
38649
+ className: "perseus-widget perseus-widget-measurer " + "graphie-container blank-background",
39128
38650
  style: {
39129
38651
  width: this.props.box[0],
39130
38652
  height: this.props.box[1]
@@ -39161,9 +38683,7 @@ var Measurer$1 = {
39161
38683
  hidden: true,
39162
38684
  widget: Measurer,
39163
38685
  version: measurerLogic.version,
39164
- propUpgrades: measurerLogic.widgetOptionsUpgrades,
39165
- // TODO: things that aren't interactive shouldn't need scoring functions
39166
- scorer: () => scoreNoop(1)
38686
+ propUpgrades: measurerLogic.widgetOptionsUpgrades
39167
38687
  };
39168
38688
 
39169
38689
  /**
@@ -40051,8 +39571,7 @@ var Molecule$1 = {
40051
39571
  name: "molecule-renderer",
40052
39572
  displayName: "Molecule renderer",
40053
39573
  hidden: true,
40054
- widget: MoleculeWidget,
40055
- scorer: () => scoreNoop()
39574
+ widget: MoleculeWidget
40056
39575
  };
40057
39576
 
40058
39577
  const getPromptJSON$6 = (renderProps, userInput) => {
@@ -40621,12 +40140,7 @@ var NumberLine$1 = {
40621
40140
  widget: NumberLine,
40622
40141
  transform: numberLineTransform,
40623
40142
  staticTransform: staticTransform$1,
40624
- // TODO(LEMS-2656): remove TS suppression
40625
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
40626
- scorer: scoreNumberLine,
40627
- // TODO(LEMS-2656): remove TS suppression
40628
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
40629
- validator: validateNumberLine
40143
+ getPublicWidgetOptions: getNumberLinePublicWidgetOptions
40630
40144
  };
40631
40145
 
40632
40146
  const getPromptJSON$5 = (renderProps, userInput) => {
@@ -40644,7 +40158,7 @@ const getPromptJSON$5 = (renderProps, userInput) => {
40644
40158
  class PlaceholderCard extends React.Component {
40645
40159
  render() {
40646
40160
  return /*#__PURE__*/React.createElement("div", {
40647
- className: "card-wrap " + ClassNames.INTERACTIVE,
40161
+ className: "card-wrap",
40648
40162
  style: {
40649
40163
  width: this.props.width
40650
40164
  }
@@ -40659,7 +40173,7 @@ class PlaceholderCard extends React.Component {
40659
40173
  class DragHintCard extends React.Component {
40660
40174
  render() {
40661
40175
  return /*#__PURE__*/React.createElement("div", {
40662
- className: "card-wrap " + ClassNames.INTERACTIVE
40176
+ className: "card-wrap"
40663
40177
  }, /*#__PURE__*/React.createElement("div", {
40664
40178
  className: "card drag-hint"
40665
40179
  }));
@@ -40795,7 +40309,7 @@ class Card extends React.Component {
40795
40309
  const rendererProps = _.pick(this.props, "content");
40796
40310
  const onMouseDown = this.props.animating ? $.noop : this.onMouseDown;
40797
40311
  return /*#__PURE__*/React.createElement("div", {
40798
- className: "card-wrap " + ClassNames.INTERACTIVE,
40312
+ className: "card-wrap",
40799
40313
  style: style,
40800
40314
  onMouseDown: onMouseDown,
40801
40315
  onTouchStart: onMouseDown,
@@ -41166,7 +40680,7 @@ class Orderer extends React.Component {
41166
40680
  });
41167
40681
  }, this));
41168
40682
  return /*#__PURE__*/React.createElement("div", {
41169
- className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "above-scratchpad blank-background " + "perseus-clearfix " + ClassNames.INTERACTIVE
40683
+ className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "blank-background " + "perseus-clearfix "
41170
40684
  // eslint-disable-next-line react/no-string-refs
41171
40685
  ,
41172
40686
  ref: "orderer"
@@ -41187,12 +40701,6 @@ var Orderer$1 = {
41187
40701
  hidden: true,
41188
40702
  widget: Orderer,
41189
40703
  isLintable: true,
41190
- // TODO(LEMS-2656): remove TS suppression
41191
- // @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
41192
- scorer: scoreOrderer,
41193
- // TODO(LEMS-2656): remove TS suppression
41194
- // @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
41195
- validator: validateOrderer,
41196
40704
  getPublicWidgetOptions: getOrdererPublicWidgetOptions
41197
40705
  };
41198
40706
 
@@ -41231,15 +40739,12 @@ PassageRefTarget.defaultProps = {
41231
40739
  var PassageRefTarget$1 = {
41232
40740
  name: "passage-ref-target",
41233
40741
  displayName: "PassageRefTarget",
41234
- defaultAlignment: "inline",
41235
40742
  widget: PassageRefTarget,
41236
40743
  hidden: true,
41237
40744
  transform: editorProps => {
41238
40745
  return _.pick(editorProps, "content");
41239
40746
  },
41240
- isLintable: true,
41241
- // TODO: things that aren't interactive shouldn't need scoring functions
41242
- scorer: () => scoreNoop()
40747
+ isLintable: true
41243
40748
  };
41244
40749
 
41245
40750
  const getPromptJSON$4 = () => {
@@ -42226,7 +41731,7 @@ class Plotter extends React.Component {
42226
41731
  marginBottom: this.props.labels[0] ? paddingForBottomLabel : 0
42227
41732
  };
42228
41733
  return /*#__PURE__*/React.createElement("div", {
42229
- className: "perseus-widget-plotter graphie " + ClassNames.INTERACTIVE
41734
+ className: "perseus-widget-plotter graphie"
42230
41735
  // eslint-disable-next-line react/no-string-refs
42231
41736
  ,
42232
41737
  ref: "graphieDiv",
@@ -42256,13 +41761,7 @@ var Plotter$1 = {
42256
41761
  displayName: "Plotter",
42257
41762
  hidden: true,
42258
41763
  widget: Plotter,
42259
- staticTransform: staticTransform,
42260
- // TODO(LEMS-2656): remove TS suppression
42261
- // @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
42262
- scorer: scorePlotter,
42263
- // TODO(LEMS-2656): remove TS suppression
42264
- // @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
42265
- validator: validatePlotter
41764
+ staticTransform: staticTransform
42266
41765
  };
42267
41766
 
42268
41767
  const getPromptJSON$2 = () => {
@@ -42425,12 +41924,7 @@ var Sorter$1 = {
42425
41924
  displayName: "Sorter",
42426
41925
  widget: Sorter,
42427
41926
  isLintable: true,
42428
- // TODO(LEMS-2656): remove TS suppression
42429
- // @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
42430
- scorer: scoreSorter,
42431
- // TODO(LEMS-2656): remove TS suppression
42432
- // @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
42433
- validator: validateSorter
41927
+ getPublicWidgetOptions: getSorterPublicWidgetOptions
42434
41928
  };
42435
41929
 
42436
41930
  const {
@@ -42649,13 +42143,7 @@ var Table$1 = {
42649
42143
  widget: Table,
42650
42144
  transform: propTransform,
42651
42145
  hidden: true,
42652
- isLintable: true,
42653
- // TODO(LEMS-2656): remove TS suppression
42654
- // @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
42655
- scorer: scoreTable,
42656
- // TODO(LEMS-2656): remove TS suppression
42657
- // @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
42658
- validator: validateTable
42146
+ isLintable: true
42659
42147
  };
42660
42148
 
42661
42149
  const getPromptJSON = () => {
@@ -42815,11 +42303,7 @@ Video.contextType = PerseusI18nContext;
42815
42303
  var Video$1 = {
42816
42304
  name: "video",
42817
42305
  displayName: "Video",
42818
- defaultAlignment: "block",
42819
- supportedAlignments: ["block", "float-left", "float-right", "full-width"],
42820
- widget: Video,
42821
- // TODO: things that aren't interactive shouldn't need scoring functions
42822
- scorer: () => scoreNoop()
42306
+ widget: Video
42823
42307
  };
42824
42308
 
42825
42309
  /* eslint-disable import/no-named-as-default */
@@ -42838,7 +42322,7 @@ const init = function init() {
42838
42322
 
42839
42323
  // This file is processed by a Rollup plugin (replace) to inject the production
42840
42324
  const libName = "@khanacademy/perseus";
42841
- const libVersion = "52.0.1";
42325
+ const libVersion = "53.0.0";
42842
42326
  addLibraryVersionToPerseusDebug(libName, libVersion);
42843
42327
 
42844
42328
  const apiVersion = {
@@ -43705,6 +43189,42 @@ const ref = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.cre
43705
43189
  ref: ref
43706
43190
  }))));
43707
43191
 
43192
+ function isCorrect(score) {
43193
+ return score.type === "points" && score.earned >= score.total;
43194
+ }
43195
+ function keScoreFromPerseusScore(score,
43196
+ // It's weird, but this is what we're passing it
43197
+ guess, state) {
43198
+ if (score.type === "points") {
43199
+ return {
43200
+ empty: false,
43201
+ correct: isCorrect(score),
43202
+ message: score.message,
43203
+ guess: guess,
43204
+ state: state
43205
+ };
43206
+ }
43207
+ if (score.type === "invalid") {
43208
+ return {
43209
+ empty: true,
43210
+ correct: false,
43211
+ message: score.message,
43212
+ suppressAlmostThere: score.suppressAlmostThere,
43213
+ guess: guess,
43214
+ state: state
43215
+ };
43216
+ }
43217
+ throw new PerseusError(
43218
+ // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
43219
+ "Invalid score type: " + score.type, Errors.InvalidInput, {
43220
+ metadata: {
43221
+ score: JSON.stringify(score),
43222
+ guess: JSON.stringify(guess),
43223
+ state: JSON.stringify(state)
43224
+ }
43225
+ });
43226
+ }
43227
+
43708
43228
  /**
43709
43229
  * Traverses a {content, widgets, images} renderer props object,
43710
43230
  * such as `itemData.question`
@@ -45081,5 +44601,5 @@ const ScoringUtil = {
45081
44601
  * Types
45082
44602
  */
45083
44603
 
45084
- 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, JiptParagraphs, KhanColors, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, ItemVersion as itemVersion, libVersion, makeSafeUrl, mathOnlyParser, mediaQueries, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting, scorePerseusItem, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
44604
+ 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, JiptParagraphs, KhanColors, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, ItemVersion as itemVersion, libVersion, makeSafeUrl, mathOnlyParser, mediaQueries, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
45085
44605
  //# sourceMappingURL=index.js.map