@khanacademy/perseus 52.0.0 → 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 (65) 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 +516 -1195
  6. package/dist/es/index.js.map +1 -1
  7. package/dist/es/strings.js +21 -13
  8. package/dist/es/strings.js.map +1 -1
  9. package/dist/index.css +0 -30
  10. package/dist/index.d.ts +1 -2
  11. package/dist/index.js +514 -1198
  12. package/dist/index.js.map +1 -1
  13. package/dist/perseus-api.d.ts +0 -1
  14. package/dist/renderer.d.ts +1 -1
  15. package/dist/strings.d.ts +1 -1
  16. package/dist/strings.js +21 -13
  17. package/dist/strings.js.map +1 -1
  18. package/dist/styles/constants.d.ts +0 -2
  19. package/dist/types.d.ts +3 -20
  20. package/dist/util/math-utils.d.ts +1 -0
  21. package/dist/util/scoring.d.ts +0 -15
  22. package/dist/util/test-utils.d.ts +1 -1
  23. package/dist/widget-ai-utils/mock-widget/prompt-utils.d.ts +1 -1
  24. package/dist/widget-container.d.ts +1 -1
  25. package/dist/widgets/categorizer/categorizer.d.ts +0 -3
  26. package/dist/widgets/cs-program/cs-program.d.ts +2 -3
  27. package/dist/widgets/definition/definition.d.ts +0 -2
  28. package/dist/widgets/deprecated-standin/deprecated-standin.d.ts +0 -1
  29. package/dist/widgets/dropdown/dropdown.d.ts +2 -4
  30. package/dist/widgets/explanation/explanation.d.ts +0 -2
  31. package/dist/widgets/expression/expression.d.ts +1 -5
  32. package/dist/widgets/grapher/grapher.d.ts +0 -2
  33. package/dist/widgets/group/group.d.ts +0 -4
  34. package/dist/widgets/iframe/iframe.d.ts +0 -2
  35. package/dist/widgets/image/image.d.ts +0 -3
  36. package/dist/widgets/input-number/input-number.d.ts +0 -3
  37. package/dist/widgets/interaction/interaction.d.ts +0 -1
  38. package/dist/widgets/interactive-graph.d.ts +0 -2
  39. package/dist/widgets/interactive-graphs/backgrounds/axis-ticks.d.ts +1 -0
  40. package/dist/widgets/label-image/label-image.d.ts +2 -2
  41. package/dist/widgets/matcher/matcher.d.ts +0 -2
  42. package/dist/widgets/matrix/matrix.d.ts +0 -3
  43. package/dist/widgets/measurer/measurer.d.ts +0 -1
  44. package/dist/widgets/mock-widgets/mock-widget-types.d.ts +0 -6
  45. package/dist/widgets/mock-widgets/mock-widget.d.ts +2 -5
  46. package/dist/widgets/molecule/molecule.d.ts +0 -1
  47. package/dist/widgets/number-line/number-line.d.ts +2 -3
  48. package/dist/widgets/numeric-input/numeric-input.d.ts +2 -3
  49. package/dist/widgets/orderer/orderer.d.ts +0 -3
  50. package/dist/widgets/passage/passage.d.ts +0 -1
  51. package/dist/widgets/passage-ref/passage-ref.d.ts +0 -2
  52. package/dist/widgets/passage-ref-target/passage-ref-target.d.ts +0 -2
  53. package/dist/widgets/plotter/plotter.d.ts +0 -3
  54. package/dist/widgets/radio/radio.d.ts +0 -3
  55. package/dist/widgets/sorter/sorter.d.ts +2 -4
  56. package/dist/widgets/table/table.d.ts +0 -3
  57. package/dist/widgets/video/video.d.ts +0 -3
  58. package/dist/widgets.d.ts +1 -32
  59. package/package.json +10 -10
  60. package/dist/renderer-util.d.ts +0 -14
  61. package/dist/widgets/__shared__/score-noop.d.ts +0 -11
  62. package/dist/widgets/group/score-group.d.ts +0 -4
  63. package/dist/widgets/group/validate-group.d.ts +0 -4
  64. package/dist/widgets/mock-widgets/score-mock-widget.d.ts +0 -5
  65. 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';
