@khanacademy/perseus-core 3.6.0 → 3.7.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/data-schema.d.ts +10 -3
- package/dist/es/index.js +195 -65
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +190 -55
- package/dist/index.js.map +1 -1
- package/dist/parse-perseus-json/perseus-parsers/perseus-answer-area.d.ts +2 -0
- package/dist/utils/random-util.d.ts +5 -0
- package/dist/widgets/group/index.d.ts +1 -1
- package/dist/widgets/logic-export.types.d.ts +2 -1
- package/dist/widgets/matcher/matcher-util.d.ts +28 -0
- package/dist/widgets/sorter/sorter-util.d.ts +1 -0
- package/package.json +1 -1
package/dist/data-schema.d.ts
CHANGED
|
@@ -226,7 +226,14 @@ export type PerseusRenderer = {
|
|
|
226
226
|
* field.
|
|
227
227
|
*/
|
|
228
228
|
widgets: PerseusWidgetsMap;
|
|
229
|
-
|
|
229
|
+
/**
|
|
230
|
+
* Formerly used in the PerseusGradedGroup widget. A list of "tags" that
|
|
231
|
+
* are keys that represent other content in the system. Not rendered to
|
|
232
|
+
* the user. NOTE: perseus_data.go says this is required even though it
|
|
233
|
+
* isn't necessary.
|
|
234
|
+
* @deprecated
|
|
235
|
+
*/
|
|
236
|
+
metadata?: any;
|
|
230
237
|
/**
|
|
231
238
|
* A dictionary of {[imageUrl]: PerseusImageDetail}.
|
|
232
239
|
*/
|
|
@@ -258,7 +265,7 @@ export type WidgetOptions<Type extends string, Options> = {
|
|
|
258
265
|
graded?: boolean;
|
|
259
266
|
alignment?: string;
|
|
260
267
|
options: Options;
|
|
261
|
-
key?: number;
|
|
268
|
+
key?: number | null;
|
|
262
269
|
version?: Version;
|
|
263
270
|
};
|
|
264
271
|
export type CategorizerWidget = WidgetOptions<'categorizer', PerseusCategorizerWidgetOptions>;
|
|
@@ -304,7 +311,7 @@ export type PerseusImageBackground = {
|
|
|
304
311
|
height?: number;
|
|
305
312
|
top?: number;
|
|
306
313
|
left?: number;
|
|
307
|
-
scale?: number
|
|
314
|
+
scale?: number;
|
|
308
315
|
bottom?: number;
|
|
309
316
|
};
|
|
310
317
|
/**
|
package/dist/es/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { seededRNG as seededRNG$1, shuffle as shuffle$2 } from '@khanacademy/perseus-core';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Adds the given perseus library version information to the __perseus_debug__
|
|
3
5
|
* object and ensures that the object is attached to `globalThis` (`window` in
|
|
@@ -818,7 +820,7 @@ function times(n, iteratee, context) {
|
|
|
818
820
|
}
|
|
819
821
|
|
|
820
822
|
// Return a random integer between `min` and `max` (inclusive).
|
|
821
|
-
function random(min, max) {
|
|
823
|
+
function random$1(min, max) {
|
|
822
824
|
if (max == null) {
|
|
823
825
|
max = min;
|
|
824
826
|
min = 0;
|
|
@@ -1561,14 +1563,14 @@ function toArray(obj) {
|
|
|
1561
1563
|
function sample(obj, n, guard) {
|
|
1562
1564
|
if (n == null || guard) {
|
|
1563
1565
|
if (!isArrayLike(obj)) obj = values(obj);
|
|
1564
|
-
return obj[random(obj.length - 1)];
|
|
1566
|
+
return obj[random$1(obj.length - 1)];
|
|
1565
1567
|
}
|
|
1566
1568
|
var sample = toArray(obj);
|
|
1567
1569
|
var length = getLength(sample);
|
|
1568
1570
|
n = Math.max(Math.min(n, length), 0);
|
|
1569
1571
|
var last = length - 1;
|
|
1570
1572
|
for (var index = 0; index < n; index++) {
|
|
1571
|
-
var rand = random(index, last);
|
|
1573
|
+
var rand = random$1(index, last);
|
|
1572
1574
|
var temp = sample[index];
|
|
1573
1575
|
sample[index] = sample[rand];
|
|
1574
1576
|
sample[rand] = temp;
|
|
@@ -1577,7 +1579,7 @@ function sample(obj, n, guard) {
|
|
|
1577
1579
|
}
|
|
1578
1580
|
|
|
1579
1581
|
// Shuffle a collection.
|
|
1580
|
-
function shuffle(obj) {
|
|
1582
|
+
function shuffle$1(obj) {
|
|
1581
1583
|
return sample(obj, Infinity);
|
|
1582
1584
|
}
|
|
1583
1585
|
|
|
@@ -1963,7 +1965,7 @@ var allExports = /*#__PURE__*/Object.freeze({
|
|
|
1963
1965
|
matcher: matcher,
|
|
1964
1966
|
matches: matcher,
|
|
1965
1967
|
times: times,
|
|
1966
|
-
random: random,
|
|
1968
|
+
random: random$1,
|
|
1967
1969
|
now: now,
|
|
1968
1970
|
escape: escape,
|
|
1969
1971
|
unescape: unescape,
|
|
@@ -2020,7 +2022,7 @@ var allExports = /*#__PURE__*/Object.freeze({
|
|
|
2020
2022
|
where: where,
|
|
2021
2023
|
max: max,
|
|
2022
2024
|
min: min,
|
|
2023
|
-
shuffle: shuffle,
|
|
2025
|
+
shuffle: shuffle$1,
|
|
2024
2026
|
sample: sample,
|
|
2025
2027
|
sortBy: sortBy,
|
|
2026
2028
|
groupBy: groupBy,
|
|
@@ -3076,7 +3078,7 @@ function parseWidget(parseType, parseOptions) {
|
|
|
3076
3078
|
graded: optional(boolean),
|
|
3077
3079
|
alignment: optional(string),
|
|
3078
3080
|
options: parseOptions,
|
|
3079
|
-
key: optional(number),
|
|
3081
|
+
key: optional(nullable(number)),
|
|
3080
3082
|
version: optional(object({
|
|
3081
3083
|
major: number,
|
|
3082
3084
|
minor: number
|
|
@@ -3462,14 +3464,15 @@ const imageDimensionToNumber = pipeParsers(union(number).or(string).parser)
|
|
|
3462
3464
|
// string parses to either NaN (using parseInt) or 0 (using unary +) and
|
|
3463
3465
|
// CSS will treat NaN as invalid and default to 0 instead.
|
|
3464
3466
|
.then(convert(emptyToZero)).then(stringToNumber).parser;
|
|
3467
|
+
const dimensionOrUndefined = defaulted(imageDimensionToNumber, () => undefined);
|
|
3465
3468
|
const parsePerseusImageBackground = object({
|
|
3466
3469
|
url: optional(nullable(string)),
|
|
3467
|
-
width:
|
|
3468
|
-
height:
|
|
3469
|
-
top:
|
|
3470
|
-
left:
|
|
3471
|
-
bottom:
|
|
3472
|
-
scale:
|
|
3470
|
+
width: dimensionOrUndefined,
|
|
3471
|
+
height: dimensionOrUndefined,
|
|
3472
|
+
top: dimensionOrUndefined,
|
|
3473
|
+
left: dimensionOrUndefined,
|
|
3474
|
+
bottom: dimensionOrUndefined,
|
|
3475
|
+
scale: dimensionOrUndefined
|
|
3473
3476
|
});
|
|
3474
3477
|
|
|
3475
3478
|
const pairOfNumbers$2 = pair(number, number);
|
|
@@ -4210,7 +4213,7 @@ const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
|
4210
4213
|
// the data, simplify this.
|
|
4211
4214
|
value: optional(nullable(number)),
|
|
4212
4215
|
status: string,
|
|
4213
|
-
answerForms:
|
|
4216
|
+
answerForms: defaulted(array(parseMathFormat), () => undefined),
|
|
4214
4217
|
strict: boolean,
|
|
4215
4218
|
maxError: optional(nullable(number)),
|
|
4216
4219
|
// TODO(benchristel): simplify should never be a boolean, but we
|
|
@@ -4308,7 +4311,7 @@ const parseRadioWidget = parseWidget(constant("radio"), object({
|
|
|
4308
4311
|
// There is an import cycle between radio-widget.ts and
|
|
4309
4312
|
// widgets-map.ts. The anonymous function below ensures that we
|
|
4310
4313
|
// don't refer to parseWidgetsMap before it's defined.
|
|
4311
|
-
widgets:
|
|
4314
|
+
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => undefined)
|
|
4312
4315
|
})),
|
|
4313
4316
|
hasNoneOfTheAbove: optional(boolean),
|
|
4314
4317
|
countChoices: optional(boolean),
|
|
@@ -4461,13 +4464,17 @@ const parseDeprecatedWidget = parseWidget(
|
|
|
4461
4464
|
(_, ctx) => ctx.success("deprecated-standin"),
|
|
4462
4465
|
// Allow any widget options
|
|
4463
4466
|
object({}));
|
|
4464
|
-
const
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
+
const parseStringToNonNegativeInt = (rawValue, ctx) => {
|
|
4468
|
+
// The article renderer seems to allow the numeric part of a widget ID to
|
|
4469
|
+
// be 0, at least for image widgets. However, if widget IDs in an exercise
|
|
4470
|
+
// contain 0, the exercise renderer will blow up. We allow 0 here for
|
|
4471
|
+
// compatibility with articles.
|
|
4472
|
+
if (typeof rawValue !== "string" || !/^(0|[1-9][0-9]*)$/.test(rawValue)) {
|
|
4473
|
+
return ctx.failure("a string representing a non-negative integer", rawValue);
|
|
4467
4474
|
}
|
|
4468
4475
|
return ctx.success(+rawValue);
|
|
4469
4476
|
};
|
|
4470
|
-
const parseWidgetIdComponents = pair(string,
|
|
4477
|
+
const parseWidgetIdComponents = pair(string, parseStringToNonNegativeInt);
|
|
4471
4478
|
|
|
4472
4479
|
const parsePerseusRenderer = defaulted(object({
|
|
4473
4480
|
// TODO(benchristel): content is also defaulted to empty string in
|
|
@@ -4478,8 +4485,9 @@ const parsePerseusRenderer = defaulted(object({
|
|
|
4478
4485
|
// The anonymous function below ensures that we don't try to access
|
|
4479
4486
|
// parseWidgetsMap before it's defined.
|
|
4480
4487
|
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
|
|
4481
|
-
|
|
4482
|
-
|
|
4488
|
+
images: parseImages,
|
|
4489
|
+
// deprecated
|
|
4490
|
+
metadata: any
|
|
4483
4491
|
}),
|
|
4484
4492
|
// Default value
|
|
4485
4493
|
() => ({
|
|
@@ -4490,29 +4498,48 @@ const parsePerseusRenderer = defaulted(object({
|
|
|
4490
4498
|
|
|
4491
4499
|
const parsePerseusArticle = union(parsePerseusRenderer).or(array(parsePerseusRenderer)).parser;
|
|
4492
4500
|
|
|
4493
|
-
function _objectWithoutPropertiesLoose(r, e) {
|
|
4494
|
-
if (null == r) return {};
|
|
4495
|
-
var t = {};
|
|
4496
|
-
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
|
|
4497
|
-
if (-1 !== e.indexOf(n)) continue;
|
|
4498
|
-
t[n] = r[n];
|
|
4499
|
-
}
|
|
4500
|
-
return t;
|
|
4501
|
-
}
|
|
4502
|
-
|
|
4503
4501
|
const parseHint = object({
|
|
4504
4502
|
replace: optional(boolean),
|
|
4505
4503
|
content: string,
|
|
4506
4504
|
widgets: defaulted(parseWidgetsMap, () => ({})),
|
|
4507
|
-
|
|
4508
|
-
|
|
4505
|
+
images: parseImages,
|
|
4506
|
+
// deprecated
|
|
4507
|
+
metadata: any
|
|
4509
4508
|
});
|
|
4510
4509
|
|
|
4511
|
-
const
|
|
4510
|
+
const parsePerseusAnswerArea = pipeParsers(defaulted(object({}), () => ({}))).then(convert(toAnswerArea)).parser;
|
|
4511
|
+
|
|
4512
|
+
// Some answerAreas have extra, bogus fields, like:
|
|
4513
|
+
//
|
|
4514
|
+
// "answerArea": {
|
|
4515
|
+
// "type": "multiple",
|
|
4516
|
+
// "options": {},
|
|
4517
|
+
// "version": null,
|
|
4518
|
+
// "static": false,
|
|
4519
|
+
// "graded": false,
|
|
4520
|
+
// "alignment": "",
|
|
4521
|
+
// }
|
|
4522
|
+
//
|
|
4523
|
+
// This function filters the fields of an answerArea object, keeping only the
|
|
4524
|
+
// known ones, and converts `undefined` and `null` values to `false`.
|
|
4525
|
+
function toAnswerArea(raw) {
|
|
4526
|
+
return {
|
|
4527
|
+
zTable: !!raw.zTable,
|
|
4528
|
+
calculator: !!raw.calculator,
|
|
4529
|
+
chi2Table: !!raw.chi2Table,
|
|
4530
|
+
financialCalculatorMonthlyPayment: !!raw.financialCalculatorMonthlyPayment,
|
|
4531
|
+
financialCalculatorTotalAmount: !!raw.financialCalculatorTotalAmount,
|
|
4532
|
+
financialCalculatorTimeToPayOff: !!raw.financialCalculatorTimeToPayOff,
|
|
4533
|
+
periodicTable: !!raw.periodicTable,
|
|
4534
|
+
periodicTableWithKey: !!raw.periodicTableWithKey,
|
|
4535
|
+
tTable: !!raw.tTable
|
|
4536
|
+
};
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4512
4539
|
const parsePerseusItem$1 = object({
|
|
4513
4540
|
question: parsePerseusRenderer,
|
|
4514
4541
|
hints: defaulted(array(parseHint), () => []),
|
|
4515
|
-
answerArea:
|
|
4542
|
+
answerArea: parsePerseusAnswerArea,
|
|
4516
4543
|
itemDataVersion: optional(object({
|
|
4517
4544
|
major: number,
|
|
4518
4545
|
minor: number
|
|
@@ -4521,24 +4548,6 @@ const parsePerseusItem$1 = object({
|
|
|
4521
4548
|
answer: any
|
|
4522
4549
|
});
|
|
4523
4550
|
|
|
4524
|
-
// Some answerAreas have extra fields, like:
|
|
4525
|
-
//
|
|
4526
|
-
// "answerArea": {
|
|
4527
|
-
// "type": "multiple",
|
|
4528
|
-
// "options": {
|
|
4529
|
-
// "content": "",
|
|
4530
|
-
// "images": {},
|
|
4531
|
-
// "widgets": {}
|
|
4532
|
-
// }
|
|
4533
|
-
// }
|
|
4534
|
-
//
|
|
4535
|
-
// The "type" and "options" fields don't seem to be used anywhere. This
|
|
4536
|
-
// migration function removes them.
|
|
4537
|
-
function migrateAnswerArea(rawValue, ctx) {
|
|
4538
|
-
const rest = _objectWithoutPropertiesLoose(rawValue, _excluded$a);
|
|
4539
|
-
return ctx.success(rest);
|
|
4540
|
-
}
|
|
4541
|
-
|
|
4542
4551
|
/**
|
|
4543
4552
|
* Helper to parse PerseusItem JSON
|
|
4544
4553
|
* Why not just use JSON.parse? We want:
|
|
@@ -4613,7 +4622,7 @@ function throwErrorIfCheatingDetected() {
|
|
|
4613
4622
|
|
|
4614
4623
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
4615
4624
|
const libName = "@khanacademy/perseus-core";
|
|
4616
|
-
const libVersion = "3.
|
|
4625
|
+
const libVersion = "3.7.0";
|
|
4617
4626
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
4618
4627
|
|
|
4619
4628
|
/**
|
|
@@ -4898,6 +4907,16 @@ const gradedGroupSetWidgetLogic = {
|
|
|
4898
4907
|
defaultWidgetOptions: defaultWidgetOptions$o
|
|
4899
4908
|
};
|
|
4900
4909
|
|
|
4910
|
+
function _objectWithoutPropertiesLoose(r, e) {
|
|
4911
|
+
if (null == r) return {};
|
|
4912
|
+
var t = {};
|
|
4913
|
+
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
|
|
4914
|
+
if (-1 !== e.indexOf(n)) continue;
|
|
4915
|
+
t[n] = r[n];
|
|
4916
|
+
}
|
|
4917
|
+
return t;
|
|
4918
|
+
}
|
|
4919
|
+
|
|
4901
4920
|
const _excluded$9 = ["correct"];
|
|
4902
4921
|
function getGrapherPublicWidgetOptions(options) {
|
|
4903
4922
|
const publicOptions = _objectWithoutPropertiesLoose(options, _excluded$9);
|
|
@@ -4933,10 +4952,7 @@ const grapherWidgetLogic = {
|
|
|
4933
4952
|
const defaultWidgetOptions$m = {
|
|
4934
4953
|
content: "",
|
|
4935
4954
|
widgets: {},
|
|
4936
|
-
images: {}
|
|
4937
|
-
// `undefined` instead of `null` so that getDefaultProps works for
|
|
4938
|
-
// `the GroupMetadataEditor`
|
|
4939
|
-
metadata: undefined
|
|
4955
|
+
images: {}
|
|
4940
4956
|
};
|
|
4941
4957
|
const groupWidgetLogic = {
|
|
4942
4958
|
name: "group",
|
|
@@ -5077,6 +5093,63 @@ const labelImageWidgetLogic = {
|
|
|
5077
5093
|
getPublicWidgetOptions: getLabelImagePublicWidgetOptions
|
|
5078
5094
|
};
|
|
5079
5095
|
|
|
5096
|
+
// TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
|
|
5097
|
+
|
|
5098
|
+
// TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
|
|
5099
|
+
const shuffleMatcher = props => {
|
|
5100
|
+
// Use the same random() function to shuffle both columns sequentially
|
|
5101
|
+
const rng = seededRNG$1(props.problemNum);
|
|
5102
|
+
let left;
|
|
5103
|
+
if (!props.orderMatters) {
|
|
5104
|
+
// If the order doesn't matter, don't shuffle the left column
|
|
5105
|
+
left = props.left;
|
|
5106
|
+
} else {
|
|
5107
|
+
left = shuffle$2(props.left, rng, /* ensurePermuted */true);
|
|
5108
|
+
}
|
|
5109
|
+
const right = shuffle$2(props.right, rng, /* ensurePermuted */true);
|
|
5110
|
+
return {
|
|
5111
|
+
left,
|
|
5112
|
+
right
|
|
5113
|
+
};
|
|
5114
|
+
};
|
|
5115
|
+
|
|
5116
|
+
// TODO(LEMS-2841): Can shorten to shuffleMatcher after above function removed
|
|
5117
|
+
function shuffleMatcherWithRandom(data) {
|
|
5118
|
+
// Use the same random() function to shuffle both columns sequentially
|
|
5119
|
+
let left;
|
|
5120
|
+
if (!data.orderMatters) {
|
|
5121
|
+
// If the order doesn't matter, don't shuffle the left column
|
|
5122
|
+
left = data.left;
|
|
5123
|
+
} else {
|
|
5124
|
+
left = shuffle$2(data.left, Math.random, /* ensurePermuted */true);
|
|
5125
|
+
}
|
|
5126
|
+
const right = shuffle$2(data.right, Math.random, /* ensurePermuted */true);
|
|
5127
|
+
return {
|
|
5128
|
+
left,
|
|
5129
|
+
right
|
|
5130
|
+
};
|
|
5131
|
+
}
|
|
5132
|
+
|
|
5133
|
+
/**
|
|
5134
|
+
* For details on the individual options, see the
|
|
5135
|
+
* PerseusMatcherWidgetOptions type
|
|
5136
|
+
*/
|
|
5137
|
+
|
|
5138
|
+
/**
|
|
5139
|
+
* Given a PerseusMatcherWidgetOptions object, return a new object with only
|
|
5140
|
+
* the public options that should be exposed to the client.
|
|
5141
|
+
*/
|
|
5142
|
+
function getMatcherPublicWidgetOptions(options) {
|
|
5143
|
+
const {
|
|
5144
|
+
left,
|
|
5145
|
+
right
|
|
5146
|
+
} = shuffleMatcherWithRandom(options);
|
|
5147
|
+
return _extends({}, options, {
|
|
5148
|
+
left: left,
|
|
5149
|
+
right: right
|
|
5150
|
+
});
|
|
5151
|
+
}
|
|
5152
|
+
|
|
5080
5153
|
const defaultWidgetOptions$f = {
|
|
5081
5154
|
left: ["$x$", "$y$", "$z$"],
|
|
5082
5155
|
right: ["$1$", "$2$", "$3$"],
|
|
@@ -5086,7 +5159,8 @@ const defaultWidgetOptions$f = {
|
|
|
5086
5159
|
};
|
|
5087
5160
|
const matcherWidgetLogic = {
|
|
5088
5161
|
name: "matcher",
|
|
5089
|
-
defaultWidgetOptions: defaultWidgetOptions$f
|
|
5162
|
+
defaultWidgetOptions: defaultWidgetOptions$f,
|
|
5163
|
+
getPublicWidgetOptions: getMatcherPublicWidgetOptions
|
|
5090
5164
|
};
|
|
5091
5165
|
|
|
5092
5166
|
const _excluded$6 = ["answers"];
|
|
@@ -5418,14 +5492,17 @@ const radioWidgetLogic = {
|
|
|
5418
5492
|
* the public options that should be exposed to the client.
|
|
5419
5493
|
*/
|
|
5420
5494
|
function getSorterPublicWidgetOptions(options) {
|
|
5421
|
-
|
|
5495
|
+
const shuffledCorrect = shuffle$2(options.correct, Math.random, /* ensurePermuted */true);
|
|
5496
|
+
return _extends({}, options, {
|
|
5422
5497
|
// Note(Tamara): This does not provide correct answer information any longer.
|
|
5423
5498
|
// To maintain compatibility with the original widget options, we are
|
|
5424
5499
|
// keeping the key the same. Represents initial state of the cards here.
|
|
5425
|
-
correct:
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5500
|
+
correct: shuffledCorrect,
|
|
5501
|
+
// Note(Tamara): This new key is only added here with "true". There isn't
|
|
5502
|
+
// a place where it is set to false. It indicates that the correct field
|
|
5503
|
+
// has been shuffled and no longer contains correct answer info.
|
|
5504
|
+
isCorrectShuffled: true
|
|
5505
|
+
});
|
|
5429
5506
|
}
|
|
5430
5507
|
|
|
5431
5508
|
const defaultWidgetOptions$2 = {
|
|
@@ -5724,5 +5801,58 @@ function splitPerseusItem(originalItem) {
|
|
|
5724
5801
|
});
|
|
5725
5802
|
}
|
|
5726
5803
|
|
|
5727
|
-
|
|
5804
|
+
/* Note(tamara): Brought over from the perseus package packages/perseus/src/util.ts file.
|
|
5805
|
+
May be useful to bring other perseus package utilities here. Contains utility functions
|
|
5806
|
+
and types used across multiple widgets for randomization and shuffling. */
|
|
5807
|
+
const seededRNG = function seededRNG(seed) {
|
|
5808
|
+
let randomSeed = seed;
|
|
5809
|
+
return function () {
|
|
5810
|
+
// Robert Jenkins' 32 bit integer hash function.
|
|
5811
|
+
let seed = randomSeed;
|
|
5812
|
+
seed = seed + 0x7ed55d16 + (seed << 12) & 0xffffffff;
|
|
5813
|
+
seed = (seed ^ 0xc761c23c ^ seed >>> 19) & 0xffffffff;
|
|
5814
|
+
seed = seed + 0x165667b1 + (seed << 5) & 0xffffffff;
|
|
5815
|
+
seed = (seed + 0xd3a2646c ^ seed << 9) & 0xffffffff;
|
|
5816
|
+
seed = seed + 0xfd7046c5 + (seed << 3) & 0xffffffff;
|
|
5817
|
+
seed = (seed ^ 0xb55a4f09 ^ seed >>> 16) & 0xffffffff;
|
|
5818
|
+
return (randomSeed = seed & 0xfffffff) / 0x10000000;
|
|
5819
|
+
};
|
|
5820
|
+
};
|
|
5821
|
+
|
|
5822
|
+
// Shuffle an array using a given random seed or function.
|
|
5823
|
+
// If `ensurePermuted` is true, the input and output are guaranteed to be
|
|
5824
|
+
// distinct permutations.
|
|
5825
|
+
function shuffle(array, randomSeed, ensurePermuted = false) {
|
|
5826
|
+
// Always return a copy of the input array
|
|
5827
|
+
const shuffled = _.clone(array);
|
|
5828
|
+
|
|
5829
|
+
// Handle edge cases (input array is empty or uniform)
|
|
5830
|
+
if (!shuffled.length || _.all(shuffled, function (value) {
|
|
5831
|
+
return _.isEqual(value, shuffled[0]);
|
|
5832
|
+
})) {
|
|
5833
|
+
return shuffled;
|
|
5834
|
+
}
|
|
5835
|
+
let random;
|
|
5836
|
+
if (typeof randomSeed === "function") {
|
|
5837
|
+
random = randomSeed;
|
|
5838
|
+
} else {
|
|
5839
|
+
random = seededRNG(randomSeed);
|
|
5840
|
+
}
|
|
5841
|
+
do {
|
|
5842
|
+
// Fischer-Yates shuffle
|
|
5843
|
+
for (let top = shuffled.length; top > 0; top--) {
|
|
5844
|
+
const newEnd = Math.floor(random() * top);
|
|
5845
|
+
const temp = shuffled[newEnd];
|
|
5846
|
+
|
|
5847
|
+
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
5848
|
+
shuffled[newEnd] = shuffled[top - 1];
|
|
5849
|
+
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
5850
|
+
shuffled[top - 1] = temp;
|
|
5851
|
+
}
|
|
5852
|
+
} while (ensurePermuted && _.isEqual(array, shuffled));
|
|
5853
|
+
return shuffled;
|
|
5854
|
+
}
|
|
5855
|
+
const random = seededRNG(new Date().getTime() & 0xffffffff);
|
|
5856
|
+
|
|
5857
|
+
export { coreWidgetRegistry as CoreWidgetRegistry, Errors, grapherUtil as GrapherUtil, ItemExtras, PerseusError, PerseusExpressionAnswerFormConsidered, addLibraryVersionToPerseusDebug, addWidget, approximateDeepEqual, approximateEqual, categorizerWidgetLogic as categorizerLogic, csProgramWidgetLogic as csProgramLogic, deepClone, definitionWidgetLogic as definitionLogic, dropdownWidgetLogic as dropdownLogic, explanationWidgetLogic as explanationLogic, expressionWidgetLogic as expressionLogic, getCSProgramPublicWidgetOptions, getCategorizerPublicWidgetOptions, getDecimalSeparator, getDropdownPublicWidgetOptions, getExpressionPublicWidgetOptions, getGrapherPublicWidgetOptions, getIFramePublicWidgetOptions, getInteractiveGraphPublicWidgetOptions, getLabelImagePublicWidgetOptions, getMatcherPublicWidgetOptions, getMatrixPublicWidgetOptions, getMatrixSize, getNumberLinePublicWidgetOptions, getNumericInputPublicWidgetOptions, getOrdererPublicWidgetOptions, getPlotterPublicWidgetOptions, getRadioPublicWidgetOptions, getSorterPublicWidgetOptions, getTablePublicWidgetOptions, getUpgradedWidgetOptions, getWidgetIdsFromContent, getWidgetIdsFromContentByType, gradedGroupWidgetLogic as gradedGroupLogic, gradedGroupSetWidgetLogic as gradedGroupSetLogic, grapherWidgetLogic as grapherLogic, groupWidgetLogic as groupLogic, iframeWidgetLogic as iframeLogic, imageWidgetLogic as imageLogic, inputNumberWidgetLogic as inputNumberLogic, interactionWidgetLogic as interactionLogic, interactiveGraphWidgetLogic as interactiveGraphLogic, isFailure, isSuccess, labelImageWidgetLogic as labelImageLogic, libVersion, lockedFigureColorNames, lockedFigureColors, lockedFigureFillStyles, mapObject, matcherWidgetLogic as matcherLogic, matrixWidgetLogic as matrixLogic, measurerWidgetLogic as measurerLogic, numberLineWidgetLogic as numberLineLogic, numericInputWidgetLogic as numericInputLogic, ordererWidgetLogic as ordererLogic, parseAndMigratePerseusArticle, parseAndMigratePerseusItem, parsePerseusItem, passageWidgetLogic as passageLogic, passageRefWidgetLogic as passageRefLogic, passageRefTargetWidgetLogic as passageRefTargetLogic, phetSimulationWidgetLogic as phetSimulationLogic, plotterWidgetLogic as plotterLogic, plotterPlotTypes, pluck, pythonProgramWidgetLogic as pythonProgramLogic, radioWidgetLogic as radioLogic, random, seededRNG, shuffle, shuffleMatcher, sorterWidgetLogic as sorterLogic, splitPerseusItem, tableWidgetLogic as tableLogic, upgradeWidgetInfoToLatestVersion, videoWidgetLogic as videoLogic };
|
|
5728
5858
|
//# sourceMappingURL=index.js.map
|