@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/index.d.ts
CHANGED
|
@@ -101,3 +101,5 @@ export { default as getTablePublicWidgetOptions } from "./widgets/table/table-ut
|
|
|
101
101
|
export { default as getIFramePublicWidgetOptions } from "./widgets/iframe/iframe-util";
|
|
102
102
|
export { default as getMatrixPublicWidgetOptions } from "./widgets/matrix/matrix-util";
|
|
103
103
|
export { default as getPlotterPublicWidgetOptions } from "./widgets/plotter/plotter-util";
|
|
104
|
+
export { default as getMatcherPublicWidgetOptions, shuffleMatcher, } from "./widgets/matcher/matcher-util";
|
|
105
|
+
export { shuffle, seededRNG, random } from "./utils/random-util";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
var perseusCore = require('@khanacademy/perseus-core');
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Adds the given perseus library version information to the __perseus_debug__
|
|
7
9
|
* object and ensures that the object is attached to `globalThis` (`window` in
|
|
@@ -821,7 +823,7 @@ function times(n, iteratee, context) {
|
|
|
821
823
|
}
|
|
822
824
|
|
|
823
825
|
// Return a random integer between `min` and `max` (inclusive).
|
|
824
|
-
function random(min, max) {
|
|
826
|
+
function random$1(min, max) {
|
|
825
827
|
if (max == null) {
|
|
826
828
|
max = min;
|
|
827
829
|
min = 0;
|
|
@@ -1564,14 +1566,14 @@ function toArray(obj) {
|
|
|
1564
1566
|
function sample(obj, n, guard) {
|
|
1565
1567
|
if (n == null || guard) {
|
|
1566
1568
|
if (!isArrayLike(obj)) obj = values(obj);
|
|
1567
|
-
return obj[random(obj.length - 1)];
|
|
1569
|
+
return obj[random$1(obj.length - 1)];
|
|
1568
1570
|
}
|
|
1569
1571
|
var sample = toArray(obj);
|
|
1570
1572
|
var length = getLength(sample);
|
|
1571
1573
|
n = Math.max(Math.min(n, length), 0);
|
|
1572
1574
|
var last = length - 1;
|
|
1573
1575
|
for (var index = 0; index < n; index++) {
|
|
1574
|
-
var rand = random(index, last);
|
|
1576
|
+
var rand = random$1(index, last);
|
|
1575
1577
|
var temp = sample[index];
|
|
1576
1578
|
sample[index] = sample[rand];
|
|
1577
1579
|
sample[rand] = temp;
|
|
@@ -1580,7 +1582,7 @@ function sample(obj, n, guard) {
|
|
|
1580
1582
|
}
|
|
1581
1583
|
|
|
1582
1584
|
// Shuffle a collection.
|
|
1583
|
-
function shuffle(obj) {
|
|
1585
|
+
function shuffle$1(obj) {
|
|
1584
1586
|
return sample(obj, Infinity);
|
|
1585
1587
|
}
|
|
1586
1588
|
|
|
@@ -1966,7 +1968,7 @@ var allExports = /*#__PURE__*/Object.freeze({
|
|
|
1966
1968
|
matcher: matcher,
|
|
1967
1969
|
matches: matcher,
|
|
1968
1970
|
times: times,
|
|
1969
|
-
random: random,
|
|
1971
|
+
random: random$1,
|
|
1970
1972
|
now: now,
|
|
1971
1973
|
escape: escape,
|
|
1972
1974
|
unescape: unescape,
|
|
@@ -2023,7 +2025,7 @@ var allExports = /*#__PURE__*/Object.freeze({
|
|
|
2023
2025
|
where: where,
|
|
2024
2026
|
max: max,
|
|
2025
2027
|
min: min,
|
|
2026
|
-
shuffle: shuffle,
|
|
2028
|
+
shuffle: shuffle$1,
|
|
2027
2029
|
sample: sample,
|
|
2028
2030
|
sortBy: sortBy,
|
|
2029
2031
|
groupBy: groupBy,
|
|
@@ -3075,7 +3077,7 @@ function parseWidget(parseType, parseOptions) {
|
|
|
3075
3077
|
graded: optional(boolean),
|
|
3076
3078
|
alignment: optional(string),
|
|
3077
3079
|
options: parseOptions,
|
|
3078
|
-
key: optional(number),
|
|
3080
|
+
key: optional(nullable(number)),
|
|
3079
3081
|
version: optional(object({
|
|
3080
3082
|
major: number,
|
|
3081
3083
|
minor: number
|
|
@@ -3463,14 +3465,15 @@ const imageDimensionToNumber = pipeParsers(union(number).or(string).parser)
|
|
|
3463
3465
|
// string parses to either NaN (using parseInt) or 0 (using unary +) and
|
|
3464
3466
|
// CSS will treat NaN as invalid and default to 0 instead.
|
|
3465
3467
|
.then(convert(emptyToZero)).then(stringToNumber).parser;
|
|
3468
|
+
const dimensionOrUndefined = defaulted(imageDimensionToNumber, () => undefined);
|
|
3466
3469
|
const parsePerseusImageBackground = object({
|
|
3467
3470
|
url: optional(nullable(string)),
|
|
3468
|
-
width:
|
|
3469
|
-
height:
|
|
3470
|
-
top:
|
|
3471
|
-
left:
|
|
3472
|
-
bottom:
|
|
3473
|
-
scale:
|
|
3471
|
+
width: dimensionOrUndefined,
|
|
3472
|
+
height: dimensionOrUndefined,
|
|
3473
|
+
top: dimensionOrUndefined,
|
|
3474
|
+
left: dimensionOrUndefined,
|
|
3475
|
+
bottom: dimensionOrUndefined,
|
|
3476
|
+
scale: dimensionOrUndefined
|
|
3474
3477
|
});
|
|
3475
3478
|
|
|
3476
3479
|
const pairOfNumbers$2 = pair(number, number);
|
|
@@ -4211,7 +4214,7 @@ const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
|
4211
4214
|
// the data, simplify this.
|
|
4212
4215
|
value: optional(nullable(number)),
|
|
4213
4216
|
status: string,
|
|
4214
|
-
answerForms:
|
|
4217
|
+
answerForms: defaulted(array(parseMathFormat), () => undefined),
|
|
4215
4218
|
strict: boolean,
|
|
4216
4219
|
maxError: optional(nullable(number)),
|
|
4217
4220
|
// TODO(benchristel): simplify should never be a boolean, but we
|
|
@@ -4309,7 +4312,7 @@ const parseRadioWidget = parseWidget(constant("radio"), object({
|
|
|
4309
4312
|
// There is an import cycle between radio-widget.ts and
|
|
4310
4313
|
// widgets-map.ts. The anonymous function below ensures that we
|
|
4311
4314
|
// don't refer to parseWidgetsMap before it's defined.
|
|
4312
|
-
widgets:
|
|
4315
|
+
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => undefined)
|
|
4313
4316
|
})),
|
|
4314
4317
|
hasNoneOfTheAbove: optional(boolean),
|
|
4315
4318
|
countChoices: optional(boolean),
|
|
@@ -4463,13 +4466,17 @@ const parseDeprecatedWidget = parseWidget(
|
|
|
4463
4466
|
(_, ctx) => ctx.success("deprecated-standin"),
|
|
4464
4467
|
// Allow any widget options
|
|
4465
4468
|
object({}));
|
|
4466
|
-
const
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
+
const parseStringToNonNegativeInt = (rawValue, ctx) => {
|
|
4470
|
+
// The article renderer seems to allow the numeric part of a widget ID to
|
|
4471
|
+
// be 0, at least for image widgets. However, if widget IDs in an exercise
|
|
4472
|
+
// contain 0, the exercise renderer will blow up. We allow 0 here for
|
|
4473
|
+
// compatibility with articles.
|
|
4474
|
+
if (typeof rawValue !== "string" || !/^(0|[1-9][0-9]*)$/.test(rawValue)) {
|
|
4475
|
+
return ctx.failure("a string representing a non-negative integer", rawValue);
|
|
4469
4476
|
}
|
|
4470
4477
|
return ctx.success(+rawValue);
|
|
4471
4478
|
};
|
|
4472
|
-
const parseWidgetIdComponents = pair(string,
|
|
4479
|
+
const parseWidgetIdComponents = pair(string, parseStringToNonNegativeInt);
|
|
4473
4480
|
|
|
4474
4481
|
const parsePerseusRenderer = defaulted(object({
|
|
4475
4482
|
// TODO(benchristel): content is also defaulted to empty string in
|
|
@@ -4480,8 +4487,9 @@ const parsePerseusRenderer = defaulted(object({
|
|
|
4480
4487
|
// The anonymous function below ensures that we don't try to access
|
|
4481
4488
|
// parseWidgetsMap before it's defined.
|
|
4482
4489
|
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
|
|
4483
|
-
|
|
4484
|
-
|
|
4490
|
+
images: parseImages,
|
|
4491
|
+
// deprecated
|
|
4492
|
+
metadata: any
|
|
4485
4493
|
}),
|
|
4486
4494
|
// Default value
|
|
4487
4495
|
() => ({
|
|
@@ -4496,14 +4504,44 @@ const parseHint = object({
|
|
|
4496
4504
|
replace: optional(boolean),
|
|
4497
4505
|
content: string,
|
|
4498
4506
|
widgets: defaulted(parseWidgetsMap, () => ({})),
|
|
4499
|
-
|
|
4500
|
-
|
|
4507
|
+
images: parseImages,
|
|
4508
|
+
// deprecated
|
|
4509
|
+
metadata: any
|
|
4501
4510
|
});
|
|
4502
4511
|
|
|
4512
|
+
const parsePerseusAnswerArea = pipeParsers(defaulted(object({}), () => ({}))).then(convert(toAnswerArea)).parser;
|
|
4513
|
+
|
|
4514
|
+
// Some answerAreas have extra, bogus fields, like:
|
|
4515
|
+
//
|
|
4516
|
+
// "answerArea": {
|
|
4517
|
+
// "type": "multiple",
|
|
4518
|
+
// "options": {},
|
|
4519
|
+
// "version": null,
|
|
4520
|
+
// "static": false,
|
|
4521
|
+
// "graded": false,
|
|
4522
|
+
// "alignment": "",
|
|
4523
|
+
// }
|
|
4524
|
+
//
|
|
4525
|
+
// This function filters the fields of an answerArea object, keeping only the
|
|
4526
|
+
// known ones, and converts `undefined` and `null` values to `false`.
|
|
4527
|
+
function toAnswerArea(raw) {
|
|
4528
|
+
return {
|
|
4529
|
+
zTable: !!raw.zTable,
|
|
4530
|
+
calculator: !!raw.calculator,
|
|
4531
|
+
chi2Table: !!raw.chi2Table,
|
|
4532
|
+
financialCalculatorMonthlyPayment: !!raw.financialCalculatorMonthlyPayment,
|
|
4533
|
+
financialCalculatorTotalAmount: !!raw.financialCalculatorTotalAmount,
|
|
4534
|
+
financialCalculatorTimeToPayOff: !!raw.financialCalculatorTimeToPayOff,
|
|
4535
|
+
periodicTable: !!raw.periodicTable,
|
|
4536
|
+
periodicTableWithKey: !!raw.periodicTableWithKey,
|
|
4537
|
+
tTable: !!raw.tTable
|
|
4538
|
+
};
|
|
4539
|
+
}
|
|
4540
|
+
|
|
4503
4541
|
const parsePerseusItem$1 = object({
|
|
4504
4542
|
question: parsePerseusRenderer,
|
|
4505
4543
|
hints: defaulted(array(parseHint), () => []),
|
|
4506
|
-
answerArea:
|
|
4544
|
+
answerArea: parsePerseusAnswerArea,
|
|
4507
4545
|
itemDataVersion: optional(object({
|
|
4508
4546
|
major: number,
|
|
4509
4547
|
minor: number
|
|
@@ -4512,28 +4550,6 @@ const parsePerseusItem$1 = object({
|
|
|
4512
4550
|
answer: any
|
|
4513
4551
|
});
|
|
4514
4552
|
|
|
4515
|
-
// Some answerAreas have extra fields, like:
|
|
4516
|
-
//
|
|
4517
|
-
// "answerArea": {
|
|
4518
|
-
// "type": "multiple",
|
|
4519
|
-
// "options": {
|
|
4520
|
-
// "content": "",
|
|
4521
|
-
// "images": {},
|
|
4522
|
-
// "widgets": {}
|
|
4523
|
-
// }
|
|
4524
|
-
// }
|
|
4525
|
-
//
|
|
4526
|
-
// The "type" and "options" fields don't seem to be used anywhere. This
|
|
4527
|
-
// migration function removes them.
|
|
4528
|
-
function migrateAnswerArea(rawValue, ctx) {
|
|
4529
|
-
const {
|
|
4530
|
-
type: _,
|
|
4531
|
-
options: __,
|
|
4532
|
-
...rest
|
|
4533
|
-
} = rawValue;
|
|
4534
|
-
return ctx.success(rest);
|
|
4535
|
-
}
|
|
4536
|
-
|
|
4537
4553
|
/**
|
|
4538
4554
|
* Helper to parse PerseusItem JSON
|
|
4539
4555
|
* Why not just use JSON.parse? We want:
|
|
@@ -4608,7 +4624,7 @@ function throwErrorIfCheatingDetected() {
|
|
|
4608
4624
|
|
|
4609
4625
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
4610
4626
|
const libName = "@khanacademy/perseus-core";
|
|
4611
|
-
const libVersion = "3.
|
|
4627
|
+
const libVersion = "3.7.0";
|
|
4612
4628
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
4613
4629
|
|
|
4614
4630
|
/**
|
|
@@ -4930,10 +4946,7 @@ const grapherWidgetLogic = {
|
|
|
4930
4946
|
const defaultWidgetOptions$m = {
|
|
4931
4947
|
content: "",
|
|
4932
4948
|
widgets: {},
|
|
4933
|
-
images: {}
|
|
4934
|
-
// `undefined` instead of `null` so that getDefaultProps works for
|
|
4935
|
-
// `the GroupMetadataEditor`
|
|
4936
|
-
metadata: undefined
|
|
4949
|
+
images: {}
|
|
4937
4950
|
};
|
|
4938
4951
|
const groupWidgetLogic = {
|
|
4939
4952
|
name: "group",
|
|
@@ -5079,6 +5092,64 @@ const labelImageWidgetLogic = {
|
|
|
5079
5092
|
getPublicWidgetOptions: getLabelImagePublicWidgetOptions
|
|
5080
5093
|
};
|
|
5081
5094
|
|
|
5095
|
+
// TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
|
|
5096
|
+
|
|
5097
|
+
// TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
|
|
5098
|
+
const shuffleMatcher = props => {
|
|
5099
|
+
// Use the same random() function to shuffle both columns sequentially
|
|
5100
|
+
const rng = perseusCore.seededRNG(props.problemNum);
|
|
5101
|
+
let left;
|
|
5102
|
+
if (!props.orderMatters) {
|
|
5103
|
+
// If the order doesn't matter, don't shuffle the left column
|
|
5104
|
+
left = props.left;
|
|
5105
|
+
} else {
|
|
5106
|
+
left = perseusCore.shuffle(props.left, rng, /* ensurePermuted */true);
|
|
5107
|
+
}
|
|
5108
|
+
const right = perseusCore.shuffle(props.right, rng, /* ensurePermuted */true);
|
|
5109
|
+
return {
|
|
5110
|
+
left,
|
|
5111
|
+
right
|
|
5112
|
+
};
|
|
5113
|
+
};
|
|
5114
|
+
|
|
5115
|
+
// TODO(LEMS-2841): Can shorten to shuffleMatcher after above function removed
|
|
5116
|
+
function shuffleMatcherWithRandom(data) {
|
|
5117
|
+
// Use the same random() function to shuffle both columns sequentially
|
|
5118
|
+
let left;
|
|
5119
|
+
if (!data.orderMatters) {
|
|
5120
|
+
// If the order doesn't matter, don't shuffle the left column
|
|
5121
|
+
left = data.left;
|
|
5122
|
+
} else {
|
|
5123
|
+
left = perseusCore.shuffle(data.left, Math.random, /* ensurePermuted */true);
|
|
5124
|
+
}
|
|
5125
|
+
const right = perseusCore.shuffle(data.right, Math.random, /* ensurePermuted */true);
|
|
5126
|
+
return {
|
|
5127
|
+
left,
|
|
5128
|
+
right
|
|
5129
|
+
};
|
|
5130
|
+
}
|
|
5131
|
+
|
|
5132
|
+
/**
|
|
5133
|
+
* For details on the individual options, see the
|
|
5134
|
+
* PerseusMatcherWidgetOptions type
|
|
5135
|
+
*/
|
|
5136
|
+
|
|
5137
|
+
/**
|
|
5138
|
+
* Given a PerseusMatcherWidgetOptions object, return a new object with only
|
|
5139
|
+
* the public options that should be exposed to the client.
|
|
5140
|
+
*/
|
|
5141
|
+
function getMatcherPublicWidgetOptions(options) {
|
|
5142
|
+
const {
|
|
5143
|
+
left,
|
|
5144
|
+
right
|
|
5145
|
+
} = shuffleMatcherWithRandom(options);
|
|
5146
|
+
return {
|
|
5147
|
+
...options,
|
|
5148
|
+
left: left,
|
|
5149
|
+
right: right
|
|
5150
|
+
};
|
|
5151
|
+
}
|
|
5152
|
+
|
|
5082
5153
|
const defaultWidgetOptions$f = {
|
|
5083
5154
|
left: ["$x$", "$y$", "$z$"],
|
|
5084
5155
|
right: ["$1$", "$2$", "$3$"],
|
|
@@ -5088,7 +5159,8 @@ const defaultWidgetOptions$f = {
|
|
|
5088
5159
|
};
|
|
5089
5160
|
const matcherWidgetLogic = {
|
|
5090
5161
|
name: "matcher",
|
|
5091
|
-
defaultWidgetOptions: defaultWidgetOptions$f
|
|
5162
|
+
defaultWidgetOptions: defaultWidgetOptions$f,
|
|
5163
|
+
getPublicWidgetOptions: getMatcherPublicWidgetOptions
|
|
5092
5164
|
};
|
|
5093
5165
|
|
|
5094
5166
|
function getMatrixPublicWidgetOptions(options) {
|
|
@@ -5430,13 +5502,17 @@ const radioWidgetLogic = {
|
|
|
5430
5502
|
* the public options that should be exposed to the client.
|
|
5431
5503
|
*/
|
|
5432
5504
|
function getSorterPublicWidgetOptions(options) {
|
|
5505
|
+
const shuffledCorrect = perseusCore.shuffle(options.correct, Math.random, /* ensurePermuted */true);
|
|
5433
5506
|
return {
|
|
5507
|
+
...options,
|
|
5434
5508
|
// Note(Tamara): This does not provide correct answer information any longer.
|
|
5435
5509
|
// To maintain compatibility with the original widget options, we are
|
|
5436
5510
|
// keeping the key the same. Represents initial state of the cards here.
|
|
5437
|
-
correct:
|
|
5438
|
-
|
|
5439
|
-
|
|
5511
|
+
correct: shuffledCorrect,
|
|
5512
|
+
// Note(Tamara): This new key is only added here with "true". There isn't
|
|
5513
|
+
// a place where it is set to false. It indicates that the correct field
|
|
5514
|
+
// has been shuffled and no longer contains correct answer info.
|
|
5515
|
+
isCorrectShuffled: true
|
|
5440
5516
|
};
|
|
5441
5517
|
}
|
|
5442
5518
|
|
|
@@ -5743,6 +5819,60 @@ function splitPerseusItem(originalItem) {
|
|
|
5743
5819
|
};
|
|
5744
5820
|
}
|
|
5745
5821
|
|
|
5822
|
+
/* Note(tamara): Brought over from the perseus package packages/perseus/src/util.ts file.
|
|
5823
|
+
May be useful to bring other perseus package utilities here. Contains utility functions
|
|
5824
|
+
and types used across multiple widgets for randomization and shuffling. */
|
|
5825
|
+
const seededRNG = function (seed) {
|
|
5826
|
+
let randomSeed = seed;
|
|
5827
|
+
return function () {
|
|
5828
|
+
// Robert Jenkins' 32 bit integer hash function.
|
|
5829
|
+
let seed = randomSeed;
|
|
5830
|
+
seed = seed + 0x7ed55d16 + (seed << 12) & 0xffffffff;
|
|
5831
|
+
seed = (seed ^ 0xc761c23c ^ seed >>> 19) & 0xffffffff;
|
|
5832
|
+
seed = seed + 0x165667b1 + (seed << 5) & 0xffffffff;
|
|
5833
|
+
seed = (seed + 0xd3a2646c ^ seed << 9) & 0xffffffff;
|
|
5834
|
+
seed = seed + 0xfd7046c5 + (seed << 3) & 0xffffffff;
|
|
5835
|
+
seed = (seed ^ 0xb55a4f09 ^ seed >>> 16) & 0xffffffff;
|
|
5836
|
+
return (randomSeed = seed & 0xfffffff) / 0x10000000;
|
|
5837
|
+
};
|
|
5838
|
+
};
|
|
5839
|
+
|
|
5840
|
+
// Shuffle an array using a given random seed or function.
|
|
5841
|
+
// If `ensurePermuted` is true, the input and output are guaranteed to be
|
|
5842
|
+
// distinct permutations.
|
|
5843
|
+
function shuffle(array, randomSeed) {
|
|
5844
|
+
let ensurePermuted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
5845
|
+
// Always return a copy of the input array
|
|
5846
|
+
const shuffled = _.clone(array);
|
|
5847
|
+
|
|
5848
|
+
// Handle edge cases (input array is empty or uniform)
|
|
5849
|
+
if (!shuffled.length || _.all(shuffled, function (value) {
|
|
5850
|
+
return _.isEqual(value, shuffled[0]);
|
|
5851
|
+
})) {
|
|
5852
|
+
return shuffled;
|
|
5853
|
+
}
|
|
5854
|
+
let random;
|
|
5855
|
+
if (typeof randomSeed === "function") {
|
|
5856
|
+
random = randomSeed;
|
|
5857
|
+
} else {
|
|
5858
|
+
random = seededRNG(randomSeed);
|
|
5859
|
+
}
|
|
5860
|
+
do {
|
|
5861
|
+
// Fischer-Yates shuffle
|
|
5862
|
+
for (let top = shuffled.length; top > 0; top--) {
|
|
5863
|
+
const newEnd = Math.floor(random() * top);
|
|
5864
|
+
const temp = shuffled[newEnd];
|
|
5865
|
+
|
|
5866
|
+
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
5867
|
+
shuffled[newEnd] = shuffled[top - 1];
|
|
5868
|
+
// @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
|
|
5869
|
+
shuffled[top - 1] = temp;
|
|
5870
|
+
}
|
|
5871
|
+
} while (ensurePermuted && _.isEqual(array, shuffled));
|
|
5872
|
+
return shuffled;
|
|
5873
|
+
}
|
|
5874
|
+
const random = seededRNG(new Date().getTime() & 0xffffffff);
|
|
5875
|
+
|
|
5746
5876
|
exports.CoreWidgetRegistry = coreWidgetRegistry;
|
|
5747
5877
|
exports.Errors = Errors;
|
|
5748
5878
|
exports.GrapherUtil = grapherUtil;
|
|
@@ -5769,6 +5899,7 @@ exports.getGrapherPublicWidgetOptions = getGrapherPublicWidgetOptions;
|
|
|
5769
5899
|
exports.getIFramePublicWidgetOptions = getIFramePublicWidgetOptions;
|
|
5770
5900
|
exports.getInteractiveGraphPublicWidgetOptions = getInteractiveGraphPublicWidgetOptions;
|
|
5771
5901
|
exports.getLabelImagePublicWidgetOptions = getLabelImagePublicWidgetOptions;
|
|
5902
|
+
exports.getMatcherPublicWidgetOptions = getMatcherPublicWidgetOptions;
|
|
5772
5903
|
exports.getMatrixPublicWidgetOptions = getMatrixPublicWidgetOptions;
|
|
5773
5904
|
exports.getMatrixSize = getMatrixSize;
|
|
5774
5905
|
exports.getNumberLinePublicWidgetOptions = getNumberLinePublicWidgetOptions;
|
|
@@ -5816,6 +5947,10 @@ exports.plotterPlotTypes = plotterPlotTypes;
|
|
|
5816
5947
|
exports.pluck = pluck;
|
|
5817
5948
|
exports.pythonProgramLogic = pythonProgramWidgetLogic;
|
|
5818
5949
|
exports.radioLogic = radioWidgetLogic;
|
|
5950
|
+
exports.random = random;
|
|
5951
|
+
exports.seededRNG = seededRNG;
|
|
5952
|
+
exports.shuffle = shuffle;
|
|
5953
|
+
exports.shuffleMatcher = shuffleMatcher;
|
|
5819
5954
|
exports.sorterLogic = sorterWidgetLogic;
|
|
5820
5955
|
exports.splitPerseusItem = splitPerseusItem;
|
|
5821
5956
|
exports.tableLogic = tableWidgetLogic;
|