@@ -59,213 +59,6 @@ function _extends() {
59
59
  * The translated strings that are used to render Perseus.
60
60
  */
61
61
 
62
- /**
63
- * Untranslated strings used in Perseus. To be used by an external
64
- * translator to produce translated strings, passed in as `PerseusStrings`.
65
- * !! Note: Ensure that all escape sequences are double-escaped. (e.g. `\\text` -> `\\\\text`)
66
- */
67
- const strings = {
68
- closeKeypad: "close math keypad",
69
- openKeypad: "open math keypad",
70
- mathInputBox: "Math input box",
71
- removeHighlight: "Remove highlight",
72
- addHighlight: "Add highlight",
73
- hintPos: "Hint #%(pos)s",
74
- errorRendering: "Error rendering: %(error)s",
75
- APPROXIMATED_PI_ERROR: "Your answer is close, but you may " + "have approximated pi. Enter your " + "answer as a multiple of pi, like " + "12 pi or " + "2/3 pi",
76
- EXTRA_SYMBOLS_ERROR: "We could not understand your " + "answer. Please check your answer for extra " + "text or symbols.",
77
- NEEDS_TO_BE_SIMPLFIED_ERROR: "Your answer is almost correct, " + "but it needs to be simplified.",
78
- MISSING_PERCENT_ERROR: "Your answer is almost correct, " + "but it is missing a " + "<code>\\\\%</code> at the end.",
79
- MULTIPLICATION_SIGN_ERROR: "I'm a computer. I only understand " + "multiplication if you use an asterisk " + "(*) as the multiplication sign.",
80
- WRONG_CASE_ERROR: "Your answer includes use of a variable with the wrong case.",
81
- WRONG_LETTER_ERROR: "Your answer includes a wrong variable letter.",
82
- invalidSelection: "Make sure you select something for every row.",
83
- ERROR_TITLE: "Oops!",
84
- ERROR_MESSAGE: "Sorry, I don't understand that!",
85
- hints: "Hints",
86
- getAnotherHint: "Get another hint",
87
- deprecatedStandin: "Sorry, this part of the question is no longer available. 😅 Don't worry, you won't be graded on this part. Keep going!",
88
- keepTrying: "Keep trying",
89
- tryAgain: "Try again",
90
- check: "Check",
91
- correctExcited: "Correct!",
92
- nextQuestion: "Next question",
93
- skipToTitle: "Skip to %(title)s",
94
- current: "Current",
95
- correct: "Correct",
96
- correctSelected: "Correct (selected)",
97
- correctCrossedOut: "Correct (but you crossed it out)",
98
- incorrect: "Incorrect",
99
- incorrectSelected: "Incorrect (selected)",
100
- hideExplanation: "Hide explanation",
101
- explain: "Explain",
102
- INVALID_MESSAGE_PREFIX: "We couldn't grade your answer.",
103
- DEFAULT_INVALID_MESSAGE_1: "It looks like you left something blank or ",
104
- DEFAULT_INVALID_MESSAGE_2: "entered in an invalid answer.",
105
- integerExample: "an integer, like $6$",
106
- properExample: "a *proper* fraction, like $1/2$ or $6/10$",
107
- simplifiedProperExample: "a *simplified proper* fraction, like $3/5$",
108
- improperExample: "an *improper* fraction, like $10/7$ or $14/8$",
109
- simplifiedImproperExample: "a *simplified improper* fraction, like $7/4$",
110
- mixedExample: "a mixed number, like $1\\\\ 3/4$",
111
- decimalExample: "an *exact* decimal, like $0.75$",
112
- percentExample: "a percent, like $12.34\\\\%$",
113
- piExample: "a multiple of pi, like $12$ pi or $2/3$ pi",
114
- yourAnswer: "**Your answer should be** ",
115
- yourAnswerLabel: "Your answer:",
116
- addPoints: "Click to add points",
117
- addVertices: "Click to add vertices",
118
- tapMultiple: "Tap each dot on the image to select all answers that apply.",
119
- tapSingle: "Tap each dot on the image to select an answer.",
120
- clickMultiple: "Click each dot on the image to select all answers that apply.",
121
- clickSingle: "Click each dot on the image to select an answer.",
122
- choices: "Choices:",
123
- answers: {
124
- one: "%(num)s answer",
125
- other: "%(num)s answers"
126
- },
127
- hideAnswersToggleLabel: "Hide answer choices",
128
- moves: {
129
- one: "Moves: %(num)s",
130
- other: "Moves: %(num)s"
131
- },
132
- clickTiles: "Click on the tiles to change the lights.",
133
- turnOffLights: "You must turn on all of the lights to continue.",
134
- fillAllCells: "Make sure you fill in all cells in the matrix.",
135
- molecularDrawing: "A molecular structure drawing. SMILES notation: %(content)s",
136
- switchDirection: "Switch direction",
137
- circleOpen: "Make circle open",
138
- circleFilled: "Make circle filled",
139
- numDivisions: "Number of divisions:",
140
- divisions: "Please make sure the number of divisions is in the range %(divRangeString)s.",
141
- lineRange: "lines %(lineRange)s",
142
- lineNumber: "line %(lineNumber)s",
143
- symbolPassage: "The symbol %(questionSymbol)s indicates that question %(questionNumber)s references this portion of the passage.",
144
- symbolQuestion: " The symbol %(sentenceSymbol)s indicates that the following sentence is referenced in a question.",
145
- lineLabel: {
146
- context: "a label next to a reading passage to denote the line number",
147
- message: "Line"
148
- },
149
- beginningPassage: "Beginning of reading passage.",
150
- beginningFootnotes: "Beginning of reading passage footnotes.",
151
- endPassage: "End of reading passage.",
152
- questionMarker: "[Marker for question %(number)s]",
153
- circleMarker: "[Circle marker %(number)s]",
154
- sentenceMarker: "[Sentence %(number)s]",
155
- dragHandles: "Drag handles to make graph",
156
- tapAddPoints: "Tap to add points",
157
- false: "False",
158
- true: "True",
159
- no: "No",
160
- yes: "Yes",
161
- chooseCorrectNum: "Please choose the correct number of answers.",
162
- notNoneOfTheAbove: "'None of the above' may not be selected when other answers are selected.",
163
- noneOfTheAbove: "None of the above",
164
- chooseNumAnswers: "Choose %(numCorrect)s answers:",
165
- chooseAllAnswers: "Choose all answers that apply:",
166
- chooseOneAnswer: "Choose 1 answer:",
167
- choiceCheckedCorrect: "(Choice %(letter)s, Checked, Correct)",
168
- choiceCrossedOutCorrect: "(Choice %(letter)s, Crossed out, Correct)",
169
- choiceCorrect: "(Choice %(letter)s, Correct)",
170
- choiceCheckedIncorrect: "(Choice %(letter)s, Checked, Incorrect)",
171
- choiceCrossedOutIncorrect: "(Choice %(letter)s, Crossed out, Incorrect)",
172
- choiceIncorrect: "(Choice %(letter)s, Incorrect)",
173
- choiceChecked: "(Choice %(letter)s, Checked)",
174
- choiceCrossedOut: "(Choice %(letter)s, Crossed out)",
175
- choice: "(Choice %(letter)s)",
176
- crossOut: "Cross out",
177
- crossOutOption: "Cross out option",
178
- crossOutChoice: "Cross out Choice %(letter)s",
179
- bringBack: "Bring back",
180
- openMenuForChoice: "Open menu for Choice %(letter)s",
181
- letters: {
182
- context: "This is a list of single-character labels that will appear in front of multiple-choice options. For instance, a multiple-choice question with three options would display (A) first option (B) second option (C) third option. There must be spaces between each of the different characters. The characters will show up next to options in the order that they are listed here. Most multiple choice questions have 5 or fewer options.",
183
- message: "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
184
- },
185
- rightArrow: "Reaction arrow pointing to the right.",
186
- dontUnderstandUnits: "I couldn't understand those units.",
187
- checkSigFigs: "Check your significant figures.",
188
- answerNumericallyIncorrect: "That answer is numerically incorrect.",
189
- checkUnits: "Check your units.",
190
- dontUnderstand: "I don't understand that",
191
- loading: "Loading...",
192
- videoTranscript: "See video transcript",
193
- somethingWrong: "Something went wrong.",
194
- videoWrapper: "Khan Academy video wrapper",
195
- mathInputTitle: "mathematics keyboard",
196
- mathInputDescription: "Use keyboard/mouse to interact with math-based input fields",
197
- sin: "sin",
198
- cos: "cos",
199
- tan: "tan",
200
- simulationLoadFail: "Sorry, this simulation cannot load.",
201
- simulationLocaleWarning: "Sorry, this simulation isn't available in your language.",
202
- selectAnAnswer: "Select an answer",
203
- // The following strings are used for interactive graph SR descriptions.
204
- addPoint: "Add Point",
205
- removePoint: "Remove Point",
206
- graphKeyboardPrompt: "Press Shift + Enter to interact with the graph",
207
- closePolygon: "Close shape",
208
- openPolygon: "Re-open shape",
209
- srInteractiveElements: "Interactive elements: %(elements)s",
210
- srNoInteractiveElements: "No interactive elements",
211
- // TODO(LEMS-2660): The following strings are ones that will need
212
- // translation tickets after all interactive graph SR strings have
213
- // been finalized. Remove this comment after the tickets have been
214
- // created.
215
- srPointAtCoordinates: "Point %(num)s at %(x)s comma %(y)s",
216
- srCircleGraph: "A circle on a coordinate plane.",
217
- srCircleShape: "Circle. The center point is at %(centerX)s comma %(centerY)s.",
218
- srCircleRadiusPoint: "Radius point at %(radiusPointX)s comma %(radiusPointY)s.",
219
- srCircleRadius: "Circle radius is %(radius)s.",
220
- srCircleOuterPoints: "Points on the circle at %(point1X)s comma %(point1Y)s, %(point2X)s comma %(point2Y)s, %(point3X)s comma %(point3Y)s, %(point4X)s comma %(point4Y)s.",
221
- srLinearGraph: "A line on a coordinate plane.",
222
- srLinearGraphPoints: "The line has two points, point 1 at %(point1X)s comma %(point1Y)s and point 2 at %(point2X)s comma %(point2Y)s.",
223
- srLinearGraphSlopeIncreasing: "Its slope increases from left to right.",
224
- srLinearGraphSlopeDecreasing: "Its slope decreases from left to right.",
225
- srLinearGraphSlopeHorizontal: "Its slope is zero.",
226
- srLinearGraphSlopeVertical: "Its slope is undefined.",
227
- srLinearGraphXOnlyIntercept: "The line crosses the X-axis at %(xIntercept)s comma 0.",
228
- srLinearGraphYOnlyIntercept: "The line crosses the Y-axis at 0 comma %(yIntercept)s.",
229
- srLinearGraphBothIntercepts: "The line crosses the X-axis at %(xIntercept)s comma 0 and the Y-axis at 0 comma %(yIntercept)s.",
230
- srLinearGraphOriginIntercept: "The line crosses the x and y axes at the graph's origin.",
231
- srLinearGrabHandle: "Line from %(point1X)s comma %(point1Y)s to %(point2X)s comma %(point2Y)s.",
232
- srAngleSideAtCoordinates: "Point %(point)s, %(side)s at %(x)s comma %(y)s",
233
- srAngleVertexAtCoordinatesWithAngleMeasure: "Point 2, vertex at %(x)s comma %(y)s. Angle %(angleMeasure)s degrees",
234
- srAngleGraphAriaLabel: "An angle on a coordinate plane.",
235
- srAngleGraphAriaDescription: "The angle measure is %(angleMeasure)s degrees with a vertex at %(vertexX)s comma %(vertexY)s, a point on the starting side at %(startingSideX)s comma %(startingSideY)s and a point on the ending side at %(endingSideX)s comma %(endingSideY)s",
236
- srSingleSegmentGraphAriaLabel: "A line segment on a coordinate plane.",
237
- srMultipleSegmentGraphAriaLabel: "%(countOfSegments)s line segments on a coordinate plane.",
238
- srMultipleSegmentIndividualLabel: "Segment %(indexOfSegment)s: Endpoint 1 at %(point1X)s comma %(point1Y)s. Endpoint 2 %(point2X)s comma %(point2Y)s.",
239
- srSingleSegmentLabel: "Endpoint 1 at %(point1X)s comma %(point1Y)s. Endpoint 2 %(point2X)s comma %(point2Y)s.",
240
- srSegmentLength: "Segment length %(length)s units.",
241
- srSingleSegmentGraphEndpointAriaLabel: "Endpoint %(endpointNumber)s at %(x)s comma %(y)s.",
242
- srMultipleSegmentGraphEndpointAriaLabel: "Endpoint %(endpointNumber)s on segment %(indexOfSegment)s at %(x)s comma %(y)s.",
243
- srSegmentGrabHandle: "Segment from %(point1X)s comma %(point1Y)s to %(point2X)s comma %(point2Y)s.",
244
- srLinearSystemGraph: "Two lines on a coordinate plane.",
245
- srLinearSystemPoints: "Line %(lineNumber)s has two points, point 1 at %(point1X)s comma %(point1Y)s and point 2 at %(point2X)s comma %(point2Y)s.",
246
- srLinearSystemPoint: "Point %(pointSequence)s on line %(lineNumber)s at %(x)s comma %(y)s.",
247
- srRayGraph: "A ray on a coordinate plane.",
248
- srRayPoints: "The endpoint is at %(point1X)s comma %(point1Y)s and the ray goes through point %(point2X)s comma %(point2Y)s.",
249
- srRayGrabHandle: "Ray with endpoint %(point1X)s comma %(point1Y)s going through point %(point2X)s comma %(point2Y)s.",
250
- srRayEndpoint: "Endpoint at %(x)s comma %(y)s.",
251
- srRayTerminalPoint: "Through point at %(x)s comma %(y)s.",
252
- srQuadraticGraph: "A parabola on a 4-quadrant coordinate plane.",
253
- srQuadraticFaceUp: "The parabola opens upward.",
254
- srQuadraticFaceDown: "The parabola opens downward.",
255
- srQuadraticGraphVertexOrigin: "Vertex is at the origin.",
256
- srQuadraticGraphVertexXAxis: "Vertex is on the X-axis.",
257
- srQuadraticGraphVertexYAxis: "Vertex is on the Y-axis.",
258
- srQuadraticGraphVertexQuadrant: "Vertex is in quadrant %(quadrant)s.",
259
- srQuadraticTwoXIntercepts: "The X-intercepts are at %(intercept1)s comma 0 and %(intercept2)s comma 0.",
260
- srQuadraticOneXIntercept: "The X-intercept is at %(intercept)s comma 0.",
261
- srQuadraticYIntercept: "The Y-intercept is at 0 comma %(intercept)s.",
262
- srQuadraticPointOrigin: "Point %(pointNumber)s on parabola at the origin.",
263
- srQuadraticPointAxis: "Point %(pointNumber)s on parabola at %(x)s comma %(y)s.",
264
- srQuadraticPointQuadrant: "Point %(pointNumber)s on parabola in quadrant %(quadrant)s at %(x)s comma %(y)s.",
265
- srQuadraticInteractiveElements: "Parabola with points at %(point1X)s comma %(point1Y)s, %(point2X)s comma %(point2Y)s, and %(point3X)s comma %(point3Y)s."
266
- // The above strings are used for interactive graph SR descriptions.
267
- };
268
-
269
62
  /**
270
63
  * Mock strings for the Perseus package, to be used for tests and Storybook.
271
64
  */
@@ -650,24 +443,32 @@ const mockStrings = {
650
443
 
651
444
  // This type helps us make sure all error codes are mapped to strings
652
445
 
446
+ /**
447
+ * Map an error string to a PerseusStrings key
448
+ * that we can use to get the translated error message
449
+ */
653
450
  const errorToString = {
654
- MISSING_PERCENT_ERROR: strings.MISSING_PERCENT_ERROR,
655
- NEEDS_TO_BE_SIMPLIFIED_ERROR: strings.NEEDS_TO_BE_SIMPLFIED_ERROR,
656
- APPROXIMATED_PI_ERROR: strings.APPROXIMATED_PI_ERROR,
657
- EXTRA_SYMBOLS_ERROR: strings.EXTRA_SYMBOLS_ERROR,
658
- WRONG_CASE_ERROR: strings.WRONG_CASE_ERROR,
659
- WRONG_LETTER_ERROR: strings.WRONG_LETTER_ERROR,
660
- MULTIPLICATION_SIGN_ERROR: strings.MULTIPLICATION_SIGN_ERROR,
661
- INVALID_SELECTION_ERROR: strings.invalidSelection,
662
- CHOOSE_CORRECT_NUM_ERROR: strings.chooseCorrectNum,
663
- NOT_NONE_ABOVE_ERROR: strings.notNoneOfTheAbove,
664
- FILL_ALL_CELLS_ERROR: strings.fillAllCells
665
- };
666
- function mapErrorToString(err) {
451
+ MISSING_PERCENT_ERROR: "MISSING_PERCENT_ERROR",
452
+ NEEDS_TO_BE_SIMPLIFIED_ERROR: "NEEDS_TO_BE_SIMPLFIED_ERROR",
453
+ APPROXIMATED_PI_ERROR: "APPROXIMATED_PI_ERROR",
454
+ EXTRA_SYMBOLS_ERROR: "EXTRA_SYMBOLS_ERROR",
455
+ WRONG_CASE_ERROR: "WRONG_CASE_ERROR",
456
+ WRONG_LETTER_ERROR: "WRONG_LETTER_ERROR",
457
+ MULTIPLICATION_SIGN_ERROR: "MULTIPLICATION_SIGN_ERROR",
458
+ INVALID_SELECTION_ERROR: "invalidSelection",
459
+ CHOOSE_CORRECT_NUM_ERROR: "chooseCorrectNum",
460
+ NOT_NONE_ABOVE_ERROR: "notNoneOfTheAbove",
461
+ FILL_ALL_CELLS_ERROR: "fillAllCells"
462
+ };
463
+ function mapErrorToString(
464
+ // the string representing an error code
465
+ err,
466
+ // the translated Perseus strings
467
+ translatedStrings) {
667
468
  if (!err) {
668
469
  return err;
669
470
  }
670
- return errorToString[err] || err;
471
+ return translatedStrings[errorToString[err]] || err;
671
472
  }
672
473
 
673
474
  /**
@@ -1588,7 +1389,6 @@ const ClassNames = {
1588
1389
  SELECTED: "perseus-radio-selected",
1589
1390
  OPTION_CONTENT: "perseus-radio-option-content"
1590
1391
  },
1591
- INTERACTIVE: "perseus-interactive",
1592
1392
  CORRECT: "perseus-correct",
1593
1393
  INCORRECT: "perseus-incorrect",
1594
1394
  UNANSWERED: "perseus-unanswered",
@@ -1836,7 +1636,6 @@ class Expression extends React.Component {
1836
1636
  // eslint-disable-next-line react/no-string-refs
1837
1637
  , {
1838
1638
  ref: "input",
1839
- className: ClassNames.INTERACTIVE,
1840
1639
  value: this.props.value,
1841
1640
  onChange: this.changeAndTrack,
1842
1641
  convertDotToTimes: this.props.times,
@@ -1949,7 +1748,6 @@ var Expression$1 = {
1949
1748
  name: "expression",
1950
1749
  displayName: "Expression / Equation",
1951
1750
  accessible: true,
1952
- defaultAlignment: "inline-block",
1953
1751
  widget: ExpressionWithDependencies,
1954
1752
  transform: widgetOptions => {
1955
1753
  const {
@@ -1974,12 +1772,6 @@ var Expression$1 = {
1974
1772
  propUpgrades: expressionLogic.widgetOptionsUpgrades,
1975
1773
  // For use by the editor
1976
1774
  isLintable: true,
1977
- // TODO(LEMS-2656): remove TS suppression
1978
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
1979
- scorer: scoreExpression,
1980
- // TODO(LEMS-2656): remove TS suppression
1981
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
1982
- validator: validateExpression,
1983
1775
  getPublicWidgetOptions: getExpressionPublicWidgetOptions,
1984
1776
  // TODO(LEMS-2656): remove TS suppression
1985
1777
  // @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusExpressionRubric'.
@@ -3260,10 +3052,6 @@ const pureMdMax$1 = "1023px";
3260
3052
  const pureLgMax$1 = "1279px";
3261
3053
  // @tableBackgroundAccent: #f9f9f9; // for striping
3262
3054
  const tableBackgroundAccent = "#F9F9F9";
3263
- // @zIndexAboveScratchpad: @zIndexScratchPad + 1;
3264
- const zIndexAboveScratchpad$1 = 2;
3265
- // @zIndexInteractiveComponent: @zIndexAboveScratchpad + 1;
3266
- const zIndexInteractiveComponent$1 = 3;
3267
3055
  // @phoneMargin: 16px;
3268
3056
  const phoneMargin = 16;
3269
3057
  const negativePhoneMargin = -16;
@@ -3307,8 +3095,6 @@ var constants = /*#__PURE__*/Object.freeze({
3307
3095
  pureMdMax: pureMdMax$1,
3308
3096
  pureLgMax: pureLgMax$1,
3309
3097
  tableBackgroundAccent: tableBackgroundAccent,
3310
- zIndexAboveScratchpad: zIndexAboveScratchpad$1,
3311
- zIndexInteractiveComponent: zIndexInteractiveComponent$1,
3312
3098
  phoneMargin: phoneMargin,
3313
3099
  negativePhoneMargin: negativePhoneMargin,
3314
3100
  hintBorderWidth: hintBorderWidth,
@@ -17533,658 +17319,6 @@ class QuestionParagraph extends React.Component {
17533
17319
  }
17534
17320
  }
17535
17321
 
17536
- const noScore = {
17537
- type: "points",
17538
- earned: 0,
17539
- total: 0,
17540
- message: null
17541
- };
17542
-
17543
- /**
17544
- * If a widget says that it is empty once it is graded.
17545
- * Trying to encapsulate references to the score format.
17546
- */
17547
- function scoreIsEmpty(score) {
17548
- // HACK(benkomalo): ugh. this isn't great; the Perseus score objects
17549
- // overload the type "invalid" for what should probably be three
17550
- // distinct cases:
17551
- // - truly empty or not fully filled out
17552
- // - invalid or malformed inputs
17553
- // - "almost correct" like inputs where the widget wants to give
17554
- // feedback (e.g. a fraction needs to be reduced, or `pi` should
17555
- // be used instead of 3.14)
17556
- //
17557
- // Unfortunately the coercion happens all over the place, as these
17558
- // Perseus style score objects are created *everywhere* (basically
17559
- // in every widget), so it's hard to change now. We assume that
17560
- // anything with a "message" is not truly empty, and one of the
17561
- // latter two cases for now.
17562
- return score.type === "invalid" && (!score.message || score.message.length === 0);
17563
- }
17564
-
17565
- /**
17566
- * Combine two score objects.
17567
- *
17568
- * Given two score objects for two different widgets, combine them so that
17569
- * if one is wrong, the total score is wrong, etc.
17570
- */
17571
- function combineScores(scoreA, scoreB) {
17572
- let message;
17573
- if (scoreA.type === "points" && scoreB.type === "points") {
17574
- if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
17575
- // TODO(alpert): Figure out how to combine messages usefully
17576
- message = null;
17577
- } else {
17578
- message = scoreA.message || scoreB.message;
17579
- }
17580
- return {
17581
- type: "points",
17582
- earned: scoreA.earned + scoreB.earned,
17583
- total: scoreA.total + scoreB.total,
17584
- message: message
17585
- };
17586
- }
17587
- if (scoreA.type === "points" && scoreB.type === "invalid") {
17588
- return scoreB;
17589
- }
17590
- if (scoreA.type === "invalid" && scoreB.type === "points") {
17591
- return scoreA;
17592
- }
17593
- if (scoreA.type === "invalid" && scoreB.type === "invalid") {
17594
- if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
17595
- // TODO(alpert): Figure out how to combine messages usefully
17596
- message = null;
17597
- } else {
17598
- message = scoreA.message || scoreB.message;
17599
- }
17600
- return {
17601
- type: "invalid",
17602
- message: message
17603
- };
17604
- }
17605
-
17606
- /**
17607
- * The above checks cover all combinations of score type, so if we get here
17608
- * then something is amiss with our inputs.
17609
- */
17610
- throw new PerseusError("PerseusScore with unknown type encountered", Errors.InvalidInput, {
17611
- metadata: {
17612
- scoreA: JSON.stringify(scoreA),
17613
- scoreB: JSON.stringify(scoreB)
17614
- }
17615
- });
17616
- }
17617
- function flattenScores(widgetScoreMap) {
17618
- return Object.values(widgetScoreMap).reduce(combineScores, noScore);
17619
- }
17620
- function isCorrect(score) {
17621
- return score.type === "points" && score.earned >= score.total;
17622
- }
17623
- function keScoreFromPerseusScore(score,
17624
- // It's weird, but this is what we're passing it
17625
- guess, state) {
17626
- if (score.type === "points") {
17627
- return {
17628
- empty: false,
17629
- correct: isCorrect(score),
17630
- message: score.message,
17631
- guess: guess,
17632
- state: state
17633
- };
17634
- }
17635
- if (score.type === "invalid") {
17636
- return {
17637
- empty: true,
17638
- correct: false,
17639
- message: score.message,
17640
- suppressAlmostThere: score.suppressAlmostThere,
17641
- guess: guess,
17642
- state: state
17643
- };
17644
- }
17645
- throw new PerseusError(
17646
- // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
17647
- "Invalid score type: " + score.type, Errors.InvalidInput, {
17648
- metadata: {
17649
- score: JSON.stringify(score),
17650
- guess: JSON.stringify(guess),
17651
- state: JSON.stringify(state)
17652
- }
17653
- });
17654
- }
17655
-
17656
- const DEFAULT_ALIGNMENT$1 = "block";
17657
- // NOTE(kevinb): "default" is not one in `validAlignments`.
17658
- const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
17659
- const DEFAULT_STATIC = false;
17660
- const DEFAULT_TRACKING = "";
17661
- const DEFAULT_LINTABLE = false;
17662
- const widgets = {};
17663
- const editors = {};
17664
-
17665
- // Widgets must be registered to avoid circular dependencies with the
17666
- // core Editor and Renderer components.
17667
- // TODO(jeremy): The widget name is already embedded in the WidgetExports type
17668
- // so could we drop the `name` parameter here?
17669
- const registerWidget = (name, widget) => {
17670
- widgets[name] = widget;
17671
- };
17672
- const registerWidgets = widgets => {
17673
- widgets.forEach(widget => {
17674
- registerWidget(widget.name, widget);
17675
- });
17676
- validateAlignments();
17677
- };
17678
-
17679
- /**
17680
- *
17681
- * @param name - the widget that you are trying to replace
17682
- * @param replacementName - the name of the widget that takes its place
17683
- *
17684
- * e.g. replaceWidget("transformer", "deprecated-standin") will make it so the
17685
- * transformer widget is replaced by the always correct widget
17686
- */
17687
- const replaceWidget = (name, replacementName) => {
17688
- const substituteWidget = widgets[replacementName];
17689
-
17690
- // If the replacement widget isn't found, we need to throw. Otherwise after
17691
- // removing the deprecated widget, we'll have data asking for a widget type
17692
- // that doesn't exist at all.
17693
- if (!substituteWidget) {
17694
- const errorMsg = `Failed to replace ${name} with ${replacementName}`;
17695
- throw new PerseusError(errorMsg, Errors.Internal);
17696
- }
17697
- registerWidget(name, substituteWidget);
17698
- };
17699
- const replaceDeprecatedWidgets = () => {
17700
- replaceWidget("transformer", "deprecated-standin");
17701
- replaceWidget("lights-puzzle", "deprecated-standin");
17702
- replaceWidget("reaction-diagram", "deprecated-standin");
17703
- replaceWidget("sequence", "deprecated-standin");
17704
- replaceWidget("simulator", "deprecated-standin");
17705
- replaceWidget("unit-input", "deprecated-standin");
17706
- };
17707
- const registerEditors = editorsToRegister => {
17708
- editorsToRegister.forEach(editor => {
17709
- if (!editor.widgetName) {
17710
- throw new PerseusError(`Editor ${editor.displayName} doesn't have a widgetName property`, Errors.Internal);
17711
- }
17712
- editors[editor.widgetName] = editor;
17713
- });
17714
- };
17715
-
17716
- /**
17717
- *
17718
- * @param name - the widget that you are trying to replace
17719
- * @param replacementName - the name of the widget that takes its place
17720
- *
17721
- * e.g. replaceEditor("transformer", "deprecated-standin") will make it so the
17722
- * transformer widget is replaced by the deprecated stand-in widget
17723
- */
17724
- const replaceEditor = (name, replacementName) => {
17725
- const substituteEditor = editors[replacementName];
17726
- if (!substituteEditor && Log) {
17727
- const errorMsg = `Failed to replace editor ${name} with ${replacementName}`;
17728
- Log.error(errorMsg, Errors.Internal);
17729
- return;
17730
- }
17731
- editors[name] = substituteEditor;
17732
- };
17733
- const replaceDeprecatedEditors = () => {
17734
- replaceEditor("transformer", "deprecated-standin");
17735
- replaceEditor("lights-puzzle", "deprecated-standin");
17736
- replaceEditor("reaction-diagram", "deprecated-standin");
17737
- replaceEditor("sequence", "deprecated-standin");
17738
- replaceEditor("simulator", "deprecated-standin");
17739
- replaceEditor("unit-input", "deprecated-standin");
17740
- };
17741
- const getWidget = name => {
17742
- var _widgets$name;
17743
- // TODO(alex): Consider referring to these as renderers to avoid
17744
- // overloading "widget"
17745
- if (!_.has(widgets, name)) {
17746
- return null;
17747
- }
17748
-
17749
- // Allow widgets to specify a widget directly or via a function
17750
- if ((_widgets$name = widgets[name]) != null && _widgets$name.getWidget) {
17751
- var _widgets$name$getWidg, _widgets$name2;
17752
- return (_widgets$name$getWidg = (_widgets$name2 = widgets[name]).getWidget) == null ? void 0 : _widgets$name$getWidg.call(_widgets$name2);
17753
- }
17754
- return widgets[name].widget;
17755
- };
17756
- const getWidgetExport = name => {
17757
- var _widgets$name3;
17758
- return (_widgets$name3 = widgets[name]) != null ? _widgets$name3 : null;
17759
- };
17760
- const getWidgetValidator = name => {
17761
- var _widgets$name$validat, _widgets$name4;
17762
- return (_widgets$name$validat = (_widgets$name4 = widgets[name]) == null ? void 0 : _widgets$name4.validator) != null ? _widgets$name$validat : null;
17763
- };
17764
- const getWidgetScorer = name => {
17765
- var _widgets$name$scorer, _widgets$name5;
17766
- return (_widgets$name$scorer = (_widgets$name5 = widgets[name]) == null ? void 0 : _widgets$name5.scorer) != null ? _widgets$name$scorer : null;
17767
- };
17768
- const getPublicWidgetOptionsFunction = name => {
17769
- var _widgets$name$getPubl, _widgets$name6;
17770
- return (_widgets$name$getPubl = (_widgets$name6 = widgets[name]) == null ? void 0 : _widgets$name6.getPublicWidgetOptions) != null ? _widgets$name$getPubl : i => i;
17771
- };
17772
- const getEditor = name => {
17773
- return _.has(editors, name) ? editors[name] : null;
17774
- };
17775
- const getTransform = name => {
17776
- return _.has(widgets, name) ? widgets[name].transform || _.identity : null;
17777
- };
17778
- const getVersion = name => {
17779
- const widgetInfo = widgets[name];
17780
- if (widgetInfo) {
17781
- return widgets[name].version || {
17782
- major: 0,
17783
- minor: 0
17784
- };
17785
- }
17786
- return;
17787
- };
17788
- const getVersionVector = () => {
17789
- const version = {};
17790
- _.each(_.keys(widgets), function (name) {
17791
- version[name] = getVersion(name);
17792
- });
17793
- return version;
17794
- };
17795
- const getPublicWidgets = () => {
17796
- // TODO(alex): Update underscore.js so that _.pick can take a function.
17797
- // @ts-expect-error - TS2740 - Type 'Pick<{ [key: string]: Readonly<{ name: string; displayName: string; getWidget?: (() => ComponentType<any>) | undefined; accessible?: boolean | ((props: any) => boolean) | undefined; hidden?: boolean | undefined; ... 10 more ...; widget: ComponentType<...>; }>; }, string>' is missing the following properties from type 'readonly Readonly<{ name: string; displayName: string; getWidget?: (() => ComponentType<any>) | undefined; accessible?: boolean | ((props: any) => boolean) | undefined; hidden?: boolean | undefined; ... 10 more ...; widget: ComponentType<...>; }>[]': length, concat, join, slice, and 18 more.
17798
- return _.pick(widgets,
17799
- // @ts-expect-error - TS2345 - Argument of type '(name: string) => boolean | undefined' is not assignable to parameter of type 'Iteratee<string[], boolean, string>'.
17800
- _.reject(_.keys(widgets), function (name) {
17801
- return widgets[name].hidden;
17802
- }));
17803
- };
17804
- const isAccessible = widgetInfo => {
17805
- const accessible = widgets[widgetInfo.type].accessible;
17806
- if (typeof accessible === "function") {
17807
- return accessible(widgetInfo.options);
17808
- }
17809
- return !!accessible;
17810
- };
17811
- const getAllWidgetTypes = () => {
17812
- return _.keys(widgets);
17813
- };
17814
- const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
17815
- const type = oldWidgetInfo.type;
17816
- // NOTE(jeremy): This looks like it could be replaced by fixing types so
17817
- // that `type` is non-optional. But we're seeing this in Sentry today so I
17818
- // suspect we have legacy data (potentially unpublished) and we should
17819
- // figure that out before depending solely on types.
17820
- if (!_.isString(type)) {
17821
- throw new PerseusError("widget type must be a string, but was: " + type, Errors.Internal);
17822
- }
17823
- const widgetExports = widgets[type];
17824
- if (widgetExports == null) {
17825
- // If we have a widget that isn't registered, we can't upgrade it
17826
- // TODO(aria): Figure out what the best thing to do here would be
17827
- return oldWidgetInfo;
17828
- }
17829
-
17830
- // Unversioned widgets (pre-July 2014) are all implicitly 0.0
17831
- const initialVersion = oldWidgetInfo.version || {
17832
- major: 0,
17833
- minor: 0
17834
- };
17835
- const latestVersion = widgetExports.version || {
17836
- major: 0,
17837
- minor: 0
17838
- };
17839
-
17840
- // If the widget version is later than what we understand (major
17841
- // version is higher than latest, or major versions are equal and minor
17842
- // version is higher than latest), don't perform any upgrades.
17843
- if (initialVersion.major > latestVersion.major || initialVersion.major === latestVersion.major && initialVersion.minor > latestVersion.minor) {
17844
- return oldWidgetInfo;
17845
- }
17846
-
17847
- // We do a clone here so that it's safe to mutate the input parameter
17848
- // in propUpgrades functions (which I will probably accidentally do at
17849
- // some point, and we would like to not break when that happens).
17850
- let newEditorProps = _.clone(oldWidgetInfo.options) || {};
17851
- const upgradePropsMap = widgetExports.propUpgrades || {};
17852
-
17853
- // Empty props usually mean a newly created widget by the editor,
17854
- // and are always considerered up-to-date.
17855
- // Mostly, we'd rather not run upgrade functions on props that are
17856
- // not complete.
17857
- if (_.keys(newEditorProps).length !== 0) {
17858
- // We loop through all the versions after the current version of
17859
- // the loaded widget, up to and including the latest version of the
17860
- // loaded widget, and run the upgrade function to bring our loaded
17861
- // widget's props up to that version.
17862
- // There is a little subtlety here in that we call
17863
- // upgradePropsMap[1] to upgrade *to* version 1,
17864
- // (not from version 1).
17865
- for (let nextVersion = initialVersion.major + 1; nextVersion <= latestVersion.major; nextVersion++) {
17866
- if (upgradePropsMap[String(nextVersion)]) {
17867
- newEditorProps = upgradePropsMap[String(nextVersion)](newEditorProps);
17868
- } else {
17869
- // This is a Log.error because it is unlikely to be hit in
17870
- // local testing, and a Log.error is slightly less scary in
17871
- // prod than a `throw new Error`
17872
- Log.error("No upgrade found for widget. Cannot render.", Errors.Internal, {
17873
- loggedMetadata: {
17874
- type,
17875
- fromMajorVersion: nextVersion - 1,
17876
- toMajorVersion: nextVersion
17877
- }
17878
- });
17879
- // But try to keep going anyways (yolo!)
17880
- // (Throwing an error here would just break the page
17881
- // silently anyways, so that doesn't seem much better
17882
- // than a halfhearted attempt to continue, however
17883
- // shallow...)
17884
- }
17885
- }
17886
- }
17887
-
17888
- // Minor version upgrades (eg. new optional props) don't have
17889
- // transform functions. Instead, we fill in the new props with their
17890
- // defaults.
17891
- const defaultProps = type in editors ? editors[type].defaultProps : {};
17892
- newEditorProps = _extends({}, defaultProps, newEditorProps);
17893
- let alignment = oldWidgetInfo.alignment;
17894
-
17895
- // Widgets that support multiple alignments will "lock in" the
17896
- // alignment to the alignment that would be listed first in the
17897
- // select box. If the widget only supports one alignment, the
17898
- // alignment value will likely just end up as "default".
17899
- if (alignment == null || alignment === "default") {
17900
- alignment = getSupportedAlignments(type)[0];
17901
- }
17902
- let widgetStatic = oldWidgetInfo.static;
17903
- if (widgetStatic == null) {
17904
- widgetStatic = DEFAULT_STATIC;
17905
- }
17906
- return _.extend({}, oldWidgetInfo, {
17907
- // maintain other info, like type
17908
- // After upgrading we guarantee that the version is up-to-date
17909
- version: latestVersion,
17910
- // Default graded to true (so null/undefined becomes true):
17911
- graded: oldWidgetInfo.graded != null ? oldWidgetInfo.graded : true,
17912
- alignment: alignment,
17913
- static: widgetStatic,
17914
- options: newEditorProps
17915
- });
17916
- };
17917
- const getRendererPropsForWidgetInfo = (widgetInfo, strings, problemNum) => {
17918
- const type = widgetInfo.type;
17919
- const widgetExports = widgets[type];
17920
- if (widgetExports == null) {
17921
- // The widget is not a registered widget
17922
- // It shouldn't matter what we return here, but for consistency
17923
- // we return the untransformed options, as if the widget did
17924
- // not have a transform defined.
17925
- return widgetInfo;
17926
- }
17927
- let transform;
17928
- if (widgetInfo.static) {
17929
- // There aren't a lot of real places where we'll have to default to
17930
- // _.identity, but it's theoretically possible if someone changes
17931
- // the JSON manually / we have to back out static support for a
17932
- // widget.
17933
- transform = getStaticTransform(type) || _.identity;
17934
- } else {
17935
- transform = widgetExports.transform || _.identity;
17936
- }
17937
- // widgetInfo.options are the widgetEditor's props:
17938
- return transform(widgetInfo.options, strings, problemNum);
17939
- };
17940
- const traverseChildWidgets$3 = (widgetInfo, traverseRenderer) => {
17941
- if (!traverseRenderer) {
17942
- throw new PerseusError("traverseRenderer must be provided, but was not", Errors.Internal);
17943
- }
17944
- if (!widgetInfo || !widgetInfo.type || !widgets[widgetInfo.type]) {
17945
- return widgetInfo;
17946
- }
17947
- const widgetExports = widgets[widgetInfo.type];
17948
- const props = widgetInfo.options;
17949
- if (widgetExports.traverseChildWidgets && props) {
17950
- const newProps = widgetExports.traverseChildWidgets(props, traverseRenderer);
17951
- return _.extend({}, widgetInfo, {
17952
- options: newProps
17953
- });
17954
- }
17955
- return widgetInfo;
17956
- };
17957
-
17958
- /**
17959
- * Handling for the optional alignments for widgets
17960
- * See widget-container.jsx for details on how alignments are implemented.
17961
- */
17962
-
17963
- /**
17964
- * Returns the list of supported alignments for the given (string) widget
17965
- * type. This is used primarily at editing time to display the choices
17966
- * for the user.
17967
- *
17968
- * Supported alignments are given as an array of strings in the exports of
17969
- * a widget's module.
17970
- */
17971
- const getSupportedAlignments = type => {
17972
- const widgetExport = widgets[type];
17973
- // @ts-expect-error - TS2322 - Type 'string[] | readonly Alignment[]' is not assignable to type 'readonly Alignment[]'.
17974
- return widgetExport && widgetExport.supportedAlignments || DEFAULT_SUPPORTED_ALIGNMENTS;
17975
- };
17976
-
17977
- /**
17978
- * For the given (string) widget type, determine the default alignment for
17979
- * the widget. This is used at rendering time to go from "default" alignment
17980
- * to the actual alignment displayed on the screen.
17981
- *
17982
- * The default alignment is given either as a string (called
17983
- * `defaultAlignment`) or a function (called `getDefaultAlignment`) on
17984
- * the exports of a widget's module.
17985
- */
17986
- const getDefaultAlignment = type => {
17987
- const widgetExports = widgets[type];
17988
- let alignment;
17989
- if (!widgetExports) {
17990
- return DEFAULT_ALIGNMENT$1;
17991
- }
17992
- if (widgetExports.getDefaultAlignment) {
17993
- alignment = widgetExports.getDefaultAlignment();
17994
- } else {
17995
- alignment = widgetExports.defaultAlignment;
17996
- }
17997
- return alignment || DEFAULT_ALIGNMENT$1;
17998
- };
17999
- const validAlignments = ["block", "inline-block", "inline", "float-left", "float-right", "full-width"];
18000
-
18001
- /**
18002
- * Used at startup to fail fast if an alignment given by a widget is
18003
- * invalid.
18004
- */
18005
- // TODO(alex): Change this to run as a testcase (vs. being run at runtime)
18006
- // TODO(LP-10707): I think this can be completely removed because our TypeScript types
18007
- // enforce this!
18008
- const validateAlignments = () => {
18009
- _.each(widgets, function (widgetInfo) {
18010
- if (widgetInfo.defaultAlignment && !_.contains(validAlignments, widgetInfo.defaultAlignment)) {
18011
- throw new PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid defaultAlignment value: " + widgetInfo.defaultAlignment, Errors.InvalidInput);
18012
- }
18013
- if (widgetInfo.supportedAlignments) {
18014
- const unknownAlignments = _.difference(widgetInfo.supportedAlignments, validAlignments);
18015
- if (unknownAlignments.length) {
18016
- throw new PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid value for supportedAlignments: " + unknownAlignments.join(" "), Errors.InvalidInput);
18017
- }
18018
- }
18019
- });
18020
- };
18021
-
18022
- /**
18023
- * Handling for static mode for widgets that support it.
18024
- */
18025
-
18026
- /**
18027
- * Returns true iff the widget supports static mode.
18028
- * A widget implicitly supports static mode if it exports a
18029
- * staticTransform function.
18030
- */
18031
- const supportsStaticMode = type => {
18032
- const widgetInfo = widgets[type];
18033
- return widgetInfo && widgetInfo.staticTransform != null;
18034
- };
18035
-
18036
- /**
18037
- * Return the staticTransform function used to convert the editorProps to
18038
- * the rendered widget state.
18039
- */
18040
- const getStaticTransform = type => {
18041
- const widgetInfo = widgets[type];
18042
- return widgetInfo && widgetInfo.staticTransform;
18043
- };
18044
-
18045
- /**
18046
- * Returns the tracking option for the widget. The default is "",
18047
- * which means simply to track interactions once. The other available
18048
- * option is "all" which means to track all interactions.
18049
- */
18050
- const getTracking = type => {
18051
- const widgetExport = widgets[type];
18052
- return widgetExport && widgetExport.tracking || DEFAULT_TRACKING;
18053
- };
18054
-
18055
- /**
18056
- * Returns true if this widget can include lintable markdown text
18057
- * and supports a highlightLint prop, or false otherwise.
18058
- */
18059
- const isLintable = type => {
18060
- const widgetExports = widgets[type];
18061
- return widgetExports && widgetExports.isLintable || DEFAULT_LINTABLE;
18062
- };
18063
-
18064
- var widgets$1 = /*#__PURE__*/Object.freeze({
18065
- __proto__: null,
18066
- registerWidget: registerWidget,
18067
- registerWidgets: registerWidgets,
18068
- replaceWidget: replaceWidget,
18069
- replaceDeprecatedWidgets: replaceDeprecatedWidgets,
18070
- registerEditors: registerEditors,
18071
- replaceEditor: replaceEditor,
18072
- replaceDeprecatedEditors: replaceDeprecatedEditors,
18073
- getWidget: getWidget,
18074
- getWidgetExport: getWidgetExport,
18075
- getWidgetValidator: getWidgetValidator,
18076
- getWidgetScorer: getWidgetScorer,
18077
- getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
18078
- getEditor: getEditor,
18079
- getTransform: getTransform,
18080
- getVersion: getVersion,
18081
- getVersionVector: getVersionVector,
18082
- getPublicWidgets: getPublicWidgets,
18083
- isAccessible: isAccessible,
18084
- getAllWidgetTypes: getAllWidgetTypes,
18085
- upgradeWidgetInfoToLatestVersion: upgradeWidgetInfoToLatestVersion,
18086
- getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
18087
- traverseChildWidgets: traverseChildWidgets$3,
18088
- getSupportedAlignments: getSupportedAlignments,
18089
- getDefaultAlignment: getDefaultAlignment,
18090
- validateAlignments: validateAlignments,
18091
- supportsStaticMode: supportsStaticMode,
18092
- getStaticTransform: getStaticTransform,
18093
- getTracking: getTracking,
18094
- isLintable: isLintable
18095
- });
18096
-
18097
- function getUpgradedWidgetOptions(oldWidgetOptions) {
18098
- return mapObject(oldWidgetOptions, (widgetInfo, widgetId) => {
18099
- if (!widgetInfo.type || !widgetInfo.alignment) {
18100
- const newValues = {};
18101
- if (!widgetInfo.type) {
18102
- // TODO: why does widget have no type?
18103
- // We don't want to derive type from widget ID
18104
- // see: LEMS-1845
18105
- newValues.type = widgetId.split(" ")[0];
18106
- }
18107
- if (!widgetInfo.alignment) {
18108
- newValues.alignment = "default";
18109
- }
18110
- widgetInfo = _extends({}, widgetInfo, newValues);
18111
- }
18112
- // TODO(LEMS-2656): remove TS suppression
18113
- return upgradeWidgetInfoToLatestVersion(widgetInfo);
18114
- });
18115
- }
18116
-
18117
- /**
18118
- * Checks the given user input to see if any answerable widgets have not been
18119
- * "filled in" (ie. if they're empty). Another way to think about this
18120
- * function is that its a check to see if we can score the provided input.
18121
- */
18122
- function emptyWidgetsFunctional(widgets,
18123
- // This is a port of old code, I'm not sure why
18124
- // we need widgetIds vs the keys of the widgets object
18125
- widgetIds, userInputMap, strings, locale) {
18126
- return widgetIds.filter(id => {
18127
- const widget = widgets[id];
18128
- if (!widget || widget.static === true) {
18129
- // Static widgets shouldn't count as empty
18130
- return false;
18131
- }
18132
- const validator = getWidgetValidator(widget.type);
18133
- const userInput = userInputMap[id];
18134
- const validationData = widget.options;
18135
- const score = validator == null ? void 0 : validator(userInput, validationData, strings, locale);
18136
- if (score) {
18137
- return scoreIsEmpty(score);
18138
- }
18139
- });
18140
- }
18141
-
18142
- // TODO: combine scorePerseusItem with scoreWidgetsFunctional
18143
- // once scorePerseusItem is the only one calling scoreWidgetsFunctional
18144
- function scorePerseusItem(perseusRenderData, userInputMap,
18145
- // TODO(LEMS-2461,LEMS-2391): these probably
18146
- // need to be removed before we move this to the server
18147
- strings, locale) {
18148
- // There seems to be a chance that PerseusRenderer.widgets might include
18149
- // widget data for widgets that are not in PerseusRenderer.content,
18150
- // so this checks that the widgets are being used before scoring them
18151
- const usedWidgetIds = getWidgetIdsFromContent(perseusRenderData.content);
18152
- const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
18153
- return flattenScores(scores);
18154
- }
18155
- function scoreWidgetsFunctional(widgets,
18156
- // This is a port of old code, I'm not sure why
18157
- // we need widgetIds vs the keys of the widgets object
18158
- widgetIds, userInputMap, strings, locale) {
18159
- const upgradedWidgets = getUpgradedWidgetOptions(widgets);
18160
- const gradedWidgetIds = widgetIds.filter(id => {
18161
- const props = upgradedWidgets[id];
18162
- const widgetIsGraded = (props == null ? void 0 : props.graded) == null || props.graded;
18163
- const widgetIsStatic = !!(props != null && props.static);
18164
- // Ungraded widgets or widgets set to static shouldn't be graded.
18165
- return widgetIsGraded && !widgetIsStatic;
18166
- });
18167
- const widgetScores = {};
18168
- gradedWidgetIds.forEach(id => {
18169
- var _validator;
18170
- const widget = upgradedWidgets[id];
18171
- if (!widget) {
18172
- return;
18173
- }
18174
- const userInput = userInputMap[id];
18175
- const validator = getWidgetValidator(widget.type);
18176
- const scorer = getWidgetScorer(widget.type);
18177
-
18178
- // We do validation (empty checks) first and then scoring. If
18179
- // validation fails, it's result is itself a PerseusScore.
18180
- 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);
18181
- if (score != null) {
18182
- widgetScores[id] = score;
18183
- }
18184
- });
18185
- return widgetScores;
18186
- }
18187
-
18188
17322
  /**
18189
17323
  * TranslationLinter asynchronously calls Khan Academy's poentry linter.
18190
17324
  *
@@ -18341,6 +17475,261 @@ const getInteractiveBoxFromSizeClass = sizeClass => {
18341
17475
  return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
18342
17476
  };
18343
17477
 
17478
+ const DEFAULT_TRACKING = "";
17479
+ const DEFAULT_LINTABLE = false;
17480
+ const widgets = {};
17481
+ const editors = {};
17482
+
17483
+ // Widgets must be registered to avoid circular dependencies with the
17484
+ // core Editor and Renderer components.
17485
+ // TODO(jeremy): The widget name is already embedded in the WidgetExports type
17486
+ // so could we drop the `name` parameter here?
17487
+ const registerWidget = (name, widget) => {
17488
+ widgets[name] = widget;
17489
+ };
17490
+ const registerWidgets = widgets => {
17491
+ widgets.forEach(widget => {
17492
+ registerWidget(widget.name, widget);
17493
+ });
17494
+ };
17495
+
17496
+ /**
17497
+ *
17498
+ * @param name - the widget that you are trying to replace
17499
+ * @param replacementName - the name of the widget that takes its place
17500
+ *
17501
+ * e.g. replaceWidget("transformer", "deprecated-standin") will make it so the
17502
+ * transformer widget is replaced by the always correct widget
17503
+ */
17504
+ const replaceWidget = (name, replacementName) => {
17505
+ const substituteWidget = widgets[replacementName];
17506
+
17507
+ // If the replacement widget isn't found, we need to throw. Otherwise after
17508
+ // removing the deprecated widget, we'll have data asking for a widget type
17509
+ // that doesn't exist at all.
17510
+ if (!substituteWidget) {
17511
+ const errorMsg = `Failed to replace ${name} with ${replacementName}`;
17512
+ throw new PerseusError(errorMsg, Errors.Internal);
17513
+ }
17514
+ registerWidget(name, substituteWidget);
17515
+ };
17516
+ const replaceDeprecatedWidgets = () => {
17517
+ replaceWidget("transformer", "deprecated-standin");
17518
+ replaceWidget("lights-puzzle", "deprecated-standin");
17519
+ replaceWidget("reaction-diagram", "deprecated-standin");
17520
+ replaceWidget("sequence", "deprecated-standin");
17521
+ replaceWidget("simulator", "deprecated-standin");
17522
+ replaceWidget("unit-input", "deprecated-standin");
17523
+ };
17524
+ const registerEditors = editorsToRegister => {
17525
+ editorsToRegister.forEach(editor => {
17526
+ if (!editor.widgetName) {
17527
+ throw new PerseusError(`Editor ${editor.displayName} doesn't have a widgetName property`, Errors.Internal);
17528
+ }
17529
+ editors[editor.widgetName] = editor;
17530
+ });
17531
+ };
17532
+
17533
+ /**
17534
+ *
17535
+ * @param name - the widget that you are trying to replace
17536
+ * @param replacementName - the name of the widget that takes its place
17537
+ *
17538
+ * e.g. replaceEditor("transformer", "deprecated-standin") will make it so the
17539
+ * transformer widget is replaced by the deprecated stand-in widget
17540
+ */
17541
+ const replaceEditor = (name, replacementName) => {
17542
+ const substituteEditor = editors[replacementName];
17543
+ if (!substituteEditor && Log) {
17544
+ const errorMsg = `Failed to replace editor ${name} with ${replacementName}`;
17545
+ Log.error(errorMsg, Errors.Internal);
17546
+ return;
17547
+ }
17548
+ editors[name] = substituteEditor;
17549
+ };
17550
+ const replaceDeprecatedEditors = () => {
17551
+ replaceEditor("transformer", "deprecated-standin");
17552
+ replaceEditor("lights-puzzle", "deprecated-standin");
17553
+ replaceEditor("reaction-diagram", "deprecated-standin");
17554
+ replaceEditor("sequence", "deprecated-standin");
17555
+ replaceEditor("simulator", "deprecated-standin");
17556
+ replaceEditor("unit-input", "deprecated-standin");
17557
+ };
17558
+ const getWidget = name => {
17559
+ var _widgets$name;
17560
+ // TODO(alex): Consider referring to these as renderers to avoid
17561
+ // overloading "widget"
17562
+ if (!_.has(widgets, name)) {
17563
+ return null;
17564
+ }
17565
+
17566
+ // Allow widgets to specify a widget directly or via a function
17567
+ if ((_widgets$name = widgets[name]) != null && _widgets$name.getWidget) {
17568
+ var _widgets$name$getWidg, _widgets$name2;
17569
+ return (_widgets$name$getWidg = (_widgets$name2 = widgets[name]).getWidget) == null ? void 0 : _widgets$name$getWidg.call(_widgets$name2);
17570
+ }
17571
+ return widgets[name].widget;
17572
+ };
17573
+ const getWidgetExport = name => {
17574
+ var _widgets$name3;
17575
+ return (_widgets$name3 = widgets[name]) != null ? _widgets$name3 : null;
17576
+ };
17577
+ const getPublicWidgetOptionsFunction = name => {
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;
17580
+ };
17581
+ const getEditor = name => {
17582
+ return _.has(editors, name) ? editors[name] : null;
17583
+ };
17584
+ const getTransform = name => {
17585
+ return _.has(widgets, name) ? widgets[name].transform || _.identity : null;
17586
+ };
17587
+ const getVersion = name => {
17588
+ const widgetInfo = widgets[name];
17589
+ if (widgetInfo) {
17590
+ return widgets[name].version || {
17591
+ major: 0,
17592
+ minor: 0
17593
+ };
17594
+ }
17595
+ return;
17596
+ };
17597
+ const getVersionVector = () => {
17598
+ const version = {};
17599
+ _.each(_.keys(widgets), function (name) {
17600
+ version[name] = getVersion(name);
17601
+ });
17602
+ return version;
17603
+ };
17604
+ const getPublicWidgets = () => {
17605
+ // TODO(alex): Update underscore.js so that _.pick can take a function.
17606
+ // @ts-expect-error - TS2740 - Type 'Pick<{ [key: string]: Readonly<{ name: string; displayName: string; getWidget?: (() => ComponentType<any>) | undefined; accessible?: boolean | ((props: any) => boolean) | undefined; hidden?: boolean | undefined; ... 10 more ...; widget: ComponentType<...>; }>; }, string>' is missing the following properties from type 'readonly Readonly<{ name: string; displayName: string; getWidget?: (() => ComponentType<any>) | undefined; accessible?: boolean | ((props: any) => boolean) | undefined; hidden?: boolean | undefined; ... 10 more ...; widget: ComponentType<...>; }>[]': length, concat, join, slice, and 18 more.
17607
+ return _.pick(widgets,
17608
+ // @ts-expect-error - TS2345 - Argument of type '(name: string) => boolean | undefined' is not assignable to parameter of type 'Iteratee<string[], boolean, string>'.
17609
+ _.reject(_.keys(widgets), function (name) {
17610
+ return widgets[name].hidden;
17611
+ }));
17612
+ };
17613
+ const isAccessible = widgetInfo => {
17614
+ const accessible = widgets[widgetInfo.type].accessible;
17615
+ if (typeof accessible === "function") {
17616
+ return accessible(widgetInfo.options);
17617
+ }
17618
+ return !!accessible;
17619
+ };
17620
+ const getAllWidgetTypes = () => {
17621
+ return _.keys(widgets);
17622
+ };
17623
+ const getRendererPropsForWidgetInfo = (widgetInfo, strings, problemNum) => {
17624
+ const type = widgetInfo.type;
17625
+ const widgetExports = widgets[type];
17626
+ if (widgetExports == null) {
17627
+ // The widget is not a registered widget
17628
+ // It shouldn't matter what we return here, but for consistency
17629
+ // we return the untransformed options, as if the widget did
17630
+ // not have a transform defined.
17631
+ return widgetInfo;
17632
+ }
17633
+ let transform;
17634
+ if (widgetInfo.static) {
17635
+ // There aren't a lot of real places where we'll have to default to
17636
+ // _.identity, but it's theoretically possible if someone changes
17637
+ // the JSON manually / we have to back out static support for a
17638
+ // widget.
17639
+ transform = getStaticTransform(type) || _.identity;
17640
+ } else {
17641
+ transform = widgetExports.transform || _.identity;
17642
+ }
17643
+ // widgetInfo.options are the widgetEditor's props:
17644
+ return transform(widgetInfo.options, strings, problemNum);
17645
+ };
17646
+ const traverseChildWidgets$3 = (widgetInfo, traverseRenderer) => {
17647
+ if (!traverseRenderer) {
17648
+ throw new PerseusError("traverseRenderer must be provided, but was not", Errors.Internal);
17649
+ }
17650
+ if (!widgetInfo || !widgetInfo.type || !widgets[widgetInfo.type]) {
17651
+ return widgetInfo;
17652
+ }
17653
+ const widgetExports = widgets[widgetInfo.type];
17654
+ const props = widgetInfo.options;
17655
+ if (widgetExports.traverseChildWidgets && props) {
17656
+ const newProps = widgetExports.traverseChildWidgets(props, traverseRenderer);
17657
+ return _.extend({}, widgetInfo, {
17658
+ options: newProps
17659
+ });
17660
+ }
17661
+ return widgetInfo;
17662
+ };
17663
+
17664
+ /**
17665
+ * Handling for static mode for widgets that support it.
17666
+ */
17667
+
17668
+ /**
17669
+ * Returns true iff the widget supports static mode.
17670
+ * A widget implicitly supports static mode if it exports a
17671
+ * staticTransform function.
17672
+ */
17673
+ const supportsStaticMode = type => {
17674
+ const widgetInfo = widgets[type];
17675
+ return widgetInfo && widgetInfo.staticTransform != null;
17676
+ };
17677
+
17678
+ /**
17679
+ * Return the staticTransform function used to convert the editorProps to
17680
+ * the rendered widget state.
17681
+ */
17682
+ const getStaticTransform = type => {
17683
+ const widgetInfo = widgets[type];
17684
+ return widgetInfo && widgetInfo.staticTransform;
17685
+ };
17686
+
17687
+ /**
17688
+ * Returns the tracking option for the widget. The default is "",
17689
+ * which means simply to track interactions once. The other available
17690
+ * option is "all" which means to track all interactions.
17691
+ */
17692
+ const getTracking = type => {
17693
+ const widgetExport = widgets[type];
17694
+ return widgetExport && widgetExport.tracking || DEFAULT_TRACKING;
17695
+ };
17696
+
17697
+ /**
17698
+ * Returns true if this widget can include lintable markdown text
17699
+ * and supports a highlightLint prop, or false otherwise.
17700
+ */
17701
+ const isLintable = type => {
17702
+ const widgetExports = widgets[type];
17703
+ return widgetExports && widgetExports.isLintable || DEFAULT_LINTABLE;
17704
+ };
17705
+
17706
+ var widgets$1 = /*#__PURE__*/Object.freeze({
17707
+ __proto__: null,
17708
+ registerWidget: registerWidget,
17709
+ registerWidgets: registerWidgets,
17710
+ replaceWidget: replaceWidget,
17711
+ replaceDeprecatedWidgets: replaceDeprecatedWidgets,
17712
+ registerEditors: registerEditors,
17713
+ replaceEditor: replaceEditor,
17714
+ replaceDeprecatedEditors: replaceDeprecatedEditors,
17715
+ getWidget: getWidget,
17716
+ getWidgetExport: getWidgetExport,
17717
+ getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
17718
+ getEditor: getEditor,
17719
+ getTransform: getTransform,
17720
+ getVersion: getVersion,
17721
+ getVersionVector: getVersionVector,
17722
+ getPublicWidgets: getPublicWidgets,
17723
+ isAccessible: isAccessible,
17724
+ getAllWidgetTypes: getAllWidgetTypes,
17725
+ getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
17726
+ traverseChildWidgets: traverseChildWidgets$3,
17727
+ supportsStaticMode: supportsStaticMode,
17728
+ getStaticTransform: getStaticTransform,
17729
+ getTracking: getTracking,
17730
+ isLintable: isLintable
17731
+ });
17732
+
18344
17733
  class WidgetContainer extends React.Component {
18345
17734
  constructor(...args) {
18346
17735
  super(...args);
@@ -18413,7 +17802,7 @@ class WidgetContainer extends React.Component {
18413
17802
  }
18414
17803
  let alignment = this.state.widgetProps.alignment;
18415
17804
  if (alignment === "default") {
18416
- alignment = getDefaultAlignment(type);
17805
+ alignment = CoreWidgetRegistry.getDefaultAlignment(type);
18417
17806
  }
18418
17807
  className += " widget-" + alignment;
18419
17808
  const apiOptions = this.state.widgetProps.apiOptions;
@@ -18421,6 +17810,7 @@ class WidgetContainer extends React.Component {
18421
17810
  // Hack to prevent interaction with static widgets: we overlay a big
18422
17811
  // div on top of the widget and overflow: hidden the container.
18423
17812
  // Ideally widgets themselves should know how to prevent interaction.
17813
+ // UPDATE HTML5: `inert` on the underlying div would be better
18424
17814
  const isStatic = this.state.widgetProps.static || apiOptions.readOnly;
18425
17815
  const staticContainerStyles = {
18426
17816
  position: "relative",
@@ -18432,7 +17822,7 @@ class WidgetContainer extends React.Component {
18432
17822
  position: "absolute",
18433
17823
  top: 0,
18434
17824
  left: 0,
18435
- zIndex: zIndexInteractiveComponent$1
17825
+ zIndex: 3
18436
17826
  };
18437
17827
 
18438
17828
  // Some widgets may include strings of markdown that we may
@@ -19616,7 +19006,7 @@ class Renderer extends React.Component {
19616
19006
  * empty if the graph is in the starting state.
19617
19007
  */
19618
19008
  emptyWidgets() {
19619
- 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);
19620
19010
  }
19621
19011
  /**
19622
19012
  * Returns an array of the widget `.getUserInput()` results
@@ -19676,7 +19066,7 @@ class Renderer extends React.Component {
19676
19066
  * @deprecated use scorePerseusItem
19677
19067
  */
19678
19068
  score() {
19679
- 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);
19680
19070
  const combinedScore = flattenScores(scores);
19681
19071
  return combinedScore;
19682
19072
  }
@@ -20216,7 +19606,7 @@ class InputWithExamples extends React.Component {
20216
19606
  };
20217
19607
  this._getInputClassName = () => {
20218
19608
  // Otherwise, we need to add these INPUT and FOCUSED tags here.
20219
- let className = ClassNames.INPUT + " " + ClassNames.INTERACTIVE;
19609
+ let className = ClassNames.INPUT;
20220
19610
  if (this.state.focused) {
20221
19611
  className += " " + ClassNames.FOCUSED;
20222
19612
  }
@@ -20628,14 +20018,10 @@ const propTransform$3 = widgetOptions => {
20628
20018
  var InputNumber$1 = {
20629
20019
  name: "input-number",
20630
20020
  displayName: "Input number (deprecated - use numeric input instead)",
20631
- defaultAlignment: "inline-block",
20632
20021
  hidden: true,
20633
20022
  widget: InputNumber,
20634
20023
  transform: propTransform$3,
20635
20024
  isLintable: true,
20636
- // TODO(LEMS-2656): remove TS suppression
20637
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInputNumberUserInput'.
20638
- scorer: scoreInputNumber,
20639
20025
  getOneCorrectAnswerFromRubric(rubric) {
20640
20026
  if (rubric.value == null) {
20641
20027
  return;
@@ -20892,14 +20278,11 @@ const propsTransform = function propsTransform(widgetOptions) {
20892
20278
  var NumericInput$1 = {
20893
20279
  name: "numeric-input",
20894
20280
  displayName: "Numeric input",
20895
- defaultAlignment: "inline-block",
20896
20281
  accessible: true,
20897
20282
  widget: NumericInput,
20898
20283
  transform: propsTransform,
20899
20284
  isLintable: true,
20900
- // TODO(LEMS-2656): remove TS suppression
20901
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumericInputUserInput'.
20902
- scorer: scoreNumericInput,
20285
+ getPublicWidgetOptions: getNumericInputPublicWidgetOptions,
20903
20286
  // TODO(LEMS-2656): remove TS suppression
20904
20287
  // @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusNumericInputRubric'
20905
20288
  getOneCorrectAnswerFromRubric(rubric) {
@@ -20960,23 +20343,6 @@ const getPromptJSON$p = renderProps => {
20960
20343
  };
20961
20344
  };
20962
20345
 
20963
- /**
20964
- * Several widgets don't have "right"/"wrong" scoring logic,
20965
- * so this just says to move on past those widgets
20966
- *
20967
- * TODO(LEMS-2543) widgets that use this probably shouldn't have any
20968
- * scoring logic and the thing scoring an exercise
20969
- * should just know to skip these
20970
- */
20971
- function scoreNoop(points = 0) {
20972
- return {
20973
- type: "points",
20974
- earned: points,
20975
- total: points,
20976
- message: null
20977
- };
20978
- }
20979
-
20980
20346
  /**
20981
20347
  * Utility functions for manipulating ranges of highlightable content.
20982
20348
  */
@@ -23152,8 +22518,7 @@ var Passage$1 = {
23152
22518
  transform: editorProps => {
23153
22519
  return _.pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
23154
22520
  },
23155
- isLintable: true,
23156
- scorer: () => scoreNoop()
22521
+ isLintable: true
23157
22522
  };
23158
22523
 
23159
22524
  /**
@@ -23275,16 +22640,13 @@ var PassageRef$1 = {
23275
22640
  name: "passage-ref",
23276
22641
  displayName: "PassageRef (SAT only)",
23277
22642
  hidden: true,
23278
- defaultAlignment: "inline",
23279
22643
  widget: PassageRef,
23280
22644
  transform: widgetOptions => ({
23281
22645
  passageNumber: widgetOptions.passageNumber,
23282
22646
  referenceNumber: widgetOptions.referenceNumber,
23283
22647
  summaryText: widgetOptions.summaryText
23284
22648
  }),
23285
- version: passageRefLogic.version,
23286
- // TODO: things that aren't interactive shouldn't need scoring functions
23287
- scorer: () => scoreNoop()
22649
+ version: passageRefLogic.version
23288
22650
  };
23289
22651
 
23290
22652
  /**
@@ -23326,109 +22688,6 @@ var mediaQueries = {
23326
22688
  xlOrLarger: `@media screen and (min-width: ${pureXlMin})`
23327
22689
  };
23328
22690
 
23329
- const {
23330
- zIndexAboveScratchpad,
23331
- zIndexInteractiveComponent,
23332
- radioBorderColor,
23333
- checkedColor,
23334
- circleSize,
23335
- radioMarginWidth
23336
- } = constants;
23337
- var sharedStyles = StyleSheet.create({
23338
- perseusInteractive: {
23339
- zIndex: zIndexInteractiveComponent,
23340
- position: "relative"
23341
- },
23342
- aboveScratchpad: {
23343
- position: "relative",
23344
- zIndex: zIndexAboveScratchpad
23345
- },
23346
- blankBackground: {
23347
- // TODO(emily): Use WB colors?
23348
- backgroundColor: "#FDFDFD"
23349
- },
23350
- perseusSrOnly: {
23351
- border: 0,
23352
- clip: "rect(0,0,0,0)",
23353
- height: 1,
23354
- margin: -1,
23355
- overflow: "hidden",
23356
- padding: 0,
23357
- position: "absolute",
23358
- width: 1
23359
- },
23360
- responsiveLabel: {
23361
- // NOTE(charlie): The values used here should be kept in sync with the
23362
- // caption text sizing in Perseus' styles.less.
23363
- // TODO(charlie): Migrate the captions over to using this style.
23364
- [mediaQueries.smOrSmaller]: {
23365
- fontSize: 14,
23366
- lineHeight: 1.3
23367
- },
23368
- [mediaQueries.md]: {
23369
- fontSize: 17,
23370
- lineHeight: 1.4
23371
- },
23372
- [mediaQueries.lgOrLarger]: {
23373
- fontSize: 20,
23374
- lineHeight: 1.4
23375
- }
23376
- },
23377
- responsiveInput: {
23378
- display: "inline-block",
23379
- WebkitAppearance: "none",
23380
- appearance: "none",
23381
- "::-ms-check": {
23382
- display: "none"
23383
- },
23384
- backgroundColor: "#fff",
23385
- border: "2px solid #fff",
23386
- boxShadow: `0 0px 0px 1px ${radioBorderColor}`,
23387
- outline: "none",
23388
- boxSizing: "border-box",
23389
- flexShrink: 0,
23390
- marginBottom: 1,
23391
- marginLeft: 1,
23392
- marginRight: 1,
23393
- marginTop: 1,
23394
- height: circleSize - 2,
23395
- width: circleSize - 2
23396
- },
23397
- responsiveRadioInput: {
23398
- borderRadius: "50%",
23399
- ":checked": {
23400
- backgroundColor: checkedColor,
23401
- border: "none",
23402
- borderRadius: "50%",
23403
- boxShadow: `inset 0px 0px 0px 2px white, ` + `0 0px 0px 2px ${checkedColor}`,
23404
- marginTop: radioMarginWidth,
23405
- marginBottom: radioMarginWidth,
23406
- marginLeft: radioMarginWidth,
23407
- marginRight: radioMarginWidth,
23408
- height: circleSize - 2 * radioMarginWidth,
23409
- width: circleSize - 2 * radioMarginWidth
23410
- }
23411
- },
23412
- responsiveRadioInputActive: {
23413
- backgroundColor: "#fff",
23414
- border: "2px solid #fff",
23415
- borderRadius: "50%",
23416
- boxShadow: `0 0px 0px 2px ${checkedColor}`,
23417
- marginTop: radioMarginWidth,
23418
- marginBottom: radioMarginWidth,
23419
- marginLeft: radioMarginWidth,
23420
- marginRight: radioMarginWidth,
23421
- height: circleSize - 2 * radioMarginWidth,
23422
- width: circleSize - 2 * radioMarginWidth,
23423
- ":checked": {
23424
- backgroundColor: "#fff"
23425
- }
23426
- },
23427
- disableTextSelection: {
23428
- userSelect: "none"
23429
- }
23430
- });
23431
-
23432
22691
  /**
23433
22692
  * A small collection of useful scrolling utility functions.
23434
22693
  */
@@ -24487,7 +23746,7 @@ const BaseRadio = function BaseRadio({
24487
23746
  const aphroditeClassName = checked => {
24488
23747
  // Whether or not to show correctness borders
24489
23748
  // for this choice and the next choice.
24490
- 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);
24491
23750
  };
24492
23751
 
24493
23752
  // HACK(abdulrahman): Preloads the selection-state
@@ -25076,18 +24335,105 @@ var Radio = {
25076
24335
  staticTransform: transform,
25077
24336
  version: radioLogic.version,
25078
24337
  propUpgrades: radioLogic.widgetOptionsUpgrades,
25079
- isLintable: true,
25080
- // TODO(LEMS-2656): remove TS suppression
25081
- // @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
25082
- scorer: scoreRadio,
25083
- // TODO(LEMS-2656): remove TS suppression
25084
- // @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
25085
- validator: validateRadio
24338
+ isLintable: true
25086
24339
  };
25087
24340
 
25088
24341
  /* eslint-disable import/no-named-as-default */
25089
24342
  var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
25090
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
+
25091
24437
  const getPromptJSON$n = (renderProps, userInput) => {
25092
24438
  return {
25093
24439
  type: "categorizer",
@@ -25178,7 +24524,6 @@ class Categorizer extends React.Component {
25178
24524
  className: "category " + css(styles$n.cell),
25179
24525
  key: catNum
25180
24526
  }, /*#__PURE__*/React.createElement("div", {
25181
- className: ClassNames.INTERACTIVE,
25182
24527
  role: "button",
25183
24528
  "aria-label": catName,
25184
24529
  onClick: () => this.onChange(itemNum, catNum)
@@ -25277,12 +24622,6 @@ var Categorizer$1 = {
25277
24622
  return _.pick(editorProps, "items", "categories", "values", "randomizeItems");
25278
24623
  },
25279
24624
  isLintable: true,
25280
- // TODO(LEMS-2656): remove TS suppression
25281
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
25282
- scorer: scoreCategorizer,
25283
- // TODO(LEMS-2656): remove TS suppression
25284
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
25285
- validator: validateCategorizer,
25286
24625
  getPublicWidgetOptions: getCategorizerPublicWidgetOptions
25287
24626
  };
25288
24627
 
@@ -25488,12 +24827,9 @@ const styles$m = StyleSheet.create({
25488
24827
  var CSProgram$1 = {
25489
24828
  name: "cs-program",
25490
24829
  displayName: "CS Program",
25491
- supportedAlignments: ["block", "full-width"],
25492
24830
  widget: CSProgram,
25493
24831
  hidden: true,
25494
- // TODO(LEMS-2656): remove TS suppression
25495
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
25496
- scorer: scoreCSProgram
24832
+ getPublicWidgetOptions: getCSProgramPublicWidgetOptions
25497
24833
  };
25498
24834
 
25499
24835
  const getPromptJSON$l = renderProps => {
@@ -25566,11 +24902,8 @@ var Definition$1 = {
25566
24902
  name: "definition",
25567
24903
  displayName: "Definition",
25568
24904
  accessible: true,
25569
- defaultAlignment: "inline",
25570
24905
  widget: Definition,
25571
- transform: x => x,
25572
- // TODO: things that aren't interactive shouldn't need scoring functions
25573
- scorer: () => scoreNoop()
24906
+ transform: x => x
25574
24907
  };
25575
24908
 
25576
24909
  // There are no required props, but this component can receive props from any
@@ -25600,9 +24933,7 @@ var DeprecatedStandin$1 = {
25600
24933
  name: "deprecated-standin",
25601
24934
  displayName: "Deprecated Standin",
25602
24935
  widget: DeprecatedStandin,
25603
- hidden: true,
25604
- // TODO: things that aren't interactive shouldn't need scoring functions
25605
- scorer: () => scoreNoop(1)
24936
+ hidden: true
25606
24937
  };
25607
24938
 
25608
24939
  const getPromptJSON$k = (renderProps, userInput) => {
@@ -25714,16 +25045,10 @@ const optionsTransform = widgetOptions => {
25714
25045
  var Dropdown$1 = {
25715
25046
  name: "dropdown",
25716
25047
  displayName: "Drop down",
25717
- defaultAlignment: "inline-block",
25718
25048
  accessible: true,
25719
25049
  widget: Dropdown,
25720
25050
  transform: optionsTransform,
25721
- // TODO(LEMS-2656): remove TS suppression
25722
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
25723
- scorer: scoreDropdown,
25724
- // TODO(LEMS-2656): remove TS suppression
25725
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
25726
- validator: validateDropdown
25051
+ getPublicWidgetOptions: getDropdownPublicWidgetOptions
25727
25052
  };
25728
25053
 
25729
25054
  const getPromptJSON$j = renderProps => {
@@ -25878,12 +25203,9 @@ var Explanation$1 = {
25878
25203
  name: "explanation",
25879
25204
  displayName: "Explanation",
25880
25205
  accessible: true,
25881
- defaultAlignment: "inline",
25882
25206
  widget: Explanation,
25883
25207
  transform: _.identity,
25884
- isLintable: true,
25885
- // TODO: things that aren't interactive shouldn't need scoring functions
25886
- scorer: () => scoreNoop()
25208
+ isLintable: true
25887
25209
  };
25888
25210
 
25889
25211
  const getPromptJSON$i = (title, rendererJSON, hintRendererJSON) => {
@@ -26072,7 +25394,7 @@ class GradedGroup extends React.Component {
26072
25394
  DEFAULT_INVALID_MESSAGE_2
26073
25395
  } = this.context.strings;
26074
25396
  const status = score.type === "points" ? score.total === score.earned ? GRADING_STATUSES.correct : GRADING_STATUSES.incorrect : GRADING_STATUSES.invalid;
26075
- const message = score.type === "points" ? score.message || "" : score.message ? `${INVALID_MESSAGE_PREFIX} ${mapErrorToString(score.message)}` : `${INVALID_MESSAGE_PREFIX} ${DEFAULT_INVALID_MESSAGE_1}${DEFAULT_INVALID_MESSAGE_2}`;
25397
+ const message = score.type === "points" ? score.message || "" : score.message ? `${INVALID_MESSAGE_PREFIX} ${mapErrorToString(score.message, this.context.strings)}` : `${INVALID_MESSAGE_PREFIX} ${DEFAULT_INVALID_MESSAGE_1}${DEFAULT_INVALID_MESSAGE_2}`;
26076
25398
  this.setState({
26077
25399
  status: status,
26078
25400
  message: message,
@@ -26983,7 +26305,7 @@ class FunctionGrapher extends React.Component {
26983
26305
  boxSizing: "initial"
26984
26306
  }
26985
26307
  }, /*#__PURE__*/React.createElement("div", {
26986
- className: "graphie-container above-scratchpad",
26308
+ className: "graphie-container blank-background",
26987
26309
  style: {
26988
26310
  width: box[0],
26989
26311
  height: box[1]
@@ -27122,8 +26444,7 @@ class Grapher extends React.Component {
27122
26444
  const coords = this.props.plot.coords;
27123
26445
  const asymptote = this.props.plot.asymptote;
27124
26446
  const typeSelector = /*#__PURE__*/React.createElement("div", {
27125
- style: typeSelectorStyle,
27126
- className: "above-scratchpad"
26447
+ style: typeSelectorStyle
27127
26448
  }, /*#__PURE__*/React.createElement(ButtonGroup, {
27128
26449
  value: type,
27129
26450
  allowEmpty: true,
@@ -27201,10 +26522,7 @@ var Grapher$1 = {
27201
26522
  hidden: true,
27202
26523
  widget: Grapher,
27203
26524
  transform: propTransform$2,
27204
- staticTransform: staticTransform$4,
27205
- // TODO(LEMS-2656): remove TS suppression
27206
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGrapherUserInput'.
27207
- scorer: scoreGrapher
26525
+ staticTransform: staticTransform$4
27208
26526
  };
27209
26527
 
27210
26528
  const getPromptJSON$f = rendererJSON => {
@@ -27220,24 +26538,6 @@ const getPromptJSON$f = rendererJSON => {
27220
26538
  });
27221
26539
  };
27222
26540
 
27223
- // The `group` widget is basically a widget hosting a full Perseus system in
27224
- // it. As such, scoring a group means scoring all widgets it contains.
27225
- function scoreGroup(userInput, rubric, strings, locale) {
27226
- const scores = scoreWidgetsFunctional(rubric.widgets, Object.keys(rubric.widgets), userInput, strings, locale);
27227
- return flattenScores(scores);
27228
- }
27229
-
27230
- function validateGroup(userInput, validationData, strings, locale) {
27231
- const emptyWidgets = emptyWidgetsFunctional(validationData.widgets, Object.keys(validationData.widgets), userInput, strings, locale);
27232
- if (emptyWidgets.length === 0) {
27233
- return null;
27234
- }
27235
- return {
27236
- type: "invalid",
27237
- message: null
27238
- };
27239
- }
27240
-
27241
26541
  // exports has no 'transform'
27242
26542
 
27243
26543
  class Group extends React.Component {
@@ -27392,12 +26692,6 @@ var Group$1 = {
27392
26692
  displayName: "Group (SAT only)",
27393
26693
  widget: Group,
27394
26694
  traverseChildWidgets: traverseChildWidgets,
27395
- // TODO(LEMS-2656): remove TS suppression
27396
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
27397
- scorer: scoreGroup,
27398
- // TODO(LEMS-2656): remove TS suppression
27399
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
27400
- validator: validateGroup,
27401
26695
  hidden: true,
27402
26696
  isLintable: true
27403
26697
  };
@@ -27533,10 +26827,7 @@ var Iframe$1 = {
27533
26827
  displayName: "Iframe (deprecated)",
27534
26828
  widget: Iframe,
27535
26829
  // Let's not expose it to all content creators yet
27536
- hidden: true,
27537
- // TODO(LEMS-2656): remove TS suppression
27538
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusIframeUserInput'.
27539
- scorer: scoreIframe
26830
+ hidden: true
27540
26831
  };
27541
26832
 
27542
26833
  const getPromptJSON$d = renderProps => {
@@ -27558,8 +26849,6 @@ const defaultBackgroundImage$2 = {
27558
26849
  width: 0,
27559
26850
  height: 0
27560
26851
  };
27561
- const editorAlignments = ["block", "full-width"];
27562
- const DEFAULT_ALIGNMENT = "block";
27563
26852
 
27564
26853
  // there is no transform as part of exports
27565
26854
 
@@ -27708,7 +26997,7 @@ class ImageWidget extends React.Component {
27708
26997
  }
27709
26998
  ImageWidget.contextType = PerseusI18nContext;
27710
26999
  ImageWidget.defaultProps = {
27711
- alignment: DEFAULT_ALIGNMENT,
27000
+ alignment: "block",
27712
27001
  title: "",
27713
27002
  range: [defaultRange, defaultRange],
27714
27003
  box: [defaultBoxSize, defaultBoxSize],
@@ -27726,13 +27015,9 @@ var Image$1 = {
27726
27015
  const bgImage = widgetOptions.backgroundImage;
27727
27016
  return !(bgImage && bgImage.url && !widgetOptions.alt);
27728
27017
  },
27729
- defaultAlignment: DEFAULT_ALIGNMENT,
27730
- supportedAlignments: editorAlignments,
27731
27018
  displayName: "Image",
27732
27019
  widget: ImageWidget,
27733
- isLintable: true,
27734
- // TODO: things that aren't interactive shouldn't need scoring functions
27735
- scorer: () => scoreNoop()
27020
+ isLintable: true
27736
27021
  };
27737
27022
 
27738
27023
  const getPromptJSON$c = () => {
@@ -28330,9 +27615,7 @@ var Interactive = {
28330
27615
  displayName: "Interaction",
28331
27616
  widget: Interaction,
28332
27617
  transform: _.identity,
28333
- hidden: true,
28334
- // TODO: things that aren't interactive shouldn't need scoring functions
28335
- scorer: () => scoreNoop()
27618
+ hidden: true
28336
27619
  };
28337
27620
 
28338
27621
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
@@ -28601,7 +27884,7 @@ class Graph extends React.Component {
28601
27884
  image = null;
28602
27885
  }
28603
27886
  return /*#__PURE__*/React.createElement("div", {
28604
- className: "graphie-container above-scratchpad",
27887
+ className: "graphie-container blank-background",
28605
27888
  style: {
28606
27889
  width: this.props.box[0],
28607
27890
  height: this.props.box[1]
@@ -29236,7 +28519,8 @@ const tickLabelSize = 14;
29236
28519
  const YGridTick = ({
29237
28520
  y,
29238
28521
  range,
29239
- tickStep
28522
+ tickStep,
28523
+ showPi
29240
28524
  }) => {
29241
28525
  // If the graph requires out-of-bounds labels, we want to make sure to set the
29242
28526
  // coordinates to the edge of the visible range of the graph. Otherwise,
@@ -29272,6 +28556,7 @@ const YGridTick = ({
29272
28556
  // If the graph displays both the y and x axis lines within the graph, we want
29273
28557
  // to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
29274
28558
  const showLabel = shouldShowLabel(y, range, tickStep);
28559
+ const yLabel = showPi ? divideByAndShowPi(y) : y.toString();
29275
28560
  return /*#__PURE__*/React.createElement("g", {
29276
28561
  className: "tick",
29277
28562
  "aria-hidden": true
@@ -29289,11 +28574,12 @@ const YGridTick = ({
29289
28574
  textAnchor: "end",
29290
28575
  x: xPositionText,
29291
28576
  y: yPositionText
29292
- }, y.toString()));
28577
+ }, yLabel));
29293
28578
  };
29294
28579
  const XGridTick = ({
29295
28580
  x,
29296
- range
28581
+ range,
28582
+ showPi
29297
28583
  }) => {
29298
28584
  // If the graph requires out-of-bounds labels, we want to make sure to set the
29299
28585
  // coordinates to the edge of the visible range of the graph. Otherwise,
@@ -29332,6 +28618,7 @@ const XGridTick = ({
29332
28618
  // Apply the adjustments to the x and y positions for the text
29333
28619
  const xPositionText = xPosition + xAdjustment;
29334
28620
  const yPositionText = yPosition + yAdjustment;
28621
+ const xLabel = showPi ? divideByAndShowPi(x) : x.toString();
29335
28622
  return /*#__PURE__*/React.createElement("g", {
29336
28623
  className: "tick",
29337
28624
  "aria-hidden": true
@@ -29349,7 +28636,7 @@ const XGridTick = ({
29349
28636
  textAnchor: "middle",
29350
28637
  x: xPositionText,
29351
28638
  y: yPositionText
29352
- }, x.toString()));
28639
+ }, xLabel));
29353
28640
  };
29354
28641
 
29355
28642
  // Determines whether to show the label for the given tick
@@ -29396,6 +28683,22 @@ const countSignificantDecimals = number => {
29396
28683
  }
29397
28684
  return numStr.split(".")[1].length;
29398
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
+ }
29399
28702
  const AxisTicks = () => {
29400
28703
  const {
29401
28704
  tickStep,
@@ -29418,6 +28721,10 @@ const AxisTicks = () => {
29418
28721
  key: `y-grid-tick-${y}`,
29419
28722
  range: range,
29420
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
29421
28728
  });
29422
28729
  })), /*#__PURE__*/React.createElement("g", {
29423
28730
  className: "x-axis-ticks"
@@ -29426,6 +28733,10 @@ const AxisTicks = () => {
29426
28733
  x: x,
29427
28734
  key: `x-grid-tick-${x}`,
29428
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
29429
28740
  });
29430
28741
  })));
29431
28742
  };
@@ -29733,6 +29044,15 @@ const styles$d = StyleSheet.create({
29733
29044
  }
29734
29045
  });
29735
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
+
29736
29056
  const _excluded$1 = ["onFormatChange", "checkValidity", "useArrowKeys"];
29737
29057
  const {
29738
29058
  firstNumericalParse,
@@ -29870,6 +29190,19 @@ class NumberInput extends React.Component {
29870
29190
  this._getInput().value = toNumericString(val, format);
29871
29191
  };
29872
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
+ }
29873
29206
  componentDidUpdate(prevProps) {
29874
29207
  if (!number.equal(this.getValue(), this.props.value)) {
29875
29208
  this._setValue(this.props.value, this.state.format);
@@ -29912,7 +29245,8 @@ NumberInput.propTypes = {
29912
29245
  onFormatChange: PropTypes.func,
29913
29246
  checkValidity: PropTypes.func,
29914
29247
  size: PropTypes.oneOf(["mini", "small", "normal"]),
29915
- label: PropTypes.oneOf(["put your labels outside your inputs!"])
29248
+ label: PropTypes.oneOf(["put your labels outside your inputs!"]),
29249
+ allowPiTruncation: PropTypes.bool
29916
29250
  };
29917
29251
  NumberInput.defaultProps = {
29918
29252
  value: null,
@@ -29951,14 +29285,16 @@ class RangeInput extends React.Component {
29951
29285
  // eslint-disable-next-line react/jsx-no-bind
29952
29286
  ,
29953
29287
  onChange: this.onChange.bind(this, 0),
29954
- placeholder: this.props.placeholder[0]
29288
+ placeholder: this.props.placeholder[0],
29289
+ allowPiTruncation: this.props.allowPiTruncation
29955
29290
  })), /*#__PURE__*/React.createElement(NumberInput, _extends({}, this.props, {
29956
29291
  value: value[1],
29957
29292
  checkValidity: val => checkValidity([value[0], val])
29958
29293
  // eslint-disable-next-line react/jsx-no-bind
29959
29294
  ,
29960
29295
  onChange: this.onChange.bind(this, 1),
29961
- placeholder: this.props.placeholder[1]
29296
+ placeholder: this.props.placeholder[1],
29297
+ allowPiTruncation: this.props.allowPiTruncation
29962
29298
  })));
29963
29299
  }
29964
29300
  }
@@ -29966,7 +29302,8 @@ RangeInput.propTypes = {
29966
29302
  value: PropTypes.array.isRequired,
29967
29303
  onChange: PropTypes.func.isRequired,
29968
29304
  placeholder: PropTypes.array,
29969
- checkValidity: PropTypes.func
29305
+ checkValidity: PropTypes.func,
29306
+ allowPiTruncation: PropTypes.bool
29970
29307
  };
29971
29308
  RangeInput.defaultProps = {
29972
29309
  placeholder: [null, null]
@@ -36949,10 +36286,7 @@ var InteractiveGraph$1 = {
36949
36286
  name: "interactive-graph",
36950
36287
  displayName: "Interactive graph (Assessments only)",
36951
36288
  widget: InteractiveGraph,
36952
- staticTransform: staticTransform$3,
36953
- // TODO(LEMS-2656): remove TS suppression
36954
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInteractiveGraphUserInput'.
36955
- scorer: scoreInteractiveGraph
36289
+ staticTransform: staticTransform$3
36956
36290
  };
36957
36291
 
36958
36292
  // TODO: can we use WB typography?
@@ -37894,9 +37228,7 @@ var LabelImage$1 = {
37894
37228
  widget: LabelImageWithDependencies,
37895
37229
  accessible: true,
37896
37230
  isLintable: true,
37897
- // TODO(LEMS-2656): remove TS suppression
37898
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusLabelImageUserInput'.
37899
- scorer: scoreLabelImage
37231
+ getPublicWidgetOptions: getLabelImagePublicWidgetOptions
37900
37232
  };
37901
37233
 
37902
37234
  var ItemState = /*#__PURE__*/function (ItemState) {
@@ -38129,7 +37461,7 @@ class Draggable extends React.Component {
38129
37461
  // We need to keep backwards compatbility with rules specified directly
38130
37462
  // in CSS. Hence the hacky tacking on of manual classNames.
38131
37463
  // See sortable.less for details.
38132
- 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";
38133
37465
  if (!includePadding) {
38134
37466
  className += " perseus-sortable-draggable-unpadded";
38135
37467
  }
@@ -38830,10 +38162,7 @@ var Matcher$1 = {
38830
38162
  name: "matcher",
38831
38163
  displayName: "Matcher (two column)",
38832
38164
  widget: Matcher,
38833
- isLintable: true,
38834
- // TODO(LEMS-2656): remove TS suppression
38835
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatcherUserInput'.
38836
- scorer: scoreMatcher
38165
+ isLintable: true
38837
38166
  };
38838
38167
 
38839
38168
  const getPromptJSON$8 = (renderProps, userInput) => {
@@ -39237,13 +38566,7 @@ var Matrix$1 = {
39237
38566
  widget: Matrix,
39238
38567
  transform: propTransform$1,
39239
38568
  staticTransform: staticTransform$2,
39240
- isLintable: true,
39241
- // TODO(LEMS-2656): remove TS suppression
39242
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
39243
- scorer: scoreMatrix,
39244
- // TODO(LEMS-2656): remove TS suppression
39245
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
39246
- validator: validateMatrix
38569
+ isLintable: true
39247
38570
  };
39248
38571
 
39249
38572
  const getPromptJSON$7 = () => {
@@ -39323,7 +38646,7 @@ class Measurer extends React.Component {
39323
38646
  // TODO(scottgrant): This isn't a11y-friendly! We should insist on
39324
38647
  // finding some valid alt text when this widget is used.
39325
38648
  return /*#__PURE__*/React.createElement("div", {
39326
- className: "perseus-widget perseus-widget-measurer " + "graphie-container above-scratchpad",
38649
+ className: "perseus-widget perseus-widget-measurer " + "graphie-container blank-background",
39327
38650
  style: {
39328
38651
  width: this.props.box[0],
39329
38652
  height: this.props.box[1]
@@ -39360,9 +38683,7 @@ var Measurer$1 = {
39360
38683
  hidden: true,
39361
38684
  widget: Measurer,
39362
38685
  version: measurerLogic.version,
39363
- propUpgrades: measurerLogic.widgetOptionsUpgrades,
39364
- // TODO: things that aren't interactive shouldn't need scoring functions
39365
- scorer: () => scoreNoop(1)
38686
+ propUpgrades: measurerLogic.widgetOptionsUpgrades
39366
38687
  };
39367
38688
 
39368
38689
  /**
@@ -40250,8 +39571,7 @@ var Molecule$1 = {
40250
39571
  name: "molecule-renderer",
40251
39572
  displayName: "Molecule renderer",
40252
39573
  hidden: true,
40253
- widget: MoleculeWidget,
40254
- scorer: () => scoreNoop()
39574
+ widget: MoleculeWidget
40255
39575
  };
40256
39576
 
40257
39577
  const getPromptJSON$6 = (renderProps, userInput) => {
@@ -40820,12 +40140,7 @@ var NumberLine$1 = {
40820
40140
  widget: NumberLine,
40821
40141
  transform: numberLineTransform,
40822
40142
  staticTransform: staticTransform$1,
40823
- // TODO(LEMS-2656): remove TS suppression
40824
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
40825
- scorer: scoreNumberLine,
40826
- // TODO(LEMS-2656): remove TS suppression
40827
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
40828
- validator: validateNumberLine
40143
+ getPublicWidgetOptions: getNumberLinePublicWidgetOptions
40829
40144
  };
40830
40145
 
40831
40146
  const getPromptJSON$5 = (renderProps, userInput) => {
@@ -40843,7 +40158,7 @@ const getPromptJSON$5 = (renderProps, userInput) => {
40843
40158
  class PlaceholderCard extends React.Component {
40844
40159
  render() {
40845
40160
  return /*#__PURE__*/React.createElement("div", {
40846
- className: "card-wrap " + ClassNames.INTERACTIVE,
40161
+ className: "card-wrap",
40847
40162
  style: {
40848
40163
  width: this.props.width
40849
40164
  }
@@ -40858,7 +40173,7 @@ class PlaceholderCard extends React.Component {
40858
40173
  class DragHintCard extends React.Component {
40859
40174
  render() {
40860
40175
  return /*#__PURE__*/React.createElement("div", {
40861
- className: "card-wrap " + ClassNames.INTERACTIVE
40176
+ className: "card-wrap"
40862
40177
  }, /*#__PURE__*/React.createElement("div", {
40863
40178
  className: "card drag-hint"
40864
40179
  }));
@@ -40994,7 +40309,7 @@ class Card extends React.Component {
40994
40309
  const rendererProps = _.pick(this.props, "content");
40995
40310
  const onMouseDown = this.props.animating ? $.noop : this.onMouseDown;
40996
40311
  return /*#__PURE__*/React.createElement("div", {
40997
- className: "card-wrap " + ClassNames.INTERACTIVE,
40312
+ className: "card-wrap",
40998
40313
  style: style,
40999
40314
  onMouseDown: onMouseDown,
41000
40315
  onTouchStart: onMouseDown,
@@ -41365,7 +40680,7 @@ class Orderer extends React.Component {
41365
40680
  });
41366
40681
  }, this));
41367
40682
  return /*#__PURE__*/React.createElement("div", {
41368
- 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 "
41369
40684
  // eslint-disable-next-line react/no-string-refs
41370
40685
  ,
41371
40686
  ref: "orderer"
@@ -41386,12 +40701,6 @@ var Orderer$1 = {
41386
40701
  hidden: true,
41387
40702
  widget: Orderer,
41388
40703
  isLintable: true,
41389
- // TODO(LEMS-2656): remove TS suppression
41390
- // @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
41391
- scorer: scoreOrderer,
41392
- // TODO(LEMS-2656): remove TS suppression
41393
- // @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
41394
- validator: validateOrderer,
41395
40704
  getPublicWidgetOptions: getOrdererPublicWidgetOptions
41396
40705
  };
41397
40706
 
@@ -41430,15 +40739,12 @@ PassageRefTarget.defaultProps = {
41430
40739
  var PassageRefTarget$1 = {
41431
40740
  name: "passage-ref-target",
41432
40741
  displayName: "PassageRefTarget",
41433
- defaultAlignment: "inline",
41434
40742
  widget: PassageRefTarget,
41435
40743
  hidden: true,
41436
40744
  transform: editorProps => {
41437
40745
  return _.pick(editorProps, "content");
41438
40746
  },
41439
- isLintable: true,
41440
- // TODO: things that aren't interactive shouldn't need scoring functions
41441
- scorer: () => scoreNoop()
40747
+ isLintable: true
41442
40748
  };
41443
40749
 
41444
40750
  const getPromptJSON$4 = () => {
@@ -42425,7 +41731,7 @@ class Plotter extends React.Component {
42425
41731
  marginBottom: this.props.labels[0] ? paddingForBottomLabel : 0
42426
41732
  };
42427
41733
  return /*#__PURE__*/React.createElement("div", {
42428
- className: "perseus-widget-plotter graphie " + ClassNames.INTERACTIVE
41734
+ className: "perseus-widget-plotter graphie"
42429
41735
  // eslint-disable-next-line react/no-string-refs
42430
41736
  ,
42431
41737
  ref: "graphieDiv",
@@ -42455,13 +41761,7 @@ var Plotter$1 = {
42455
41761
  displayName: "Plotter",
42456
41762
  hidden: true,
42457
41763
  widget: Plotter,
42458
- staticTransform: staticTransform,
42459
- // TODO(LEMS-2656): remove TS suppression
42460
- // @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
42461
- scorer: scorePlotter,
42462
- // TODO(LEMS-2656): remove TS suppression
42463
- // @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
42464
- validator: validatePlotter
41764
+ staticTransform: staticTransform
42465
41765
  };
42466
41766
 
42467
41767
  const getPromptJSON$2 = () => {
@@ -42624,12 +41924,7 @@ var Sorter$1 = {
42624
41924
  displayName: "Sorter",
42625
41925
  widget: Sorter,
42626
41926
  isLintable: true,
42627
- // TODO(LEMS-2656): remove TS suppression
42628
- // @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
42629
- scorer: scoreSorter,
42630
- // TODO(LEMS-2656): remove TS suppression
42631
- // @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
42632
- validator: validateSorter
41927
+ getPublicWidgetOptions: getSorterPublicWidgetOptions
42633
41928
  };
42634
41929
 
42635
41930
  const {
@@ -42848,13 +42143,7 @@ var Table$1 = {
42848
42143
  widget: Table,
42849
42144
  transform: propTransform,
42850
42145
  hidden: true,
42851
- isLintable: true,
42852
- // TODO(LEMS-2656): remove TS suppression
42853
- // @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
42854
- scorer: scoreTable,
42855
- // TODO(LEMS-2656): remove TS suppression
42856
- // @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
42857
- validator: validateTable
42146
+ isLintable: true
42858
42147
  };
42859
42148
 
42860
42149
  const getPromptJSON = () => {
@@ -43014,11 +42303,7 @@ Video.contextType = PerseusI18nContext;
43014
42303
  var Video$1 = {
43015
42304
  name: "video",
43016
42305
  displayName: "Video",
43017
- defaultAlignment: "block",
43018
- supportedAlignments: ["block", "float-left", "float-right", "full-width"],
43019
- widget: Video,
43020
- // TODO: things that aren't interactive shouldn't need scoring functions
43021
- scorer: () => scoreNoop()
42306
+ widget: Video
43022
42307
  };
43023
42308
 
43024
42309
  /* eslint-disable import/no-named-as-default */
@@ -43037,7 +42322,7 @@ const init = function init() {
43037
42322
 
43038
42323
  // This file is processed by a Rollup plugin (replace) to inject the production
43039
42324
  const libName = "@khanacademy/perseus";
43040
- const libVersion = "52.0.0";
42325
+ const libVersion = "53.0.0";
43041
42326
  addLibraryVersionToPerseusDebug(libName, libVersion);
43042
42327
 
43043
42328
  const apiVersion = {
@@ -43904,6 +43189,42 @@ const ref = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.cre
43904
43189
  ref: ref
43905
43190
  }))));
43906
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
+
43907
43228
  /**
43908
43229
  * Traverses a {content, widgets, images} renderer props object,
43909
43230
  * such as `itemData.question`
@@ -45280,5 +44601,5 @@ const ScoringUtil = {
45280
44601
  * Types
45281
44602
  */
45282
44603
 
45283
- 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 };
45284
44605
  //# sourceMappingURL=index.js.map