@khanacademy/perseus-core 4.0.0 → 5.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.
package/dist/index.js CHANGED
@@ -3,11 +3,31 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var _ = require('underscore');
6
+ var KAS = require('@khanacademy/kas');
6
7
  var perseusCore = require('@khanacademy/perseus-core');
7
8
 
8
9
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
9
10
 
11
+ function _interopNamespace(e) {
12
+ if (e && e.__esModule) return e;
13
+ var n = Object.create(null);
14
+ if (e) {
15
+ Object.keys(e).forEach(function (k) {
16
+ if (k !== 'default') {
17
+ var d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: function () { return e[k]; }
21
+ });
22
+ }
23
+ });
24
+ }
25
+ n["default"] = e;
26
+ return Object.freeze(n);
27
+ }
28
+
10
29
  var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
30
+ var KAS__namespace = /*#__PURE__*/_interopNamespace(KAS);
11
31
 
12
32
  function getMatrixSize(matrix) {
13
33
  const matrixSize = [1, 1];
@@ -1093,6 +1113,76 @@ const parseExplanationWidget = parseWidget(constant("explanation"), object({
1093
1113
  static: defaulted(boolean, () => false)
1094
1114
  }));
1095
1115
 
1116
+ const KeypadKeys = ["PLUS", "MINUS", "NEGATIVE", "TIMES", "DIVIDE", "DECIMAL", "PERIOD", "PERCENT", "CDOT", "EQUAL", "NEQ", "GT", "LT", "GEQ", "LEQ",
1117
+ // mobile native only
1118
+ "FRAC_INCLUSIVE",
1119
+ // mobile native only
1120
+ "FRAC_EXCLUSIVE",
1121
+ // mobile native only
1122
+ "FRAC", "EXP", "EXP_2", "EXP_3", "SQRT", "CUBE_ROOT", "RADICAL", "LEFT_PAREN", "RIGHT_PAREN", "LN", "LOG", "LOG_N", "SIN", "COS",
1123
+ // TODO(charlie): Add in additional Greek letters.,
1124
+ "TAN", "PI", "THETA", "UP", "RIGHT", "DOWN", "LEFT", "BACKSPACE", "DISMISS", "JUMP_OUT_PARENTHESES", "JUMP_OUT_EXPONENT", "JUMP_OUT_BASE", "JUMP_INTO_NUMERATOR", "JUMP_OUT_NUMERATOR", "JUMP_OUT_DENOMINATOR",
1125
+ // Multi-functional keys.
1126
+ "NUM_0", "NUM_1", "NUM_2", "NUM_3", "NUM_4", "NUM_5", "NUM_6", "NUM_7", "NUM_8", "NUM_9", "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", "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"];
1127
+
1128
+ // Used by KeypadContext to pass around a renderer reference
1129
+
1130
+ /**
1131
+ * Scrape the answer forms for any variables or contants (like Pi)
1132
+ * that need to be included as keys on the keypad.
1133
+ */
1134
+ function deriveExtraKeys(widgetOptions) {
1135
+ if (widgetOptions.extraKeys) {
1136
+ return widgetOptions.extraKeys;
1137
+ }
1138
+
1139
+ // If there are no extra symbols available, we include Pi anyway, so
1140
+ // that the "extra symbols" button doesn't appear empty.
1141
+ const defaultKeys = ["PI"];
1142
+ if (widgetOptions.answerForms == null) {
1143
+ return defaultKeys;
1144
+ }
1145
+
1146
+ // Extract any and all variables and constants from the answer forms.
1147
+ const uniqueExtraVariables = {};
1148
+ const uniqueExtraConstants = {};
1149
+ for (const answerForm of widgetOptions.answerForms) {
1150
+ const maybeExpr = KAS__namespace.parse(answerForm.value, widgetOptions);
1151
+ if (maybeExpr.parsed) {
1152
+ const expr = maybeExpr.expr;
1153
+
1154
+ // The keypad expects Greek letters to be capitalized (e.g., it
1155
+ // requires `PI` instead of `pi`). Right now, it only supports Pi
1156
+ // and Theta, so we special-case.
1157
+ const isGreek = symbol => symbol === "pi" || symbol === "theta";
1158
+ const toKey = symbol => isGreek(symbol) ? symbol.toUpperCase() : symbol;
1159
+ const isKey = key => KeypadKeys.includes(key);
1160
+ for (const variable of expr.getVars()) {
1161
+ const maybeKey = toKey(variable);
1162
+ if (isKey(maybeKey)) {
1163
+ uniqueExtraVariables[maybeKey] = true;
1164
+ }
1165
+ }
1166
+ for (const constant of expr.getConsts()) {
1167
+ const maybeKey = toKey(constant);
1168
+ if (isKey(maybeKey)) {
1169
+ uniqueExtraConstants[maybeKey] = true;
1170
+ }
1171
+ }
1172
+ }
1173
+ }
1174
+
1175
+ // TODO(charlie): Alert the keypad as to which of these symbols should be
1176
+ // treated as functions.
1177
+ const extraVariables = Object.keys(uniqueExtraVariables).sort();
1178
+ const extraConstants = Object.keys(uniqueExtraConstants).sort();
1179
+ const extraKeys = [...extraVariables, ...extraConstants];
1180
+ if (!extraKeys.length) {
1181
+ return defaultKeys;
1182
+ }
1183
+ return extraKeys;
1184
+ }
1185
+
1096
1186
  // Given a function, creates a PartialParser that converts one type to another
1097
1187
  // using that function. The returned parser never fails.
1098
1188
  function convert(f) {
@@ -1195,11 +1285,25 @@ function removeInvalidAnswerForms(possiblyInvalid) {
1195
1285
  }
1196
1286
  return valid;
1197
1287
  }
1198
- const version1 = object({
1288
+ const version2$1 = object({
1289
+ major: constant(2),
1290
+ minor: number
1291
+ });
1292
+ const parseExpressionWidgetV2 = parseWidgetWithVersion(version2$1, constant("expression"), object({
1293
+ answerForms: pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,
1294
+ functions: array(string),
1295
+ times: boolean,
1296
+ visibleLabel: optional(string),
1297
+ ariaLabel: optional(string),
1298
+ buttonSets: parseLegacyButtonSets,
1299
+ buttonsVisible: optional(enumeration("always", "never", "focused")),
1300
+ extraKeys: array(enumeration(...KeypadKeys))
1301
+ }));
1302
+ const version1$1 = object({
1199
1303
  major: constant(1),
1200
1304
  minor: number
1201
1305
  });
1202
- const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expression"), object({
1306
+ const parseExpressionWidgetV1 = parseWidgetWithVersion(version1$1, constant("expression"), object({
1203
1307
  answerForms: pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,
1204
1308
  functions: array(string),
1205
1309
  times: boolean,
@@ -1208,11 +1312,33 @@ const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expre
1208
1312
  buttonSets: parseLegacyButtonSets,
1209
1313
  buttonsVisible: optional(enumeration("always", "never", "focused"))
1210
1314
  }));
1211
- const version0 = optional(object({
1315
+ function migrateV1ToV2$1(widget) {
1316
+ const {
1317
+ options
1318
+ } = widget;
1319
+ return {
1320
+ ...widget,
1321
+ version: {
1322
+ major: 2,
1323
+ minor: 0
1324
+ },
1325
+ options: {
1326
+ times: options.times,
1327
+ buttonSets: options.buttonSets,
1328
+ functions: options.functions,
1329
+ buttonsVisible: options.buttonsVisible,
1330
+ visibleLabel: options.visibleLabel,
1331
+ ariaLabel: options.ariaLabel,
1332
+ answerForms: options.answerForms,
1333
+ extraKeys: deriveExtraKeys(options)
1334
+ }
1335
+ };
1336
+ }
1337
+ const version0$1 = optional(object({
1212
1338
  major: constant(0),
1213
1339
  minor: number
1214
1340
  }));
1215
- const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expression"), object({
1341
+ const parseExpressionWidgetV0 = parseWidgetWithVersion(version0$1, constant("expression"), object({
1216
1342
  functions: array(string),
1217
1343
  times: boolean,
1218
1344
  visibleLabel: optional(string),
@@ -1223,7 +1349,7 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expre
1223
1349
  buttonSets: parseLegacyButtonSets,
1224
1350
  buttonsVisible: optional(enumeration("always", "never", "focused"))
1225
1351
  }));
1226
- function migrateV0ToV1(widget) {
1352
+ function migrateV0ToV1$1(widget) {
1227
1353
  const {
1228
1354
  options
1229
1355
  } = widget;
@@ -1249,7 +1375,7 @@ function migrateV0ToV1(widget) {
1249
1375
  }
1250
1376
  };
1251
1377
  }
1252
- const parseExpressionWidget = versionedWidgetOptions(1, parseExpressionWidgetV1).withMigrationFrom(0, parseExpressionWidgetV0, migrateV0ToV1).parser;
1378
+ const parseExpressionWidget = versionedWidgetOptions(2, parseExpressionWidgetV2).withMigrationFrom(1, parseExpressionWidgetV1, migrateV1ToV2$1).withMigrationFrom(0, parseExpressionWidgetV0, migrateV0ToV1$1).parser;
1253
1379
 
1254
1380
  const falseToNull = pipeParsers(constant(false)).then(convert(() => null)).parser;
1255
1381
  const parseGradedGroupWidgetOptions = object({
@@ -2253,7 +2379,84 @@ const parsePythonProgramWidget = parseWidget(constant("python-program"), object(
2253
2379
  height: number
2254
2380
  }));
2255
2381
 
2256
- const parseRadioWidget = parseWidget(constant("radio"), object({
2382
+ const currentVersion$3 = {
2383
+ major: 2,
2384
+ minor: 0
2385
+ };
2386
+ function deriveNumCorrect(options) {
2387
+ const {
2388
+ choices,
2389
+ numCorrect
2390
+ } = options;
2391
+ return numCorrect ?? choices.filter(c => c.correct).length;
2392
+ }
2393
+ const widgetOptionsUpgrades$2 = {
2394
+ "2": v1props => {
2395
+ const upgraded = {
2396
+ ...v1props,
2397
+ numCorrect: deriveNumCorrect(v1props)
2398
+ };
2399
+ return upgraded;
2400
+ },
2401
+ "1": v0props => {
2402
+ const {
2403
+ noneOfTheAbove,
2404
+ ...rest
2405
+ } = v0props;
2406
+ if (noneOfTheAbove) {
2407
+ throw new Error("radio widget v0 no longer supports auto noneOfTheAbove");
2408
+ }
2409
+ return {
2410
+ ...rest,
2411
+ hasNoneOfTheAbove: false
2412
+ };
2413
+ }
2414
+ };
2415
+ const defaultWidgetOptions$v = {
2416
+ choices: [{}, {}, {}, {}],
2417
+ displayCount: null,
2418
+ randomize: false,
2419
+ hasNoneOfTheAbove: false,
2420
+ multipleSelect: false,
2421
+ countChoices: false,
2422
+ deselectEnabled: false
2423
+ };
2424
+
2425
+ const version2 = optional(object({
2426
+ major: constant(2),
2427
+ minor: number
2428
+ }));
2429
+ const parseRadioWidgetV2 = parseWidgetWithVersion(version2, constant("radio"), object({
2430
+ numCorrect: optional(number),
2431
+ choices: array(object({
2432
+ content: defaulted(string, () => ""),
2433
+ clue: optional(string),
2434
+ correct: optional(boolean),
2435
+ isNoneOfTheAbove: optional(boolean),
2436
+ // deprecated
2437
+ // There is an import cycle between radio-widget.ts and
2438
+ // widgets-map.ts. The anonymous function below ensures that we
2439
+ // don't refer to parseWidgetsMap before it's defined.
2440
+ widgets: optional((rawVal, ctx) => parseWidgetsMap(rawVal, ctx))
2441
+ })),
2442
+ hasNoneOfTheAbove: optional(boolean),
2443
+ countChoices: optional(boolean),
2444
+ randomize: optional(boolean),
2445
+ multipleSelect: optional(boolean),
2446
+ deselectEnabled: optional(boolean),
2447
+ // deprecated
2448
+ onePerLine: optional(boolean),
2449
+ // deprecated
2450
+ displayCount: optional(any),
2451
+ // v0 props
2452
+ // `noneOfTheAbove` is still in use (but only set to `false`).
2453
+ noneOfTheAbove: optional(constant(false))
2454
+ }));
2455
+ const version1 = optional(object({
2456
+ major: constant(1),
2457
+ minor: number
2458
+ }));
2459
+ const parseRadioWidgetV1 = parseWidgetWithVersion(version1, constant("radio"), object({
2257
2460
  choices: array(object({
2258
2461
  content: defaulted(string, () => ""),
2259
2462
  clue: optional(string),
@@ -2278,6 +2481,72 @@ const parseRadioWidget = parseWidget(constant("radio"), object({
2278
2481
  // `noneOfTheAbove` is still in use (but only set to `false`).
2279
2482
  noneOfTheAbove: optional(constant(false))
2280
2483
  }));
2484
+ function migrateV1ToV2(widget) {
2485
+ const {
2486
+ options
2487
+ } = widget;
2488
+ return {
2489
+ ...widget,
2490
+ version: {
2491
+ major: 2,
2492
+ minor: 0
2493
+ },
2494
+ options: {
2495
+ ...options,
2496
+ numCorrect: deriveNumCorrect(options)
2497
+ }
2498
+ };
2499
+ }
2500
+ const version0 = optional(object({
2501
+ major: constant(0),
2502
+ minor: number
2503
+ }));
2504
+ const parseRadioWidgetV0 = parseWidgetWithVersion(version0, constant("radio"), object({
2505
+ choices: array(object({
2506
+ content: defaulted(string, () => ""),
2507
+ clue: optional(string),
2508
+ correct: optional(boolean),
2509
+ isNoneOfTheAbove: optional(boolean),
2510
+ // deprecated
2511
+ // There is an import cycle between radio-widget.ts and
2512
+ // widgets-map.ts. The anonymous function below ensures that we
2513
+ // don't refer to parseWidgetsMap before it's defined.
2514
+ widgets: optional((rawVal, ctx) => parseWidgetsMap(rawVal, ctx))
2515
+ })),
2516
+ hasNoneOfTheAbove: optional(boolean),
2517
+ countChoices: optional(boolean),
2518
+ randomize: optional(boolean),
2519
+ multipleSelect: optional(boolean),
2520
+ deselectEnabled: optional(boolean),
2521
+ // deprecated
2522
+ onePerLine: optional(boolean),
2523
+ // deprecated
2524
+ displayCount: optional(any),
2525
+ // v0 props
2526
+ // `noneOfTheAbove` is still in use (but only set to `false`).
2527
+ noneOfTheAbove: optional(constant(false))
2528
+ }));
2529
+ function migrateV0ToV1(widget) {
2530
+ const {
2531
+ options
2532
+ } = widget;
2533
+ const {
2534
+ noneOfTheAbove: _,
2535
+ ...rest
2536
+ } = options;
2537
+ return {
2538
+ ...widget,
2539
+ version: {
2540
+ major: 1,
2541
+ minor: 0
2542
+ },
2543
+ options: {
2544
+ ...rest,
2545
+ hasNoneOfTheAbove: false
2546
+ }
2547
+ };
2548
+ }
2549
+ const parseRadioWidget = versionedWidgetOptions(2, parseRadioWidgetV2).withMigrationFrom(1, parseRadioWidgetV1, migrateV1ToV2).withMigrationFrom(0, parseRadioWidgetV0, migrateV0ToV1).parser;
2281
2550
 
2282
2551
  const parseSorterWidget = parseWidget(constant("sorter"), object({
2283
2552
  correct: array(string),
@@ -2616,7 +2885,7 @@ const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
2616
2885
 
2617
2886
  // This file is processed by a Rollup plugin (replace) to inject the production
2618
2887
  const libName = "@khanacademy/perseus-core";
2619
- const libVersion = "4.0.0";
2888
+ const libVersion = "5.0.0";
2620
2889
  addLibraryVersionToPerseusDebug(libName, libVersion);
2621
2890
 
2622
2891
  /**
@@ -2733,7 +3002,7 @@ function getCategorizerPublicWidgetOptions(options) {
2733
3002
  };
2734
3003
  }
2735
3004
 
2736
- const defaultWidgetOptions$v = {
3005
+ const defaultWidgetOptions$u = {
2737
3006
  items: [],
2738
3007
  categories: [],
2739
3008
  values: [],
@@ -2741,7 +3010,7 @@ const defaultWidgetOptions$v = {
2741
3010
  };
2742
3011
  const categorizerWidgetLogic = {
2743
3012
  name: "categorizer",
2744
- defaultWidgetOptions: defaultWidgetOptions$v,
3013
+ defaultWidgetOptions: defaultWidgetOptions$u,
2745
3014
  getPublicWidgetOptions: getCategorizerPublicWidgetOptions
2746
3015
  };
2747
3016
 
@@ -2750,7 +3019,7 @@ function getCSProgramPublicWidgetOptions(options) {
2750
3019
  }
2751
3020
 
2752
3021
  const DEFAULT_HEIGHT = 400;
2753
- const defaultWidgetOptions$u = {
3022
+ const defaultWidgetOptions$t = {
2754
3023
  programID: "",
2755
3024
  programType: null,
2756
3025
  settings: [{
@@ -2763,18 +3032,18 @@ const defaultWidgetOptions$u = {
2763
3032
  };
2764
3033
  const csProgramWidgetLogic = {
2765
3034
  name: "cs-program",
2766
- defaultWidgetOptions: defaultWidgetOptions$u,
3035
+ defaultWidgetOptions: defaultWidgetOptions$t,
2767
3036
  supportedAlignments: ["block", "full-width"],
2768
3037
  getPublicWidgetOptions: getCSProgramPublicWidgetOptions
2769
3038
  };
2770
3039
 
2771
- const defaultWidgetOptions$t = {
3040
+ const defaultWidgetOptions$s = {
2772
3041
  togglePrompt: "",
2773
3042
  definition: ""
2774
3043
  };
2775
3044
  const definitionWidgetLogic = {
2776
3045
  name: "definition",
2777
- defaultWidgetOptions: defaultWidgetOptions$t,
3046
+ defaultWidgetOptions: defaultWidgetOptions$s,
2778
3047
  defaultAlignment: "inline"
2779
3048
  };
2780
3049
 
@@ -2799,7 +3068,7 @@ function getDropdownPublicWidgetOptions(options) {
2799
3068
  };
2800
3069
  }
2801
3070
 
2802
- const defaultWidgetOptions$s = {
3071
+ const defaultWidgetOptions$r = {
2803
3072
  placeholder: "",
2804
3073
  choices: [{
2805
3074
  content: "",
@@ -2808,12 +3077,12 @@ const defaultWidgetOptions$s = {
2808
3077
  };
2809
3078
  const dropdownWidgetLogic = {
2810
3079
  name: "definition",
2811
- defaultWidgetOptions: defaultWidgetOptions$s,
3080
+ defaultWidgetOptions: defaultWidgetOptions$r,
2812
3081
  defaultAlignment: "inline-block",
2813
3082
  getPublicWidgetOptions: getDropdownPublicWidgetOptions
2814
3083
  };
2815
3084
 
2816
- const defaultWidgetOptions$r = {
3085
+ const defaultWidgetOptions$q = {
2817
3086
  showPrompt: "Explain",
2818
3087
  hidePrompt: "Hide explanation",
2819
3088
  explanation: "explanation goes here\n\nmore explanation",
@@ -2821,31 +3090,46 @@ const defaultWidgetOptions$r = {
2821
3090
  };
2822
3091
  const explanationWidgetLogic = {
2823
3092
  name: "explanation",
2824
- defaultWidgetOptions: defaultWidgetOptions$r,
3093
+ defaultWidgetOptions: defaultWidgetOptions$q,
2825
3094
  defaultAlignment: "inline"
2826
3095
  };
2827
3096
 
2828
- const currentVersion$3 = {
2829
- major: 1,
3097
+ const currentVersion$2 = {
3098
+ major: 2,
2830
3099
  minor: 0
2831
3100
  };
2832
- const widgetOptionsUpgrades$2 = {
2833
- "1": v0options => ({
2834
- times: v0options.times,
2835
- buttonSets: v0options.buttonSets,
2836
- functions: v0options.functions,
2837
- buttonsVisible: v0options.buttonsVisible,
2838
- visibleLabel: v0options.visibleLabel,
2839
- ariaLabel: v0options.ariaLabel,
2840
- answerForms: [{
2841
- considered: "correct",
2842
- form: v0options.form,
2843
- simplify: v0options.simplify,
2844
- value: v0options.value
2845
- }]
2846
- })
3101
+ const widgetOptionsUpgrades$1 = {
3102
+ "2": v1options => {
3103
+ return {
3104
+ times: v1options.times,
3105
+ buttonSets: v1options.buttonSets,
3106
+ functions: v1options.functions,
3107
+ buttonsVisible: v1options.buttonsVisible,
3108
+ visibleLabel: v1options.visibleLabel,
3109
+ ariaLabel: v1options.ariaLabel,
3110
+ answerForms: v1options.answerForms,
3111
+ extraKeys: v1options.extraKeys || deriveExtraKeys(v1options)
3112
+ };
3113
+ },
3114
+ "1": v0options => {
3115
+ return {
3116
+ times: v0options.times,
3117
+ buttonSets: v0options.buttonSets,
3118
+ functions: v0options.functions,
3119
+ buttonsVisible: v0options.buttonsVisible,
3120
+ visibleLabel: v0options.visibleLabel,
3121
+ ariaLabel: v0options.ariaLabel,
3122
+ extraKeys: v0options.extraKeys,
3123
+ answerForms: [{
3124
+ considered: "correct",
3125
+ form: v0options.form,
3126
+ simplify: v0options.simplify,
3127
+ value: v0options.value
3128
+ }]
3129
+ };
3130
+ }
2847
3131
  };
2848
- const defaultWidgetOptions$q = {
3132
+ const defaultWidgetOptions$p = {
2849
3133
  answerForms: [],
2850
3134
  times: false,
2851
3135
  buttonSets: ["basic"],
@@ -2868,20 +3152,21 @@ function getExpressionPublicWidgetOptions(options) {
2868
3152
  times: options.times,
2869
3153
  visibleLabel: options.visibleLabel,
2870
3154
  ariaLabel: options.ariaLabel,
2871
- buttonsVisible: options.buttonsVisible
3155
+ buttonsVisible: options.buttonsVisible,
3156
+ extraKeys: options.extraKeys
2872
3157
  };
2873
3158
  }
2874
3159
 
2875
3160
  const expressionWidgetLogic = {
2876
3161
  name: "expression",
2877
- version: currentVersion$3,
2878
- widgetOptionsUpgrades: widgetOptionsUpgrades$2,
2879
- defaultWidgetOptions: defaultWidgetOptions$q,
3162
+ version: currentVersion$2,
3163
+ widgetOptionsUpgrades: widgetOptionsUpgrades$1,
3164
+ defaultWidgetOptions: defaultWidgetOptions$p,
2880
3165
  defaultAlignment: "inline-block",
2881
3166
  getPublicWidgetOptions: getExpressionPublicWidgetOptions
2882
3167
  };
2883
3168
 
2884
- const defaultWidgetOptions$p = {
3169
+ const defaultWidgetOptions$o = {
2885
3170
  title: "",
2886
3171
  content: "",
2887
3172
  widgets: {},
@@ -2890,15 +3175,15 @@ const defaultWidgetOptions$p = {
2890
3175
  };
2891
3176
  const gradedGroupWidgetLogic = {
2892
3177
  name: "graded-group",
2893
- defaultWidgetOptions: defaultWidgetOptions$p
3178
+ defaultWidgetOptions: defaultWidgetOptions$o
2894
3179
  };
2895
3180
 
2896
- const defaultWidgetOptions$o = {
3181
+ const defaultWidgetOptions$n = {
2897
3182
  gradedGroups: []
2898
3183
  };
2899
3184
  const gradedGroupSetWidgetLogic = {
2900
3185
  name: "graded-group-set",
2901
- defaultWidgetOptions: defaultWidgetOptions$o
3186
+ defaultWidgetOptions: defaultWidgetOptions$n
2902
3187
  };
2903
3188
 
2904
3189
  function getGrapherPublicWidgetOptions(options) {
@@ -2909,7 +3194,7 @@ function getGrapherPublicWidgetOptions(options) {
2909
3194
  return publicOptions;
2910
3195
  }
2911
3196
 
2912
- const defaultWidgetOptions$n = {
3197
+ const defaultWidgetOptions$m = {
2913
3198
  graph: {
2914
3199
  labels: ["x", "y"],
2915
3200
  range: [[-10, 10], [-10, 10]],
@@ -2931,25 +3216,25 @@ const defaultWidgetOptions$n = {
2931
3216
  };
2932
3217
  const grapherWidgetLogic = {
2933
3218
  name: "grapher",
2934
- defaultWidgetOptions: defaultWidgetOptions$n,
3219
+ defaultWidgetOptions: defaultWidgetOptions$m,
2935
3220
  getPublicWidgetOptions: getGrapherPublicWidgetOptions
2936
3221
  };
2937
3222
 
2938
- const defaultWidgetOptions$m = {
3223
+ const defaultWidgetOptions$l = {
2939
3224
  content: "",
2940
3225
  widgets: {},
2941
3226
  images: {}
2942
3227
  };
2943
3228
  const groupWidgetLogic = {
2944
3229
  name: "group",
2945
- defaultWidgetOptions: defaultWidgetOptions$m
3230
+ defaultWidgetOptions: defaultWidgetOptions$l
2946
3231
  };
2947
3232
 
2948
3233
  function getIFramePublicWidgetOptions(options) {
2949
3234
  return options;
2950
3235
  }
2951
3236
 
2952
- const defaultWidgetOptions$l = {
3237
+ const defaultWidgetOptions$k = {
2953
3238
  url: "",
2954
3239
  settings: [{
2955
3240
  name: "",
@@ -2962,11 +3247,11 @@ const defaultWidgetOptions$l = {
2962
3247
  };
2963
3248
  const iframeWidgetLogic = {
2964
3249
  name: "iframe",
2965
- defaultWidgetOptions: defaultWidgetOptions$l,
3250
+ defaultWidgetOptions: defaultWidgetOptions$k,
2966
3251
  getPublicWidgetOptions: getIFramePublicWidgetOptions
2967
3252
  };
2968
3253
 
2969
- const defaultWidgetOptions$k = {
3254
+ const defaultWidgetOptions$j = {
2970
3255
  title: "",
2971
3256
  range: [[0, 10], [0, 10]],
2972
3257
  box: [400, 400],
@@ -2981,12 +3266,12 @@ const defaultWidgetOptions$k = {
2981
3266
  };
2982
3267
  const imageWidgetLogic = {
2983
3268
  name: "image",
2984
- defaultWidgetOptions: defaultWidgetOptions$k,
3269
+ defaultWidgetOptions: defaultWidgetOptions$j,
2985
3270
  supportedAlignments: ["block", "full-width"],
2986
3271
  defaultAlignment: "block"
2987
3272
  };
2988
3273
 
2989
- const defaultWidgetOptions$j = {
3274
+ const defaultWidgetOptions$i = {
2990
3275
  value: 0,
2991
3276
  simplify: "required",
2992
3277
  size: "normal",
@@ -2997,11 +3282,11 @@ const defaultWidgetOptions$j = {
2997
3282
  };
2998
3283
  const inputNumberWidgetLogic = {
2999
3284
  name: "input-number",
3000
- defaultWidgetOptions: defaultWidgetOptions$j,
3285
+ defaultWidgetOptions: defaultWidgetOptions$i,
3001
3286
  defaultAlignment: "inline-block"
3002
3287
  };
3003
3288
 
3004
- const defaultWidgetOptions$i = {
3289
+ const defaultWidgetOptions$h = {
3005
3290
  graph: {
3006
3291
  box: [400, 400],
3007
3292
  labels: ["x", "y"],
@@ -3014,7 +3299,7 @@ const defaultWidgetOptions$i = {
3014
3299
  };
3015
3300
  const interactionWidgetLogic = {
3016
3301
  name: "interaction",
3017
- defaultWidgetOptions: defaultWidgetOptions$i
3302
+ defaultWidgetOptions: defaultWidgetOptions$h
3018
3303
  };
3019
3304
 
3020
3305
  function getInteractiveGraphPublicWidgetOptions(options) {
@@ -3025,7 +3310,7 @@ function getInteractiveGraphPublicWidgetOptions(options) {
3025
3310
  return publicOptions;
3026
3311
  }
3027
3312
 
3028
- const defaultWidgetOptions$h = {
3313
+ const defaultWidgetOptions$g = {
3029
3314
  labels: ["x", "y"],
3030
3315
  range: [[-10, 10], [-10, 10]],
3031
3316
  step: [1, 1],
@@ -3045,7 +3330,7 @@ const defaultWidgetOptions$h = {
3045
3330
  };
3046
3331
  const interactiveGraphWidgetLogic = {
3047
3332
  name: "interactive-graph",
3048
- defaultWidgetOptions: defaultWidgetOptions$h,
3333
+ defaultWidgetOptions: defaultWidgetOptions$g,
3049
3334
  getPublicWidgetOptions: getInteractiveGraphPublicWidgetOptions
3050
3335
  };
3051
3336
 
@@ -3068,7 +3353,7 @@ function getLabelImageMarkerPublicData(marker) {
3068
3353
  return publicData;
3069
3354
  }
3070
3355
 
3071
- const defaultWidgetOptions$g = {
3356
+ const defaultWidgetOptions$f = {
3072
3357
  choices: [],
3073
3358
  imageAlt: "",
3074
3359
  imageUrl: "",
@@ -3080,7 +3365,7 @@ const defaultWidgetOptions$g = {
3080
3365
  };
3081
3366
  const labelImageWidgetLogic = {
3082
3367
  name: "label-image",
3083
- defaultWidgetOptions: defaultWidgetOptions$g,
3368
+ defaultWidgetOptions: defaultWidgetOptions$f,
3084
3369
  getPublicWidgetOptions: getLabelImagePublicWidgetOptions
3085
3370
  };
3086
3371
 
@@ -3142,7 +3427,7 @@ function getMatcherPublicWidgetOptions(options) {
3142
3427
  };
3143
3428
  }
3144
3429
 
3145
- const defaultWidgetOptions$f = {
3430
+ const defaultWidgetOptions$e = {
3146
3431
  left: ["$x$", "$y$", "$z$"],
3147
3432
  right: ["$1$", "$2$", "$3$"],
3148
3433
  labels: ["test", "label"],
@@ -3151,7 +3436,7 @@ const defaultWidgetOptions$f = {
3151
3436
  };
3152
3437
  const matcherWidgetLogic = {
3153
3438
  name: "matcher",
3154
- defaultWidgetOptions: defaultWidgetOptions$f,
3439
+ defaultWidgetOptions: defaultWidgetOptions$e,
3155
3440
  getPublicWidgetOptions: getMatcherPublicWidgetOptions
3156
3441
  };
3157
3442
 
@@ -3163,7 +3448,7 @@ function getMatrixPublicWidgetOptions(options) {
3163
3448
  return publicOptions;
3164
3449
  }
3165
3450
 
3166
- const defaultWidgetOptions$e = {
3451
+ const defaultWidgetOptions$d = {
3167
3452
  matrixBoardSize: [3, 3],
3168
3453
  answers: [[]],
3169
3454
  prefix: "",
@@ -3172,15 +3457,15 @@ const defaultWidgetOptions$e = {
3172
3457
  };
3173
3458
  const matrixWidgetLogic = {
3174
3459
  name: "matrix",
3175
- defaultWidgetOptions: defaultWidgetOptions$e,
3460
+ defaultWidgetOptions: defaultWidgetOptions$d,
3176
3461
  getPublicWidgetOptions: getMatrixPublicWidgetOptions
3177
3462
  };
3178
3463
 
3179
- const currentVersion$2 = {
3464
+ const currentVersion$1 = {
3180
3465
  major: 1,
3181
3466
  minor: 0
3182
3467
  };
3183
- const widgetOptionsUpgrades$1 = {
3468
+ const widgetOptionsUpgrades = {
3184
3469
  "1": v0options => {
3185
3470
  const {
3186
3471
  imageUrl,
@@ -3198,7 +3483,7 @@ const widgetOptionsUpgrades$1 = {
3198
3483
  };
3199
3484
  }
3200
3485
  };
3201
- const defaultWidgetOptions$d = {
3486
+ const defaultWidgetOptions$c = {
3202
3487
  box: [480, 480],
3203
3488
  image: {},
3204
3489
  showProtractor: true,
@@ -3211,9 +3496,9 @@ const defaultWidgetOptions$d = {
3211
3496
 
3212
3497
  const measurerWidgetLogic = {
3213
3498
  name: "measurer",
3214
- version: currentVersion$2,
3215
- widgetOptionsUpgrades: widgetOptionsUpgrades$1,
3216
- defaultWidgetOptions: defaultWidgetOptions$d
3499
+ version: currentVersion$1,
3500
+ widgetOptionsUpgrades: widgetOptionsUpgrades,
3501
+ defaultWidgetOptions: defaultWidgetOptions$c
3217
3502
  };
3218
3503
 
3219
3504
  function getNumberLinePublicWidgetOptions(options) {
@@ -3225,7 +3510,7 @@ function getNumberLinePublicWidgetOptions(options) {
3225
3510
  return publicOptions;
3226
3511
  }
3227
3512
 
3228
- const defaultWidgetOptions$c = {
3513
+ const defaultWidgetOptions$b = {
3229
3514
  range: [0, 10],
3230
3515
  labelRange: [null, null],
3231
3516
  labelStyle: "decimal",
@@ -3241,7 +3526,7 @@ const defaultWidgetOptions$c = {
3241
3526
  };
3242
3527
  const numberLineWidgetLogic = {
3243
3528
  name: "number-line",
3244
- defaultWidgetOptions: defaultWidgetOptions$c,
3529
+ defaultWidgetOptions: defaultWidgetOptions$b,
3245
3530
  getPublicWidgetOptions: getNumberLinePublicWidgetOptions
3246
3531
  };
3247
3532
 
@@ -3282,7 +3567,7 @@ function getNumericInputPublicWidgetOptions(options) {
3282
3567
  };
3283
3568
  }
3284
3569
 
3285
- const defaultWidgetOptions$b = {
3570
+ const defaultWidgetOptions$a = {
3286
3571
  answers: [{
3287
3572
  value: null,
3288
3573
  status: "correct",
@@ -3299,7 +3584,7 @@ const defaultWidgetOptions$b = {
3299
3584
  };
3300
3585
  const numericInputWidgetLogic = {
3301
3586
  name: "numeric-input",
3302
- defaultWidgetOptions: defaultWidgetOptions$b,
3587
+ defaultWidgetOptions: defaultWidgetOptions$a,
3303
3588
  defaultAlignment: "inline-block",
3304
3589
  getPublicWidgetOptions: getNumericInputPublicWidgetOptions
3305
3590
  };
@@ -3321,7 +3606,7 @@ function getOrdererPublicWidgetOptions(options) {
3321
3606
  };
3322
3607
  }
3323
3608
 
3324
- const defaultWidgetOptions$a = {
3609
+ const defaultWidgetOptions$9 = {
3325
3610
  correctOptions: [{
3326
3611
  content: "$x$"
3327
3612
  }],
@@ -3333,11 +3618,11 @@ const defaultWidgetOptions$a = {
3333
3618
  };
3334
3619
  const ordererWidgetLogic = {
3335
3620
  name: "orderer",
3336
- defaultWidgetOptions: defaultWidgetOptions$a,
3621
+ defaultWidgetOptions: defaultWidgetOptions$9,
3337
3622
  getPublicWidgetOptions: getOrdererPublicWidgetOptions
3338
3623
  };
3339
3624
 
3340
- const defaultWidgetOptions$9 = {
3625
+ const defaultWidgetOptions$8 = {
3341
3626
  passageTitle: "",
3342
3627
  passageText: "",
3343
3628
  footnotes: "",
@@ -3345,14 +3630,14 @@ const defaultWidgetOptions$9 = {
3345
3630
  };
3346
3631
  const passageWidgetLogic = {
3347
3632
  name: "passage",
3348
- defaultWidgetOptions: defaultWidgetOptions$9
3633
+ defaultWidgetOptions: defaultWidgetOptions$8
3349
3634
  };
3350
3635
 
3351
- const currentVersion$1 = {
3636
+ const currentVersion = {
3352
3637
  major: 0,
3353
3638
  minor: 1
3354
3639
  };
3355
- const defaultWidgetOptions$8 = {
3640
+ const defaultWidgetOptions$7 = {
3356
3641
  passageNumber: 1,
3357
3642
  referenceNumber: 1,
3358
3643
  summaryText: ""
@@ -3360,27 +3645,27 @@ const defaultWidgetOptions$8 = {
3360
3645
 
3361
3646
  const passageRefWidgetLogic = {
3362
3647
  name: "passageRef",
3363
- version: currentVersion$1,
3364
- defaultWidgetOptions: defaultWidgetOptions$8,
3648
+ version: currentVersion,
3649
+ defaultWidgetOptions: defaultWidgetOptions$7,
3365
3650
  defaultAlignment: "inline"
3366
3651
  };
3367
3652
 
3368
- const defaultWidgetOptions$7 = {
3653
+ const defaultWidgetOptions$6 = {
3369
3654
  content: ""
3370
3655
  };
3371
3656
  const passageRefTargetWidgetLogic = {
3372
3657
  name: "passageRefTarget",
3373
- defaultWidgetOptions: defaultWidgetOptions$7,
3658
+ defaultWidgetOptions: defaultWidgetOptions$6,
3374
3659
  defaultAlignment: "inline"
3375
3660
  };
3376
3661
 
3377
- const defaultWidgetOptions$6 = {
3662
+ const defaultWidgetOptions$5 = {
3378
3663
  url: "",
3379
3664
  description: ""
3380
3665
  };
3381
3666
  const phetSimulationWidgetLogic = {
3382
3667
  name: "phet-simulation",
3383
- defaultWidgetOptions: defaultWidgetOptions$6
3668
+ defaultWidgetOptions: defaultWidgetOptions$5
3384
3669
  };
3385
3670
 
3386
3671
  /**
@@ -3400,7 +3685,7 @@ function getPlotterPublicWidgetOptions(options) {
3400
3685
  return publicOptions;
3401
3686
  }
3402
3687
 
3403
- const defaultWidgetOptions$5 = {
3688
+ const defaultWidgetOptions$4 = {
3404
3689
  scaleY: 1,
3405
3690
  maxY: 10,
3406
3691
  snapsPerLine: 2,
@@ -3417,46 +3702,17 @@ const defaultWidgetOptions$5 = {
3417
3702
  };
3418
3703
  const plotterWidgetLogic = {
3419
3704
  name: "plotter",
3420
- defaultWidgetOptions: defaultWidgetOptions$5,
3705
+ defaultWidgetOptions: defaultWidgetOptions$4,
3421
3706
  getPublicWidgetOptions: getPlotterPublicWidgetOptions
3422
3707
  };
3423
3708
 
3424
- const defaultWidgetOptions$4 = {
3709
+ const defaultWidgetOptions$3 = {
3425
3710
  programID: "",
3426
3711
  height: 400
3427
3712
  };
3428
3713
  const pythonProgramWidgetLogic = {
3429
3714
  name: "python-program",
3430
- defaultWidgetOptions: defaultWidgetOptions$4
3431
- };
3432
-
3433
- const currentVersion = {
3434
- major: 1,
3435
- minor: 0
3436
- };
3437
- const widgetOptionsUpgrades = {
3438
- "1": v0props => {
3439
- const {
3440
- noneOfTheAbove,
3441
- ...rest
3442
- } = v0props;
3443
- if (noneOfTheAbove) {
3444
- throw new Error("radio widget v0 no longer supports auto noneOfTheAbove");
3445
- }
3446
- return {
3447
- ...rest,
3448
- hasNoneOfTheAbove: false
3449
- };
3450
- }
3451
- };
3452
- const defaultWidgetOptions$3 = {
3453
- choices: [{}, {}, {}, {}],
3454
- displayCount: null,
3455
- randomize: false,
3456
- hasNoneOfTheAbove: false,
3457
- multipleSelect: false,
3458
- countChoices: false,
3459
- deselectEnabled: false
3715
+ defaultWidgetOptions: defaultWidgetOptions$3
3460
3716
  };
3461
3717
 
3462
3718
  /**
@@ -3485,22 +3741,41 @@ function getRadioChoicePublicData(choice) {
3485
3741
  };
3486
3742
  }
3487
3743
 
3744
+ /**
3745
+ * Shared functionality to determine if numCorrect is used, because:
3746
+ *
3747
+ * 1. numCorrect is conditionally used for rendering pre-scoring
3748
+ * 2. numCorrect also exposes information about answers
3749
+ *
3750
+ * So only include/use numCorrect when we know it's useful.
3751
+ */
3752
+ function usesNumCorrect(multipleSelect, countChoices, numCorrect) {
3753
+ return multipleSelect && countChoices && numCorrect;
3754
+ }
3755
+
3488
3756
  /**
3489
3757
  * Given a PerseusRadioWidgetOptions object, return a new object with only
3490
3758
  * the public options that should be exposed to the client.
3491
3759
  */
3492
3760
  function getRadioPublicWidgetOptions(options) {
3761
+ const {
3762
+ numCorrect,
3763
+ choices,
3764
+ multipleSelect,
3765
+ countChoices
3766
+ } = options;
3493
3767
  return {
3494
3768
  ...options,
3495
- choices: options.choices.map(getRadioChoicePublicData)
3769
+ numCorrect: usesNumCorrect(multipleSelect, countChoices, numCorrect) ? numCorrect : undefined,
3770
+ choices: choices.map(getRadioChoicePublicData)
3496
3771
  };
3497
3772
  }
3498
3773
 
3499
3774
  const radioWidgetLogic = {
3500
3775
  name: "radio",
3501
- version: currentVersion,
3502
- widgetOptionsUpgrades: widgetOptionsUpgrades,
3503
- defaultWidgetOptions: defaultWidgetOptions$3,
3776
+ version: currentVersion$3,
3777
+ widgetOptionsUpgrades: widgetOptionsUpgrades$2,
3778
+ defaultWidgetOptions: defaultWidgetOptions$v,
3504
3779
  getPublicWidgetOptions: getRadioPublicWidgetOptions
3505
3780
  };
3506
3781
 
@@ -3898,6 +4173,8 @@ exports.categorizerLogic = categorizerWidgetLogic;
3898
4173
  exports.csProgramLogic = csProgramWidgetLogic;
3899
4174
  exports.deepClone = deepClone;
3900
4175
  exports.definitionLogic = definitionWidgetLogic;
4176
+ exports.deriveExtraKeys = deriveExtraKeys;
4177
+ exports.deriveNumCorrect = deriveNumCorrect;
3901
4178
  exports.dropdownLogic = dropdownWidgetLogic;
3902
4179
  exports.explanationLogic = explanationWidgetLogic;
3903
4180
  exports.expressionLogic = expressionWidgetLogic;
@@ -3966,5 +4243,6 @@ exports.sorterLogic = sorterWidgetLogic;
3966
4243
  exports.splitPerseusItem = splitPerseusItem;
3967
4244
  exports.tableLogic = tableWidgetLogic;
3968
4245
  exports.upgradeWidgetInfoToLatestVersion = upgradeWidgetInfoToLatestVersion;
4246
+ exports.usesNumCorrect = usesNumCorrect;
3969
4247
  exports.videoLogic = videoWidgetLogic;
3970
4248
  //# sourceMappingURL=index.js.map