@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/index.js CHANGED
@@ -6,7 +6,6 @@ var KAS = require('@khanacademy/kas');
6
6
  var mathInput = require('@khanacademy/math-input');
7
7
  var perseusCore = require('@khanacademy/perseus-core');
8
8
  var PerseusLinter = require('@khanacademy/perseus-linter');
9
- var perseusScore = require('@khanacademy/perseus-score');
10
9
  var wonderBlocksCore = require('@khanacademy/wonder-blocks-core');
11
10
  var Tooltip$1 = require('@khanacademy/wonder-blocks-tooltip');
12
11
  var wonderBlocksTypography = require('@khanacademy/wonder-blocks-typography');
@@ -21,6 +20,7 @@ var wonderBlocksTokens = require('@khanacademy/wonder-blocks-tokens');
21
20
  var $ = require('jquery');
22
21
  var uuid = require('uuid');
23
22
  var PropTypes = require('prop-types');
23
+ var perseusScore = require('@khanacademy/perseus-score');
24
24
  var wonderStuffCore = require('@khanacademy/wonder-stuff-core');
25
25
  var wonderBlocksProgressSpinner = require('@khanacademy/wonder-blocks-progress-spinner');
26
26
  var kmath = require('@khanacademy/kmath');
@@ -109,213 +109,6 @@ function _extends() {
109
109
  * The translated strings that are used to render Perseus.
110
110
  */
111
111
 
112
- /**
113
- * Untranslated strings used in Perseus. To be used by an external
114
- * translator to produce translated strings, passed in as `PerseusStrings`.
115
- * !! Note: Ensure that all escape sequences are double-escaped. (e.g. `\\text` -> `\\\\text`)
116
- */
117
- const strings = {
118
- closeKeypad: "close math keypad",
119
- openKeypad: "open math keypad",
120
- mathInputBox: "Math input box",
121
- removeHighlight: "Remove highlight",
122
- addHighlight: "Add highlight",
123
- hintPos: "Hint #%(pos)s",
124
- errorRendering: "Error rendering: %(error)s",
125
- 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",
126
- EXTRA_SYMBOLS_ERROR: "We could not understand your " + "answer. Please check your answer for extra " + "text or symbols.",
127
- NEEDS_TO_BE_SIMPLFIED_ERROR: "Your answer is almost correct, " + "but it needs to be simplified.",
128
- MISSING_PERCENT_ERROR: "Your answer is almost correct, " + "but it is missing a " + "<code>\\\\%</code> at the end.",
129
- MULTIPLICATION_SIGN_ERROR: "I'm a computer. I only understand " + "multiplication if you use an asterisk " + "(*) as the multiplication sign.",
130
- WRONG_CASE_ERROR: "Your answer includes use of a variable with the wrong case.",
131
- WRONG_LETTER_ERROR: "Your answer includes a wrong variable letter.",
132
- invalidSelection: "Make sure you select something for every row.",
133
- ERROR_TITLE: "Oops!",
134
- ERROR_MESSAGE: "Sorry, I don't understand that!",
135
- hints: "Hints",
136
- getAnotherHint: "Get another hint",
137
- deprecatedStandin: "Sorry, this part of the question is no longer available. 😅 Don't worry, you won't be graded on this part. Keep going!",
138
- keepTrying: "Keep trying",
139
- tryAgain: "Try again",
140
- check: "Check",
141
- correctExcited: "Correct!",
142
- nextQuestion: "Next question",
143
- skipToTitle: "Skip to %(title)s",
144
- current: "Current",
145
- correct: "Correct",
146
- correctSelected: "Correct (selected)",
147
- correctCrossedOut: "Correct (but you crossed it out)",
148
- incorrect: "Incorrect",
149
- incorrectSelected: "Incorrect (selected)",
150
- hideExplanation: "Hide explanation",
151
- explain: "Explain",
152
- INVALID_MESSAGE_PREFIX: "We couldn't grade your answer.",
153
- DEFAULT_INVALID_MESSAGE_1: "It looks like you left something blank or ",
154
- DEFAULT_INVALID_MESSAGE_2: "entered in an invalid answer.",
155
- integerExample: "an integer, like $6$",
156
- properExample: "a *proper* fraction, like $1/2$ or $6/10$",
157
- simplifiedProperExample: "a *simplified proper* fraction, like $3/5$",
158
- improperExample: "an *improper* fraction, like $10/7$ or $14/8$",
159
- simplifiedImproperExample: "a *simplified improper* fraction, like $7/4$",
160
- mixedExample: "a mixed number, like $1\\\\ 3/4$",
161
- decimalExample: "an *exact* decimal, like $0.75$",
162
- percentExample: "a percent, like $12.34\\\\%$",
163
- piExample: "a multiple of pi, like $12$ pi or $2/3$ pi",
164
- yourAnswer: "**Your answer should be** ",
165
- yourAnswerLabel: "Your answer:",
166
- addPoints: "Click to add points",
167
- addVertices: "Click to add vertices",
168
- tapMultiple: "Tap each dot on the image to select all answers that apply.",
169
- tapSingle: "Tap each dot on the image to select an answer.",
170
- clickMultiple: "Click each dot on the image to select all answers that apply.",
171
- clickSingle: "Click each dot on the image to select an answer.",
172
- choices: "Choices:",
173
- answers: {
174
- one: "%(num)s answer",
175
- other: "%(num)s answers"
176
- },
177
- hideAnswersToggleLabel: "Hide answer choices",
178
- moves: {
179
- one: "Moves: %(num)s",
180
- other: "Moves: %(num)s"
181
- },
182
- clickTiles: "Click on the tiles to change the lights.",
183
- turnOffLights: "You must turn on all of the lights to continue.",
184
- fillAllCells: "Make sure you fill in all cells in the matrix.",
185
- molecularDrawing: "A molecular structure drawing. SMILES notation: %(content)s",
186
- switchDirection: "Switch direction",
187
- circleOpen: "Make circle open",
188
- circleFilled: "Make circle filled",
189
- numDivisions: "Number of divisions:",
190
- divisions: "Please make sure the number of divisions is in the range %(divRangeString)s.",
191
- lineRange: "lines %(lineRange)s",
192
- lineNumber: "line %(lineNumber)s",
193
- symbolPassage: "The symbol %(questionSymbol)s indicates that question %(questionNumber)s references this portion of the passage.",
194
- symbolQuestion: " The symbol %(sentenceSymbol)s indicates that the following sentence is referenced in a question.",
195
- lineLabel: {
196
- context: "a label next to a reading passage to denote the line number",
197
- message: "Line"
198
- },
199
- beginningPassage: "Beginning of reading passage.",
200
- beginningFootnotes: "Beginning of reading passage footnotes.",
201
- endPassage: "End of reading passage.",
202
- questionMarker: "[Marker for question %(number)s]",
203
- circleMarker: "[Circle marker %(number)s]",
204
- sentenceMarker: "[Sentence %(number)s]",
205
- dragHandles: "Drag handles to make graph",
206
- tapAddPoints: "Tap to add points",
207
- false: "False",
208
- true: "True",
209
- no: "No",
210
- yes: "Yes",
211
- chooseCorrectNum: "Please choose the correct number of answers.",
212
- notNoneOfTheAbove: "'None of the above' may not be selected when other answers are selected.",
213
- noneOfTheAbove: "None of the above",
214
- chooseNumAnswers: "Choose %(numCorrect)s answers:",
215
- chooseAllAnswers: "Choose all answers that apply:",
216
- chooseOneAnswer: "Choose 1 answer:",
217
- choiceCheckedCorrect: "(Choice %(letter)s, Checked, Correct)",
218
- choiceCrossedOutCorrect: "(Choice %(letter)s, Crossed out, Correct)",
219
- choiceCorrect: "(Choice %(letter)s, Correct)",
220
- choiceCheckedIncorrect: "(Choice %(letter)s, Checked, Incorrect)",
221
- choiceCrossedOutIncorrect: "(Choice %(letter)s, Crossed out, Incorrect)",
222
- choiceIncorrect: "(Choice %(letter)s, Incorrect)",
223
- choiceChecked: "(Choice %(letter)s, Checked)",
224
- choiceCrossedOut: "(Choice %(letter)s, Crossed out)",
225
- choice: "(Choice %(letter)s)",
226
- crossOut: "Cross out",
227
- crossOutOption: "Cross out option",
228
- crossOutChoice: "Cross out Choice %(letter)s",
229
- bringBack: "Bring back",
230
- openMenuForChoice: "Open menu for Choice %(letter)s",
231
- letters: {
232
- 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.",
233
- 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"
234
- },
235
- rightArrow: "Reaction arrow pointing to the right.",
236
- dontUnderstandUnits: "I couldn't understand those units.",
237
- checkSigFigs: "Check your significant figures.",
238
- answerNumericallyIncorrect: "That answer is numerically incorrect.",
239
- checkUnits: "Check your units.",
240
- dontUnderstand: "I don't understand that",
241
- loading: "Loading...",
242
- videoTranscript: "See video transcript",
243
- somethingWrong: "Something went wrong.",
244
- videoWrapper: "Khan Academy video wrapper",
245
- mathInputTitle: "mathematics keyboard",
246
- mathInputDescription: "Use keyboard/mouse to interact with math-based input fields",
247
- sin: "sin",
248
- cos: "cos",
249
- tan: "tan",
250
- simulationLoadFail: "Sorry, this simulation cannot load.",
251
- simulationLocaleWarning: "Sorry, this simulation isn't available in your language.",
252
- selectAnAnswer: "Select an answer",
253
- // The following strings are used for interactive graph SR descriptions.
254
- addPoint: "Add Point",
255
- removePoint: "Remove Point",
256
- graphKeyboardPrompt: "Press Shift + Enter to interact with the graph",
257
- closePolygon: "Close shape",
258
- openPolygon: "Re-open shape",
259
- srInteractiveElements: "Interactive elements: %(elements)s",
260
- srNoInteractiveElements: "No interactive elements",
261
- // TODO(LEMS-2660): The following strings are ones that will need
262
- // translation tickets after all interactive graph SR strings have
263
- // been finalized. Remove this comment after the tickets have been
264
- // created.
265
- srPointAtCoordinates: "Point %(num)s at %(x)s comma %(y)s",
266
- srCircleGraph: "A circle on a coordinate plane.",
267
- srCircleShape: "Circle. The center point is at %(centerX)s comma %(centerY)s.",
268
- srCircleRadiusPoint: "Radius point at %(radiusPointX)s comma %(radiusPointY)s.",
269
- srCircleRadius: "Circle radius is %(radius)s.",
270
- 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.",
271
- srLinearGraph: "A line on a coordinate plane.",
272
- srLinearGraphPoints: "The line has two points, point 1 at %(point1X)s comma %(point1Y)s and point 2 at %(point2X)s comma %(point2Y)s.",
273
- srLinearGraphSlopeIncreasing: "Its slope increases from left to right.",
274
- srLinearGraphSlopeDecreasing: "Its slope decreases from left to right.",
275
- srLinearGraphSlopeHorizontal: "Its slope is zero.",
276
- srLinearGraphSlopeVertical: "Its slope is undefined.",
277
- srLinearGraphXOnlyIntercept: "The line crosses the X-axis at %(xIntercept)s comma 0.",
278
- srLinearGraphYOnlyIntercept: "The line crosses the Y-axis at 0 comma %(yIntercept)s.",
279
- srLinearGraphBothIntercepts: "The line crosses the X-axis at %(xIntercept)s comma 0 and the Y-axis at 0 comma %(yIntercept)s.",
280
- srLinearGraphOriginIntercept: "The line crosses the x and y axes at the graph's origin.",
281
- srLinearGrabHandle: "Line from %(point1X)s comma %(point1Y)s to %(point2X)s comma %(point2Y)s.",
282
- srAngleSideAtCoordinates: "Point %(point)s, %(side)s at %(x)s comma %(y)s",
283
- srAngleVertexAtCoordinatesWithAngleMeasure: "Point 2, vertex at %(x)s comma %(y)s. Angle %(angleMeasure)s degrees",
284
- srAngleGraphAriaLabel: "An angle on a coordinate plane.",
285
- 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",
286
- srSingleSegmentGraphAriaLabel: "A line segment on a coordinate plane.",
287
- srMultipleSegmentGraphAriaLabel: "%(countOfSegments)s line segments on a coordinate plane.",
288
- srMultipleSegmentIndividualLabel: "Segment %(indexOfSegment)s: Endpoint 1 at %(point1X)s comma %(point1Y)s. Endpoint 2 %(point2X)s comma %(point2Y)s.",
289
- srSingleSegmentLabel: "Endpoint 1 at %(point1X)s comma %(point1Y)s. Endpoint 2 %(point2X)s comma %(point2Y)s.",
290
- srSegmentLength: "Segment length %(length)s units.",
291
- srSingleSegmentGraphEndpointAriaLabel: "Endpoint %(endpointNumber)s at %(x)s comma %(y)s.",
292
- srMultipleSegmentGraphEndpointAriaLabel: "Endpoint %(endpointNumber)s on segment %(indexOfSegment)s at %(x)s comma %(y)s.",
293
- srSegmentGrabHandle: "Segment from %(point1X)s comma %(point1Y)s to %(point2X)s comma %(point2Y)s.",
294
- srLinearSystemGraph: "Two lines on a coordinate plane.",
295
- srLinearSystemPoints: "Line %(lineNumber)s has two points, point 1 at %(point1X)s comma %(point1Y)s and point 2 at %(point2X)s comma %(point2Y)s.",
296
- srLinearSystemPoint: "Point %(pointSequence)s on line %(lineNumber)s at %(x)s comma %(y)s.",
297
- srRayGraph: "A ray on a coordinate plane.",
298
- srRayPoints: "The endpoint is at %(point1X)s comma %(point1Y)s and the ray goes through point %(point2X)s comma %(point2Y)s.",
299
- srRayGrabHandle: "Ray with endpoint %(point1X)s comma %(point1Y)s going through point %(point2X)s comma %(point2Y)s.",
300
- srRayEndpoint: "Endpoint at %(x)s comma %(y)s.",
301
- srRayTerminalPoint: "Through point at %(x)s comma %(y)s.",
302
- srQuadraticGraph: "A parabola on a 4-quadrant coordinate plane.",
303
- srQuadraticFaceUp: "The parabola opens upward.",
304
- srQuadraticFaceDown: "The parabola opens downward.",
305
- srQuadraticGraphVertexOrigin: "Vertex is at the origin.",
306
- srQuadraticGraphVertexXAxis: "Vertex is on the X-axis.",
307
- srQuadraticGraphVertexYAxis: "Vertex is on the Y-axis.",
308
- srQuadraticGraphVertexQuadrant: "Vertex is in quadrant %(quadrant)s.",
309
- srQuadraticTwoXIntercepts: "The X-intercepts are at %(intercept1)s comma 0 and %(intercept2)s comma 0.",
310
- srQuadraticOneXIntercept: "The X-intercept is at %(intercept)s comma 0.",
311
- srQuadraticYIntercept: "The Y-intercept is at 0 comma %(intercept)s.",
312
- srQuadraticPointOrigin: "Point %(pointNumber)s on parabola at the origin.",
313
- srQuadraticPointAxis: "Point %(pointNumber)s on parabola at %(x)s comma %(y)s.",
314
- srQuadraticPointQuadrant: "Point %(pointNumber)s on parabola in quadrant %(quadrant)s at %(x)s comma %(y)s.",
315
- srQuadraticInteractiveElements: "Parabola with points at %(point1X)s comma %(point1Y)s, %(point2X)s comma %(point2Y)s, and %(point3X)s comma %(point3Y)s."
316
- // The above strings are used for interactive graph SR descriptions.
317
- };
318
-
319
112
  /**
320
113
  * Mock strings for the Perseus package, to be used for tests and Storybook.
321
114
  */
@@ -883,24 +676,32 @@ const mockStrings = {
883
676
 
884
677
  // This type helps us make sure all error codes are mapped to strings
885
678
 
679
+ /**
680
+ * Map an error string to a PerseusStrings key
681
+ * that we can use to get the translated error message
682
+ */
886
683
  const errorToString = {
887
- MISSING_PERCENT_ERROR: strings.MISSING_PERCENT_ERROR,
888
- NEEDS_TO_BE_SIMPLIFIED_ERROR: strings.NEEDS_TO_BE_SIMPLFIED_ERROR,
889
- APPROXIMATED_PI_ERROR: strings.APPROXIMATED_PI_ERROR,
890
- EXTRA_SYMBOLS_ERROR: strings.EXTRA_SYMBOLS_ERROR,
891
- WRONG_CASE_ERROR: strings.WRONG_CASE_ERROR,
892
- WRONG_LETTER_ERROR: strings.WRONG_LETTER_ERROR,
893
- MULTIPLICATION_SIGN_ERROR: strings.MULTIPLICATION_SIGN_ERROR,
894
- INVALID_SELECTION_ERROR: strings.invalidSelection,
895
- CHOOSE_CORRECT_NUM_ERROR: strings.chooseCorrectNum,
896
- NOT_NONE_ABOVE_ERROR: strings.notNoneOfTheAbove,
897
- FILL_ALL_CELLS_ERROR: strings.fillAllCells
898
- };
899
- function mapErrorToString(err) {
684
+ MISSING_PERCENT_ERROR: "MISSING_PERCENT_ERROR",
685
+ NEEDS_TO_BE_SIMPLIFIED_ERROR: "NEEDS_TO_BE_SIMPLFIED_ERROR",
686
+ APPROXIMATED_PI_ERROR: "APPROXIMATED_PI_ERROR",
687
+ EXTRA_SYMBOLS_ERROR: "EXTRA_SYMBOLS_ERROR",
688
+ WRONG_CASE_ERROR: "WRONG_CASE_ERROR",
689
+ WRONG_LETTER_ERROR: "WRONG_LETTER_ERROR",
690
+ MULTIPLICATION_SIGN_ERROR: "MULTIPLICATION_SIGN_ERROR",
691
+ INVALID_SELECTION_ERROR: "invalidSelection",
692
+ CHOOSE_CORRECT_NUM_ERROR: "chooseCorrectNum",
693
+ NOT_NONE_ABOVE_ERROR: "notNoneOfTheAbove",
694
+ FILL_ALL_CELLS_ERROR: "fillAllCells"
695
+ };
696
+ function mapErrorToString(
697
+ // the string representing an error code
698
+ err,
699
+ // the translated Perseus strings
700
+ translatedStrings) {
900
701
  if (!err) {
901
702
  return err;
902
703
  }
903
- return errorToString[err] || err;
704
+ return translatedStrings[errorToString[err]] || err;
904
705
  }
905
706
 
906
707
  /**
@@ -1811,7 +1612,6 @@ const ClassNames = {
1811
1612
  SELECTED: "perseus-radio-selected",
1812
1613
  OPTION_CONTENT: "perseus-radio-option-content"
1813
1614
  },
1814
- INTERACTIVE: "perseus-interactive",
1815
1615
  CORRECT: "perseus-correct",
1816
1616
  INCORRECT: "perseus-incorrect",
1817
1617
  UNANSWERED: "perseus-unanswered",
@@ -2067,7 +1867,6 @@ class Expression extends React__namespace.Component {
2067
1867
  // eslint-disable-next-line react/no-string-refs
2068
1868
  , {
2069
1869
  ref: "input",
2070
- className: ClassNames.INTERACTIVE,
2071
1870
  value: this.props.value,
2072
1871
  onChange: this.changeAndTrack,
2073
1872
  convertDotToTimes: this.props.times,
@@ -2169,7 +1968,6 @@ var Expression$1 = {
2169
1968
  name: "expression",
2170
1969
  displayName: "Expression / Equation",
2171
1970
  accessible: true,
2172
- defaultAlignment: "inline-block",
2173
1971
  widget: ExpressionWithDependencies,
2174
1972
  transform: widgetOptions => {
2175
1973
  const {
@@ -2194,12 +1992,6 @@ var Expression$1 = {
2194
1992
  propUpgrades: perseusCore.expressionLogic.widgetOptionsUpgrades,
2195
1993
  // For use by the editor
2196
1994
  isLintable: true,
2197
- // TODO(LEMS-2656): remove TS suppression
2198
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
2199
- scorer: perseusScore.scoreExpression,
2200
- // TODO(LEMS-2656): remove TS suppression
2201
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
2202
- validator: perseusScore.validateExpression,
2203
1995
  getPublicWidgetOptions: perseusCore.getExpressionPublicWidgetOptions,
2204
1996
  // TODO(LEMS-2656): remove TS suppression
2205
1997
  // @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusExpressionRubric'.
@@ -3479,10 +3271,6 @@ const pureMdMax$1 = "1023px";
3479
3271
  const pureLgMax$1 = "1279px";
3480
3272
  // @tableBackgroundAccent: #f9f9f9; // for striping
3481
3273
  const tableBackgroundAccent = "#F9F9F9";
3482
- // @zIndexAboveScratchpad: @zIndexScratchPad + 1;
3483
- const zIndexAboveScratchpad$1 = 2;
3484
- // @zIndexInteractiveComponent: @zIndexAboveScratchpad + 1;
3485
- const zIndexInteractiveComponent$1 = 3;
3486
3274
  // @phoneMargin: 16px;
3487
3275
  const phoneMargin = 16;
3488
3276
  const negativePhoneMargin = -16;
@@ -3526,8 +3314,6 @@ var constants = /*#__PURE__*/Object.freeze({
3526
3314
  pureMdMax: pureMdMax$1,
3527
3315
  pureLgMax: pureLgMax$1,
3528
3316
  tableBackgroundAccent: tableBackgroundAccent,
3529
- zIndexAboveScratchpad: zIndexAboveScratchpad$1,
3530
- zIndexInteractiveComponent: zIndexInteractiveComponent$1,
3531
3317
  phoneMargin: phoneMargin,
3532
3318
  negativePhoneMargin: negativePhoneMargin,
3533
3319
  hintBorderWidth: hintBorderWidth,
@@ -17822,657 +17608,6 @@ class QuestionParagraph extends React__namespace.Component {
17822
17608
  }
17823
17609
  }
17824
17610
 
17825
- const noScore = {
17826
- type: "points",
17827
- earned: 0,
17828
- total: 0,
17829
- message: null
17830
- };
17831
-
17832
- /**
17833
- * If a widget says that it is empty once it is graded.
17834
- * Trying to encapsulate references to the score format.
17835
- */
17836
- function scoreIsEmpty(score) {
17837
- // HACK(benkomalo): ugh. this isn't great; the Perseus score objects
17838
- // overload the type "invalid" for what should probably be three
17839
- // distinct cases:
17840
- // - truly empty or not fully filled out
17841
- // - invalid or malformed inputs
17842
- // - "almost correct" like inputs where the widget wants to give
17843
- // feedback (e.g. a fraction needs to be reduced, or `pi` should
17844
- // be used instead of 3.14)
17845
- //
17846
- // Unfortunately the coercion happens all over the place, as these
17847
- // Perseus style score objects are created *everywhere* (basically
17848
- // in every widget), so it's hard to change now. We assume that
17849
- // anything with a "message" is not truly empty, and one of the
17850
- // latter two cases for now.
17851
- return score.type === "invalid" && (!score.message || score.message.length === 0);
17852
- }
17853
-
17854
- /**
17855
- * Combine two score objects.
17856
- *
17857
- * Given two score objects for two different widgets, combine them so that
17858
- * if one is wrong, the total score is wrong, etc.
17859
- */
17860
- function combineScores(scoreA, scoreB) {
17861
- let message;
17862
- if (scoreA.type === "points" && scoreB.type === "points") {
17863
- if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
17864
- // TODO(alpert): Figure out how to combine messages usefully
17865
- message = null;
17866
- } else {
17867
- message = scoreA.message || scoreB.message;
17868
- }
17869
- return {
17870
- type: "points",
17871
- earned: scoreA.earned + scoreB.earned,
17872
- total: scoreA.total + scoreB.total,
17873
- message: message
17874
- };
17875
- }
17876
- if (scoreA.type === "points" && scoreB.type === "invalid") {
17877
- return scoreB;
17878
- }
17879
- if (scoreA.type === "invalid" && scoreB.type === "points") {
17880
- return scoreA;
17881
- }
17882
- if (scoreA.type === "invalid" && scoreB.type === "invalid") {
17883
- if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
17884
- // TODO(alpert): Figure out how to combine messages usefully
17885
- message = null;
17886
- } else {
17887
- message = scoreA.message || scoreB.message;
17888
- }
17889
- return {
17890
- type: "invalid",
17891
- message: message
17892
- };
17893
- }
17894
-
17895
- /**
17896
- * The above checks cover all combinations of score type, so if we get here
17897
- * then something is amiss with our inputs.
17898
- */
17899
- throw new perseusCore.PerseusError("PerseusScore with unknown type encountered", perseusCore.Errors.InvalidInput, {
17900
- metadata: {
17901
- scoreA: JSON.stringify(scoreA),
17902
- scoreB: JSON.stringify(scoreB)
17903
- }
17904
- });
17905
- }
17906
- function flattenScores(widgetScoreMap) {
17907
- return Object.values(widgetScoreMap).reduce(combineScores, noScore);
17908
- }
17909
- function isCorrect(score) {
17910
- return score.type === "points" && score.earned >= score.total;
17911
- }
17912
- function keScoreFromPerseusScore(score,
17913
- // It's weird, but this is what we're passing it
17914
- guess, state) {
17915
- if (score.type === "points") {
17916
- return {
17917
- empty: false,
17918
- correct: isCorrect(score),
17919
- message: score.message,
17920
- guess: guess,
17921
- state: state
17922
- };
17923
- }
17924
- if (score.type === "invalid") {
17925
- return {
17926
- empty: true,
17927
- correct: false,
17928
- message: score.message,
17929
- suppressAlmostThere: score.suppressAlmostThere,
17930
- guess: guess,
17931
- state: state
17932
- };
17933
- }
17934
- throw new perseusCore.PerseusError(
17935
- // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
17936
- "Invalid score type: " + score.type, perseusCore.Errors.InvalidInput, {
17937
- metadata: {
17938
- score: JSON.stringify(score),
17939
- guess: JSON.stringify(guess),
17940
- state: JSON.stringify(state)
17941
- }
17942
- });
17943
- }
17944
-
17945
- const DEFAULT_ALIGNMENT$1 = "block";
17946
- // NOTE(kevinb): "default" is not one in `validAlignments`.
17947
- const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
17948
- const DEFAULT_STATIC = false;
17949
- const DEFAULT_TRACKING = "";
17950
- const DEFAULT_LINTABLE = false;
17951
- const widgets = {};
17952
- const editors = {};
17953
-
17954
- // Widgets must be registered to avoid circular dependencies with the
17955
- // core Editor and Renderer components.
17956
- // TODO(jeremy): The widget name is already embedded in the WidgetExports type
17957
- // so could we drop the `name` parameter here?
17958
- const registerWidget = (name, widget) => {
17959
- widgets[name] = widget;
17960
- };
17961
- const registerWidgets = widgets => {
17962
- widgets.forEach(widget => {
17963
- registerWidget(widget.name, widget);
17964
- });
17965
- validateAlignments();
17966
- };
17967
-
17968
- /**
17969
- *
17970
- * @param name - the widget that you are trying to replace
17971
- * @param replacementName - the name of the widget that takes its place
17972
- *
17973
- * e.g. replaceWidget("transformer", "deprecated-standin") will make it so the
17974
- * transformer widget is replaced by the always correct widget
17975
- */
17976
- const replaceWidget = (name, replacementName) => {
17977
- const substituteWidget = widgets[replacementName];
17978
-
17979
- // If the replacement widget isn't found, we need to throw. Otherwise after
17980
- // removing the deprecated widget, we'll have data asking for a widget type
17981
- // that doesn't exist at all.
17982
- if (!substituteWidget) {
17983
- const errorMsg = `Failed to replace ${name} with ${replacementName}`;
17984
- throw new perseusCore.PerseusError(errorMsg, perseusCore.Errors.Internal);
17985
- }
17986
- registerWidget(name, substituteWidget);
17987
- };
17988
- const replaceDeprecatedWidgets = () => {
17989
- replaceWidget("transformer", "deprecated-standin");
17990
- replaceWidget("lights-puzzle", "deprecated-standin");
17991
- replaceWidget("reaction-diagram", "deprecated-standin");
17992
- replaceWidget("sequence", "deprecated-standin");
17993
- replaceWidget("simulator", "deprecated-standin");
17994
- replaceWidget("unit-input", "deprecated-standin");
17995
- };
17996
- const registerEditors = editorsToRegister => {
17997
- editorsToRegister.forEach(editor => {
17998
- if (!editor.widgetName) {
17999
- throw new perseusCore.PerseusError(`Editor ${editor.displayName} doesn't have a widgetName property`, perseusCore.Errors.Internal);
18000
- }
18001
- editors[editor.widgetName] = editor;
18002
- });
18003
- };
18004
-
18005
- /**
18006
- *
18007
- * @param name - the widget that you are trying to replace
18008
- * @param replacementName - the name of the widget that takes its place
18009
- *
18010
- * e.g. replaceEditor("transformer", "deprecated-standin") will make it so the
18011
- * transformer widget is replaced by the deprecated stand-in widget
18012
- */
18013
- const replaceEditor = (name, replacementName) => {
18014
- const substituteEditor = editors[replacementName];
18015
- if (!substituteEditor && Log) {
18016
- const errorMsg = `Failed to replace editor ${name} with ${replacementName}`;
18017
- Log.error(errorMsg, perseusCore.Errors.Internal);
18018
- return;
18019
- }
18020
- editors[name] = substituteEditor;
18021
- };
18022
- const replaceDeprecatedEditors = () => {
18023
- replaceEditor("transformer", "deprecated-standin");
18024
- replaceEditor("lights-puzzle", "deprecated-standin");
18025
- replaceEditor("reaction-diagram", "deprecated-standin");
18026
- replaceEditor("sequence", "deprecated-standin");
18027
- replaceEditor("simulator", "deprecated-standin");
18028
- replaceEditor("unit-input", "deprecated-standin");
18029
- };
18030
- const getWidget = name => {
18031
- // TODO(alex): Consider referring to these as renderers to avoid
18032
- // overloading "widget"
18033
- if (!___default["default"].has(widgets, name)) {
18034
- return null;
18035
- }
18036
-
18037
- // Allow widgets to specify a widget directly or via a function
18038
- if (widgets[name]?.getWidget) {
18039
- return widgets[name].getWidget?.();
18040
- }
18041
- return widgets[name].widget;
18042
- };
18043
- const getWidgetExport = name => {
18044
- return widgets[name] ?? null;
18045
- };
18046
- const getWidgetValidator = name => {
18047
- return widgets[name]?.validator ?? null;
18048
- };
18049
- const getWidgetScorer = name => {
18050
- return widgets[name]?.scorer ?? null;
18051
- };
18052
- const getPublicWidgetOptionsFunction = name => {
18053
- return widgets[name]?.getPublicWidgetOptions ?? (i => i);
18054
- };
18055
- const getEditor = name => {
18056
- return ___default["default"].has(editors, name) ? editors[name] : null;
18057
- };
18058
- const getTransform = name => {
18059
- return ___default["default"].has(widgets, name) ? widgets[name].transform || ___default["default"].identity : null;
18060
- };
18061
- const getVersion = name => {
18062
- const widgetInfo = widgets[name];
18063
- if (widgetInfo) {
18064
- return widgets[name].version || {
18065
- major: 0,
18066
- minor: 0
18067
- };
18068
- }
18069
- return;
18070
- };
18071
- const getVersionVector = () => {
18072
- const version = {};
18073
- ___default["default"].each(___default["default"].keys(widgets), function (name) {
18074
- version[name] = getVersion(name);
18075
- });
18076
- return version;
18077
- };
18078
- const getPublicWidgets = () => {
18079
- // TODO(alex): Update underscore.js so that _.pick can take a function.
18080
- // @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.
18081
- return ___default["default"].pick(widgets,
18082
- // @ts-expect-error - TS2345 - Argument of type '(name: string) => boolean | undefined' is not assignable to parameter of type 'Iteratee<string[], boolean, string>'.
18083
- ___default["default"].reject(___default["default"].keys(widgets), function (name) {
18084
- return widgets[name].hidden;
18085
- }));
18086
- };
18087
- const isAccessible = widgetInfo => {
18088
- const accessible = widgets[widgetInfo.type].accessible;
18089
- if (typeof accessible === "function") {
18090
- return accessible(widgetInfo.options);
18091
- }
18092
- return !!accessible;
18093
- };
18094
- const getAllWidgetTypes = () => {
18095
- return ___default["default"].keys(widgets);
18096
- };
18097
- const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
18098
- const type = oldWidgetInfo.type;
18099
- // NOTE(jeremy): This looks like it could be replaced by fixing types so
18100
- // that `type` is non-optional. But we're seeing this in Sentry today so I
18101
- // suspect we have legacy data (potentially unpublished) and we should
18102
- // figure that out before depending solely on types.
18103
- if (!___default["default"].isString(type)) {
18104
- throw new perseusCore.PerseusError("widget type must be a string, but was: " + type, perseusCore.Errors.Internal);
18105
- }
18106
- const widgetExports = widgets[type];
18107
- if (widgetExports == null) {
18108
- // If we have a widget that isn't registered, we can't upgrade it
18109
- // TODO(aria): Figure out what the best thing to do here would be
18110
- return oldWidgetInfo;
18111
- }
18112
-
18113
- // Unversioned widgets (pre-July 2014) are all implicitly 0.0
18114
- const initialVersion = oldWidgetInfo.version || {
18115
- major: 0,
18116
- minor: 0
18117
- };
18118
- const latestVersion = widgetExports.version || {
18119
- major: 0,
18120
- minor: 0
18121
- };
18122
-
18123
- // If the widget version is later than what we understand (major
18124
- // version is higher than latest, or major versions are equal and minor
18125
- // version is higher than latest), don't perform any upgrades.
18126
- if (initialVersion.major > latestVersion.major || initialVersion.major === latestVersion.major && initialVersion.minor > latestVersion.minor) {
18127
- return oldWidgetInfo;
18128
- }
18129
-
18130
- // We do a clone here so that it's safe to mutate the input parameter
18131
- // in propUpgrades functions (which I will probably accidentally do at
18132
- // some point, and we would like to not break when that happens).
18133
- let newEditorProps = ___default["default"].clone(oldWidgetInfo.options) || {};
18134
- const upgradePropsMap = widgetExports.propUpgrades || {};
18135
-
18136
- // Empty props usually mean a newly created widget by the editor,
18137
- // and are always considerered up-to-date.
18138
- // Mostly, we'd rather not run upgrade functions on props that are
18139
- // not complete.
18140
- if (___default["default"].keys(newEditorProps).length !== 0) {
18141
- // We loop through all the versions after the current version of
18142
- // the loaded widget, up to and including the latest version of the
18143
- // loaded widget, and run the upgrade function to bring our loaded
18144
- // widget's props up to that version.
18145
- // There is a little subtlety here in that we call
18146
- // upgradePropsMap[1] to upgrade *to* version 1,
18147
- // (not from version 1).
18148
- for (let nextVersion = initialVersion.major + 1; nextVersion <= latestVersion.major; nextVersion++) {
18149
- if (upgradePropsMap[String(nextVersion)]) {
18150
- newEditorProps = upgradePropsMap[String(nextVersion)](newEditorProps);
18151
- } else {
18152
- // This is a Log.error because it is unlikely to be hit in
18153
- // local testing, and a Log.error is slightly less scary in
18154
- // prod than a `throw new Error`
18155
- Log.error("No upgrade found for widget. Cannot render.", perseusCore.Errors.Internal, {
18156
- loggedMetadata: {
18157
- type,
18158
- fromMajorVersion: nextVersion - 1,
18159
- toMajorVersion: nextVersion
18160
- }
18161
- });
18162
- // But try to keep going anyways (yolo!)
18163
- // (Throwing an error here would just break the page
18164
- // silently anyways, so that doesn't seem much better
18165
- // than a halfhearted attempt to continue, however
18166
- // shallow...)
18167
- }
18168
- }
18169
- }
18170
-
18171
- // Minor version upgrades (eg. new optional props) don't have
18172
- // transform functions. Instead, we fill in the new props with their
18173
- // defaults.
18174
- const defaultProps = type in editors ? editors[type].defaultProps : {};
18175
- newEditorProps = {
18176
- ...defaultProps,
18177
- ...newEditorProps
18178
- };
18179
- let alignment = oldWidgetInfo.alignment;
18180
-
18181
- // Widgets that support multiple alignments will "lock in" the
18182
- // alignment to the alignment that would be listed first in the
18183
- // select box. If the widget only supports one alignment, the
18184
- // alignment value will likely just end up as "default".
18185
- if (alignment == null || alignment === "default") {
18186
- alignment = getSupportedAlignments(type)[0];
18187
- }
18188
- let widgetStatic = oldWidgetInfo.static;
18189
- if (widgetStatic == null) {
18190
- widgetStatic = DEFAULT_STATIC;
18191
- }
18192
- return ___default["default"].extend({}, oldWidgetInfo, {
18193
- // maintain other info, like type
18194
- // After upgrading we guarantee that the version is up-to-date
18195
- version: latestVersion,
18196
- // Default graded to true (so null/undefined becomes true):
18197
- graded: oldWidgetInfo.graded != null ? oldWidgetInfo.graded : true,
18198
- alignment: alignment,
18199
- static: widgetStatic,
18200
- options: newEditorProps
18201
- });
18202
- };
18203
- const getRendererPropsForWidgetInfo = (widgetInfo, strings, problemNum) => {
18204
- const type = widgetInfo.type;
18205
- const widgetExports = widgets[type];
18206
- if (widgetExports == null) {
18207
- // The widget is not a registered widget
18208
- // It shouldn't matter what we return here, but for consistency
18209
- // we return the untransformed options, as if the widget did
18210
- // not have a transform defined.
18211
- return widgetInfo;
18212
- }
18213
- let transform;
18214
- if (widgetInfo.static) {
18215
- // There aren't a lot of real places where we'll have to default to
18216
- // _.identity, but it's theoretically possible if someone changes
18217
- // the JSON manually / we have to back out static support for a
18218
- // widget.
18219
- transform = getStaticTransform(type) || ___default["default"].identity;
18220
- } else {
18221
- transform = widgetExports.transform || ___default["default"].identity;
18222
- }
18223
- // widgetInfo.options are the widgetEditor's props:
18224
- return transform(widgetInfo.options, strings, problemNum);
18225
- };
18226
- const traverseChildWidgets$3 = (widgetInfo, traverseRenderer) => {
18227
- if (!traverseRenderer) {
18228
- throw new perseusCore.PerseusError("traverseRenderer must be provided, but was not", perseusCore.Errors.Internal);
18229
- }
18230
- if (!widgetInfo || !widgetInfo.type || !widgets[widgetInfo.type]) {
18231
- return widgetInfo;
18232
- }
18233
- const widgetExports = widgets[widgetInfo.type];
18234
- const props = widgetInfo.options;
18235
- if (widgetExports.traverseChildWidgets && props) {
18236
- const newProps = widgetExports.traverseChildWidgets(props, traverseRenderer);
18237
- return ___default["default"].extend({}, widgetInfo, {
18238
- options: newProps
18239
- });
18240
- }
18241
- return widgetInfo;
18242
- };
18243
-
18244
- /**
18245
- * Handling for the optional alignments for widgets
18246
- * See widget-container.jsx for details on how alignments are implemented.
18247
- */
18248
-
18249
- /**
18250
- * Returns the list of supported alignments for the given (string) widget
18251
- * type. This is used primarily at editing time to display the choices
18252
- * for the user.
18253
- *
18254
- * Supported alignments are given as an array of strings in the exports of
18255
- * a widget's module.
18256
- */
18257
- const getSupportedAlignments = type => {
18258
- const widgetExport = widgets[type];
18259
- // @ts-expect-error - TS2322 - Type 'string[] | readonly Alignment[]' is not assignable to type 'readonly Alignment[]'.
18260
- return widgetExport && widgetExport.supportedAlignments || DEFAULT_SUPPORTED_ALIGNMENTS;
18261
- };
18262
-
18263
- /**
18264
- * For the given (string) widget type, determine the default alignment for
18265
- * the widget. This is used at rendering time to go from "default" alignment
18266
- * to the actual alignment displayed on the screen.
18267
- *
18268
- * The default alignment is given either as a string (called
18269
- * `defaultAlignment`) or a function (called `getDefaultAlignment`) on
18270
- * the exports of a widget's module.
18271
- */
18272
- const getDefaultAlignment = type => {
18273
- const widgetExports = widgets[type];
18274
- let alignment;
18275
- if (!widgetExports) {
18276
- return DEFAULT_ALIGNMENT$1;
18277
- }
18278
- if (widgetExports.getDefaultAlignment) {
18279
- alignment = widgetExports.getDefaultAlignment();
18280
- } else {
18281
- alignment = widgetExports.defaultAlignment;
18282
- }
18283
- return alignment || DEFAULT_ALIGNMENT$1;
18284
- };
18285
- const validAlignments = ["block", "inline-block", "inline", "float-left", "float-right", "full-width"];
18286
-
18287
- /**
18288
- * Used at startup to fail fast if an alignment given by a widget is
18289
- * invalid.
18290
- */
18291
- // TODO(alex): Change this to run as a testcase (vs. being run at runtime)
18292
- // TODO(LP-10707): I think this can be completely removed because our TypeScript types
18293
- // enforce this!
18294
- const validateAlignments = () => {
18295
- ___default["default"].each(widgets, function (widgetInfo) {
18296
- if (widgetInfo.defaultAlignment && !___default["default"].contains(validAlignments, widgetInfo.defaultAlignment)) {
18297
- throw new perseusCore.PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid defaultAlignment value: " + widgetInfo.defaultAlignment, perseusCore.Errors.InvalidInput);
18298
- }
18299
- if (widgetInfo.supportedAlignments) {
18300
- const unknownAlignments = ___default["default"].difference(widgetInfo.supportedAlignments, validAlignments);
18301
- if (unknownAlignments.length) {
18302
- throw new perseusCore.PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid value for supportedAlignments: " + unknownAlignments.join(" "), perseusCore.Errors.InvalidInput);
18303
- }
18304
- }
18305
- });
18306
- };
18307
-
18308
- /**
18309
- * Handling for static mode for widgets that support it.
18310
- */
18311
-
18312
- /**
18313
- * Returns true iff the widget supports static mode.
18314
- * A widget implicitly supports static mode if it exports a
18315
- * staticTransform function.
18316
- */
18317
- const supportsStaticMode = type => {
18318
- const widgetInfo = widgets[type];
18319
- return widgetInfo && widgetInfo.staticTransform != null;
18320
- };
18321
-
18322
- /**
18323
- * Return the staticTransform function used to convert the editorProps to
18324
- * the rendered widget state.
18325
- */
18326
- const getStaticTransform = type => {
18327
- const widgetInfo = widgets[type];
18328
- return widgetInfo && widgetInfo.staticTransform;
18329
- };
18330
-
18331
- /**
18332
- * Returns the tracking option for the widget. The default is "",
18333
- * which means simply to track interactions once. The other available
18334
- * option is "all" which means to track all interactions.
18335
- */
18336
- const getTracking = type => {
18337
- const widgetExport = widgets[type];
18338
- return widgetExport && widgetExport.tracking || DEFAULT_TRACKING;
18339
- };
18340
-
18341
- /**
18342
- * Returns true if this widget can include lintable markdown text
18343
- * and supports a highlightLint prop, or false otherwise.
18344
- */
18345
- const isLintable = type => {
18346
- const widgetExports = widgets[type];
18347
- return widgetExports && widgetExports.isLintable || DEFAULT_LINTABLE;
18348
- };
18349
-
18350
- var widgets$1 = /*#__PURE__*/Object.freeze({
18351
- __proto__: null,
18352
- registerWidget: registerWidget,
18353
- registerWidgets: registerWidgets,
18354
- replaceWidget: replaceWidget,
18355
- replaceDeprecatedWidgets: replaceDeprecatedWidgets,
18356
- registerEditors: registerEditors,
18357
- replaceEditor: replaceEditor,
18358
- replaceDeprecatedEditors: replaceDeprecatedEditors,
18359
- getWidget: getWidget,
18360
- getWidgetExport: getWidgetExport,
18361
- getWidgetValidator: getWidgetValidator,
18362
- getWidgetScorer: getWidgetScorer,
18363
- getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
18364
- getEditor: getEditor,
18365
- getTransform: getTransform,
18366
- getVersion: getVersion,
18367
- getVersionVector: getVersionVector,
18368
- getPublicWidgets: getPublicWidgets,
18369
- isAccessible: isAccessible,
18370
- getAllWidgetTypes: getAllWidgetTypes,
18371
- upgradeWidgetInfoToLatestVersion: upgradeWidgetInfoToLatestVersion,
18372
- getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
18373
- traverseChildWidgets: traverseChildWidgets$3,
18374
- getSupportedAlignments: getSupportedAlignments,
18375
- getDefaultAlignment: getDefaultAlignment,
18376
- validateAlignments: validateAlignments,
18377
- supportsStaticMode: supportsStaticMode,
18378
- getStaticTransform: getStaticTransform,
18379
- getTracking: getTracking,
18380
- isLintable: isLintable
18381
- });
18382
-
18383
- function getUpgradedWidgetOptions(oldWidgetOptions) {
18384
- return perseusCore.mapObject(oldWidgetOptions, (widgetInfo, widgetId) => {
18385
- if (!widgetInfo.type || !widgetInfo.alignment) {
18386
- const newValues = {};
18387
- if (!widgetInfo.type) {
18388
- // TODO: why does widget have no type?
18389
- // We don't want to derive type from widget ID
18390
- // see: LEMS-1845
18391
- newValues.type = widgetId.split(" ")[0];
18392
- }
18393
- if (!widgetInfo.alignment) {
18394
- newValues.alignment = "default";
18395
- }
18396
- widgetInfo = {
18397
- ...widgetInfo,
18398
- ...newValues
18399
- };
18400
- }
18401
- // TODO(LEMS-2656): remove TS suppression
18402
- return upgradeWidgetInfoToLatestVersion(widgetInfo);
18403
- });
18404
- }
18405
-
18406
- /**
18407
- * Checks the given user input to see if any answerable widgets have not been
18408
- * "filled in" (ie. if they're empty). Another way to think about this
18409
- * function is that its a check to see if we can score the provided input.
18410
- */
18411
- function emptyWidgetsFunctional(widgets,
18412
- // This is a port of old code, I'm not sure why
18413
- // we need widgetIds vs the keys of the widgets object
18414
- widgetIds, userInputMap, strings, locale) {
18415
- return widgetIds.filter(id => {
18416
- const widget = widgets[id];
18417
- if (!widget || widget.static === true) {
18418
- // Static widgets shouldn't count as empty
18419
- return false;
18420
- }
18421
- const validator = getWidgetValidator(widget.type);
18422
- const userInput = userInputMap[id];
18423
- const validationData = widget.options;
18424
- const score = validator?.(userInput, validationData, strings, locale);
18425
- if (score) {
18426
- return scoreIsEmpty(score);
18427
- }
18428
- });
18429
- }
18430
-
18431
- // TODO: combine scorePerseusItem with scoreWidgetsFunctional
18432
- // once scorePerseusItem is the only one calling scoreWidgetsFunctional
18433
- function scorePerseusItem(perseusRenderData, userInputMap,
18434
- // TODO(LEMS-2461,LEMS-2391): these probably
18435
- // need to be removed before we move this to the server
18436
- strings, locale) {
18437
- // There seems to be a chance that PerseusRenderer.widgets might include
18438
- // widget data for widgets that are not in PerseusRenderer.content,
18439
- // so this checks that the widgets are being used before scoring them
18440
- const usedWidgetIds = perseusCore.getWidgetIdsFromContent(perseusRenderData.content);
18441
- const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
18442
- return flattenScores(scores);
18443
- }
18444
- function scoreWidgetsFunctional(widgets,
18445
- // This is a port of old code, I'm not sure why
18446
- // we need widgetIds vs the keys of the widgets object
18447
- widgetIds, userInputMap, strings, locale) {
18448
- const upgradedWidgets = getUpgradedWidgetOptions(widgets);
18449
- const gradedWidgetIds = widgetIds.filter(id => {
18450
- const props = upgradedWidgets[id];
18451
- const widgetIsGraded = props?.graded == null || props.graded;
18452
- const widgetIsStatic = !!props?.static;
18453
- // Ungraded widgets or widgets set to static shouldn't be graded.
18454
- return widgetIsGraded && !widgetIsStatic;
18455
- });
18456
- const widgetScores = {};
18457
- gradedWidgetIds.forEach(id => {
18458
- const widget = upgradedWidgets[id];
18459
- if (!widget) {
18460
- return;
18461
- }
18462
- const userInput = userInputMap[id];
18463
- const validator = getWidgetValidator(widget.type);
18464
- const scorer = getWidgetScorer(widget.type);
18465
-
18466
- // We do validation (empty checks) first and then scoring. If
18467
- // validation fails, it's result is itself a PerseusScore.
18468
- const score = validator?.(userInput, widget.options, strings, locale) ?? scorer?.(userInput, widget.options, strings, locale);
18469
- if (score != null) {
18470
- widgetScores[id] = score;
18471
- }
18472
- });
18473
- return widgetScores;
18474
- }
18475
-
18476
17611
  /**
18477
17612
  * TranslationLinter asynchronously calls Khan Academy's poentry linter.
18478
17613
  *
@@ -18629,6 +17764,257 @@ const getInteractiveBoxFromSizeClass = sizeClass => {
18629
17764
  return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
18630
17765
  };
18631
17766
 
17767
+ const DEFAULT_TRACKING = "";
17768
+ const DEFAULT_LINTABLE = false;
17769
+ const widgets = {};
17770
+ const editors = {};
17771
+
17772
+ // Widgets must be registered to avoid circular dependencies with the
17773
+ // core Editor and Renderer components.
17774
+ // TODO(jeremy): The widget name is already embedded in the WidgetExports type
17775
+ // so could we drop the `name` parameter here?
17776
+ const registerWidget = (name, widget) => {
17777
+ widgets[name] = widget;
17778
+ };
17779
+ const registerWidgets = widgets => {
17780
+ widgets.forEach(widget => {
17781
+ registerWidget(widget.name, widget);
17782
+ });
17783
+ };
17784
+
17785
+ /**
17786
+ *
17787
+ * @param name - the widget that you are trying to replace
17788
+ * @param replacementName - the name of the widget that takes its place
17789
+ *
17790
+ * e.g. replaceWidget("transformer", "deprecated-standin") will make it so the
17791
+ * transformer widget is replaced by the always correct widget
17792
+ */
17793
+ const replaceWidget = (name, replacementName) => {
17794
+ const substituteWidget = widgets[replacementName];
17795
+
17796
+ // If the replacement widget isn't found, we need to throw. Otherwise after
17797
+ // removing the deprecated widget, we'll have data asking for a widget type
17798
+ // that doesn't exist at all.
17799
+ if (!substituteWidget) {
17800
+ const errorMsg = `Failed to replace ${name} with ${replacementName}`;
17801
+ throw new perseusCore.PerseusError(errorMsg, perseusCore.Errors.Internal);
17802
+ }
17803
+ registerWidget(name, substituteWidget);
17804
+ };
17805
+ const replaceDeprecatedWidgets = () => {
17806
+ replaceWidget("transformer", "deprecated-standin");
17807
+ replaceWidget("lights-puzzle", "deprecated-standin");
17808
+ replaceWidget("reaction-diagram", "deprecated-standin");
17809
+ replaceWidget("sequence", "deprecated-standin");
17810
+ replaceWidget("simulator", "deprecated-standin");
17811
+ replaceWidget("unit-input", "deprecated-standin");
17812
+ };
17813
+ const registerEditors = editorsToRegister => {
17814
+ editorsToRegister.forEach(editor => {
17815
+ if (!editor.widgetName) {
17816
+ throw new perseusCore.PerseusError(`Editor ${editor.displayName} doesn't have a widgetName property`, perseusCore.Errors.Internal);
17817
+ }
17818
+ editors[editor.widgetName] = editor;
17819
+ });
17820
+ };
17821
+
17822
+ /**
17823
+ *
17824
+ * @param name - the widget that you are trying to replace
17825
+ * @param replacementName - the name of the widget that takes its place
17826
+ *
17827
+ * e.g. replaceEditor("transformer", "deprecated-standin") will make it so the
17828
+ * transformer widget is replaced by the deprecated stand-in widget
17829
+ */
17830
+ const replaceEditor = (name, replacementName) => {
17831
+ const substituteEditor = editors[replacementName];
17832
+ if (!substituteEditor && Log) {
17833
+ const errorMsg = `Failed to replace editor ${name} with ${replacementName}`;
17834
+ Log.error(errorMsg, perseusCore.Errors.Internal);
17835
+ return;
17836
+ }
17837
+ editors[name] = substituteEditor;
17838
+ };
17839
+ const replaceDeprecatedEditors = () => {
17840
+ replaceEditor("transformer", "deprecated-standin");
17841
+ replaceEditor("lights-puzzle", "deprecated-standin");
17842
+ replaceEditor("reaction-diagram", "deprecated-standin");
17843
+ replaceEditor("sequence", "deprecated-standin");
17844
+ replaceEditor("simulator", "deprecated-standin");
17845
+ replaceEditor("unit-input", "deprecated-standin");
17846
+ };
17847
+ const getWidget = name => {
17848
+ // TODO(alex): Consider referring to these as renderers to avoid
17849
+ // overloading "widget"
17850
+ if (!___default["default"].has(widgets, name)) {
17851
+ return null;
17852
+ }
17853
+
17854
+ // Allow widgets to specify a widget directly or via a function
17855
+ if (widgets[name]?.getWidget) {
17856
+ return widgets[name].getWidget?.();
17857
+ }
17858
+ return widgets[name].widget;
17859
+ };
17860
+ const getWidgetExport = name => {
17861
+ return widgets[name] ?? null;
17862
+ };
17863
+ const getPublicWidgetOptionsFunction = name => {
17864
+ return widgets[name]?.getPublicWidgetOptions ?? (i => i);
17865
+ };
17866
+ const getEditor = name => {
17867
+ return ___default["default"].has(editors, name) ? editors[name] : null;
17868
+ };
17869
+ const getTransform = name => {
17870
+ return ___default["default"].has(widgets, name) ? widgets[name].transform || ___default["default"].identity : null;
17871
+ };
17872
+ const getVersion = name => {
17873
+ const widgetInfo = widgets[name];
17874
+ if (widgetInfo) {
17875
+ return widgets[name].version || {
17876
+ major: 0,
17877
+ minor: 0
17878
+ };
17879
+ }
17880
+ return;
17881
+ };
17882
+ const getVersionVector = () => {
17883
+ const version = {};
17884
+ ___default["default"].each(___default["default"].keys(widgets), function (name) {
17885
+ version[name] = getVersion(name);
17886
+ });
17887
+ return version;
17888
+ };
17889
+ const getPublicWidgets = () => {
17890
+ // TODO(alex): Update underscore.js so that _.pick can take a function.
17891
+ // @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.
17892
+ return ___default["default"].pick(widgets,
17893
+ // @ts-expect-error - TS2345 - Argument of type '(name: string) => boolean | undefined' is not assignable to parameter of type 'Iteratee<string[], boolean, string>'.
17894
+ ___default["default"].reject(___default["default"].keys(widgets), function (name) {
17895
+ return widgets[name].hidden;
17896
+ }));
17897
+ };
17898
+ const isAccessible = widgetInfo => {
17899
+ const accessible = widgets[widgetInfo.type].accessible;
17900
+ if (typeof accessible === "function") {
17901
+ return accessible(widgetInfo.options);
17902
+ }
17903
+ return !!accessible;
17904
+ };
17905
+ const getAllWidgetTypes = () => {
17906
+ return ___default["default"].keys(widgets);
17907
+ };
17908
+ const getRendererPropsForWidgetInfo = (widgetInfo, strings, problemNum) => {
17909
+ const type = widgetInfo.type;
17910
+ const widgetExports = widgets[type];
17911
+ if (widgetExports == null) {
17912
+ // The widget is not a registered widget
17913
+ // It shouldn't matter what we return here, but for consistency
17914
+ // we return the untransformed options, as if the widget did
17915
+ // not have a transform defined.
17916
+ return widgetInfo;
17917
+ }
17918
+ let transform;
17919
+ if (widgetInfo.static) {
17920
+ // There aren't a lot of real places where we'll have to default to
17921
+ // _.identity, but it's theoretically possible if someone changes
17922
+ // the JSON manually / we have to back out static support for a
17923
+ // widget.
17924
+ transform = getStaticTransform(type) || ___default["default"].identity;
17925
+ } else {
17926
+ transform = widgetExports.transform || ___default["default"].identity;
17927
+ }
17928
+ // widgetInfo.options are the widgetEditor's props:
17929
+ return transform(widgetInfo.options, strings, problemNum);
17930
+ };
17931
+ const traverseChildWidgets$3 = (widgetInfo, traverseRenderer) => {
17932
+ if (!traverseRenderer) {
17933
+ throw new perseusCore.PerseusError("traverseRenderer must be provided, but was not", perseusCore.Errors.Internal);
17934
+ }
17935
+ if (!widgetInfo || !widgetInfo.type || !widgets[widgetInfo.type]) {
17936
+ return widgetInfo;
17937
+ }
17938
+ const widgetExports = widgets[widgetInfo.type];
17939
+ const props = widgetInfo.options;
17940
+ if (widgetExports.traverseChildWidgets && props) {
17941
+ const newProps = widgetExports.traverseChildWidgets(props, traverseRenderer);
17942
+ return ___default["default"].extend({}, widgetInfo, {
17943
+ options: newProps
17944
+ });
17945
+ }
17946
+ return widgetInfo;
17947
+ };
17948
+
17949
+ /**
17950
+ * Handling for static mode for widgets that support it.
17951
+ */
17952
+
17953
+ /**
17954
+ * Returns true iff the widget supports static mode.
17955
+ * A widget implicitly supports static mode if it exports a
17956
+ * staticTransform function.
17957
+ */
17958
+ const supportsStaticMode = type => {
17959
+ const widgetInfo = widgets[type];
17960
+ return widgetInfo && widgetInfo.staticTransform != null;
17961
+ };
17962
+
17963
+ /**
17964
+ * Return the staticTransform function used to convert the editorProps to
17965
+ * the rendered widget state.
17966
+ */
17967
+ const getStaticTransform = type => {
17968
+ const widgetInfo = widgets[type];
17969
+ return widgetInfo && widgetInfo.staticTransform;
17970
+ };
17971
+
17972
+ /**
17973
+ * Returns the tracking option for the widget. The default is "",
17974
+ * which means simply to track interactions once. The other available
17975
+ * option is "all" which means to track all interactions.
17976
+ */
17977
+ const getTracking = type => {
17978
+ const widgetExport = widgets[type];
17979
+ return widgetExport && widgetExport.tracking || DEFAULT_TRACKING;
17980
+ };
17981
+
17982
+ /**
17983
+ * Returns true if this widget can include lintable markdown text
17984
+ * and supports a highlightLint prop, or false otherwise.
17985
+ */
17986
+ const isLintable = type => {
17987
+ const widgetExports = widgets[type];
17988
+ return widgetExports && widgetExports.isLintable || DEFAULT_LINTABLE;
17989
+ };
17990
+
17991
+ var widgets$1 = /*#__PURE__*/Object.freeze({
17992
+ __proto__: null,
17993
+ registerWidget: registerWidget,
17994
+ registerWidgets: registerWidgets,
17995
+ replaceWidget: replaceWidget,
17996
+ replaceDeprecatedWidgets: replaceDeprecatedWidgets,
17997
+ registerEditors: registerEditors,
17998
+ replaceEditor: replaceEditor,
17999
+ replaceDeprecatedEditors: replaceDeprecatedEditors,
18000
+ getWidget: getWidget,
18001
+ getWidgetExport: getWidgetExport,
18002
+ getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
18003
+ getEditor: getEditor,
18004
+ getTransform: getTransform,
18005
+ getVersion: getVersion,
18006
+ getVersionVector: getVersionVector,
18007
+ getPublicWidgets: getPublicWidgets,
18008
+ isAccessible: isAccessible,
18009
+ getAllWidgetTypes: getAllWidgetTypes,
18010
+ getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
18011
+ traverseChildWidgets: traverseChildWidgets$3,
18012
+ supportsStaticMode: supportsStaticMode,
18013
+ getStaticTransform: getStaticTransform,
18014
+ getTracking: getTracking,
18015
+ isLintable: isLintable
18016
+ });
18017
+
18632
18018
  class WidgetContainer extends React__namespace.Component {
18633
18019
  widgetRef = /*#__PURE__*/React__namespace.createRef();
18634
18020
  static defaultProps = {
@@ -18701,7 +18087,7 @@ class WidgetContainer extends React__namespace.Component {
18701
18087
  }
18702
18088
  let alignment = this.state.widgetProps.alignment;
18703
18089
  if (alignment === "default") {
18704
- alignment = getDefaultAlignment(type);
18090
+ alignment = perseusCore.CoreWidgetRegistry.getDefaultAlignment(type);
18705
18091
  }
18706
18092
  className += " widget-" + alignment;
18707
18093
  const apiOptions = this.state.widgetProps.apiOptions;
@@ -18709,6 +18095,7 @@ class WidgetContainer extends React__namespace.Component {
18709
18095
  // Hack to prevent interaction with static widgets: we overlay a big
18710
18096
  // div on top of the widget and overflow: hidden the container.
18711
18097
  // Ideally widgets themselves should know how to prevent interaction.
18098
+ // UPDATE HTML5: `inert` on the underlying div would be better
18712
18099
  const isStatic = this.state.widgetProps.static || apiOptions.readOnly;
18713
18100
  const staticContainerStyles = {
18714
18101
  position: "relative",
@@ -18720,7 +18107,7 @@ class WidgetContainer extends React__namespace.Component {
18720
18107
  position: "absolute",
18721
18108
  top: 0,
18722
18109
  left: 0,
18723
- zIndex: zIndexInteractiveComponent$1
18110
+ zIndex: 3
18724
18111
  };
18725
18112
 
18726
18113
  // Some widgets may include strings of markdown that we may
@@ -18986,7 +18373,7 @@ class Renderer extends React__namespace.Component {
18986
18373
  };
18987
18374
  };
18988
18375
  _getInitialWidgetState = props => {
18989
- const allWidgetInfo = getUpgradedWidgetOptions(props.widgets);
18376
+ const allWidgetInfo = perseusCore.getUpgradedWidgetOptions(props.widgets);
18990
18377
  return {
18991
18378
  widgetInfo: allWidgetInfo,
18992
18379
  widgetProps: this._getAllWidgetsStartProps(allWidgetInfo, props)
@@ -19898,7 +19285,7 @@ class Renderer extends React__namespace.Component {
19898
19285
  * empty if the graph is in the starting state.
19899
19286
  */
19900
19287
  emptyWidgets() {
19901
- return emptyWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
19288
+ return perseusScore.emptyWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.context.locale);
19902
19289
  }
19903
19290
  _setWidgetProps = (id, newProps, cb, silent) => {
19904
19291
  this.setState(prevState => {
@@ -20028,8 +19415,8 @@ class Renderer extends React__namespace.Component {
20028
19415
  * @deprecated use scorePerseusItem
20029
19416
  */
20030
19417
  score() {
20031
- const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
20032
- const combinedScore = flattenScores(scores);
19418
+ const scores = perseusScore.scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.context.locale);
19419
+ const combinedScore = perseusScore.flattenScores(scores);
20033
19420
  return combinedScore;
20034
19421
  }
20035
19422
 
@@ -20550,7 +19937,7 @@ class InputWithExamples extends React__namespace.Component {
20550
19937
  };
20551
19938
  _getInputClassName = () => {
20552
19939
  // Otherwise, we need to add these INPUT and FOCUSED tags here.
20553
- let className = ClassNames.INPUT + " " + ClassNames.INTERACTIVE;
19940
+ let className = ClassNames.INPUT;
20554
19941
  if (this.state.focused) {
20555
19942
  className += " " + ClassNames.FOCUSED;
20556
19943
  }
@@ -20943,14 +20330,10 @@ const propTransform$3 = widgetOptions => {
20943
20330
  var InputNumber$1 = {
20944
20331
  name: "input-number",
20945
20332
  displayName: "Input number (deprecated - use numeric input instead)",
20946
- defaultAlignment: "inline-block",
20947
20333
  hidden: true,
20948
20334
  widget: InputNumber,
20949
20335
  transform: propTransform$3,
20950
20336
  isLintable: true,
20951
- // TODO(LEMS-2656): remove TS suppression
20952
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInputNumberUserInput'.
20953
- scorer: perseusScore.scoreInputNumber,
20954
20337
  getOneCorrectAnswerFromRubric(rubric) {
20955
20338
  if (rubric.value == null) {
20956
20339
  return;
@@ -21202,14 +20585,11 @@ const propsTransform = function (widgetOptions) {
21202
20585
  var NumericInput$1 = {
21203
20586
  name: "numeric-input",
21204
20587
  displayName: "Numeric input",
21205
- defaultAlignment: "inline-block",
21206
20588
  accessible: true,
21207
20589
  widget: NumericInput,
21208
20590
  transform: propsTransform,
21209
20591
  isLintable: true,
21210
- // TODO(LEMS-2656): remove TS suppression
21211
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumericInputUserInput'.
21212
- scorer: perseusScore.scoreNumericInput,
20592
+ getPublicWidgetOptions: perseusCore.getNumericInputPublicWidgetOptions,
21213
20593
  // TODO(LEMS-2656): remove TS suppression
21214
20594
  // @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusNumericInputRubric'
21215
20595
  getOneCorrectAnswerFromRubric(rubric) {
@@ -21270,24 +20650,6 @@ const getPromptJSON$p = renderProps => {
21270
20650
  };
21271
20651
  };
21272
20652
 
21273
- /**
21274
- * Several widgets don't have "right"/"wrong" scoring logic,
21275
- * so this just says to move on past those widgets
21276
- *
21277
- * TODO(LEMS-2543) widgets that use this probably shouldn't have any
21278
- * scoring logic and the thing scoring an exercise
21279
- * should just know to skip these
21280
- */
21281
- function scoreNoop() {
21282
- let points = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
21283
- return {
21284
- type: "points",
21285
- earned: points,
21286
- total: points,
21287
- message: null
21288
- };
21289
- }
21290
-
21291
20653
  /**
21292
20654
  * Utility functions for manipulating ranges of highlightable content.
21293
20655
  */
@@ -23465,8 +22827,7 @@ var Passage$1 = {
23465
22827
  transform: editorProps => {
23466
22828
  return ___default["default"].pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
23467
22829
  },
23468
- isLintable: true,
23469
- scorer: () => scoreNoop()
22830
+ isLintable: true
23470
22831
  };
23471
22832
 
23472
22833
  /**
@@ -23592,16 +22953,13 @@ var PassageRef$1 = {
23592
22953
  name: "passage-ref",
23593
22954
  displayName: "PassageRef (SAT only)",
23594
22955
  hidden: true,
23595
- defaultAlignment: "inline",
23596
22956
  widget: PassageRef,
23597
22957
  transform: widgetOptions => ({
23598
22958
  passageNumber: widgetOptions.passageNumber,
23599
22959
  referenceNumber: widgetOptions.referenceNumber,
23600
22960
  summaryText: widgetOptions.summaryText
23601
22961
  }),
23602
- version: perseusCore.passageRefLogic.version,
23603
- // TODO: things that aren't interactive shouldn't need scoring functions
23604
- scorer: () => scoreNoop()
22962
+ version: perseusCore.passageRefLogic.version
23605
22963
  };
23606
22964
 
23607
22965
  /**
@@ -23643,109 +23001,6 @@ var mediaQueries = {
23643
23001
  xlOrLarger: `@media screen and (min-width: ${pureXlMin})`
23644
23002
  };
23645
23003
 
23646
- const {
23647
- zIndexAboveScratchpad,
23648
- zIndexInteractiveComponent,
23649
- radioBorderColor,
23650
- checkedColor,
23651
- circleSize,
23652
- radioMarginWidth
23653
- } = constants;
23654
- var sharedStyles = aphrodite.StyleSheet.create({
23655
- perseusInteractive: {
23656
- zIndex: zIndexInteractiveComponent,
23657
- position: "relative"
23658
- },
23659
- aboveScratchpad: {
23660
- position: "relative",
23661
- zIndex: zIndexAboveScratchpad
23662
- },
23663
- blankBackground: {
23664
- // TODO(emily): Use WB colors?
23665
- backgroundColor: "#FDFDFD"
23666
- },
23667
- perseusSrOnly: {
23668
- border: 0,
23669
- clip: "rect(0,0,0,0)",
23670
- height: 1,
23671
- margin: -1,
23672
- overflow: "hidden",
23673
- padding: 0,
23674
- position: "absolute",
23675
- width: 1
23676
- },
23677
- responsiveLabel: {
23678
- // NOTE(charlie): The values used here should be kept in sync with the
23679
- // caption text sizing in Perseus' styles.less.
23680
- // TODO(charlie): Migrate the captions over to using this style.
23681
- [mediaQueries.smOrSmaller]: {
23682
- fontSize: 14,
23683
- lineHeight: 1.3
23684
- },
23685
- [mediaQueries.md]: {
23686
- fontSize: 17,
23687
- lineHeight: 1.4
23688
- },
23689
- [mediaQueries.lgOrLarger]: {
23690
- fontSize: 20,
23691
- lineHeight: 1.4
23692
- }
23693
- },
23694
- responsiveInput: {
23695
- display: "inline-block",
23696
- WebkitAppearance: "none",
23697
- appearance: "none",
23698
- "::-ms-check": {
23699
- display: "none"
23700
- },
23701
- backgroundColor: "#fff",
23702
- border: "2px solid #fff",
23703
- boxShadow: `0 0px 0px 1px ${radioBorderColor}`,
23704
- outline: "none",
23705
- boxSizing: "border-box",
23706
- flexShrink: 0,
23707
- marginBottom: 1,
23708
- marginLeft: 1,
23709
- marginRight: 1,
23710
- marginTop: 1,
23711
- height: circleSize - 2,
23712
- width: circleSize - 2
23713
- },
23714
- responsiveRadioInput: {
23715
- borderRadius: "50%",
23716
- ":checked": {
23717
- backgroundColor: checkedColor,
23718
- border: "none",
23719
- borderRadius: "50%",
23720
- boxShadow: `inset 0px 0px 0px 2px white, ` + `0 0px 0px 2px ${checkedColor}`,
23721
- marginTop: radioMarginWidth,
23722
- marginBottom: radioMarginWidth,
23723
- marginLeft: radioMarginWidth,
23724
- marginRight: radioMarginWidth,
23725
- height: circleSize - 2 * radioMarginWidth,
23726
- width: circleSize - 2 * radioMarginWidth
23727
- }
23728
- },
23729
- responsiveRadioInputActive: {
23730
- backgroundColor: "#fff",
23731
- border: "2px solid #fff",
23732
- borderRadius: "50%",
23733
- boxShadow: `0 0px 0px 2px ${checkedColor}`,
23734
- marginTop: radioMarginWidth,
23735
- marginBottom: radioMarginWidth,
23736
- marginLeft: radioMarginWidth,
23737
- marginRight: radioMarginWidth,
23738
- height: circleSize - 2 * radioMarginWidth,
23739
- width: circleSize - 2 * radioMarginWidth,
23740
- ":checked": {
23741
- backgroundColor: "#fff"
23742
- }
23743
- },
23744
- disableTextSelection: {
23745
- userSelect: "none"
23746
- }
23747
- });
23748
-
23749
23004
  /**
23750
23005
  * A small collection of useful scrolling utility functions.
23751
23006
  */
@@ -24814,7 +24069,7 @@ const BaseRadio = function (_ref) {
24814
24069
  const aphroditeClassName = checked => {
24815
24070
  // Whether or not to show correctness borders
24816
24071
  // for this choice and the next choice.
24817
- return aphrodite.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);
24072
+ return aphrodite.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);
24818
24073
  };
24819
24074
 
24820
24075
  // HACK(abdulrahman): Preloads the selection-state
@@ -25418,18 +24673,105 @@ var Radio = {
25418
24673
  staticTransform: transform,
25419
24674
  version: perseusCore.radioLogic.version,
25420
24675
  propUpgrades: perseusCore.radioLogic.widgetOptionsUpgrades,
25421
- isLintable: true,
25422
- // TODO(LEMS-2656): remove TS suppression
25423
- // @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
25424
- scorer: perseusScore.scoreRadio,
25425
- // TODO(LEMS-2656): remove TS suppression
25426
- // @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
25427
- validator: perseusScore.validateRadio
24676
+ isLintable: true
25428
24677
  };
25429
24678
 
25430
24679
  /* eslint-disable import/no-named-as-default */
25431
24680
  var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
25432
24681
 
24682
+ const {
24683
+ radioBorderColor,
24684
+ checkedColor,
24685
+ circleSize,
24686
+ radioMarginWidth
24687
+ } = constants;
24688
+ var sharedStyles = aphrodite.StyleSheet.create({
24689
+ blankBackground: {
24690
+ // TODO(emily): Use WB colors?
24691
+ backgroundColor: "#FDFDFD"
24692
+ },
24693
+ perseusSrOnly: {
24694
+ border: 0,
24695
+ clip: "rect(0,0,0,0)",
24696
+ height: 1,
24697
+ margin: -1,
24698
+ overflow: "hidden",
24699
+ padding: 0,
24700
+ position: "absolute",
24701
+ width: 1
24702
+ },
24703
+ responsiveLabel: {
24704
+ // NOTE(charlie): The values used here should be kept in sync with the
24705
+ // caption text sizing in Perseus' styles.less.
24706
+ // TODO(charlie): Migrate the captions over to using this style.
24707
+ [mediaQueries.smOrSmaller]: {
24708
+ fontSize: 14,
24709
+ lineHeight: 1.3
24710
+ },
24711
+ [mediaQueries.md]: {
24712
+ fontSize: 17,
24713
+ lineHeight: 1.4
24714
+ },
24715
+ [mediaQueries.lgOrLarger]: {
24716
+ fontSize: 20,
24717
+ lineHeight: 1.4
24718
+ }
24719
+ },
24720
+ responsiveInput: {
24721
+ display: "inline-block",
24722
+ WebkitAppearance: "none",
24723
+ appearance: "none",
24724
+ "::-ms-check": {
24725
+ display: "none"
24726
+ },
24727
+ backgroundColor: "#fff",
24728
+ border: "2px solid #fff",
24729
+ boxShadow: `0 0px 0px 1px ${radioBorderColor}`,
24730
+ outline: "none",
24731
+ boxSizing: "border-box",
24732
+ flexShrink: 0,
24733
+ marginBottom: 1,
24734
+ marginLeft: 1,
24735
+ marginRight: 1,
24736
+ marginTop: 1,
24737
+ height: circleSize - 2,
24738
+ width: circleSize - 2
24739
+ },
24740
+ responsiveRadioInput: {
24741
+ borderRadius: "50%",
24742
+ ":checked": {
24743
+ backgroundColor: checkedColor,
24744
+ border: "none",
24745
+ borderRadius: "50%",
24746
+ boxShadow: `inset 0px 0px 0px 2px white, ` + `0 0px 0px 2px ${checkedColor}`,
24747
+ marginTop: radioMarginWidth,
24748
+ marginBottom: radioMarginWidth,
24749
+ marginLeft: radioMarginWidth,
24750
+ marginRight: radioMarginWidth,
24751
+ height: circleSize - 2 * radioMarginWidth,
24752
+ width: circleSize - 2 * radioMarginWidth
24753
+ }
24754
+ },
24755
+ responsiveRadioInputActive: {
24756
+ backgroundColor: "#fff",
24757
+ border: "2px solid #fff",
24758
+ borderRadius: "50%",
24759
+ boxShadow: `0 0px 0px 2px ${checkedColor}`,
24760
+ marginTop: radioMarginWidth,
24761
+ marginBottom: radioMarginWidth,
24762
+ marginLeft: radioMarginWidth,
24763
+ marginRight: radioMarginWidth,
24764
+ height: circleSize - 2 * radioMarginWidth,
24765
+ width: circleSize - 2 * radioMarginWidth,
24766
+ ":checked": {
24767
+ backgroundColor: "#fff"
24768
+ }
24769
+ },
24770
+ disableTextSelection: {
24771
+ userSelect: "none"
24772
+ }
24773
+ });
24774
+
25433
24775
  const getPromptJSON$n = (renderProps, userInput) => {
25434
24776
  return {
25435
24777
  type: "categorizer",
@@ -25530,7 +24872,6 @@ class Categorizer extends React__namespace.Component {
25530
24872
  className: "category " + aphrodite.css(styles$n.cell),
25531
24873
  key: catNum
25532
24874
  }, /*#__PURE__*/React__namespace.createElement("div", {
25533
- className: ClassNames.INTERACTIVE,
25534
24875
  role: "button",
25535
24876
  "aria-label": catName,
25536
24877
  onClick: () => this.onChange(itemNum, catNum)
@@ -25622,12 +24963,6 @@ var Categorizer$1 = {
25622
24963
  return ___default["default"].pick(editorProps, "items", "categories", "values", "randomizeItems");
25623
24964
  },
25624
24965
  isLintable: true,
25625
- // TODO(LEMS-2656): remove TS suppression
25626
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
25627
- scorer: perseusScore.scoreCategorizer,
25628
- // TODO(LEMS-2656): remove TS suppression
25629
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
25630
- validator: perseusScore.validateCategorizer,
25631
24966
  getPublicWidgetOptions: perseusCore.getCategorizerPublicWidgetOptions
25632
24967
  };
25633
24968
 
@@ -25837,12 +25172,9 @@ const styles$m = aphrodite.StyleSheet.create({
25837
25172
  var CSProgram$1 = {
25838
25173
  name: "cs-program",
25839
25174
  displayName: "CS Program",
25840
- supportedAlignments: ["block", "full-width"],
25841
25175
  widget: CSProgram,
25842
25176
  hidden: true,
25843
- // TODO(LEMS-2656): remove TS suppression
25844
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
25845
- scorer: perseusScore.scoreCSProgram
25177
+ getPublicWidgetOptions: perseusCore.getCSProgramPublicWidgetOptions
25846
25178
  };
25847
25179
 
25848
25180
  const getPromptJSON$l = renderProps => {
@@ -25919,11 +25251,8 @@ var Definition$1 = {
25919
25251
  name: "definition",
25920
25252
  displayName: "Definition",
25921
25253
  accessible: true,
25922
- defaultAlignment: "inline",
25923
25254
  widget: Definition,
25924
- transform: x => x,
25925
- // TODO: things that aren't interactive shouldn't need scoring functions
25926
- scorer: () => scoreNoop()
25255
+ transform: x => x
25927
25256
  };
25928
25257
 
25929
25258
  // There are no required props, but this component can receive props from any
@@ -25950,9 +25279,7 @@ var DeprecatedStandin$1 = {
25950
25279
  name: "deprecated-standin",
25951
25280
  displayName: "Deprecated Standin",
25952
25281
  widget: DeprecatedStandin,
25953
- hidden: true,
25954
- // TODO: things that aren't interactive shouldn't need scoring functions
25955
- scorer: () => scoreNoop(1)
25282
+ hidden: true
25956
25283
  };
25957
25284
 
25958
25285
  const getPromptJSON$k = (renderProps, userInput) => {
@@ -26061,16 +25388,10 @@ const optionsTransform = widgetOptions => {
26061
25388
  var Dropdown$1 = {
26062
25389
  name: "dropdown",
26063
25390
  displayName: "Drop down",
26064
- defaultAlignment: "inline-block",
26065
25391
  accessible: true,
26066
25392
  widget: Dropdown,
26067
25393
  transform: optionsTransform,
26068
- // TODO(LEMS-2656): remove TS suppression
26069
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
26070
- scorer: perseusScore.scoreDropdown,
26071
- // TODO(LEMS-2656): remove TS suppression
26072
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
26073
- validator: perseusScore.validateDropdown
25394
+ getPublicWidgetOptions: perseusCore.getDropdownPublicWidgetOptions
26074
25395
  };
26075
25396
 
26076
25397
  const getPromptJSON$j = renderProps => {
@@ -26229,12 +25550,9 @@ var Explanation$1 = {
26229
25550
  name: "explanation",
26230
25551
  displayName: "Explanation",
26231
25552
  accessible: true,
26232
- defaultAlignment: "inline",
26233
25553
  widget: Explanation,
26234
25554
  transform: ___default["default"].identity,
26235
- isLintable: true,
26236
- // TODO: things that aren't interactive shouldn't need scoring functions
26237
- scorer: () => scoreNoop()
25555
+ isLintable: true
26238
25556
  };
26239
25557
 
26240
25558
  const getPromptJSON$i = (title, rendererJSON, hintRendererJSON) => {
@@ -26442,7 +25760,7 @@ class GradedGroup extends React__namespace.Component {
26442
25760
  DEFAULT_INVALID_MESSAGE_2
26443
25761
  } = this.context.strings;
26444
25762
  const status = score.type === "points" ? score.total === score.earned ? GRADING_STATUSES.correct : GRADING_STATUSES.incorrect : GRADING_STATUSES.invalid;
26445
- 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}`;
25763
+ 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}`;
26446
25764
  this.setState({
26447
25765
  status: status,
26448
25766
  message: message,
@@ -27355,7 +26673,7 @@ class FunctionGrapher extends React__namespace.Component {
27355
26673
  boxSizing: "initial"
27356
26674
  }
27357
26675
  }, /*#__PURE__*/React__namespace.createElement("div", {
27358
- className: "graphie-container above-scratchpad",
26676
+ className: "graphie-container blank-background",
27359
26677
  style: {
27360
26678
  width: box[0],
27361
26679
  height: box[1]
@@ -27483,8 +26801,7 @@ class Grapher extends React__namespace.Component {
27483
26801
  const coords = this.props.plot.coords;
27484
26802
  const asymptote = this.props.plot.asymptote;
27485
26803
  const typeSelector = /*#__PURE__*/React__namespace.createElement("div", {
27486
- style: typeSelectorStyle,
27487
- className: "above-scratchpad"
26804
+ style: typeSelectorStyle
27488
26805
  }, /*#__PURE__*/React__namespace.createElement(ButtonGroup, {
27489
26806
  value: type,
27490
26807
  allowEmpty: true,
@@ -27566,10 +26883,7 @@ var Grapher$1 = {
27566
26883
  hidden: true,
27567
26884
  widget: Grapher,
27568
26885
  transform: propTransform$2,
27569
- staticTransform: staticTransform$4,
27570
- // TODO(LEMS-2656): remove TS suppression
27571
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGrapherUserInput'.
27572
- scorer: perseusScore.scoreGrapher
26886
+ staticTransform: staticTransform$4
27573
26887
  };
27574
26888
 
27575
26889
  const getPromptJSON$f = rendererJSON => {
@@ -27586,24 +26900,6 @@ const getPromptJSON$f = rendererJSON => {
27586
26900
  };
27587
26901
  };
27588
26902
 
27589
- // The `group` widget is basically a widget hosting a full Perseus system in
27590
- // it. As such, scoring a group means scoring all widgets it contains.
27591
- function scoreGroup(userInput, rubric, strings, locale) {
27592
- const scores = scoreWidgetsFunctional(rubric.widgets, Object.keys(rubric.widgets), userInput, strings, locale);
27593
- return flattenScores(scores);
27594
- }
27595
-
27596
- function validateGroup(userInput, validationData, strings, locale) {
27597
- const emptyWidgets = emptyWidgetsFunctional(validationData.widgets, Object.keys(validationData.widgets), userInput, strings, locale);
27598
- if (emptyWidgets.length === 0) {
27599
- return null;
27600
- }
27601
- return {
27602
- type: "invalid",
27603
- message: null
27604
- };
27605
- }
27606
-
27607
26903
  // exports has no 'transform'
27608
26904
 
27609
26905
  class Group extends React__namespace.Component {
@@ -27755,12 +27051,6 @@ var Group$1 = {
27755
27051
  displayName: "Group (SAT only)",
27756
27052
  widget: Group,
27757
27053
  traverseChildWidgets: traverseChildWidgets,
27758
- // TODO(LEMS-2656): remove TS suppression
27759
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
27760
- scorer: scoreGroup,
27761
- // TODO(LEMS-2656): remove TS suppression
27762
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
27763
- validator: validateGroup,
27764
27054
  hidden: true,
27765
27055
  isLintable: true
27766
27056
  };
@@ -27900,10 +27190,7 @@ var Iframe$1 = {
27900
27190
  displayName: "Iframe (deprecated)",
27901
27191
  widget: Iframe,
27902
27192
  // Let's not expose it to all content creators yet
27903
- hidden: true,
27904
- // TODO(LEMS-2656): remove TS suppression
27905
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusIframeUserInput'.
27906
- scorer: perseusScore.scoreIframe
27193
+ hidden: true
27907
27194
  };
27908
27195
 
27909
27196
  const getPromptJSON$d = renderProps => {
@@ -27925,15 +27212,13 @@ const defaultBackgroundImage$2 = {
27925
27212
  width: 0,
27926
27213
  height: 0
27927
27214
  };
27928
- const editorAlignments = ["block", "full-width"];
27929
- const DEFAULT_ALIGNMENT = "block";
27930
27215
 
27931
27216
  // there is no transform as part of exports
27932
27217
 
27933
27218
  class ImageWidget extends React__namespace.Component {
27934
27219
  static contextType = PerseusI18nContext;
27935
27220
  static defaultProps = {
27936
- alignment: DEFAULT_ALIGNMENT,
27221
+ alignment: "block",
27937
27222
  title: "",
27938
27223
  range: [defaultRange, defaultRange],
27939
27224
  box: [defaultBoxSize, defaultBoxSize],
@@ -28100,13 +27385,9 @@ var Image$1 = {
28100
27385
  const bgImage = widgetOptions.backgroundImage;
28101
27386
  return !(bgImage && bgImage.url && !widgetOptions.alt);
28102
27387
  },
28103
- defaultAlignment: DEFAULT_ALIGNMENT,
28104
- supportedAlignments: editorAlignments,
28105
27388
  displayName: "Image",
28106
27389
  widget: ImageWidget,
28107
- isLintable: true,
28108
- // TODO: things that aren't interactive shouldn't need scoring functions
28109
- scorer: () => scoreNoop()
27390
+ isLintable: true
28110
27391
  };
28111
27392
 
28112
27393
  const getPromptJSON$c = () => {
@@ -28709,9 +27990,7 @@ var Interactive = {
28709
27990
  displayName: "Interaction",
28710
27991
  widget: Interaction,
28711
27992
  transform: ___default["default"].identity,
28712
- hidden: true,
28713
- // TODO: things that aren't interactive shouldn't need scoring functions
28714
- scorer: () => scoreNoop()
27993
+ hidden: true
28715
27994
  };
28716
27995
 
28717
27996
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
@@ -28993,7 +28272,7 @@ class Graph extends React__namespace.Component {
28993
28272
  image = null;
28994
28273
  }
28995
28274
  return /*#__PURE__*/React__namespace.createElement("div", {
28996
- className: "graphie-container above-scratchpad",
28275
+ className: "graphie-container blank-background",
28997
28276
  style: {
28998
28277
  width: this.props.box[0],
28999
28278
  height: this.props.box[1]
@@ -29627,7 +28906,8 @@ const YGridTick = _ref => {
29627
28906
  let {
29628
28907
  y,
29629
28908
  range,
29630
- tickStep
28909
+ tickStep,
28910
+ showPi
29631
28911
  } = _ref;
29632
28912
  // If the graph requires out-of-bounds labels, we want to make sure to set the
29633
28913
  // coordinates to the edge of the visible range of the graph. Otherwise,
@@ -29663,6 +28943,7 @@ const YGridTick = _ref => {
29663
28943
  // If the graph displays both the y and x axis lines within the graph, we want
29664
28944
  // to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
29665
28945
  const showLabel = shouldShowLabel(y, range, tickStep);
28946
+ const yLabel = showPi ? divideByAndShowPi(y) : y.toString();
29666
28947
  return /*#__PURE__*/React__namespace.createElement("g", {
29667
28948
  className: "tick",
29668
28949
  "aria-hidden": true
@@ -29680,12 +28961,13 @@ const YGridTick = _ref => {
29680
28961
  textAnchor: "end",
29681
28962
  x: xPositionText,
29682
28963
  y: yPositionText
29683
- }, y.toString()));
28964
+ }, yLabel));
29684
28965
  };
29685
28966
  const XGridTick = _ref2 => {
29686
28967
  let {
29687
28968
  x,
29688
- range
28969
+ range,
28970
+ showPi
29689
28971
  } = _ref2;
29690
28972
  // If the graph requires out-of-bounds labels, we want to make sure to set the
29691
28973
  // coordinates to the edge of the visible range of the graph. Otherwise,
@@ -29724,6 +29006,7 @@ const XGridTick = _ref2 => {
29724
29006
  // Apply the adjustments to the x and y positions for the text
29725
29007
  const xPositionText = xPosition + xAdjustment;
29726
29008
  const yPositionText = yPosition + yAdjustment;
29009
+ const xLabel = showPi ? divideByAndShowPi(x) : x.toString();
29727
29010
  return /*#__PURE__*/React__namespace.createElement("g", {
29728
29011
  className: "tick",
29729
29012
  "aria-hidden": true
@@ -29741,7 +29024,7 @@ const XGridTick = _ref2 => {
29741
29024
  textAnchor: "middle",
29742
29025
  x: xPositionText,
29743
29026
  y: yPositionText
29744
- }, x.toString()));
29027
+ }, xLabel));
29745
29028
  };
29746
29029
 
29747
29030
  // Determines whether to show the label for the given tick
@@ -29788,6 +29071,22 @@ const countSignificantDecimals = number => {
29788
29071
  }
29789
29072
  return numStr.split(".")[1].length;
29790
29073
  };
29074
+
29075
+ // Show the given value as a multiple of pi (already assumed to be
29076
+ // a multiple of pi). Exported for testing
29077
+ function divideByAndShowPi(value) {
29078
+ const dividedValue = value / Math.PI;
29079
+ switch (dividedValue) {
29080
+ case 1:
29081
+ return "π";
29082
+ case -1:
29083
+ return "-π";
29084
+ case 0:
29085
+ return "0";
29086
+ default:
29087
+ return dividedValue + "π";
29088
+ }
29089
+ }
29791
29090
  const AxisTicks = () => {
29792
29091
  const {
29793
29092
  tickStep,
@@ -29810,6 +29109,10 @@ const AxisTicks = () => {
29810
29109
  key: `y-grid-tick-${y}`,
29811
29110
  range: range,
29812
29111
  tickStep: tickStep[Y]
29112
+ // Show the tick labels as multiples of pi
29113
+ // if the tick step is a multiple of pi.
29114
+ ,
29115
+ showPi: tickStep[Y] % Math.PI === 0
29813
29116
  });
29814
29117
  })), /*#__PURE__*/React__namespace.createElement("g", {
29815
29118
  className: "x-axis-ticks"
@@ -29818,6 +29121,10 @@ const AxisTicks = () => {
29818
29121
  x: x,
29819
29122
  key: `x-grid-tick-${x}`,
29820
29123
  range: range
29124
+ // Show the tick labels as multiples of pi
29125
+ // if the tick step is a multiple of pi.
29126
+ ,
29127
+ showPi: tickStep[X] % Math.PI === 0
29821
29128
  });
29822
29129
  })));
29823
29130
  };
@@ -30119,6 +29426,15 @@ const styles$d = aphrodite.StyleSheet.create({
30119
29426
  }
30120
29427
  });
30121
29428
 
29429
+ function isPiMultiple(value) {
29430
+ // Early return for integers that clearly aren't multiples
29431
+ // of pi to save some computation.
29432
+ if (Number.isInteger(value)) {
29433
+ return false;
29434
+ }
29435
+ 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;
29436
+ }
29437
+
30122
29438
  const {
30123
29439
  firstNumericalParse,
30124
29440
  captureScratchpadTouchStart
@@ -30156,7 +29472,8 @@ class NumberInput extends React__namespace.Component {
30156
29472
  onFormatChange: PropTypes__default["default"].func,
30157
29473
  checkValidity: PropTypes__default["default"].func,
30158
29474
  size: PropTypes__default["default"].oneOf(["mini", "small", "normal"]),
30159
- label: PropTypes__default["default"].oneOf(["put your labels outside your inputs!"])
29475
+ label: PropTypes__default["default"].oneOf(["put your labels outside your inputs!"]),
29476
+ allowPiTruncation: PropTypes__default["default"].bool
30160
29477
  };
30161
29478
  static defaultProps = {
30162
29479
  value: null,
@@ -30169,6 +29486,19 @@ class NumberInput extends React__namespace.Component {
30169
29486
  state = {
30170
29487
  format: this.props.format
30171
29488
  };
29489
+ componentDidMount() {
29490
+ // If the value is a multiple of pi, but it is not in the pi format,
29491
+ // then convert it to the pi format and show it as a multiple of pi.
29492
+ const value = this.getValue();
29493
+ if (this.props.allowPiTruncation && value !== null && value !== 0) {
29494
+ if (this.state.format !== "pi" && isPiMultiple(value)) {
29495
+ this._setValue(value / Math.PI, "pi");
29496
+ this.setState({
29497
+ format: "pi"
29498
+ });
29499
+ }
29500
+ }
29501
+ }
30172
29502
  componentDidUpdate(prevProps) {
30173
29503
  if (!kmath.number.equal(this.getValue(), this.props.value)) {
30174
29504
  this._setValue(this.props.value, this.state.format);
@@ -30325,7 +29655,8 @@ class RangeInput extends React__namespace.Component {
30325
29655
  value: PropTypes__default["default"].array.isRequired,
30326
29656
  onChange: PropTypes__default["default"].func.isRequired,
30327
29657
  placeholder: PropTypes__default["default"].array,
30328
- checkValidity: PropTypes__default["default"].func
29658
+ checkValidity: PropTypes__default["default"].func,
29659
+ allowPiTruncation: PropTypes__default["default"].bool
30329
29660
  };
30330
29661
  static defaultProps = {
30331
29662
  placeholder: [null, null]
@@ -30349,14 +29680,16 @@ class RangeInput extends React__namespace.Component {
30349
29680
  // eslint-disable-next-line react/jsx-no-bind
30350
29681
  ,
30351
29682
  onChange: this.onChange.bind(this, 0),
30352
- placeholder: this.props.placeholder[0]
29683
+ placeholder: this.props.placeholder[0],
29684
+ allowPiTruncation: this.props.allowPiTruncation
30353
29685
  })), /*#__PURE__*/React__namespace.createElement(NumberInput, _extends({}, this.props, {
30354
29686
  value: value[1],
30355
29687
  checkValidity: val => checkValidity([value[0], val])
30356
29688
  // eslint-disable-next-line react/jsx-no-bind
30357
29689
  ,
30358
29690
  onChange: this.onChange.bind(this, 1),
30359
- placeholder: this.props.placeholder[1]
29691
+ placeholder: this.props.placeholder[1],
29692
+ allowPiTruncation: this.props.allowPiTruncation
30360
29693
  })));
30361
29694
  }
30362
29695
  }
@@ -37425,10 +36758,7 @@ var InteractiveGraph$1 = {
37425
36758
  name: "interactive-graph",
37426
36759
  displayName: "Interactive graph (Assessments only)",
37427
36760
  widget: InteractiveGraph,
37428
- staticTransform: staticTransform$3,
37429
- // TODO(LEMS-2656): remove TS suppression
37430
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInteractiveGraphUserInput'.
37431
- scorer: perseusScore.scoreInteractiveGraph
36761
+ staticTransform: staticTransform$3
37432
36762
  };
37433
36763
 
37434
36764
  // TODO: can we use WB typography?
@@ -38386,9 +37716,7 @@ var LabelImage$1 = {
38386
37716
  widget: LabelImageWithDependencies,
38387
37717
  accessible: true,
38388
37718
  isLintable: true,
38389
- // TODO(LEMS-2656): remove TS suppression
38390
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusLabelImageUserInput'.
38391
- scorer: perseusScore.scoreLabelImage
37719
+ getPublicWidgetOptions: perseusCore.getLabelImagePublicWidgetOptions
38392
37720
  };
38393
37721
 
38394
37722
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
@@ -38625,7 +37953,7 @@ class Draggable extends React__namespace.Component {
38625
37953
  // We need to keep backwards compatbility with rules specified directly
38626
37954
  // in CSS. Hence the hacky tacking on of manual classNames.
38627
37955
  // See sortable.less for details.
38628
- let className = aphrodite.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";
37956
+ let className = aphrodite.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";
38629
37957
  if (!includePadding) {
38630
37958
  className += " perseus-sortable-draggable-unpadded";
38631
37959
  }
@@ -39316,10 +38644,7 @@ var Matcher$1 = {
39316
38644
  name: "matcher",
39317
38645
  displayName: "Matcher (two column)",
39318
38646
  widget: Matcher,
39319
- isLintable: true,
39320
- // TODO(LEMS-2656): remove TS suppression
39321
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatcherUserInput'.
39322
- scorer: perseusScore.scoreMatcher
38647
+ isLintable: true
39323
38648
  };
39324
38649
 
39325
38650
  const getPromptJSON$8 = (renderProps, userInput) => {
@@ -39722,13 +39047,7 @@ var Matrix$1 = {
39722
39047
  widget: Matrix,
39723
39048
  transform: propTransform$1,
39724
39049
  staticTransform: staticTransform$2,
39725
- isLintable: true,
39726
- // TODO(LEMS-2656): remove TS suppression
39727
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
39728
- scorer: perseusScore.scoreMatrix,
39729
- // TODO(LEMS-2656): remove TS suppression
39730
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
39731
- validator: perseusScore.validateMatrix
39050
+ isLintable: true
39732
39051
  };
39733
39052
 
39734
39053
  const getPromptJSON$7 = () => {
@@ -39818,7 +39137,7 @@ class Measurer extends React__namespace.Component {
39818
39137
  // TODO(scottgrant): This isn't a11y-friendly! We should insist on
39819
39138
  // finding some valid alt text when this widget is used.
39820
39139
  return /*#__PURE__*/React__namespace.createElement("div", {
39821
- className: "perseus-widget perseus-widget-measurer " + "graphie-container above-scratchpad",
39140
+ className: "perseus-widget perseus-widget-measurer " + "graphie-container blank-background",
39822
39141
  style: {
39823
39142
  width: this.props.box[0],
39824
39143
  height: this.props.box[1]
@@ -39843,9 +39162,7 @@ var Measurer$1 = {
39843
39162
  hidden: true,
39844
39163
  widget: Measurer,
39845
39164
  version: perseusCore.measurerLogic.version,
39846
- propUpgrades: perseusCore.measurerLogic.widgetOptionsUpgrades,
39847
- // TODO: things that aren't interactive shouldn't need scoring functions
39848
- scorer: () => scoreNoop(1)
39165
+ propUpgrades: perseusCore.measurerLogic.widgetOptionsUpgrades
39849
39166
  };
39850
39167
 
39851
39168
  /**
@@ -40733,8 +40050,7 @@ var Molecule$1 = {
40733
40050
  name: "molecule-renderer",
40734
40051
  displayName: "Molecule renderer",
40735
40052
  hidden: true,
40736
- widget: MoleculeWidget,
40737
- scorer: () => scoreNoop()
40053
+ widget: MoleculeWidget
40738
40054
  };
40739
40055
 
40740
40056
  const getPromptJSON$6 = (renderProps, userInput) => {
@@ -41309,12 +40625,7 @@ var NumberLine$1 = {
41309
40625
  widget: NumberLine,
41310
40626
  transform: numberLineTransform,
41311
40627
  staticTransform: staticTransform$1,
41312
- // TODO(LEMS-2656): remove TS suppression
41313
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
41314
- scorer: perseusScore.scoreNumberLine,
41315
- // TODO(LEMS-2656): remove TS suppression
41316
- // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
41317
- validator: perseusScore.validateNumberLine
40628
+ getPublicWidgetOptions: perseusCore.getNumberLinePublicWidgetOptions
41318
40629
  };
41319
40630
 
41320
40631
  const getPromptJSON$5 = (renderProps, userInput) => {
@@ -41332,7 +40643,7 @@ const getPromptJSON$5 = (renderProps, userInput) => {
41332
40643
  class PlaceholderCard extends React__namespace.Component {
41333
40644
  render() {
41334
40645
  return /*#__PURE__*/React__namespace.createElement("div", {
41335
- className: "card-wrap " + ClassNames.INTERACTIVE,
40646
+ className: "card-wrap",
41336
40647
  style: {
41337
40648
  width: this.props.width
41338
40649
  }
@@ -41347,7 +40658,7 @@ class PlaceholderCard extends React__namespace.Component {
41347
40658
  class DragHintCard extends React__namespace.Component {
41348
40659
  render() {
41349
40660
  return /*#__PURE__*/React__namespace.createElement("div", {
41350
- className: "card-wrap " + ClassNames.INTERACTIVE
40661
+ className: "card-wrap"
41351
40662
  }, /*#__PURE__*/React__namespace.createElement("div", {
41352
40663
  className: "card drag-hint"
41353
40664
  }));
@@ -41482,7 +40793,7 @@ class Card extends React__namespace.Component {
41482
40793
  const rendererProps = ___default["default"].pick(this.props, "content");
41483
40794
  const onMouseDown = this.props.animating ? $__default["default"].noop : this.onMouseDown;
41484
40795
  return /*#__PURE__*/React__namespace.createElement("div", {
41485
- className: "card-wrap " + ClassNames.INTERACTIVE,
40796
+ className: "card-wrap",
41486
40797
  style: style,
41487
40798
  onMouseDown: onMouseDown,
41488
40799
  onTouchStart: onMouseDown,
@@ -41853,7 +41164,7 @@ class Orderer extends React__namespace.Component {
41853
41164
  });
41854
41165
  }, this));
41855
41166
  return /*#__PURE__*/React__namespace.createElement("div", {
41856
- className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "above-scratchpad blank-background " + "perseus-clearfix " + ClassNames.INTERACTIVE
41167
+ className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "blank-background " + "perseus-clearfix "
41857
41168
  // eslint-disable-next-line react/no-string-refs
41858
41169
  ,
41859
41170
  ref: "orderer"
@@ -41866,12 +41177,6 @@ var Orderer$1 = {
41866
41177
  hidden: true,
41867
41178
  widget: Orderer,
41868
41179
  isLintable: true,
41869
- // TODO(LEMS-2656): remove TS suppression
41870
- // @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
41871
- scorer: perseusScore.scoreOrderer,
41872
- // TODO(LEMS-2656): remove TS suppression
41873
- // @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
41874
- validator: perseusScore.validateOrderer,
41875
41180
  getPublicWidgetOptions: perseusCore.getOrdererPublicWidgetOptions
41876
41181
  };
41877
41182
 
@@ -41915,15 +41220,12 @@ class PassageRefTarget extends React__namespace.Component {
41915
41220
  var PassageRefTarget$1 = {
41916
41221
  name: "passage-ref-target",
41917
41222
  displayName: "PassageRefTarget",
41918
- defaultAlignment: "inline",
41919
41223
  widget: PassageRefTarget,
41920
41224
  hidden: true,
41921
41225
  transform: editorProps => {
41922
41226
  return ___default["default"].pick(editorProps, "content");
41923
41227
  },
41924
- isLintable: true,
41925
- // TODO: things that aren't interactive shouldn't need scoring functions
41926
- scorer: () => scoreNoop()
41228
+ isLintable: true
41927
41229
  };
41928
41230
 
41929
41231
  const getPromptJSON$4 = () => {
@@ -42925,7 +42227,7 @@ class Plotter extends React__namespace.Component {
42925
42227
  marginBottom: this.props.labels[0] ? paddingForBottomLabel : 0
42926
42228
  };
42927
42229
  return /*#__PURE__*/React__namespace.createElement("div", {
42928
- className: "perseus-widget-plotter graphie " + ClassNames.INTERACTIVE
42230
+ className: "perseus-widget-plotter graphie"
42929
42231
  // eslint-disable-next-line react/no-string-refs
42930
42232
  ,
42931
42233
  ref: "graphieDiv",
@@ -42941,13 +42243,7 @@ var Plotter$1 = {
42941
42243
  displayName: "Plotter",
42942
42244
  hidden: true,
42943
42245
  widget: Plotter,
42944
- staticTransform: staticTransform,
42945
- // TODO(LEMS-2656): remove TS suppression
42946
- // @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
42947
- scorer: perseusScore.scorePlotter,
42948
- // TODO(LEMS-2656): remove TS suppression
42949
- // @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
42950
- validator: perseusScore.validatePlotter
42246
+ staticTransform: staticTransform
42951
42247
  };
42952
42248
 
42953
42249
  const getPromptJSON$2 = () => {
@@ -43108,12 +42404,7 @@ var Sorter$1 = {
43108
42404
  displayName: "Sorter",
43109
42405
  widget: Sorter,
43110
42406
  isLintable: true,
43111
- // TODO(LEMS-2656): remove TS suppression
43112
- // @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
43113
- scorer: perseusScore.scoreSorter,
43114
- // TODO(LEMS-2656): remove TS suppression
43115
- // @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
43116
- validator: perseusScore.validateSorter
42407
+ getPublicWidgetOptions: perseusCore.getSorterPublicWidgetOptions
43117
42408
  };
43118
42409
 
43119
42410
  const {
@@ -43329,13 +42620,7 @@ var Table$1 = {
43329
42620
  widget: Table,
43330
42621
  transform: propTransform,
43331
42622
  hidden: true,
43332
- isLintable: true,
43333
- // TODO(LEMS-2656): remove TS suppression
43334
- // @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
43335
- scorer: perseusScore.scoreTable,
43336
- // TODO(LEMS-2656): remove TS suppression
43337
- // @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
43338
- validator: perseusScore.validateTable
42623
+ isLintable: true
43339
42624
  };
43340
42625
 
43341
42626
  const getPromptJSON = () => {
@@ -43497,11 +42782,7 @@ class Video extends React__namespace.Component {
43497
42782
  var Video$1 = {
43498
42783
  name: "video",
43499
42784
  displayName: "Video",
43500
- defaultAlignment: "block",
43501
- supportedAlignments: ["block", "float-left", "float-right", "full-width"],
43502
- widget: Video,
43503
- // TODO: things that aren't interactive shouldn't need scoring functions
43504
- scorer: () => scoreNoop()
42785
+ widget: Video
43505
42786
  };
43506
42787
 
43507
42788
  /* eslint-disable import/no-named-as-default */
@@ -43520,7 +42801,7 @@ const init = function () {
43520
42801
 
43521
42802
  // This file is processed by a Rollup plugin (replace) to inject the production
43522
42803
  const libName = "@khanacademy/perseus";
43523
- const libVersion = "52.0.0";
42804
+ const libVersion = "53.0.0";
43524
42805
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
43525
42806
 
43526
42807
  const apiVersion = {
@@ -44396,6 +43677,42 @@ const ref = /*#__PURE__*/React__namespace.forwardRef((props, ref) => /*#__PURE__
44396
43677
  }));
44397
43678
  }));
44398
43679
 
43680
+ function isCorrect(score) {
43681
+ return score.type === "points" && score.earned >= score.total;
43682
+ }
43683
+ function keScoreFromPerseusScore(score,
43684
+ // It's weird, but this is what we're passing it
43685
+ guess, state) {
43686
+ if (score.type === "points") {
43687
+ return {
43688
+ empty: false,
43689
+ correct: isCorrect(score),
43690
+ message: score.message,
43691
+ guess: guess,
43692
+ state: state
43693
+ };
43694
+ }
43695
+ if (score.type === "invalid") {
43696
+ return {
43697
+ empty: true,
43698
+ correct: false,
43699
+ message: score.message,
43700
+ suppressAlmostThere: score.suppressAlmostThere,
43701
+ guess: guess,
43702
+ state: state
43703
+ };
43704
+ }
43705
+ throw new perseusCore.PerseusError(
43706
+ // @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
43707
+ "Invalid score type: " + score.type, perseusCore.Errors.InvalidInput, {
43708
+ metadata: {
43709
+ score: JSON.stringify(score),
43710
+ guess: JSON.stringify(guess),
43711
+ state: JSON.stringify(state)
43712
+ }
43713
+ });
43714
+ }
43715
+
44399
43716
  /**
44400
43717
  * Traverses a {content, widgets, images} renderer props object,
44401
43718
  * such as `itemData.question`
@@ -44416,7 +43733,7 @@ const deepCallbackFor = function (contentCallback, widgetCallback, optionsCallba
44416
43733
  // This doesn't modify the widget info if the widget info
44417
43734
  // is at a later version than is supported, which is important
44418
43735
  // for our latestVersion test below.
44419
- const upgradedWidgetInfo = upgradeWidgetInfoToLatestVersion(widgetInfo);
43736
+ const upgradedWidgetInfo = perseusCore.upgradeWidgetInfoToLatestVersion(widgetInfo);
44420
43737
  const latestVersion = getVersion(upgradedWidgetInfo.type);
44421
43738
 
44422
43739
  // Only traverse our children if we can understand this version
@@ -44528,7 +43845,7 @@ const isRawWidgetInfoRenderableBy = function (widgetInfo, rendererContentVersion
44528
43845
 
44529
43846
  // NOTE: This doesn't modify the widget info if the widget info
44530
43847
  // is at a later version than is supported.
44531
- const upgradedWidgetInfo = upgradeWidgetInfoToLatestVersion(widgetInfo);
43848
+ const upgradedWidgetInfo = perseusCore.upgradeWidgetInfoToLatestVersion(widgetInfo);
44532
43849
  return isUpgradedWidgetInfoRenderableBy(upgradedWidgetInfo, rendererContentVersion[upgradedWidgetInfo.type]);
44533
43850
  };
44534
43851
  const isRendererContentRenderableBy = function (rendererOptions, rendererContentVersion) {
@@ -45876,7 +45193,6 @@ exports.mediaQueries = mediaQueries;
45876
45193
  exports.parseDataFromJSONP = parseDataFromJSONP;
45877
45194
  exports.preprocessTex = preprocessTex;
45878
45195
  exports.registerAllWidgetsForTesting = registerAllWidgetsForTesting;
45879
- exports.scorePerseusItem = scorePerseusItem;
45880
45196
  exports.shouldHaveIndividualAnswer = shouldHaveIndividualAnswer;
45881
45197
  exports.traverse = traverse;
45882
45198
  exports.usePerseusI18n = usePerseusI18n;