@khanacademy/perseus 33.2.1 → 34.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 (42) hide show
  1. package/dist/components/sortable.d.ts +1 -4
  2. package/dist/es/index.js +541 -585
  3. package/dist/es/index.js.map +1 -1
  4. package/dist/index.d.ts +0 -1
  5. package/dist/index.js +540 -587
  6. package/dist/index.js.map +1 -1
  7. package/dist/perseus-api.d.ts +2 -0
  8. package/dist/perseus-types.d.ts +11 -6
  9. package/dist/server-item-renderer.d.ts +2 -0
  10. package/dist/types.d.ts +2 -2
  11. package/dist/util/answer-types.d.ts +1 -1
  12. package/dist/util.d.ts +2 -1
  13. package/dist/widgets/__shared__/noop-validator.d.ts +11 -0
  14. package/dist/widgets/categorizer/categorizer.d.ts +3 -1
  15. package/dist/widgets/cs-program/cs-program.d.ts +23 -17
  16. package/dist/widgets/dropdown/dropdown-validator.d.ts +4 -0
  17. package/dist/widgets/dropdown/dropdown.d.ts +1 -5
  18. package/dist/widgets/dropdown/dropdown.types.d.ts +5 -0
  19. package/dist/widgets/expression/expression-validator.d.ts +5 -0
  20. package/dist/widgets/expression/expression.d.ts +13 -6
  21. package/dist/widgets/expression/expression.types.d.ts +3 -0
  22. package/dist/widgets/expression/get-decimal-separator.d.ts +5 -0
  23. package/dist/widgets/iframe/iframe.d.ts +24 -15
  24. package/dist/widgets/interactive-graph.d.ts +3 -0
  25. package/dist/widgets/interactive-graphs/graph-locked-labels-layer.d.ts +2 -0
  26. package/dist/widgets/interactive-graphs/graphs/components/movable-point-view.d.ts +1 -1
  27. package/dist/widgets/interactive-graphs/graphs/components/movable-point.d.ts +2 -1
  28. package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +15 -4
  29. package/dist/widgets/interactive-graphs/mafs-graph.d.ts +3 -1
  30. package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +8 -1
  31. package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +2 -1
  32. package/dist/widgets/interactive-graphs/types.d.ts +5 -2
  33. package/dist/widgets/label-image/label-image.d.ts +4 -0
  34. package/dist/widgets/matcher/matcher.d.ts +17 -46
  35. package/dist/widgets/numeric-input/numeric-input-validator.d.ts +6 -0
  36. package/dist/widgets/numeric-input/numeric-input.d.ts +1 -5
  37. package/dist/widgets/numeric-input/numeric-input.types.d.ts +5 -0
  38. package/dist/widgets/orderer/orderer.d.ts +6 -5
  39. package/dist/widgets/sorter/sorter.d.ts +18 -44
  40. package/dist/widgets/table/table.d.ts +20 -24
  41. package/package.json +2 -1
  42. package/dist/mixins/widget-jsonify-deprecated.d.ts +0 -5
package/dist/es/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import * as KAS from '@khanacademy/kas';
2
2
  import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
