@khanacademy/perseus-core 5.4.0 → 5.4.2
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/data-schema.d.ts +11 -3
- package/dist/es/index.js +92 -115
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +94 -116
- package/dist/index.js.map +1 -1
- package/dist/parse-perseus-json/perseus-parsers/numeric-input-widget.d.ts +2 -1
- package/dist/parse-perseus-json/perseus-parsers/perseus-answer-area.d.ts +1 -1
- package/dist/utils/split-perseus-item.d.ts +3 -2
- package/dist/widgets/categorizer/categorizer-util.d.ts +1 -1
- package/dist/widgets/dropdown/dropdown-util.d.ts +1 -1
- package/dist/widgets/expression/expression-util.d.ts +1 -1
- package/dist/widgets/interactive-graph/interactive-graph-util.d.ts +1 -1
- package/dist/widgets/label-image/label-image-util.d.ts +1 -1
- package/dist/widgets/matcher/matcher-util.d.ts +1 -1
- package/dist/widgets/matrix/matrix-util.d.ts +1 -1
- package/dist/widgets/numeric-input/numeric-input-util.d.ts +1 -1
- package/dist/widgets/orderer/orderer-util.d.ts +1 -1
- package/dist/widgets/plotter/plotter-util.d.ts +1 -1
- package/dist/widgets/radio/radio-util.d.ts +1 -1
- package/dist/widgets/sorter/sorter-util.d.ts +1 -1
- package/dist/widgets/table/table-util.d.ts +1 -1
- package/package.json +5 -3
- package/dist/shared-utils/add-library-version-to-perseus-debug.d.ts +0 -9
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var _ = require('underscore');
|
|
6
6
|
var KAS = require('@khanacademy/kas');
|
|
7
|
-
var
|
|
7
|
+
var perseusUtils = require('@khanacademy/perseus-utils');
|
|
8
8
|
|
|
9
9
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
10
10
|
|
|
@@ -1993,6 +1993,15 @@ const lockedFigureFillStyles = {
|
|
|
1993
1993
|
|
|
1994
1994
|
// Not associated with a specific figure
|
|
1995
1995
|
|
|
1996
|
+
/**
|
|
1997
|
+
* Determines how unsimplified fractions are scored.
|
|
1998
|
+
*
|
|
1999
|
+
* - "required" means unsimplified fractions are considered invalid input, and
|
|
2000
|
+
* the learner can try again.
|
|
2001
|
+
* - "enforced" means unsimplified fractions are marked incorrect.
|
|
2002
|
+
* - "optional" means unsimplified fractions are accepted.
|
|
2003
|
+
*/
|
|
2004
|
+
|
|
1996
2005
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
1997
2006
|
|
|
1998
2007
|
// Used to represent 2-D points and ranges
|
|
@@ -2277,7 +2286,21 @@ const parseNumberLineWidget = parseWidget(constant("number-line"), object({
|
|
|
2277
2286
|
}));
|
|
2278
2287
|
|
|
2279
2288
|
const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "decimal", "percent", "pi");
|
|
2280
|
-
const parseSimplify =
|
|
2289
|
+
const parseSimplify = pipeParsers(union(constant(null)).or(constant(undefined)).or(boolean).or(constant("required")).or(constant("correct")).or(constant("enforced")).or(constant("optional")).or(constant("accepted")).parser).then(convert(deprecatedSimplifyValuesToRequired)).parser;
|
|
2290
|
+
function deprecatedSimplifyValuesToRequired(simplify) {
|
|
2291
|
+
switch (simplify) {
|
|
2292
|
+
case "enforced":
|
|
2293
|
+
case "required":
|
|
2294
|
+
case "optional":
|
|
2295
|
+
return simplify;
|
|
2296
|
+
// NOTE(benchristel): "accepted", "correct", true, false, undefined, and
|
|
2297
|
+
// null are all treated the same as "required" during scoring, so we
|
|
2298
|
+
// convert them to "required" here to preserve behavior. See the tests
|
|
2299
|
+
// in score-numeric-input.test.ts
|
|
2300
|
+
default:
|
|
2301
|
+
return "required";
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2281
2304
|
const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
2282
2305
|
answers: array(object({
|
|
2283
2306
|
message: string,
|
|
@@ -2292,12 +2315,7 @@ const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
|
2292
2315
|
// TODO(benchristel): simplify should never be a boolean, but we
|
|
2293
2316
|
// have some content where it is anyway. If we ever backfill
|
|
2294
2317
|
// the data, we should simplify `simplify`.
|
|
2295
|
-
simplify:
|
|
2296
|
-
if (typeof value === "boolean") {
|
|
2297
|
-
return value ? "required" : "optional";
|
|
2298
|
-
}
|
|
2299
|
-
return value;
|
|
2300
|
-
})).parser).parser))
|
|
2318
|
+
simplify: parseSimplify
|
|
2301
2319
|
})),
|
|
2302
2320
|
labelText: optional(string),
|
|
2303
2321
|
size: string,
|
|
@@ -2306,7 +2324,7 @@ const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
|
2306
2324
|
static: defaulted(boolean, () => false),
|
|
2307
2325
|
answerForms: optional(array(object({
|
|
2308
2326
|
name: parseMathFormat,
|
|
2309
|
-
simplify:
|
|
2327
|
+
simplify: parseSimplify
|
|
2310
2328
|
})))
|
|
2311
2329
|
}));
|
|
2312
2330
|
|
|
@@ -2762,10 +2780,10 @@ const parsePerseusItem$1 = object({
|
|
|
2762
2780
|
question: parsePerseusRenderer,
|
|
2763
2781
|
hints: defaulted(array(parseHint), () => []),
|
|
2764
2782
|
answerArea: parsePerseusAnswerArea,
|
|
2765
|
-
itemDataVersion: optional(object({
|
|
2783
|
+
itemDataVersion: optional(nullable(object({
|
|
2766
2784
|
major: number,
|
|
2767
2785
|
minor: number
|
|
2768
|
-
})),
|
|
2786
|
+
}))),
|
|
2769
2787
|
// Deprecated field
|
|
2770
2788
|
answer: any
|
|
2771
2789
|
});
|
|
@@ -2842,51 +2860,10 @@ function throwErrorIfCheatingDetected() {
|
|
|
2842
2860
|
}
|
|
2843
2861
|
}
|
|
2844
2862
|
|
|
2845
|
-
/**
|
|
2846
|
-
* Adds the given perseus library version information to the __perseus_debug__
|
|
2847
|
-
* object and ensures that the object is attached to `globalThis` (`window` in
|
|
2848
|
-
* browser environments).
|
|
2849
|
-
*
|
|
2850
|
-
* This allows each library to provide runtime version information to assist in
|
|
2851
|
-
* debugging in production environments.
|
|
2852
|
-
*/
|
|
2853
|
-
const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
|
|
2854
|
-
// If the library version is the default value, then we don't want to
|
|
2855
|
-
// prefix it with a "v" to indicate that it is a version number.
|
|
2856
|
-
let prefix = "v";
|
|
2857
|
-
if (libraryVersion === "__lib_version__") {
|
|
2858
|
-
prefix = "";
|
|
2859
|
-
}
|
|
2860
|
-
const formattedVersion = `${prefix}${libraryVersion}`;
|
|
2861
|
-
if (typeof globalThis !== "undefined") {
|
|
2862
|
-
globalThis.__perseus_debug__ = globalThis.__perseus_debug__ ?? {};
|
|
2863
|
-
const existingVersionEntry = globalThis.__perseus_debug__[libraryName];
|
|
2864
|
-
if (existingVersionEntry) {
|
|
2865
|
-
// If we already have an entry and it doesn't match the registered
|
|
2866
|
-
// version, we morph the entry into an array and log a warning.
|
|
2867
|
-
if (existingVersionEntry !== formattedVersion) {
|
|
2868
|
-
// Existing entry might be an array already (oops, at least 2
|
|
2869
|
-
// versions of the library already loaded!).
|
|
2870
|
-
const allVersions = Array.isArray(existingVersionEntry) ? existingVersionEntry : [existingVersionEntry];
|
|
2871
|
-
allVersions.push(formattedVersion);
|
|
2872
|
-
globalThis.__perseus_debug__[libraryName] = allVersions;
|
|
2873
|
-
|
|
2874
|
-
// eslint-disable-next-line no-console
|
|
2875
|
-
console.warn(`Multiple versions of ${libraryName} loaded on this page: ${allVersions.sort().join(", ")}`);
|
|
2876
|
-
}
|
|
2877
|
-
} else {
|
|
2878
|
-
globalThis.__perseus_debug__[libraryName] = formattedVersion;
|
|
2879
|
-
}
|
|
2880
|
-
} else {
|
|
2881
|
-
// eslint-disable-next-line no-console
|
|
2882
|
-
console.warn(`globalThis not found found (${formattedVersion})`);
|
|
2883
|
-
}
|
|
2884
|
-
};
|
|
2885
|
-
|
|
2886
2863
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
2887
2864
|
const libName = "@khanacademy/perseus-core";
|
|
2888
|
-
const libVersion = "5.4.
|
|
2889
|
-
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
2865
|
+
const libVersion = "5.4.2";
|
|
2866
|
+
perseusUtils.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
2890
2867
|
|
|
2891
2868
|
/**
|
|
2892
2869
|
* @typedef {Object} Errors utility for referencing the Perseus error taxonomy.
|
|
@@ -3369,20 +3346,74 @@ const labelImageWidgetLogic = {
|
|
|
3369
3346
|
getPublicWidgetOptions: getLabelImagePublicWidgetOptions
|
|
3370
3347
|
};
|
|
3371
3348
|
|
|
3349
|
+
/* Note(tamara): Brought over from the perseus package packages/perseus/src/util.ts file.
|
|
3350
|
+
May be useful to bring other perseus package utilities here. Contains utility functions
|
|
3351
|
+
and types used across multiple widgets for randomization and shuffling. */
|
|
3352
|
+
const seededRNG = function (seed) {
|
|
3353
|
+
let randomSeed = seed;
|
|
3354
|
+
return function () {
|
|
3355
|
+
// Robert Jenkins' 32 bit integer hash function.
|
|
3356
|
+
let seed = randomSeed;
|
|
3357
|
+
seed = seed + 0x7ed55d16 + (seed << 12) & 0xffffffff;
|
|
3358
|
+
seed = (seed ^ 0xc761c23c ^ seed >>> 19) & 0xffffffff;
|
|
3359
|
+
seed = seed + 0x165667b1 + (seed << 5) & 0xffffffff;
|
|
3360
|
+
seed = (seed + 0xd3a2646c ^ seed << 9) & 0xffffffff;
|
|
3361
|
+
seed = seed + 0xfd7046c5 + (seed << 3) & 0xffffffff;
|
|
3362
|
+
seed = (seed ^ 0xb55a4f09 ^ seed >>> 16) & 0xffffffff;
|
|
3363
|
+
return (randomSeed = seed & 0xfffffff) / 0x10000000;
|
|
3364
|
+
};
|
|
3365
|
+
};
|
|
3366
|
+
|
|
3367
|
+
// Shuffle an array using a given random seed or function.
|
|
3368
|
+
// If `ensurePermuted` is true, the input and output are guaranteed to be
|
|
3369
|
+
// distinct permutations.
|
|
3370
|
+
function shuffle(array, randomSeed) {
|
|
3371
|
+
let ensurePermuted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
3372
|
+
// Always return a copy of the input array
|
|
3373
|
+
const shuffled = ___default["default"].clone(array);
|
|
3374
|
+
|
|
3375
|
+
// Handle edge cases (input array is empty or uniform)
|
|
3376
|
+
if (!shuffled.length || ___default["default"].all(shuffled, function (value) {
|
|
3377
|
+
return ___default["default"].isEqual(value, shuffled[0]);
|
|
3378
|
+
})) {
|
|
3379
|
+
return shuffled;
|
|
3380
|
+
}
|
|
3381
|
+
let random;
|
|
3382
|
+
if (typeof randomSeed === "function") {
|
|
3383
|
+
random = randomSeed;
|
|
3384
|
+
} else {
|
|
3385
|
+
random = seededRNG(randomSeed);
|
|
3386
|
+
}
|
|
3387
|
+
do {
|
|
3388
|
+
// Fischer-Yates shuffle
|
|
3389
|
+
for (let top = shuffled.length; top > 0; top--) {
|
|
3390
|
+
const newEnd = Math.floor(random() * top);
|
|
3391
|
+
const temp = shuffled[newEnd];
|
|
3392
|
+
|
|
3393
|
+
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
3394
|
+
shuffled[newEnd] = shuffled[top - 1];
|
|
3395
|
+
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
3396
|
+
shuffled[top - 1] = temp;
|
|
3397
|
+
}
|
|
3398
|
+
} while (ensurePermuted && ___default["default"].isEqual(array, shuffled));
|
|
3399
|
+
return shuffled;
|
|
3400
|
+
}
|
|
3401
|
+
const random = seededRNG(new Date().getTime() & 0xffffffff);
|
|
3402
|
+
|
|
3372
3403
|
// TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
|
|
3373
3404
|
|
|
3374
3405
|
// TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
|
|
3375
3406
|
const shuffleMatcher = props => {
|
|
3376
3407
|
// Use the same random() function to shuffle both columns sequentially
|
|
3377
|
-
const rng =
|
|
3408
|
+
const rng = seededRNG(props.problemNum);
|
|
3378
3409
|
let left;
|
|
3379
3410
|
if (!props.orderMatters) {
|
|
3380
3411
|
// If the order doesn't matter, don't shuffle the left column
|
|
3381
3412
|
left = props.left;
|
|
3382
3413
|
} else {
|
|
3383
|
-
left =
|
|
3414
|
+
left = shuffle(props.left, rng, /* ensurePermuted */true);
|
|
3384
3415
|
}
|
|
3385
|
-
const right =
|
|
3416
|
+
const right = shuffle(props.right, rng, /* ensurePermuted */true);
|
|
3386
3417
|
return {
|
|
3387
3418
|
left,
|
|
3388
3419
|
right
|
|
@@ -3397,9 +3428,9 @@ function shuffleMatcherWithRandom(data) {
|
|
|
3397
3428
|
// If the order doesn't matter, don't shuffle the left column
|
|
3398
3429
|
left = data.left;
|
|
3399
3430
|
} else {
|
|
3400
|
-
left =
|
|
3431
|
+
left = shuffle(data.left, Math.random, /* ensurePermuted */true);
|
|
3401
3432
|
}
|
|
3402
|
-
const right =
|
|
3433
|
+
const right = shuffle(data.right, Math.random, /* ensurePermuted */true);
|
|
3403
3434
|
return {
|
|
3404
3435
|
left,
|
|
3405
3436
|
right
|
|
@@ -3789,7 +3820,7 @@ const radioWidgetLogic = {
|
|
|
3789
3820
|
* the public options that should be exposed to the client.
|
|
3790
3821
|
*/
|
|
3791
3822
|
function getSorterPublicWidgetOptions(options) {
|
|
3792
|
-
const shuffledCorrect =
|
|
3823
|
+
const shuffledCorrect = shuffle(options.correct, Math.random, /* ensurePermuted */true);
|
|
3793
3824
|
return {
|
|
3794
3825
|
...options,
|
|
3795
3826
|
// Note(Tamara): This does not provide correct answer information any longer.
|
|
@@ -4089,8 +4120,9 @@ function getUpgradedWidgetOptions(oldWidgetOptions) {
|
|
|
4089
4120
|
}
|
|
4090
4121
|
|
|
4091
4122
|
/**
|
|
4092
|
-
*
|
|
4093
|
-
*
|
|
4123
|
+
* Return a copy of a Perseus item with rubric data removed (ie answers)
|
|
4124
|
+
*
|
|
4125
|
+
* @param originalItem - the original, full Perseus item (which includes the rubric - aka answer data)
|
|
4094
4126
|
*/
|
|
4095
4127
|
function splitPerseusItem(originalItem) {
|
|
4096
4128
|
const item = ___default["default"].clone(originalItem);
|
|
@@ -4110,60 +4142,6 @@ function splitPerseusItem(originalItem) {
|
|
|
4110
4142
|
};
|
|
4111
4143
|
}
|
|
4112
4144
|
|
|
4113
|
-
/* Note(tamara): Brought over from the perseus package packages/perseus/src/util.ts file.
|
|
4114
|
-
May be useful to bring other perseus package utilities here. Contains utility functions
|
|
4115
|
-
and types used across multiple widgets for randomization and shuffling. */
|
|
4116
|
-
const seededRNG = function (seed) {
|
|
4117
|
-
let randomSeed = seed;
|
|
4118
|
-
return function () {
|
|
4119
|
-
// Robert Jenkins' 32 bit integer hash function.
|
|
4120
|
-
let seed = randomSeed;
|
|
4121
|
-
seed = seed + 0x7ed55d16 + (seed << 12) & 0xffffffff;
|
|
4122
|
-
seed = (seed ^ 0xc761c23c ^ seed >>> 19) & 0xffffffff;
|
|
4123
|
-
seed = seed + 0x165667b1 + (seed << 5) & 0xffffffff;
|
|
4124
|
-
seed = (seed + 0xd3a2646c ^ seed << 9) & 0xffffffff;
|
|
4125
|
-
seed = seed + 0xfd7046c5 + (seed << 3) & 0xffffffff;
|
|
4126
|
-
seed = (seed ^ 0xb55a4f09 ^ seed >>> 16) & 0xffffffff;
|
|
4127
|
-
return (randomSeed = seed & 0xfffffff) / 0x10000000;
|
|
4128
|
-
};
|
|
4129
|
-
};
|
|
4130
|
-
|
|
4131
|
-
// Shuffle an array using a given random seed or function.
|
|
4132
|
-
// If `ensurePermuted` is true, the input and output are guaranteed to be
|
|
4133
|
-
// distinct permutations.
|
|
4134
|
-
function shuffle(array, randomSeed) {
|
|
4135
|
-
let ensurePermuted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
4136
|
-
// Always return a copy of the input array
|
|
4137
|
-
const shuffled = ___default["default"].clone(array);
|
|
4138
|
-
|
|
4139
|
-
// Handle edge cases (input array is empty or uniform)
|
|
4140
|
-
if (!shuffled.length || ___default["default"].all(shuffled, function (value) {
|
|
4141
|
-
return ___default["default"].isEqual(value, shuffled[0]);
|
|
4142
|
-
})) {
|
|
4143
|
-
return shuffled;
|
|
4144
|
-
}
|
|
4145
|
-
let random;
|
|
4146
|
-
if (typeof randomSeed === "function") {
|
|
4147
|
-
random = randomSeed;
|
|
4148
|
-
} else {
|
|
4149
|
-
random = seededRNG(randomSeed);
|
|
4150
|
-
}
|
|
4151
|
-
do {
|
|
4152
|
-
// Fischer-Yates shuffle
|
|
4153
|
-
for (let top = shuffled.length; top > 0; top--) {
|
|
4154
|
-
const newEnd = Math.floor(random() * top);
|
|
4155
|
-
const temp = shuffled[newEnd];
|
|
4156
|
-
|
|
4157
|
-
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
4158
|
-
shuffled[newEnd] = shuffled[top - 1];
|
|
4159
|
-
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
4160
|
-
shuffled[top - 1] = temp;
|
|
4161
|
-
}
|
|
4162
|
-
} while (ensurePermuted && ___default["default"].isEqual(array, shuffled));
|
|
4163
|
-
return shuffled;
|
|
4164
|
-
}
|
|
4165
|
-
const random = seededRNG(new Date().getTime() & 0xffffffff);
|
|
4166
|
-
|
|
4167
4145
|
exports.CoreWidgetRegistry = coreWidgetRegistry;
|
|
4168
4146
|
exports.Errors = Errors;
|
|
4169
4147
|
exports.GrapherUtil = grapherUtil;
|