3
- import { PerseusError, Errors, addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
4
3
  import * as PerseusLinter from '@khanacademy/perseus-linter';
5
- import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
4
+ import { linterContextDefault, Rule } from '@khanacademy/perseus-linter';
6
5
  import { View, RenderStateRoot, UniqueIDProvider, useLatestRef, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
7
6
  import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
8
7
  import { HeadingMedium, LabelSmall, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
@@ -18,6 +17,7 @@ import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder
18
17
  import { color, spacing, font } from '@khanacademy/wonder-blocks-tokens';
19
18
  import $ from 'jquery';
20
19
  import { v4 } from 'uuid';
20
+ import { PerseusError, Errors, addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
21
21
  import PropTypes from 'prop-types';
22
22
  import { number, point, vector as vector$1, line } from '@khanacademy/kmath';
23
23
  import { entries, UnreachableCaseError, keys } from '@khanacademy/wonder-stuff-core';
@@ -732,23 +732,6 @@ var dependencies = /*#__PURE__*/Object.freeze({
732
732
  useDependencies: useDependencies
733
733
  });
734
734
 
735
- // TODO(LP-11481): Change the primary API we use for logging in Perseus to use
736
- // getDependencies() internally... simplify usage to just using a "singleton"
737
- // which get's the injected logger.
738
- /**
739
- * Provides logging infrastructure for Perseus
740
- */
741
- const Log = {
742
- // Logs a message
743
- log: (message, extra) => {
744
- getDependencies().Log.log(message, extra);
745
- },
746
- // Logs an error with a message
747
- error: (message, kind, extra) => {
748
- getDependencies().Log.error(message, kind, extra);
749
- }
750
- };
751
-
752
735
  /**
753
736
  * These are things that widgets should exclude when serializing themselves.
754
737
  *
@@ -1192,6 +1175,23 @@ const ClassNames = {
1192
1175
  MOBILE: "perseus-mobile"
1193
1176
  };
1194
1177
 
1178
+ // TODO(LP-11481): Change the primary API we use for logging in Perseus to use
1179
+ // getDependencies() internally... simplify usage to just using a "singleton"
1180
+ // which get's the injected logger.
1181
+ /**
1182
+ * Provides logging infrastructure for Perseus
1183
+ */
1184
+ const Log = {
1185
+ // Logs a message
1186
+ log: (message, extra) => {
1187
+ getDependencies().Log.log(message, extra);
1188
+ },
1189
+ // Logs an error with a message
1190
+ error: (message, kind, extra) => {
1191
+ getDependencies().Log.error(message, kind, extra);
1192
+ }
1193
+ };
1194
+
1195
1195
  const KhanMath = {
1196
1196
  // Simplify formulas before display
1197
1197
  cleanMath: function (expr) {
@@ -2106,6 +2106,176 @@ const KhanAnswerTypes = {
2106
2106
  }
2107
2107
  };
2108
2108
 
2109
+ /**
2110
+ * Get the character used for separating decimals.
2111
+ */
2112
+ const getDecimalSeparator = locale => {
2113
+ var _match$;
2114
+ switch (locale) {
2115
+ // TODO(somewhatabstract): Remove this when Chrome supports the `ka`
2116
+ // locale properly.
2117
+ // https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
2118
+ //
2119
+ // Supported locales in Chrome:
2120
+ // https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
2121
+ case "ka":
2122
+ return ",";
2123
+ default:
2124
+ const numberWithDecimalSeparator = 1.1;
2125
+ // TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
2126
+ // support Safari 12.
2127
+ const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
2128
+ // 0x661 is ARABIC-INDIC DIGIT ONE
2129
+ // 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
2130
+ .match(/[^\d\u0661\u06F1]/);
2131
+ return (_match$ = match == null ? void 0 : match[0]) != null ? _match$ : ".";
2132
+ }
2133
+ };
2134
+
2135
+ /* Content creators input a list of answers which are matched from top to
2136
+ * bottom. The intent is that they can include spcific solutions which should
2137
+ * be graded as correct or incorrect (or ungraded!) first, then get more
2138
+ * general.
2139
+ *
2140
+ * We iterate through each answer, trying to match it with the user's input
2141
+ * using the following angorithm:
2142
+ * - Try to parse the user's input. If it doesn't parse then return "not
2143
+ * graded".
2144
+ * - For each answer:
2145
+ * ~ Try to validate the user's input against the answer. The answer is
2146
+ * expected to parse.
2147
+ * ~ If the user's input validates (the validator judges it "correct"), we've
2148
+ * matched and can stop considering answers.
2149
+ * - If there were no matches or the matching answer is considered "ungraded",
2150
+ * show the user an error. TODO(joel) - what error?
2151
+ * - Otherwise, pass through the resulting points and message.
2152
+ */
2153
+ function expressionValidator(userInput, rubric,
2154
+ // @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
2155
+ onInputError = function () {}, strings, locale) {
2156
+ const options = _.clone(rubric);
2157
+ _.extend(options, {
2158
+ decimal_separator: getDecimalSeparator(locale)
2159
+ });
2160
+ const createValidator = answer => {
2161
+ // We give options to KAS.parse here because it is parsing the
2162
+ // solution answer, not the student answer, and we don't want a
2163
+ // solution to work if the student is using a different language
2164
+ // (different from the content creation language, ie. English).
2165
+ const expression = KAS.parse(answer.value, rubric);
2166
+ // An answer may not be parsed if the expression was defined
2167
+ // incorrectly. For example if the answer is using a symbol defined
2168
+ // in the function variables list for the expression.
2169
+ if (!expression.parsed) {
2170
+ /* c8 ignore next */
2171
+ Log.error("Unable to parse solution answer for expression", Errors.InvalidInput, {
2172
+ loggedMetadata: {
2173
+ rubric: JSON.stringify(rubric)
2174
+ }
2175
+ });
2176
+ return null;
2177
+ }
2178
+ return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, _({}).extend(options, {
2179
+ simplify: answer.simplify,
2180
+ form: answer.form
2181
+ }), strings);
2182
+ };
2183
+
2184
+ // Find the first answer form that matches the user's input and that
2185
+ // is considered correct. Also, track whether the input is
2186
+ // considered "empty" for all answer forms, and keep the validation
2187
+ // result for the first answer form for which the user's input was
2188
+ // considered "ungraded".
2189
+ // (Terminology reminder: the answer forms are provided by the
2190
+ // assessment items; they are not the user's input. Each one might
2191
+ // represent a correct answer, an incorrect one (if the exercise
2192
+ // creator has predicted certain common wrong answers and wants to
2193
+ // provide guidance via a message), or an ungraded one (same idea,
2194
+ // but without giving the user an incorrect mark for the question).
2195
+ let matchingAnswerForm;
2196
+ let matchMessage;
2197
+ let allEmpty = true;
2198
+ let firstUngradedResult;
2199
+ for (const answerForm of rubric.answerForms || []) {
2200
+ const validator = createValidator(answerForm);
2201
+ if (!validator) {
2202
+ continue;
2203
+ }
2204
+ const result = validator(userInput);
2205
+
2206
+ // Short-circuit as soon as the user's input matches some answer
2207
+ // (independently of whether the answer is correct)
2208
+ if (result.correct) {
2209
+ matchingAnswerForm = answerForm;
2210
+ matchMessage = result.message || "";
2211
+ break;
2212
+ }
2213
+ allEmpty = allEmpty && result.empty;
2214
+ // If this answer form is correct and the user's input is considered
2215
+ // "ungraded" for it, we'll want to keep the evaluation result for
2216
+ // later. If the user's input doesn't match any answer forms, we'll
2217
+ // show the message from this validation.
2218
+ if (answerForm.considered === "correct" && result.ungraded && !firstUngradedResult) {
2219
+ firstUngradedResult = result;
2220
+ }
2221
+ }
2222
+
2223
+ // Now check to see if we matched any answer form at all, and if
2224
+ // we did, whether it's considered correct, incorrect, or ungraded
2225
+ if (!matchingAnswerForm) {
2226
+ if (firstUngradedResult) {
2227
+ // While we didn't directly match with any answer form, we
2228
+ // did at some point get an "ungraded" validation result,
2229
+ // which might indicate e.g. a mismatch in variable casing.
2230
+ // We'll return "invalid", which will let the user try again
2231
+ // with no penalty, and the hopefully helpful validation
2232
+ // message.
2233
+ return {
2234
+ type: "invalid",
2235
+ message: firstUngradedResult.message,
2236
+ suppressAlmostThere: firstUngradedResult.suppressAlmostThere
2237
+ };
2238
+ }
2239
+ if (allEmpty) {
2240
+ // If everything graded as empty, it's invalid.
2241
+ return {
2242
+ type: "invalid",
2243
+ message: null
2244
+ };
2245
+ }
2246
+ // We fell through all the possibilities and we're not empty,
2247
+ // so the answer is considered incorrect.
2248
+ return {
2249
+ type: "points",
2250
+ earned: 0,
2251
+ total: 1
2252
+ };
2253
+ }
2254
+ if (matchingAnswerForm.considered === "ungraded") {
2255
+ // We matched an ungraded answer form - return "invalid", which
2256
+ // will let the user try again with no penalty
2257
+ const apiResult = onInputError(null,
2258
+ // Reserved for some widget identifier
2259
+ userInput, matchMessage);
2260
+ return {
2261
+ type: "invalid",
2262
+ message: apiResult === false ? null : matchMessage
2263
+ };
2264
+ }
2265
+ // We matched a graded answer form, so we can now tell the user
2266
+ // whether their input was correct or incorrect, and hand out
2267
+ // points accordingly
2268
+ // TODO(eater): Seems silly to translate result to this
2269
+ // invalid/points thing and immediately translate it back in
2270
+ // ItemRenderer.scoreInput()
2271
+ return {
2272
+ type: "points",
2273
+ earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
2274
+ total: 1,
2275
+ message: matchMessage
2276
+ };
2277
+ }
2278
+
2109
2279
  const _excluded$5 = ["scoring"];
2110
2280
  // Map of international operator names to their English equivalents
2111
2281
  const englishOperators = {
@@ -2136,32 +2306,6 @@ const deriveKeypadVersion = apiOptions => {
2136
2306
  // can remove this `virtualKeypadVersion` field entirely.
2137
2307
  return apiOptions.nativeKeypadProxy != null ? "REACT_NATIVE_KEYPAD" : "MATH_INPUT_KEYPAD_V2";
2138
2308
  };
2139
- /**
2140
- * Get the character used for separating decimals.
2141
- */
2142
- const getDecimalSeparator = locale => {
2143
- var _match$;
2144
- switch (locale) {
2145
- // TODO(somewhatabstract): Remove this when Chrome supports the `ka`
2146
- // locale properly.
2147
- // https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
2148
- //
2149
- // Supported locales in Chrome:
2150
- // https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
2151
- case "ka":
2152
- return ",";
2153
- default:
2154
- const numberWithDecimalSeparator = 1.1;
2155
- // TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
2156
- // support Safari 12.
2157
- const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
2158
- // 0x661 is ARABIC-INDIC DIGIT ONE
2159
- // 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
2160
- .match(/[^\d\u0661\u06F1]/);
2161
- return (_match$ = match == null ? void 0 : match[0]) != null ? _match$ : ".";
2162
- }
2163
- };
2164
-
2165
2309
  // The new, MathQuill input expression widget
2166
2310
  class Expression extends React.Component {
2167
2311
  constructor(..._args) {
@@ -2230,7 +2374,7 @@ class Expression extends React.Component {
2230
2374
  scoring
2231
2375
  } = _ref,
2232
2376
  rubric = _objectWithoutPropertiesLoose(_ref, _excluded$5);
2233
- const score = Expression.validate(this.getUserInput(), rubric, onInputError || function () {}, this.context.strings, this.context.locale);
2377
+ const score = expressionValidator(this.getUserInput(), rubric, onInputError || function () {}, this.context.strings, this.context.locale);
2234
2378
 
2235
2379
  // "scoring" is a flag that indicates when we are checking answers.
2236
2380
  // otherwise, we may just be checking validity after changes.
@@ -2320,149 +2464,11 @@ class Expression extends React.Component {
2320
2464
  }, cb);
2321
2465
  };
2322
2466
  }
2323
- //#region Previously a class extension
2324
- /* Content creators input a list of answers which are matched from top to
2325
- * bottom. The intent is that they can include spcific solutions which should
2326
- * be graded as correct or incorrect (or ungraded!) first, then get more
2327
- * general.
2328
- *
2329
- * We iterate through each answer, trying to match it with the user's input
2330
- * using the following angorithm:
2331
- * - Try to parse the user's input. If it doesn't parse then return "not
2332
- * graded".
2333
- * - For each answer:
2334
- * ~ Try to validate the user's input against the answer. The answer is
2335
- * expected to parse.
2336
- * ~ If the user's input validates (the validator judges it "correct"), we've
2337
- * matched and can stop considering answers.
2338
- * - If there were no matches or the matching answer is considered "ungraded",
2339
- * show the user an error. TODO(joel) - what error?
2340
- * - Otherwise, pass through the resulting points and message.
2341
- */
2467
+ // TODO remove this in favor of just using expressionValidator
2342
2468
  static validate(userInput, rubric,
2343
2469
  // @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
2344
2470
  onInputError = function () {}, strings, locale) {
2345
- const options = _.clone(rubric);
2346
- _.extend(options, {
2347
- decimal_separator: getDecimalSeparator(locale)
2348
- });
2349
- const createValidator = answer => {
2350
- // We give options to KAS.parse here because it is parsing the
2351
- // solution answer, not the student answer, and we don't want a
2352
- // solution to work if the student is using a different language
2353
- // (different from the content creation language, ie. English).
2354
- const expression = KAS.parse(answer.value, rubric);
2355
- // An answer may not be parsed if the expression was defined
2356
- // incorrectly. For example if the answer is using a symbol defined
2357
- // in the function variables list for the expression.
2358
- if (!expression.parsed) {
2359
- /* c8 ignore next */
2360
- Log.error("Unable to parse solution answer for expression", Errors.InvalidInput, {
2361
- loggedMetadata: {
2362
- rubric: JSON.stringify(rubric)
2363
- }
2364
- });
2365
- return null;
2366
- }
2367
- return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, _({}).extend(options, {
2368
- simplify: answer.simplify,
2369
- form: answer.form
2370
- }), strings);
2371
- };
2372
-
2373
- // Find the first answer form that matches the user's input and that
2374
- // is considered correct. Also, track whether the input is
2375
- // considered "empty" for all answer forms, and keep the validation
2376
- // result for the first answer form for which the user's input was
2377
- // considered "ungraded".
2378
- // (Terminology reminder: the answer forms are provided by the
2379
- // assessment items; they are not the user's input. Each one might
2380
- // represent a correct answer, an incorrect one (if the exercise
2381
- // creator has predicted certain common wrong answers and wants to
2382
- // provide guidance via a message), or an ungraded one (same idea,
2383
- // but without giving the user an incorrect mark for the question).
2384
- let matchingAnswerForm;
2385
- let matchMessage;
2386
- let allEmpty = true;
2387
- let firstUngradedResult;
2388
- for (const answerForm of rubric.answerForms || []) {
2389
- const validate = createValidator(answerForm);
2390
- if (!validate) {
2391
- continue;
2392
- }
2393
- const result = validate(userInput);
2394
-
2395
- // Short-circuit as soon as the user's input matches some answer
2396
- // (independently of whether the answer is correct)
2397
- if (result.correct) {
2398
- matchingAnswerForm = answerForm;
2399
- matchMessage = result.message || "";
2400
- break;
2401
- }
2402
- allEmpty = allEmpty && result.empty;
2403
- // If this answer form is correct and the user's input is considered
2404
- // "ungraded" for it, we'll want to keep the evaluation result for
2405
- // later. If the user's input doesn't match any answer forms, we'll
2406
- // show the message from this validation.
2407
- if (answerForm.considered === "correct" && result.ungraded && !firstUngradedResult) {
2408
- firstUngradedResult = result;
2409
- }
2410
- }
2411
-
2412
- // Now check to see if we matched any answer form at all, and if
2413
- // we did, whether it's considered correct, incorrect, or ungraded
2414
- if (!matchingAnswerForm) {
2415
- if (firstUngradedResult) {
2416
- // While we didn't directly match with any answer form, we
2417
- // did at some point get an "ungraded" validation result,
2418
- // which might indicate e.g. a mismatch in variable casing.
2419
- // We'll return "invalid", which will let the user try again
2420
- // with no penalty, and the hopefully helpful validation
2421
- // message.
2422
- return {
2423
- type: "invalid",
2424
- message: firstUngradedResult.message,
2425
- suppressAlmostThere: firstUngradedResult.suppressAlmostThere
2426
- };
2427
- }
2428
- if (allEmpty) {
2429
- // If everything graded as empty, it's invalid.
2430
- return {
2431
- type: "invalid",
2432
- message: null
2433
- };
2434
- }
2435
- // We fell through all the possibilities and we're not empty,
2436
- // so the answer is considered incorrect.
2437
- return {
2438
- type: "points",
2439
- earned: 0,
2440
- total: 1
2441
- };
2442
- }
2443
- if (matchingAnswerForm.considered === "ungraded") {
2444
- // We matched an ungraded answer form - return "invalid", which
2445
- // will let the user try again with no penalty
2446
- const apiResult = onInputError(null,
2447
- // Reserved for some widget identifier
2448
- userInput, matchMessage);
2449
- return {
2450
- type: "invalid",
2451
- message: apiResult === false ? null : matchMessage
2452
- };
2453
- }
2454
- // We matched a graded answer form, so we can now tell the user
2455
- // whether their input was correct or incorrect, and hand out
2456
- // points accordingly
2457
- // TODO(eater): Seems silly to translate result to this
2458
- // invalid/points thing and immediately translate it back in
2459
- // ItemRenderer.scoreInput()
2460
- return {
2461
- type: "points",
2462
- earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
2463
- total: 1,
2464
- message: matchMessage
2465
- };
2471
+ return expressionValidator(userInput, rubric, onInputError, strings, locale);
2466
2472
  }
2467
2473
  static getUserInputFromProps(props) {
2468
2474
  return normalizeTex(props.value);
@@ -2670,10 +2676,10 @@ const ExpressionWithDependencies = /*#__PURE__*/React.forwardRef((props, ref) =>
2670
2676
 
2671
2677
  // HACK: Propogate "static" methods onto our wrapper component.
2672
2678
  // In the future we should adjust client apps to not depend on these static
2673
- // methods and instead adjust Peresus to provide these facilities through
2679
+ // methods and instead adjust Perseus to provide these facilities through
2674
2680
  // instance methods on our Renderers.
2675
2681
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
2676
- ExpressionWithDependencies.validate = Expression.validate;
2682
+ ExpressionWithDependencies.validate = expressionValidator;
2677
2683
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
2678
2684
  ExpressionWithDependencies.getUserInputFromProps = Expression.getUserInputFromProps;
2679
2685
  // @ts-expect-error - TS2339 - Property 'validate' does not exist on type
@@ -19620,9 +19626,7 @@ const isIdPathPrefix = function isIdPathPrefix(prefixArray, wholeArray) {
19620
19626
 
19621
19627
  // The return type for getUserInput. Widgets have full control of what is
19622
19628
  // returned so it's not easily typed (some widgets return a scalar (string),
19623
- // some return a custom-built object, some delegate to
19624
- // `WidgetJsonifyDeprecated` which returns an object containing widget props
19625
- // (filtered by deprecated keys)).
19629
+ // some return a custom-built object
19626
19630
  /**
19627
19631
  * The Widget type represents the common API that the Renderer uses to interact
19628
19632
  * with all widgets. All widgets must implement the methods in this API, unless
@@ -22024,6 +22028,131 @@ const answerFormButtons = [{
22024
22028
  value: "pi",
22025
22029
  content: "\u03C0"
22026
22030
  }];
22031
+
22032
+ // This function checks if the user inputted a percent value, parsing
22033
+ // it as a number (and maybe scaling) so that it can be graded.
22034
+ // NOTE(michaelpolyak): Unlike `KhanAnswerTypes.number.percent()` which
22035
+ // can accept several input forms with or without "%", the decision
22036
+ // to parse based on the presence of "%" in the input, is so that we
22037
+ // don't accidently scale the user typed value before grading, CP-930.
22038
+ function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
22039
+ // If the input value is not a string ending with "%", then there's
22040
+ // nothing more to do. The value will be graded as inputted by user.
22041
+ if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
22042
+ return inputValue;
22043
+ }
22044
+ const value = parseFloat(inputValue.slice(0, -1));
22045
+ // If the input value stripped of the "%" cannot be parsed as a
22046
+ // number (the slice is not really necessary for parseFloat to work
22047
+ // if the string starts with a number) then return the original
22048
+ // input for grading.
22049
+ if (isNaN(value)) {
22050
+ return inputValue;
22051
+ }
22052
+
22053
+ // Next, if all correct answers are in the range of |0,1| then we
22054
+ // scale the user typed value. We assume this is the correct thing
22055
+ // to do since the input value ends with "%".
22056
+ if (normalizedAnswerExpected) {
22057
+ return value / 100;
22058
+ }
22059
+
22060
+ // Otherwise, we return input valuåe (number) stripped of the "%".
22061
+ return value;
22062
+ }
22063
+ function numericInputValidator(userInput, rubric, strings) {
22064
+ const defaultAnswerForms = answerFormButtons.map(e => e["value"])
22065
+ // Don't default to validating the answer as a pi answer
22066
+ // if answerForm isn't set on the answer
22067
+ // https://khanacademy.atlassian.net/browse/LC-691
22068
+ .filter(e => e !== "pi");
22069
+ const createValidator = answer => {
22070
+ var _answer$answerForms;
22071
+ const stringAnswer = `${answer.value}`;
22072
+
22073
+ // Always validate against the provided answer forms (pi, decimal, etc.)
22074
+ const validatorForms = [...((_answer$answerForms = answer.answerForms) != null ? _answer$answerForms : [])];
22075
+
22076
+ // When an answer is set to strict, we validate using ONLY
22077
+ // the provided answerForms. If strict is false, or if there
22078
+ // were no provided answer forms, we will include all
22079
+ // of the default answer forms in our validator.
22080
+ if (!answer.strict || validatorForms.length === 0) {
22081
+ validatorForms.push(...defaultAnswerForms);
22082
+ }
22083
+ return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
22084
+ message: answer.message,
22085
+ simplify: answer.status === "correct" ? answer.simplify : "optional",
22086
+ inexact: true,
22087
+ // TODO(merlob) backfill / delete
22088
+ maxError: answer.maxError,
22089
+ forms: validatorForms
22090
+ }, strings);
22091
+ };
22092
+
22093
+ // We may have received TeX; try to parse it before grading.
22094
+ // If `currentValue` is not TeX, this should be a no-op.
22095
+ const currentValue = ParseTex(userInput.currentValue);
22096
+ const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
22097
+ const normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1);
22098
+
22099
+ // Look through all correct answers for one that matches either
22100
+ // precisely or approximately and return the appropriate message:
22101
+ // - if precise, return the message that the answer came with
22102
+ // - if it needs to be simplified, etc., show that message
22103
+ let result = correctAnswers.map(answer => {
22104
+ // The coefficient is an attribute of the widget
22105
+ let localValue = currentValue;
22106
+ if (rubric.coefficient) {
22107
+ if (!localValue) {
22108
+ localValue = 1;
22109
+ } else if (localValue === "-") {
22110
+ localValue = -1;
22111
+ }
22112
+ }
22113
+ const validate = createValidator(answer);
22114
+ return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
22115
+ }).find(match => match.correct || match.empty);
22116
+ if (!result) {
22117
+ // Otherwise, if the guess is not correct
22118
+ const otherAnswers = [].concat(
22119
+ // @ts-expect-error - TS2769 - No overload matches this call.
22120
+ rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong"));
22121
+
22122
+ // Look through all other answers and if one matches either
22123
+ // precisely or approximately return the answer's message
22124
+ const match = otherAnswers.find(answer => {
22125
+ const validate = createValidator(answer);
22126
+ return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
22127
+ });
22128
+ result = {
22129
+ // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22130
+ empty: match ? match.status === "ungraded" : false,
22131
+ // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22132
+ correct: match ? match.status === "correct" : false,
22133
+ // @ts-expect-error - TS2339 - Property 'message' does not exist on type 'never'.
22134
+ message: match ? match.message : null,
22135
+ guess: currentValue
22136
+ };
22137
+ }
22138
+
22139
+ // TODO(eater): Seems silly to translate result to this
22140
+ // invalid/points thing and immediately translate it
22141
+ // back in ItemRenderer.scoreInput()
22142
+ if (result.empty) {
22143
+ return {
22144
+ type: "invalid",
22145
+ message: result.message
22146
+ };
22147
+ }
22148
+ return {
22149
+ type: "points",
22150
+ earned: result.correct ? 1 : 0,
22151
+ total: 1,
22152
+ message: result.message
22153
+ };
22154
+ }
22155
+
22027
22156
  const formExamples = {
22028
22157
  integer: (form, strings) => strings.integerExample,
22029
22158
  proper: (form, strings) => form.simplify === "optional" ? strings.properExample : strings.simplifiedProperExample,
@@ -22070,7 +22199,7 @@ class NumericInput extends React.Component {
22070
22199
  return !noFormsAccepted && !allFormsAccepted;
22071
22200
  };
22072
22201
  this.simpleValidate = rubric => {
22073
- return NumericInput.validate(this.getUserInput(), rubric, this.context.strings);
22202
+ return numericInputValidator(this.getUserInput(), rubric, this.context.strings);
22074
22203
  };
22075
22204
  this.focus = () => {
22076
22205
  var _this$inputRef;
@@ -22151,98 +22280,7 @@ class NumericInput extends React.Component {
22151
22280
  return answerStrings[0];
22152
22281
  }
22153
22282
  static validate(userInput, rubric, strings) {
22154
- const defaultAnswerForms = answerFormButtons.map(e => e["value"])
22155
- // Don't default to validating the answer as a pi answer
22156
- // if answerForm isn't set on the answer
22157
- // https://khanacademy.atlassian.net/browse/LC-691
22158
- .filter(e => e !== "pi");
22159
- const createValidator = answer => {
22160
- var _answer$answerForms;
22161
- const stringAnswer = `${answer.value}`;
22162
-
22163
- // Always validate against the provided answer forms (pi, decimal, etc.)
22164
- const validatorForms = [...((_answer$answerForms = answer.answerForms) != null ? _answer$answerForms : [])];
22165
-
22166
- // When an answer is set to strict, we validate using ONLY
22167
- // the provided answerForms. If strict is false, or if there
22168
- // were no provided answer forms, we will include all
22169
- // of the default answer forms in our validator.
22170
- if (!answer.strict || validatorForms.length === 0) {
22171
- validatorForms.push(...defaultAnswerForms);
22172
- }
22173
- return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
22174
- message: answer.message,
22175
- simplify: answer.status === "correct" ? answer.simplify : "optional",
22176
- inexact: true,
22177
- // TODO(merlob) backfill / delete
22178
- maxError: answer.maxError,
22179
- forms: validatorForms
22180
- }, strings);
22181
- };
22182
-
22183
- // We may have received TeX; try to parse it before grading.
22184
- // If `currentValue` is not TeX, this should be a no-op.
22185
- const currentValue = ParseTex(userInput.currentValue);
22186
- const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
22187
- const normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1);
22188
-
22189
- // Look through all correct answers for one that matches either
22190
- // precisely or approximately and return the appropriate message:
22191
- // - if precise, return the message that the answer came with
22192
- // - if it needs to be simplified, etc., show that message
22193
- let result = correctAnswers.map(answer => {
22194
- // The coefficient is an attribute of the widget
22195
- let localValue = currentValue;
22196
- if (rubric.coefficient) {
22197
- if (!localValue) {
22198
- // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'string'.
22199
- localValue = 1;
22200
- } else if (localValue === "-") {
22201
- // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'string'.
22202
- localValue = -1;
22203
- }
22204
- }
22205
- const validate = createValidator(answer);
22206
- return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
22207
- }).find(match => match.correct || match.empty);
22208
- if (!result) {
22209
- // Otherwise, if the guess is not correct
22210
- const otherAnswers = [].concat(
22211
- // @ts-expect-error - TS2769 - No overload matches this call.
22212
- rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong"));
22213
-
22214
- // Look through all other answers and if one matches either
22215
- // precisely or approximately return the answer's message
22216
- const match = otherAnswers.find(answer => {
22217
- const validate = createValidator(answer);
22218
- return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
22219
- });
22220
- result = {
22221
- // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22222
- empty: match ? match.status === "ungraded" : false,
22223
- // @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
22224
- correct: match ? match.status === "correct" : false,
22225
- // @ts-expect-error - TS2339 - Property 'message' does not exist on type 'never'.
22226
- message: match ? match.message : null,
22227
- guess: currentValue
22228
- };
22229
- }
22230
-
22231
- // TODO(eater): Seems silly to translate result to this
22232
- // invalid/points thing and immediately translate it
22233
- // back in ItemRenderer.scoreInput()
22234
- if (result.empty) {
22235
- return {
22236
- type: "invalid",
22237
- message: result.message
22238
- };
22239
- }
22240
- return {
22241
- type: "points",
22242
- earned: result.correct ? 1 : 0,
22243
- total: 1,
22244
- message: result.message
22245
- };
22283
+ return numericInputValidator(userInput, rubric, strings);
22246
22284
  }
22247
22285
  render() {
22248
22286
  let labelText = this.props.labelText;
@@ -22343,37 +22381,6 @@ const unionAnswerForms = function unionAnswerForms(answerFormsList) {
22343
22381
  return formExampleKeys.indexOf(form.name);
22344
22382
  });
22345
22383
  };
22346
- // This function checks if the user inputted a percent value, parsing
22347
- // it as a number (and maybe scaling) so that it can be graded.
22348
- // NOTE(michaelpolyak): Unlike `KhanAnswerTypes.number.percent()` which
22349
- // can accept several input forms with or without "%", the decision
22350
- // to parse based on the presence of "%" in the input, is so that we
22351
- // don't accidently scale the user typed value before grading, CP-930.
22352
- function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
22353
- // If the input value is not a string ending with "%", then there's
22354
- // nothing more to do. The value will be graded as inputted by user.
22355
- if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
22356
- return inputValue;
22357
- }
22358
- const value = parseFloat(inputValue.slice(0, -1));
22359
- // If the input value stripped of the "%" cannot be parsed as a
22360
- // number (the slice is not really necessary for parseFloat to work
22361
- // if the string starts with a number) then return the original
22362
- // input for grading.
22363
- if (isNaN(value)) {
22364
- return inputValue;
22365
- }
22366
-
22367
- // Next, if all correct answers are in the range of |0,1| then we
22368
- // scale the user typed value. We assume this is the correct thing
22369
- // to do since the input value ends with "%".
22370
- if (normalizedAnswerExpected) {
22371
- return value / 100;
22372
- }
22373
-
22374
- // Otherwise, we return input valuåe (number) stripped of the "%".
22375
- return value;
22376
- }
22377
22384
  const propsTransform = function propsTransform(widgetOptions) {
22378
22385
  const rendererProps = _.extend(_.omit(widgetOptions, "answers"), {
22379
22386
  answerForms: unionAnswerForms(
@@ -22401,6 +22408,23 @@ var NumericInput$1 = {
22401
22408
  isLintable: true
22402
22409
  };
22403
22410
 
22411
+ /**
22412
+ * Several widgets don't have "right"/"wrong" validation logic,
22413
+ * so this just says to move on past those widgets
22414
+ *
22415
+ * TODO: widgets that use this probably shouldn't have any
22416
+ * validation logic and the thing validating an exercise
22417
+ * should just know to skip these
22418
+ */
22419
+ function noopValidator() {
22420
+ return {
22421
+ type: "points",
22422
+ earned: 0,
22423
+ total: 0,
22424
+ message: null
22425
+ };
22426
+ }
22427
+
22404
22428
  const EN_DASH$1 = "\u2013";
22405
22429
  class PassageRef extends React.Component {
22406
22430
  constructor(..._args) {
@@ -22453,12 +22477,7 @@ class PassageRef extends React.Component {
22453
22477
  };
22454
22478
  }
22455
22479
  static validate(userInput, rubric) {
22456
- return {
22457
- type: "points",
22458
- earned: 0,
22459
- total: 0,
22460
- message: null
22461
- };
22480
+ return noopValidator();
22462
22481
  }
22463
22482
  componentDidMount() {
22464
22483
  // TODO(scottgrant): This is a hack to remove the deprecated call to
@@ -22521,6 +22540,7 @@ PassageRef.defaultProps = {
22521
22540
  var PassageRef$1 = {
22522
22541
  name: "passage-ref",
22523
22542
  displayName: "PassageRef (SAT only)",
22543
+ hidden: true,
22524
22544
  defaultAlignment: "inline",
22525
22545
  widget: PassageRef,
22526
22546
  transform: widgetOptions => ({
@@ -23743,9 +23763,8 @@ const BaseRadio = function BaseRadio(props) {
23743
23763
  // element) We have to add some extra behavior to make
23744
23764
  // sure that we can still check the choice.
23745
23765
  let listElem = null;
23746
- let clickHandler = null;
23766
+ let clickHandler;
23747
23767
  if (editMode) {
23748
- // @ts-expect-error - TS2322 - Type '(e: any) => void' is not assignable to type 'null'.
23749
23768
  clickHandler = e => {
23750
23769
  // Traverse the parent nodes of the clicked
23751
23770
  // element.
@@ -23772,19 +23791,11 @@ const BaseRadio = function BaseRadio(props) {
23772
23791
  // somehow break something happening inside a choice's
23773
23792
  // child Renderers, by changing when we mount/unmount?
23774
23793
  return /*#__PURE__*/React.createElement("li", {
23775
- key: i
23776
- // @ts-expect-error - TS2322 - Type 'HTMLLIElement | null' is not assignable to type 'null'.
23777
- ,
23778
- ref: e => listElem = e
23779
- // @ts-expect-error - TS2322 - Type 'readonly string[]' is not assignable to type 'string'.
23780
- ,
23781
- className: className
23782
- // @ts-expect-error - TS2322 - Type 'null' is not assignable to type 'MouseEventHandler<HTMLLIElement> | undefined'.
23783
- ,
23784
- onClick: clickHandler
23785
- // @ts-expect-error - TS2322 - Type '((e: TouchEvent) => void) | null' is not assignable to type 'TouchEventHandler<HTMLLIElement> | undefined'.
23786
- ,
23787
- onTouchStart: !labelWrap ? null : captureScratchpadTouchStart$1
23794
+ key: i,
23795
+ ref: e => listElem = e,
23796
+ className: className,
23797
+ onClick: clickHandler,
23798
+ onTouchStart: labelWrap ? undefined : captureScratchpadTouchStart$1
23788
23799
  }, /*#__PURE__*/React.createElement(Element, _extends({}, elementProps, {
23789
23800
  ref: ref
23790
23801
  })));
@@ -24415,23 +24426,6 @@ var Radio = {
24415
24426
  /* eslint-disable import/no-named-as-default */
24416
24427
  var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
24417
24428
 
24418
- /* Free implementation of getUserInput. This should be used sparingly, since it
24419
- * just returns all the widget's props rather than picking out those which were
24420
- * input by the user.
24421
- */
24422
- const WidgetJsonifyDeprecated = {
24423
- getUserInput: function () {
24424
- // Omit props that get passed to all widgets
24425
- // @ts-expect-error - TS2339 - Property 'props' does not exist on type '{ readonly getUserInput: () => any; readonly getUserInputFromProps: (props: any) => any; }'.
24426
- return _.omit(this.props, denylist);
24427
- },
24428
- // Static version of `WidgetJsonifyDeprecated.getUserInput`
24429
- getUserInputFromProps: function (props) {
24430
- // Omit props that get passed to all widgets
24431
- return _.omit(props, denylist);
24432
- }
24433
- };
24434
-
24435
24429
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
24436
24430
  class Categorizer extends React.Component {
24437
24431
  constructor(..._args) {
@@ -24475,7 +24469,9 @@ class Categorizer extends React.Component {
24475
24469
  };
24476
24470
  }
24477
24471
  static getUserInputFromProps(props) {
24478
- return WidgetJsonifyDeprecated.getUserInputFromProps(props);
24472
+ return {
24473
+ values: props.values
24474
+ };
24479
24475
  }
24480
24476
  onChange(itemNum, catNum) {
24481
24477
  const values = [...this.props.values];
@@ -24621,6 +24617,7 @@ const styles$s = StyleSheet.create({
24621
24617
  var Categorizer$1 = {
24622
24618
  name: "categorizer",
24623
24619
  displayName: "Categorizer",
24620
+ hidden: true,
24624
24621
  widget: Categorizer,
24625
24622
  transform: widgetOptions => {
24626
24623
  return _.pick(widgetOptions, "items", "categories", "randomizeItems");
@@ -24678,6 +24675,9 @@ const parseUrl = url => new URL(url, location().origin);
24678
24675
  */
24679
24676
  const toAbsoluteUrl = url => parseUrl(url).href;
24680
24677
 
24678
+ /**
24679
+ * This widget is for embedding Khan Academy CS programs.
24680
+ */
24681
24681
  const {
24682
24682
  updateQueryString: updateQueryString$1
24683
24683
  } = Util;
@@ -24723,12 +24723,6 @@ class CSProgram extends React.Component {
24723
24723
  // @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
24724
24724
  return change.apply(this, args);
24725
24725
  };
24726
- this.simpleValidate = rubric => {
24727
- return CSProgram.validate({
24728
- status: this.props.status,
24729
- message: this.props.message
24730
- }, rubric);
24731
- };
24732
24726
  }
24733
24727
  // The widget's grading function
24734
24728
  static validate(state, rubric) {
@@ -24761,6 +24755,12 @@ class CSProgram extends React.Component {
24761
24755
  componentWillUnmount() {
24762
24756
  $(window).off("message", this.handleMessageEvent);
24763
24757
  }
24758
+ simpleValidate(rubric) {
24759
+ return CSProgram.validate({
24760
+ status: this.props.status,
24761
+ message: this.props.message
24762
+ }, rubric);
24763
+ }
24764
24764
  render() {
24765
24765
  if (!this.props.programID) {
24766
24766
  return /*#__PURE__*/React.createElement("div", null);
@@ -24820,18 +24820,6 @@ class CSProgram extends React.Component {
24820
24820
  }));
24821
24821
  }
24822
24822
  }
24823
- CSProgram.propTypes = _extends({}, propTypes, {
24824
- programID: PropTypes.string,
24825
- programType: PropTypes.oneOf(["pjs", "sql", "webpage"]),
24826
- width: PropTypes.number,
24827
- height: PropTypes.number,
24828
- // eslint-disable-next-line react/forbid-prop-types
24829
- settings: PropTypes.array,
24830
- showEditor: PropTypes.bool,
24831
- showButtons: PropTypes.bool,
24832
- status: PropTypes.oneOf(["incomplete", "incorrect", "correct"]),
24833
- message: PropTypes.string
24834
- });
24835
24823
  CSProgram.defaultProps = {
24836
24824
  showEditor: false,
24837
24825
  showButtons: false,
@@ -24868,12 +24856,7 @@ class Definition extends React.Component {
24868
24856
  };
24869
24857
  }
24870
24858
  static validate(userInput, rubric) {
24871
- return {
24872
- type: "points",
24873
- earned: 0,
24874
- total: 0,
24875
- message: null
24876
- };
24859
+ return noopValidator();
24877
24860
  }
24878
24861
  render() {
24879
24862
  return /*#__PURE__*/React.createElement(DefinitionConsumer, null, ({
@@ -24975,6 +24958,23 @@ var DeprecatedStandin$1 = {
24975
24958
  hidden: true
24976
24959
  };
24977
24960
 
24961
+ function dropdownValidator(userInput, rubric) {
24962
+ const selected = userInput.value;
24963
+ if (selected === 0) {
24964
+ return {
24965
+ type: "invalid",
24966
+ message: null
24967
+ };
24968
+ }
24969
+ const correct = rubric.choices[selected - 1].correct;
24970
+ return {
24971
+ type: "points",
24972
+ earned: correct ? 1 : 0,
24973
+ total: 1,
24974
+ message: null
24975
+ };
24976
+ }
24977
+
24978
24978
  class Dropdown extends React.Component {
24979
24979
  constructor(...args) {
24980
24980
  super(...args);
@@ -25001,24 +25001,11 @@ class Dropdown extends React.Component {
25001
25001
  };
25002
25002
  };
25003
25003
  this.simpleValidate = rubric => {
25004
- return Dropdown.validate(this.getUserInput(), rubric);
25004
+ return dropdownValidator(this.getUserInput(), rubric);
25005
25005
  };
25006
25006
  }
25007
25007
  static validate(userInput, rubric) {
25008
- const selected = userInput.value;
25009
- if (selected === 0) {
25010
- return {
25011
- type: "invalid",
25012
- message: null
25013
- };
25014
- }
25015
- const correct = rubric.choices[selected - 1].correct;
25016
- return {
25017
- type: "points",
25018
- earned: correct ? 1 : 0,
25019
- total: 1,
25020
- message: null
25021
- };
25008
+ return dropdownValidator(userInput, rubric);
25022
25009
  }
25023
25010
  render() {
25024
25011
  const children = [/*#__PURE__*/React.createElement(OptionItem, {
@@ -25101,12 +25088,7 @@ class Explanation extends React.Component {
25101
25088
  };
25102
25089
  }
25103
25090
  static validate(userInput, rubric) {
25104
- return {
25105
- type: "points",
25106
- earned: 0,
25107
- total: 0,
25108
- message: null
25109
- };
25091
+ return noopValidator();
25110
25092
  }
25111
25093
  componentDidMount() {
25112
25094
  this._mounted = true;
@@ -26934,6 +26916,7 @@ const staticTransform$4 = editorProps => {
26934
26916
  var Grapher$1 = {
26935
26917
  name: "grapher",
26936
26918
  displayName: "Grapher",
26919
+ hidden: true,
26937
26920
  widget: Grapher,
26938
26921
  transform: propTransform$2,
26939
26922
  staticTransform: staticTransform$4
@@ -27083,21 +27066,25 @@ var Group$1 = {
27083
27066
  displayName: "Group (SAT only)",
27084
27067
  widget: Group,
27085
27068
  traverseChildWidgets: traverseChildWidgets,
27086
- hidden: false,
27069
+ hidden: true,
27087
27070
  isLintable: true
27088
27071
  };
27089
27072
 
27073
+ /**
27074
+ * This is an iframe widget. It is used for rendering an iframe that
27075
+ * then communicates its state via window.postMessage
27076
+ * This is useful for embedding arbitrary visualizations/simulations with
27077
+ * completed conditions, such as the mazes and games in Algorithms.
27078
+ * It's particularly well suited for embedding our ProcessingJS programs,
27079
+ * but could also be used for embedding viz's hosted elsewhere.
27080
+ */
27090
27081
  const {
27091
27082
  updateQueryString
27092
27083
  } = Util;
27093
-
27094
27084
  /* This renders the iframe and handles validation via window.postMessage */
27095
27085
  class Iframe extends React.Component {
27096
27086
  constructor(..._args) {
27097
27087
  super(..._args);
27098
- this.getUserInput = () => {
27099
- return WidgetJsonifyDeprecated.getUserInput.call(this);
27100
- };
27101
27088
  this.handleMessageEvent = e => {
27102
27089
  // We receive data from the iframe that contains {passed: true/false}
27103
27090
  // and use that to set the status
@@ -27132,6 +27119,12 @@ class Iframe extends React.Component {
27132
27119
  componentWillUnmount() {
27133
27120
  $(window).off("message", this.handleMessageEvent);
27134
27121
  }
27122
+ getUserInput() {
27123
+ return {
27124
+ status: this.props.status,
27125
+ message: this.props.message
27126
+ };
27127
+ }
27135
27128
  render() {
27136
27129
  const style = {
27137
27130
  width: String(this.props.width),
@@ -27197,16 +27190,6 @@ class Iframe extends React.Component {
27197
27190
  /**
27198
27191
  * This is the widget's grading function
27199
27192
  */
27200
- Iframe.propTypes = _extends({}, propTypes, {
27201
- width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
27202
- height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
27203
- url: PropTypes.string,
27204
- settings: PropTypes.array,
27205
- status: PropTypes.oneOf(["incomplete", "incorrect", "correct"]),
27206
- message: PropTypes.string,
27207
- allowFullScreen: PropTypes.bool,
27208
- allowTopNavigation: PropTypes.bool
27209
- });
27210
27193
  Iframe.defaultProps = {
27211
27194
  status: "incomplete",
27212
27195
  // optional message
@@ -27275,12 +27258,7 @@ class ImageWidget extends React.Component {
27275
27258
  this.focus = () => {};
27276
27259
  }
27277
27260
  static validate(userInput, rubric) {
27278
- return {
27279
- type: "points",
27280
- earned: 0,
27281
- total: 0,
27282
- message: null
27283
- };
27261
+ return noopValidator();
27284
27262
  }
27285
27263
  // no-op
27286
27264
 
@@ -27601,12 +27579,7 @@ class Interaction extends React.Component {
27601
27579
  };
27602
27580
  }
27603
27581
  static validate(state, rubric) {
27604
- return {
27605
- type: "points",
27606
- earned: 0,
27607
- total: 0,
27608
- message: null
27609
- };
27582
+ return noopValidator();
27610
27583
  }
27611
27584
  UNSAFE_componentWillReceiveProps(nextProps) {
27612
27585
  // HACK(michaelpolyak): This widget is not wired to use the Perseus
@@ -29386,9 +29359,7 @@ class NumberInput extends React.Component {
29386
29359
  onFocus: this._handleFocus,
29387
29360
  onBlur: this._handleBlur,
29388
29361
  onKeyPress: this._handleBlur,
29389
- onKeyDown: this._onKeyDown
29390
- // @ts-expect-error - TS2322 - Type '(e: TouchEvent) => void' is not assignable to type 'TouchEventHandler<HTMLInputElement>'.
29391
- ,
29362
+ onKeyDown: this._onKeyDown,
29392
29363
  onTouchStart: captureScratchpadTouchStart,
29393
29364
  defaultValue: toNumericString(this.props.value, this.state.format),
29394
29365
  value: undefined
@@ -29674,17 +29645,23 @@ function LockedLabel(props) {
29674
29645
 
29675
29646
  function GraphLockedLabelsLayer(props) {
29676
29647
  const {
29648
+ flags,
29677
29649
  lockedFigures
29678
29650
  } = props;
29679
29651
  return lockedFigures.map((figure, i) => {
29652
+ var _flags$mafs;
29680
29653
  if (figure.type === "label") {
29681
29654
  return /*#__PURE__*/React.createElement(LockedLabel, _extends({
29682
29655
  key: `label-${i}`
29683
29656
  }, figure));
29684
29657
  }
29685
-
29686
- // TODO(LEMS-2271): Add support for labels within
29687
- // other locked figure types
29658
+ if (flags != null && (_flags$mafs = flags["mafs"]) != null && _flags$mafs["locked-point-labels"] && figure.type === "point") {
29659
+ return /*#__PURE__*/React.createElement(React.Fragment, {
29660
+ key: `point-${i}`
29661
+ }, figure.labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
29662
+ key: `locked-figure-${i}-label-${j}`
29663
+ }, label))));
29664
+ }
29688
29665
  return null;
29689
29666
  });
29690
29667
  }
@@ -30045,7 +30022,8 @@ const actions = {
30045
30022
  addPoint,
30046
30023
  removePoint,
30047
30024
  focusPoint,
30048
- blurPoint
30025
+ blurPoint,
30026
+ clickPoint
30049
30027
  },
30050
30028
  polygon: {
30051
30029
  movePoint,
@@ -30107,6 +30085,13 @@ function blurPoint() {
30107
30085
  type: BLUR_POINT
30108
30086
  };
30109
30087
  }
30088
+ const CLICK_POINT = "click-point";
30089
+ function clickPoint(index) {
30090
+ return {
30091
+ type: CLICK_POINT,
30092
+ index
30093
+ };
30094
+ }
30110
30095
  const MOVE_ALL = "move-all";
30111
30096
  function moveAll(delta) {
30112
30097
  return {
@@ -30431,10 +30416,14 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
30431
30416
  },
30432
30417
  "data-testid": "movable-point",
30433
30418
  tabIndex: disableKeyboardInteraction ? -1 : tabIndex(focusBehavior),
30434
- onFocus: () => {
30435
- return getOnFocusChangeCallback(focusBehavior)(true);
30419
+ onFocus: event => {
30420
+ const callback = getOnFocusChangeCallback(focusBehavior);
30421
+ callback && callback(event, true);
30422
+ },
30423
+ onBlur: event => {
30424
+ const callback = getOnFocusChangeCallback(focusBehavior);
30425
+ callback && callback(event, false);
30436
30426
  },
30437
- onBlur: () => getOnFocusChangeCallback(focusBehavior)(false),
30438
30427
  onClick: onClick
30439
30428
  }, /*#__PURE__*/React.createElement("circle", {
30440
30429
  className: "movable-point-hitbox",
@@ -31080,6 +31069,7 @@ const MovablePoint$1 = props => {
31080
31069
  point,
31081
31070
  onMove = () => {},
31082
31071
  onFocusChange = () => {},
31072
+ onClick = () => {},
31083
31073
  cursor,
31084
31074
  color: color$1 = color.blue,
31085
31075
  constrain = p => snap(snapStep, p)
@@ -31104,7 +31094,8 @@ const MovablePoint$1 = props => {
31104
31094
  },
31105
31095
  onClick: () => {
31106
31096
  var _elementRef$current;
31107
- return (_elementRef$current = elementRef.current) == null ? void 0 : _elementRef$current.focus();
31097
+ onClick && onClick();
31098
+ (_elementRef$current = elementRef.current) == null || _elementRef$current.focus();
31108
31099
  },
31109
31100
  cursor: cursor
31110
31101
  });
@@ -31306,6 +31297,11 @@ function getGradableGraph(state, initialGraph) {
31306
31297
  allowReflexAngles: state.allowReflexAngles
31307
31298
  });
31308
31299
  }
31300
+ if (state.type === "none" && initialGraph.type === "none") {
31301
+ return {
31302
+ type: "none"
31303
+ };
31304
+ }
31309
31305
  throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
31310
31306
  }
31311
31307
 
@@ -31703,12 +31699,19 @@ function UnlimitedPointGraph(props) {
31703
31699
  key: i,
31704
31700
  point: point,
31705
31701
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
31706
- onFocusChange: isFocused => {
31702
+ onFocusChange: (event, isFocused) => {
31707
31703
  if (isFocused) {
31708
31704
  dispatch(actions.pointGraph.focusPoint(i));
31709
31705
  } else {
31706
+ var _event$relatedTarget;
31707
+ if (((_event$relatedTarget = event.relatedTarget) == null ? void 0 : _event$relatedTarget.id) === REMOVE_BUTTON_ID) {
31708
+ return;
31709
+ }
31710
31710
  dispatch(actions.pointGraph.blurPoint());
31711
31711
  }
31712
+ },
31713
+ onClick: () => {
31714
+ dispatch(actions.pointGraph.clickPoint(i));
31712
31715
  }
31713
31716
  })));
31714
31717
  }
@@ -31867,7 +31870,9 @@ function useDraggablePx(args) {
31867
31870
  * - Protractor
31868
31871
  * - Interactive Graph Elements
31869
31872
  */
31873
+ const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
31870
31874
  const MafsGraph = props => {
31875
+ var _props$flags;
31871
31876
  const {
31872
31877
  state,
31873
31878
  dispatch,
@@ -31975,7 +31980,8 @@ const MafsGraph = props => {
31975
31980
  props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
31976
31981
  lockedFigures: props.lockedFigures,
31977
31982
  range: state.range
31978
- }))), props.showLabelsFlag && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
31983
+ }))), ((_props$flags = props.flags) == null || (_props$flags = _props$flags["mafs"]) == null ? void 0 : _props$flags["interactive-graph-locked-features-labels"]) && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
31984
+ flags: props.flags,
31979
31985
  lockedFigures: props.lockedFigures
31980
31986
  }), /*#__PURE__*/React.createElement(View, {
31981
31987
  style: {
@@ -31992,10 +31998,10 @@ const MafsGraph = props => {
31992
31998
  zoom: false,
31993
31999
  width: width,
31994
32000
  height: height
31995
- }, props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
32001
+ }, /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
31996
32002
  state,
31997
32003
  dispatch
31998
- }))))), renderGraphControls({
32004
+ })))))), renderGraphControls({
31999
32005
  state,
32000
32006
  dispatch,
32001
32007
  width
@@ -32006,26 +32012,17 @@ const renderPointGraphControls = props => /*#__PURE__*/React.createElement(View,
32006
32012
  flexDirection: "row",
32007
32013
  width: props.width
32008
32014
  }
32009
- }, /*#__PURE__*/React.createElement(Button, {
32010
- kind: "secondary",
32011
- style: {
32012
- width: "100%",
32013
- marginLeft: "20px"
32014
- },
32015
- tabIndex: 0,
32016
- onClick: () => {
32017
- props.dispatch(actions.pointGraph.addPoint([0, 0]));
32018
- }
32019
- }, "Add Point"), (props.state.focusedPointIndex !== null || props.state.previouslyFocusedPointIndex !== null) && /*#__PURE__*/React.createElement(Button, {
32015
+ }, props.state.showRemovePointButton && props.state.focusedPointIndex !== null && /*#__PURE__*/React.createElement(Button, {
32016
+ id: REMOVE_BUTTON_ID,
32020
32017
  kind: "secondary",
32021
32018
  color: "destructive",
32022
- tabIndex: 0,
32019
+ tabIndex: -1,
32023
32020
  style: {
32024
32021
  width: "100%",
32025
32022
  marginLeft: "20px"
32026
32023
  },
32027
32024
  onClick: event => {
32028
- props.dispatch(actions.pointGraph.removePoint(props.state.previouslyFocusedPointIndex));
32025
+ props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));
32029
32026
  }
32030
32027
  }, "Remove Point"));
32031
32028
  const renderGraphControls = props => {
@@ -32160,6 +32157,8 @@ const renderGraph = props => {
32160
32157
  graphState: state,
32161
32158
  dispatch: dispatch
32162
32159
  });
32160
+ case "none":
32161
+ return null;
32163
32162
  default:
32164
32163
  throw new UnreachableCaseError(type);
32165
32164
  }
@@ -32213,7 +32212,7 @@ function initializeGraphState(params) {
32213
32212
  coords: getPointCoords(graph, range, step),
32214
32213
  numPoints: graph.numPoints || 0,
32215
32214
  focusedPointIndex: null,
32216
- previouslyFocusedPointIndex: null
32215
+ showRemovePointButton: false
32217
32216
  });
32218
32217
  case "circle":
32219
32218
  return _extends({}, shared, {
@@ -32242,6 +32241,10 @@ function initializeGraphState(params) {
32242
32241
  allowReflexAngles: Boolean(graph.allowReflexAngles),
32243
32242
  snapDegrees: Number(graph.snapDegrees)
32244
32243
  });
32244
+ case "none":
32245
+ return _extends({}, shared, {
32246
+ type: "none"
32247
+ });
32245
32248
  default:
32246
32249
  throw new UnreachableCaseError(graph);
32247
32250
  }
@@ -32477,6 +32480,8 @@ function interactiveGraphReducer(state, action) {
32477
32480
  return doBlurPoint(state);
32478
32481
  case DELETE_INTENT:
32479
32482
  return doDeleteIntent(state);
32483
+ case CLICK_POINT:
32484
+ return doClickPoint(state, action);
32480
32485
  default:
32481
32486
  throw new UnreachableCaseError(action);
32482
32487
  }
@@ -32506,13 +32511,25 @@ function doBlurPoint(state, action) {
32506
32511
  switch (state.type) {
32507
32512
  case "point":
32508
32513
  return _extends({}, state, {
32509
- previouslyFocusedPointIndex: state.focusedPointIndex,
32510
- focusedPointIndex: null
32514
+ focusedPointIndex: null,
32515
+ showRemovePointButton: false
32511
32516
  });
32512
32517
  default:
32513
32518
  return state;
32514
32519
  }
32515
32520
  }
32521
+ function doClickPoint(state, action) {
32522
+ if (state.type !== "point") {
32523
+ return state;
32524
+ }
32525
+ if (state.numPoints === "unlimited") {
32526
+ return _extends({}, state, {
32527
+ focusedPointIndex: action.index,
32528
+ showRemovePointButton: true
32529
+ });
32530
+ }
32531
+ return state;
32532
+ }
32516
32533
  function doMovePointInFigure(state, action) {
32517
32534
  switch (state.type) {
32518
32535
  case "segment":
@@ -32551,6 +32568,8 @@ function doMovePointInFigure(state, action) {
32551
32568
  }
32552
32569
  case "angle":
32553
32570
  case "circle":
32571
+ throw new Error("FIXME implement circle reducer");
32572
+ case "none":
32554
32573
  case "point":
32555
32574
  case "polygon":
32556
32575
  case "quadratic":
@@ -32852,7 +32871,8 @@ function doAddPoint(state, action) {
32852
32871
  // If there's no point in spot where we want the new point to go we add it there
32853
32872
  return _extends({}, state, {
32854
32873
  hasBeenInteractedWith: true,
32855
- coords: [...state.coords, snappedPoint]
32874
+ coords: [...state.coords, snappedPoint],
32875
+ showRemovePointButton: false
32856
32876
  });
32857
32877
  }
32858
32878
  function doRemovePoint(state, action) {
@@ -32862,7 +32882,7 @@ function doRemovePoint(state, action) {
32862
32882
  return _extends({}, state, {
32863
32883
  coords: state.coords.filter((_, i) => i !== action.index),
32864
32884
  focusedPointIndex: null,
32865
- previouslyFocusedPointIndex: null
32885
+ showRemovePointButton: false
32866
32886
  });
32867
32887
  }
32868
32888
  const getDeltaVertex = (maxMoves, minMoves, delta) => {
@@ -33246,6 +33266,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
33246
33266
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
33247
33267
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
33248
33268
  const showSides = graph.type === "polygon" ? graph.showSides : null;
33269
+ const startCoords = "startCoords" in graph ? graph.startCoords : null;
33249
33270
  const originalPropsRef = useRef(props);
33250
33271
  const latestPropsRef = useLatestRef(props);
33251
33272
  useEffect(() => {
@@ -33256,7 +33277,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
33256
33277
  if (latestPropsRef.current !== originalPropsRef.current) {
33257
33278
  dispatch(reinitialize(latestPropsRef.current));
33258
33279
  }
33259
- }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, graph.startCoords]);
33280
+ }, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, startCoords]);
33260
33281
 
33261
33282
  // If the graph is static, it always displays the correct answer. This is
33262
33283
  // standard behavior for Perseus widgets (e.g. compare the Radio widget).
@@ -33533,6 +33554,8 @@ class LegacyInteractiveGraph extends React.Component {
33533
33554
  _.invoke(this.points, "remove");
33534
33555
  (_this$line = this.line) == null || _this$line.remove();
33535
33556
  };
33557
+ this.addNoneControls = () => {};
33558
+ this.removeNoneControls = () => {};
33536
33559
  this.addLinearControls = () => {
33537
33560
  this.addLine("line");
33538
33561
  };
@@ -34567,7 +34590,7 @@ class InteractiveGraph extends React.Component {
34567
34590
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
34568
34591
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
34569
34592
  return /*#__PURE__*/React.createElement(StatefulMafsGraph, _extends({}, this.props, {
34570
- showLabelsFlag: (_this$props$apiOption2 = this.props.apiOptions) == null || (_this$props$apiOption2 = _this$props$apiOption2.flags) == null || (_this$props$apiOption2 = _this$props$apiOption2["mafs"]) == null ? void 0 : _this$props$apiOption2["interactive-graph-locked-features-labels"],
34593
+ flags: (_this$props$apiOption2 = this.props.apiOptions) == null ? void 0 : _this$props$apiOption2.flags,
34571
34594
  ref: this.mafsRef,
34572
34595
  gridStep: gridStep,
34573
34596
  snapStep: snapStep,
@@ -34800,6 +34823,9 @@ class InteractiveGraph extends React.Component {
34800
34823
  });
34801
34824
  });
34802
34825
  }
34826
+ static getNoneEquationString() {
34827
+ return "";
34828
+ }
34803
34829
  static getLinearEquationString(props) {
34804
34830
  const coords = InteractiveGraph.getLineCoords(props.graph, props);
34805
34831
  if (eq(coords[0][0], coords[1][0])) {
@@ -34913,6 +34939,16 @@ class InteractiveGraph extends React.Component {
34913
34939
  return angle.toFixed(0) + "\u00B0 angle" + " at (" + coords[1].join(", ") + ")";
34914
34940
  }
34915
34941
  static validate(userInput, rubric, component) {
34942
+ // None-type graphs are not graded
34943
+ if (userInput.type === "none" && rubric.correct.type === "none") {
34944
+ return {
34945
+ type: "points",
34946
+ earned: 0,
34947
+ total: 0,
34948
+ message: null
34949
+ };
34950
+ }
34951
+
34916
34952
  // When nothing has moved, there will neither be coords nor the
34917
34953
  // circle's center/radius fields. When those fields are absent, skip
34918
34954
  // all these checks; just go mark the answer as empty.
@@ -35142,6 +35178,8 @@ function shouldUseMafs(mafsFlags, graph) {
35142
35178
  return false;
35143
35179
  }
35144
35180
  switch (graph.type) {
35181
+ case "none":
35182
+ return true;
35145
35183
  case "point":
35146
35184
  if (graph.numPoints === UNLIMITED) {
35147
35185
  return Boolean(mafsFlags["unlimited-point"]);
@@ -35163,7 +35201,7 @@ function shouldUseMafs(mafsFlags, graph) {
35163
35201
  const staticTransform$3 = _.identity;
35164
35202
  var InteractiveGraph$1 = {
35165
35203
  name: "interactive-graph",
35166
- displayName: "Interactive graph",
35204
+ displayName: "Interactive graph (Assessments only)",
35167
35205
  widget: InteractiveGraph,
35168
35206
  staticTransform: staticTransform$3
35169
35207
  };
@@ -37099,11 +37137,6 @@ var LabelImage$1 = {
37099
37137
  isLintable: true
37100
37138
  };
37101
37139
 
37102
- let Layout = /*#__PURE__*/function (Layout) {
37103
- Layout["HORIZONTAL"] = "horizontal";
37104
- Layout["VERTICAL"] = "vertical";
37105
- return Layout;
37106
- }({});
37107
37140
  var ItemState = /*#__PURE__*/function (ItemState) {
37108
37141
  ItemState["STATIC"] = "static";
37109
37142
  ItemState["DRAGGING"] = "dragging";
@@ -37130,7 +37163,7 @@ class Placeholder extends React.Component {
37130
37163
  const {
37131
37164
  layout
37132
37165
  } = this.props;
37133
- const className = css(styles$d.card, styles$d.placeholder, layout === Layout.HORIZONTAL && styles$d.horizontalCard);
37166
+ const className = css(styles$d.card, styles$d.placeholder, layout === "horizontal" && styles$d.horizontalCard);
37134
37167
  const style = {
37135
37168
  width: this.props.width,
37136
37169
  height: this.props.height
@@ -37334,7 +37367,7 @@ class Draggable extends React.Component {
37334
37367
  // We need to keep backwards compatbility with rules specified directly
37335
37368
  // in CSS. Hence the hacky tacking on of manual classNames.
37336
37369
  // See sortable.less for details.
37337
- let className = css(styles$d.card, styles$d.draggable, layout === Layout.HORIZONTAL && styles$d.horizontalCard, layout === Layout.VERTICAL && styles$d.verticalCard, type === ItemState.DRAGGING && styles$d.dragging, type === ItemState.DISABLED && styles$d.disabled, !includePadding && styles$d.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
37370
+ let className = css(styles$d.card, styles$d.draggable, layout === "horizontal" && styles$d.horizontalCard, layout === "vertical" && styles$d.verticalCard, type === ItemState.DRAGGING && styles$d.dragging, type === ItemState.DISABLED && styles$d.disabled, !includePadding && styles$d.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
37338
37371
  if (!includePadding) {
37339
37372
  className += " perseus-sortable-draggable-unpadded";
37340
37373
  }
@@ -37463,7 +37496,7 @@ class Sortable extends React.Component {
37463
37496
  if (constraints != null && constraints.width) {
37464
37497
  // Items must be at least as wide as the specified constraint
37465
37498
  syncWidth = _.max(widths.concat(constraints.width));
37466
- } else if (layout === Layout.VERTICAL) {
37499
+ } else if (layout === "vertical") {
37467
37500
  // Sync widths to get a clean column
37468
37501
  syncWidth = _.max(widths);
37469
37502
  }
@@ -37471,7 +37504,7 @@ class Sortable extends React.Component {
37471
37504
  if (constraints != null && constraints.height) {
37472
37505
  // Items must be at least as high as the specified constraint
37473
37506
  syncHeight = _.max(heights.concat(constraints.height));
37474
- } else if (layout === Layout.HORIZONTAL) {
37507
+ } else if (layout === "horizontal") {
37475
37508
  // Sync widths to get a clean row
37476
37509
  syncHeight = _.max(heights);
37477
37510
  }
@@ -37553,7 +37586,7 @@ class Sortable extends React.Component {
37553
37586
 
37554
37587
  // @ts-expect-error - TS2551 - Property 'splice' does not exist on type 'readonly SortableItem[]'. Did you mean 'slice'?
37555
37588
  items.splice(currentIndex, 1);
37556
- if (this.props.layout === Layout.HORIZONTAL) {
37589
+ if (this.props.layout === "horizontal") {
37557
37590
  // @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'. | TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
37558
37591
  const midWidth = $draggable.offset().left - $sortable.offset().left;
37559
37592
  let sumWidth = 0;
@@ -37668,19 +37701,19 @@ class Sortable extends React.Component {
37668
37701
  // We need to keep backwards compatbility with rules specified directly
37669
37702
  // in CSS. See sortable.less for details.
37670
37703
  const className = css(styles$d.sortable) + " perseus-sortable";
37671
- const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout === Layout.VERTICAL;
37672
- const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout === Layout.HORIZONTAL;
37704
+ const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout === "vertical";
37705
+ const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout === "horizontal";
37673
37706
  _.each(this.state.items, function (item, i, items) {
37674
37707
  const isLast = i === items.length - 1;
37675
37708
  const isStatic = item.state === ItemState.STATIC || item.state === ItemState.DISABLED;
37676
37709
  let margin;
37677
37710
 
37678
37711
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37679
- if (this.props.layout === Layout.HORIZONTAL) {
37712
+ if (this.props.layout === "horizontal") {
37680
37713
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37681
37714
  margin = "0 " + this.props.margin + "px 0 0"; // right
37682
37715
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37683
- } else if (this.props.layout === Layout.VERTICAL) {
37716
+ } else if (this.props.layout === "vertical") {
37684
37717
  // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
37685
37718
  margin = "0 0 " + this.props.margin + "px 0"; // bottom
37686
37719
  }
@@ -37754,7 +37787,7 @@ class Sortable extends React.Component {
37754
37787
  }
37755
37788
  }
37756
37789
  Sortable.defaultProps = {
37757
- layout: Layout.HORIZONTAL,
37790
+ layout: "horizontal",
37758
37791
  padding: true,
37759
37792
  disabled: false,
37760
37793
  constraints: {},
@@ -37813,7 +37846,6 @@ const styles$d = StyleSheet.create({
37813
37846
  }
37814
37847
  });
37815
37848
 
37816
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
37817
37849
  const {
37818
37850
  shuffle: shuffle$1,
37819
37851
  seededRNG
@@ -37947,7 +37979,7 @@ class Matcher extends React.Component {
37947
37979
  className: css(styles$c.column)
37948
37980
  }, /*#__PURE__*/React.createElement(Sortable, {
37949
37981
  options: left,
37950
- layout: Layout.VERTICAL,
37982
+ layout: "vertical",
37951
37983
  padding: this.props.padding,
37952
37984
  disabled: !this.props.orderMatters,
37953
37985
  constraints: constraints,
@@ -37960,11 +37992,9 @@ class Matcher extends React.Component {
37960
37992
  ref: "left"
37961
37993
  })), /*#__PURE__*/React.createElement("td", {
37962
37994
  className: css(styles$c.column, styles$c.columnRight)
37963
- }, /*#__PURE__*/React.createElement(Sortable
37964
- // @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
37965
- , {
37995
+ }, /*#__PURE__*/React.createElement(Sortable, {
37966
37996
  options: right,
37967
- layout: Layout.VERTICAL,
37997
+ layout: "vertical",
37968
37998
  padding: this.props.padding,
37969
37999
  constraints: constraints,
37970
38000
  onMeasure: this.onMeasureRight,
@@ -37978,18 +38008,6 @@ class Matcher extends React.Component {
37978
38008
  }
37979
38009
  }
37980
38010
  Matcher.contextType = PerseusI18nContext;
37981
- Matcher.propTypes = {
37982
- apiOptions: ApiOptions.propTypes,
37983
- labels: PropTypes.array,
37984
- left: PropTypes.array,
37985
- onChange: PropTypes.func,
37986
- orderMatters: PropTypes.bool,
37987
- padding: PropTypes.bool,
37988
- problemNum: PropTypes.number,
37989
- right: PropTypes.array,
37990
- trackInteraction: PropTypes.func.isRequired,
37991
- linterContext: linterContextProps
37992
- };
37993
38011
  Matcher.defaultProps = {
37994
38012
  left: [],
37995
38013
  right: [],
@@ -38509,6 +38527,7 @@ const staticTransform$2 = editorProps => {
38509
38527
  var Matrix$1 = {
38510
38528
  name: "matrix",
38511
38529
  displayName: "Matrix",
38530
+ hidden: true,
38512
38531
  widget: Matrix,
38513
38532
  transform: propTransform$1,
38514
38533
  staticTransform: staticTransform$2,
@@ -38665,6 +38684,7 @@ const propUpgrades = {
38665
38684
  var Measurer$1 = {
38666
38685
  name: "measurer",
38667
38686
  displayName: "Measurer",
38687
+ hidden: true,
38668
38688
  widget: Measurer,
38669
38689
  version: {
38670
38690
  major: 1,
@@ -39554,12 +39574,7 @@ class MoleculeWidget extends React.Component {
39554
39574
  // TODO(colin): this is here as part of the interface for a component.
39555
39575
  // Figure out if there is something more appropriate that this should
39556
39576
  // return.
39557
- return {
39558
- type: "points",
39559
- earned: 0,
39560
- total: 0,
39561
- message: null
39562
- };
39577
+ return noopValidator();
39563
39578
  };
39564
39579
  }
39565
39580
  render() {
@@ -40166,15 +40181,11 @@ class PlaceholderCard extends React.Component {
40166
40181
  }, /*#__PURE__*/React.createElement("div", {
40167
40182
  className: "card placeholder",
40168
40183
  style: {
40169
- height: this.props.height
40184
+ height: this.props.width
40170
40185
  }
40171
40186
  }));
40172
40187
  }
40173
40188
  }
40174
- PlaceholderCard.propTypes = {
40175
- width: PropTypes.number.isRequired,
40176
- height: PropTypes.number.isRequired
40177
- };
40178
40189
  class DragHintCard extends React.Component {
40179
40190
  render() {
40180
40191
  return /*#__PURE__*/React.createElement("div", {
@@ -40184,10 +40195,6 @@ class DragHintCard extends React.Component {
40184
40195
  }));
40185
40196
  }
40186
40197
  }
40187
- const PropTypePosition = PropTypes.shape({
40188
- left: PropTypes.number,
40189
- top: PropTypes.number
40190
- });
40191
40198
  class Card extends React.Component {
40192
40199
  constructor(...args) {
40193
40200
  super(...args);
@@ -40268,7 +40275,7 @@ class Card extends React.Component {
40268
40275
  this.props.fakeRef !== nextProps.fakeRef;
40269
40276
  }
40270
40277
  componentDidUpdate(prevProps, prevState) {
40271
- if (this.props.animating && !prevProps.animating) {
40278
+ if (this.props.animating && !prevProps.animating && this.props.animateTo && this.props.startOffset) {
40272
40279
  // If we just were changed into animating, start the animation.
40273
40280
  // We pick the animation speed based on the distance that the card
40274
40281
  // needs to travel. (Why sqrt? Just because it looks nice -- with a
@@ -40291,10 +40298,11 @@ class Card extends React.Component {
40291
40298
  render() {
40292
40299
  let style = {};
40293
40300
  if (this.props.floating) {
40301
+ var _this$props$startOffs, _this$props$startOffs2;
40294
40302
  style = {
40295
40303
  position: "absolute",
40296
- left: this.props.startOffset.left,
40297
- top: this.props.startOffset.top
40304
+ left: (_this$props$startOffs = this.props.startOffset) == null ? void 0 : _this$props$startOffs.left,
40305
+ top: (_this$props$startOffs2 = this.props.startOffset) == null ? void 0 : _this$props$startOffs2.top
40298
40306
  };
40299
40307
  }
40300
40308
  if (this.props.width) {
@@ -40304,7 +40312,7 @@ class Card extends React.Component {
40304
40312
  if (this.props.stack) {
40305
40313
  className.push("stack");
40306
40314
  }
40307
- if (this.props.floating && !this.props.animating) {
40315
+ if (this.props.floating && !this.props.animating && this.props.mouse && this.props.startMouse) {
40308
40316
  className.push("dragging");
40309
40317
  style.left += this.props.mouse.left - this.props.startMouse.left;
40310
40318
  style.top += this.props.mouse.top - this.props.startMouse.top;
@@ -40329,28 +40337,13 @@ class Card extends React.Component {
40329
40337
  }
40330
40338
  }
40331
40339
  Card.contextType = PerseusI18nContext;
40332
- Card.propTypes = {
40333
- floating: PropTypes.bool.isRequired,
40334
- animating: PropTypes.bool,
40335
- width: PropTypes.number,
40336
- stack: PropTypes.bool,
40337
- onMouseDown: PropTypes.func,
40338
- onMouseMove: PropTypes.func,
40339
- onMouseUp: PropTypes.func,
40340
- // Used only for floating/animating cards
40341
- startMouse: PropTypePosition,
40342
- startOffset: PropTypePosition,
40343
- animateTo: PropTypePosition,
40344
- onAnimationEnd: PropTypes.func,
40345
- linterContext: linterContextProps
40346
- };
40347
40340
  Card.defaultProps = {
40348
40341
  stack: false,
40349
40342
  animating: false,
40350
40343
  linterContext: linterContextDefault
40351
40344
  };
40352
40345
  const NORMAL = "normal",
40353
- HORIZONTAL$1 = "horizontal";
40346
+ HORIZONTAL = "horizontal";
40354
40347
  class Orderer extends React.Component {
40355
40348
  constructor(...args) {
40356
40349
  super(...args);
@@ -40497,7 +40490,7 @@ class Orderer extends React.Component {
40497
40490
  };
40498
40491
  this.findCorrectIndex = (draggable, list) => {
40499
40492
  // Find the correct index for a card given the current cards.
40500
- const isHorizontal = this.props.layout === HORIZONTAL$1;
40493
+ const isHorizontal = this.props.layout === HORIZONTAL;
40501
40494
  // eslint-disable-next-line react/no-string-refs
40502
40495
  // @ts-expect-error - TS2769 - No overload matches this call.
40503
40496
  const $dragList = $(ReactDOM__default.findDOMNode(this.refs.dragList));
@@ -40547,7 +40540,7 @@ class Orderer extends React.Component {
40547
40540
  if (draggable == null) {
40548
40541
  return false;
40549
40542
  }
40550
- const isHorizontal = this.props.layout === HORIZONTAL$1,
40543
+ const isHorizontal = this.props.layout === HORIZONTAL,
40551
40544
  // @ts-expect-error - TS2769 - No overload matches this call.
40552
40545
  $draggable = $(ReactDOM__default.findDOMNode(draggable)),
40553
40546
  // eslint-disable-next-line react/no-string-refs
@@ -40719,7 +40712,7 @@ Orderer.defaultProps = {
40719
40712
  options: [],
40720
40713
  correctOptions: [],
40721
40714
  height: NORMAL,
40722
- layout: HORIZONTAL$1,
40715
+ layout: HORIZONTAL,
40723
40716
  linterContext: linterContextDefault
40724
40717
  };
40725
40718
  _.extend(Orderer, {
@@ -40742,6 +40735,7 @@ _.extend(Orderer, {
40742
40735
  var Orderer$1 = {
40743
40736
  name: "orderer",
40744
40737
  displayName: "Orderer",
40738
+ hidden: true,
40745
40739
  widget: Orderer,
40746
40740
  isLintable: true
40747
40741
  };
@@ -42546,12 +42540,7 @@ class Passage extends React.Component {
42546
42540
  };
42547
42541
  }
42548
42542
  static validate(state, rubric) {
42549
- return {
42550
- type: "points",
42551
- earned: 0,
42552
- total: 0,
42553
- message: null
42554
- };
42543
+ return noopValidator();
42555
42544
  }
42556
42545
  componentDidMount() {
42557
42546
  this._updateState();
@@ -42917,6 +42906,7 @@ Passage.defaultProps = {
42917
42906
  var Passage$1 = {
42918
42907
  name: "passage",
42919
42908
  displayName: "Passage (SAT only)",
42909
+ hidden: true,
42920
42910
  widget: Passage,
42921
42911
  transform: editorProps => {
42922
42912
  return _.pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
@@ -42928,23 +42918,21 @@ var Passage$1 = {
42928
42918
  class PassageRefTarget extends React.Component {
42929
42919
  constructor(..._args) {
42930
42920
  super(..._args);
42921
+ // TODO passage-ref-target isn't interactive; remove
42931
42922
  this.getUserInput = () => {
42932
- return WidgetJsonifyDeprecated.getUserInput.call(this);
42923
+ return;
42933
42924
  };
42925
+ // TODO passage-ref-target isn't interactive; remove
42934
42926
  this.change = (...args) => {
42935
42927
  return change.apply(this, args);
42936
42928
  };
42929
+ // TODO passage-ref-target isn't interactive; remove
42937
42930
  this.simpleValidate = rubric => {
42938
42931
  return PassageRefTarget.validate(this.getUserInput(), rubric);
42939
42932
  };
42940
42933
  }
42941
42934
  static validate(state, rubric) {
42942
- return {
42943
- type: "points",
42944
- earned: 0,
42945
- total: 0,
42946
- message: null
42947
- };
42935
+ return noopValidator();
42948
42936
  }
42949
42937
  render() {
42950
42938
  return /*#__PURE__*/React.createElement(Renderer, {
@@ -44006,6 +43994,7 @@ const staticTransform = _.identity;
44006
43994
  var Plotter$1 = {
44007
43995
  name: "plotter",
44008
43996
  displayName: "Plotter",
43997
+ hidden: true,
44009
43998
  widget: Plotter,
44010
43999
  staticTransform: staticTransform
44011
44000
  };
@@ -44056,17 +44045,13 @@ var PythonProgram$1 = {
44056
44045
  widget: PythonProgram
44057
44046
  };
44058
44047
 
44059
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
44060
44048
  const {
44061
44049
  shuffle
44062
44050
  } = Util;
44063
- const HORIZONTAL = "horizontal";
44064
- const VERTICAL = "vertical";
44065
44051
  class Sorter extends React.Component {
44066
44052
  constructor(...args) {
44067
44053
  super(...args);
44068
- // @ts-expect-error - TS2564 - Property '_isMounted' has no initializer and is not definitely assigned in the constructor.
44069
- this._isMounted = void 0;
44054
+ this._isMounted = false;
44070
44055
  this.state = {
44071
44056
  changed: false
44072
44057
  };
@@ -44140,9 +44125,7 @@ class Sorter extends React.Component {
44140
44125
  const marginPx = apiOptions.isMobile ? 8 : 5;
44141
44126
  return /*#__PURE__*/React.createElement("div", {
44142
44127
  className: "perseus-widget-sorter perseus-clearfix"
44143
- }, /*#__PURE__*/React.createElement(Sortable
44144
- // @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
44145
- , {
44128
+ }, /*#__PURE__*/React.createElement(Sortable, {
44146
44129
  options: options,
44147
44130
  layout: this.props.layout,
44148
44131
  margin: marginPx,
@@ -44155,20 +44138,9 @@ class Sorter extends React.Component {
44155
44138
  }));
44156
44139
  }
44157
44140
  }
44158
- Sorter.propTypes = {
44159
- apiOptions: ApiOptions.propTypes,
44160
- // eslint-disable-next-line react/forbid-prop-types
44161
- correct: PropTypes.array,
44162
- layout: PropTypes.oneOf([HORIZONTAL, VERTICAL]),
44163
- onChange: PropTypes.func,
44164
- padding: PropTypes.bool,
44165
- problemNum: PropTypes.number,
44166
- trackInteraction: PropTypes.func.isRequired,
44167
- linterContext: linterContextProps
44168
- };
44169
44141
  Sorter.defaultProps = {
44170
44142
  correct: [],
44171
- layout: HORIZONTAL,
44143
+ layout: "horizontal",
44172
44144
  padding: true,
44173
44145
  problemNum: 0,
44174
44146
  onChange: function () {},
@@ -44184,7 +44156,6 @@ var Sorter$1 = {
44184
44156
  const {
44185
44157
  assert
44186
44158
  } = InteractiveUtil;
44187
-
44188
44159
  /* Input handling: Maps a (row, column) pair to a unique ref used by React,
44189
44160
  * and extracts (row, column) pairs from input paths, used to allow outsiders
44190
44161
  * to focus, blur, set input values, etc. */
@@ -44371,16 +44342,6 @@ class Table extends React.Component {
44371
44342
  }
44372
44343
  }
44373
44344
  Table.contextType = PerseusI18nContext;
44374
- Table.propTypes = {
44375
- answers: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
44376
- editableHeaders: PropTypes.bool,
44377
- // The editor to use when editableHeaders is enabled
44378
- Editor: PropTypes.func,
44379
- headers: PropTypes.arrayOf(PropTypes.string),
44380
- keypadElement: keypadElementPropType,
44381
- trackInteraction: PropTypes.func.isRequired,
44382
- linterContext: linterContextProps
44383
- };
44384
44345
  Table.defaultProps = function () {
44385
44346
  const defaultRows = 4;
44386
44347
  const defaultColumns = 1;
@@ -44582,12 +44543,7 @@ class Video extends React.Component {
44582
44543
  * of Khan Academy videos.
44583
44544
  */
44584
44545
  static validate(userInput, rubric) {
44585
- return {
44586
- type: "points",
44587
- earned: 0,
44588
- total: 0,
44589
- message: null
44590
- };
44546
+ return noopValidator();
44591
44547
  }
44592
44548
  render() {
44593
44549
  const {
@@ -44692,7 +44648,7 @@ const init = function init(options) {
44692
44648
 
44693
44649
  // This file is processed by a Rollup plugin (replace) to inject the production
44694
44650
  const libName = "@khanacademy/perseus";
44695
- const libVersion = "33.2.1";
44651
+ const libVersion = "34.0.0";
44696
44652
  addLibraryVersionToPerseusDebug(libName, libVersion);
44697
44653
 
44698
44654
  const apiVersion = {
@@ -48155,5 +48111,5 @@ const EditorJsonify = {
48155
48111
  }
48156
48112
  };
48157
48113
 
48158
- export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
48114
+ export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
48159
48115
  //# sourceMappingURL=index.js.map