@khanacademy/perseus 49.1.7 → 49.2.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/components/math-input.d.ts +2 -1
- package/dist/es/index.css +5 -0
- package/dist/es/index.js +229 -55
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +16 -12
- package/dist/es/strings.js.map +1 -1
- package/dist/index.css +5 -0
- package/dist/index.js +230 -55
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +4 -3
- package/dist/strings.d.ts +6 -5
- package/dist/strings.js +16 -12
- package/dist/strings.js.map +1 -1
- package/dist/util/parse-perseus-json/general-purpose-parsers/convert.d.ts +2 -0
- package/dist/util/parse-perseus-json/general-purpose-parsers/discriminated-union.d.ts +8 -0
- package/dist/util/parse-perseus-json/perseus-parsers/versioned-widget-options.d.ts +45 -0
- package/dist/widgets/expression/expression.d.ts +1 -1
- package/dist/widgets/passage-ref/passage-ref.d.ts +7 -3
- package/package.json +2 -2
|
@@ -5,13 +5,14 @@ import type { LegacyButtonSets } from "../perseus-types";
|
|
|
5
5
|
import type { Keys, MathFieldInterface } from "@khanacademy/math-input";
|
|
6
6
|
import type { AnalyticsEventHandlerFn } from "@khanacademy/perseus-core";
|
|
7
7
|
type ButtonsVisibleType = "always" | "never" | "focused";
|
|
8
|
-
type KeypadButtonSets = {
|
|
8
|
+
export type KeypadButtonSets = {
|
|
9
9
|
advancedRelations?: boolean;
|
|
10
10
|
basicRelations?: boolean;
|
|
11
11
|
divisionKey?: boolean;
|
|
12
12
|
logarithms?: boolean;
|
|
13
13
|
preAlgebra?: boolean;
|
|
14
14
|
trigonometry?: boolean;
|
|
15
|
+
scientific?: boolean;
|
|
15
16
|
};
|
|
16
17
|
type Props = {
|
|
17
18
|
className?: string;
|
package/dist/es/index.css
CHANGED
|
@@ -2747,6 +2747,11 @@ img.zoom-img {
|
|
|
2747
2747
|
box-sizing: content-box;
|
|
2748
2748
|
padding: 25px 25px 0 0;
|
|
2749
2749
|
}
|
|
2750
|
+
.perseus-dropdown .perseus-renderer .paragraph {
|
|
2751
|
+
/* overriding overly broad selectors in .framework-perseus rules */
|
|
2752
|
+
margin-bottom: 0 !important;
|
|
2753
|
+
font-size: 18px !important;
|
|
2754
|
+
}
|
|
2750
2755
|
.categorizer-container {
|
|
2751
2756
|
margin-top: 20px;
|
|
2752
2757
|
}
|
package/dist/es/index.js
CHANGED
|
@@ -326,11 +326,11 @@ const mockStrings = {
|
|
|
326
326
|
angleMeasure,
|
|
327
327
|
vertexX,
|
|
328
328
|
vertexY,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}) => `The angle measure is ${angleMeasure} degrees with a vertex at ${vertexX} comma ${vertexY}, a point on the
|
|
329
|
+
startingSideX,
|
|
330
|
+
startingSideY,
|
|
331
|
+
endingSideX,
|
|
332
|
+
endingSideY
|
|
333
|
+
}) => `The angle measure is ${angleMeasure} degrees with a vertex at ${vertexX} comma ${vertexY}, a point on the starting side at ${startingSideX} comma ${startingSideY} and a point on the ending side at ${endingSideX} comma ${endingSideY}.`
|
|
334
334
|
// The above strings are used for interactive graph SR descriptions.
|
|
335
335
|
};
|
|
336
336
|
|
|
@@ -741,6 +741,9 @@ const mapButtonSets = buttonSets => {
|
|
|
741
741
|
case "trig":
|
|
742
742
|
keypadButtonSets.trigonometry = true;
|
|
743
743
|
break;
|
|
744
|
+
case "scientific":
|
|
745
|
+
keypadButtonSets.scientific = true;
|
|
746
|
+
break;
|
|
744
747
|
}
|
|
745
748
|
});
|
|
746
749
|
return keypadButtonSets;
|
|
@@ -3821,7 +3824,9 @@ ZoomServiceClass.prototype._scrollHandler = function (e) {
|
|
|
3821
3824
|
}
|
|
3822
3825
|
};
|
|
3823
3826
|
ZoomServiceClass.prototype._keyHandler = function (e) {
|
|
3824
|
-
|
|
3827
|
+
// 27: Esc, 13: Enter, 32: Space
|
|
3828
|
+
const keyCodes = [27, 13, 32];
|
|
3829
|
+
if (keyCodes.includes(e.keyCode)) {
|
|
3825
3830
|
this._activeZoomClose();
|
|
3826
3831
|
}
|
|
3827
3832
|
};
|
|
@@ -3910,6 +3915,7 @@ Zoom.prototype.zoomImage = function () {
|
|
|
3910
3915
|
}.bind(this);
|
|
3911
3916
|
img.src = this._targetImage.src;
|
|
3912
3917
|
img.alt = this._targetImage.alt;
|
|
3918
|
+
img.tabIndex = 0;
|
|
3913
3919
|
this.$zoomedImage = $zoomedImage;
|
|
3914
3920
|
};
|
|
3915
3921
|
Zoom.prototype._zoomOriginal = function () {
|
|
@@ -17020,6 +17026,7 @@ class ImageLoader extends React.Component {
|
|
|
17020
17026
|
}
|
|
17021
17027
|
const staticUrl = getDependencies().staticUrl;
|
|
17022
17028
|
return /*#__PURE__*/React.createElement("img", _extends({
|
|
17029
|
+
tabIndex: 0,
|
|
17023
17030
|
src: staticUrl(src),
|
|
17024
17031
|
onKeyUp: onKeyUp,
|
|
17025
17032
|
onKeyDown: onKeyDown
|
|
@@ -22219,7 +22226,7 @@ function scoreNumericInput(userInput, rubric, strings) {
|
|
|
22219
22226
|
// We may have received TeX; try to parse it before grading.
|
|
22220
22227
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
22221
22228
|
const currentValue = ParseTex(userInput.currentValue);
|
|
22222
|
-
const normalizedAnswerExpected = rubric.answers.filter(answer => answer.status === "correct").every(answer => answer.value
|
|
22229
|
+
const normalizedAnswerExpected = rubric.answers.filter(answer => answer.status === "correct").every(answer => answer.value != null && Math.abs(answer.value) <= 1);
|
|
22223
22230
|
|
|
22224
22231
|
// The coefficient is an attribute of the widget
|
|
22225
22232
|
let localValue = currentValue;
|
|
@@ -27455,6 +27462,7 @@ class Dropdown extends React.Component {
|
|
|
27455
27462
|
}, this.props.visibleLabel), /*#__PURE__*/React.createElement(SingleSelect, {
|
|
27456
27463
|
id: dropdownId,
|
|
27457
27464
|
placeholder: "",
|
|
27465
|
+
className: "perseus-dropdown",
|
|
27458
27466
|
onChange: value => this._handleChange(parseInt(value)),
|
|
27459
27467
|
selectedValue: String(this.props.selected),
|
|
27460
27468
|
disabled: this.props.apiOptions.readOnly,
|
|
@@ -34131,10 +34139,10 @@ function AngleGraph(props) {
|
|
|
34131
34139
|
angleMeasure,
|
|
34132
34140
|
vertexX: srFormatNumber(coords[1][X], locale),
|
|
34133
34141
|
vertexY: srFormatNumber(coords[1][Y], locale),
|
|
34134
|
-
|
|
34135
|
-
|
|
34136
|
-
|
|
34137
|
-
|
|
34142
|
+
startingSideX: srFormatNumber(coords[2][X], locale),
|
|
34143
|
+
startingSideY: srFormatNumber(coords[2][Y], locale),
|
|
34144
|
+
endingSideX: srFormatNumber(coords[0][X], locale),
|
|
34145
|
+
endingSideY: srFormatNumber(coords[0][Y], locale)
|
|
34138
34146
|
});
|
|
34139
34147
|
const formatCoordinates = (x, y) => ({
|
|
34140
34148
|
x: srFormatNumber(x, locale),
|
|
@@ -45552,7 +45560,7 @@ const init = function init() {
|
|
|
45552
45560
|
|
|
45553
45561
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45554
45562
|
const libName = "@khanacademy/perseus";
|
|
45555
|
-
const libVersion = "49.
|
|
45563
|
+
const libVersion = "49.2.0";
|
|
45556
45564
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45557
45565
|
|
|
45558
45566
|
const apiVersion = {
|
|
@@ -47837,10 +47845,10 @@ class UnionBuilder {
|
|
|
47837
47845
|
this.parser = parser;
|
|
47838
47846
|
}
|
|
47839
47847
|
or(newBranch) {
|
|
47840
|
-
return new UnionBuilder(either(this.parser, newBranch));
|
|
47848
|
+
return new UnionBuilder(either$1(this.parser, newBranch));
|
|
47841
47849
|
}
|
|
47842
47850
|
}
|
|
47843
|
-
function either(parseA, parseB) {
|
|
47851
|
+
function either$1(parseA, parseB) {
|
|
47844
47852
|
return (rawValue, ctx) => {
|
|
47845
47853
|
const resultA = parseA(rawValue, ctx);
|
|
47846
47854
|
if (isSuccess(resultA)) {
|
|
@@ -47942,22 +47950,107 @@ const parseExplanationWidget = parseWidget(constant("explanation"), object({
|
|
|
47942
47950
|
// We wrap parseWidgetsMap in a function here to make sure it is not
|
|
47943
47951
|
// referenced before it is defined. There is an import cycle between
|
|
47944
47952
|
// this file and widgets-map.ts that could cause it to be undefined.
|
|
47945
|
-
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
47946
|
-
static: boolean
|
|
47953
|
+
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
|
|
47954
|
+
static: defaulted(boolean, () => false)
|
|
47947
47955
|
}));
|
|
47948
47956
|
|
|
47949
|
-
|
|
47950
|
-
|
|
47951
|
-
|
|
47952
|
-
|
|
47957
|
+
// Given a function, creates a PartialParser that converts one type to another
|
|
47958
|
+
// using that function. The returned parser never fails.
|
|
47959
|
+
function convert(f) {
|
|
47960
|
+
return (rawValue, ctx) => ctx.success(f(rawValue));
|
|
47961
|
+
}
|
|
47962
|
+
|
|
47963
|
+
/**
|
|
47964
|
+
* Creates a parser for a widget options type with multiple major versions. Old
|
|
47965
|
+
* versions are migrated to the latest version. The parse fails if the input
|
|
47966
|
+
* data does not match any of the versions.
|
|
47967
|
+
*
|
|
47968
|
+
* @example
|
|
47969
|
+
* const parseOptions = versionedWidgetOptions(parseOptionsV3)
|
|
47970
|
+
* .withMigrationFrom(parseOptionsV2, migrateV2ToV3)
|
|
47971
|
+
* .withMigrationFrom(parseOptionsV1, migrateV1ToV2)
|
|
47972
|
+
* .withMigrationFrom(parseOptionsV0, migrateV0ToV1)
|
|
47973
|
+
* .parser;
|
|
47974
|
+
*
|
|
47975
|
+
* @param parseLatest a {@link Parser} for the latest version of the widget
|
|
47976
|
+
* options. This should check version.major and fail if it's not the latest
|
|
47977
|
+
* version.
|
|
47978
|
+
* @returns a builder object, to which migrations from earlier versions can be
|
|
47979
|
+
* added. Migrations must be added in "reverse chronological" order as in the
|
|
47980
|
+
* example above.
|
|
47981
|
+
*/
|
|
47982
|
+
function versionedWidgetOptions(parseLatest) {
|
|
47983
|
+
return new VersionedWidgetOptionsParserBuilder(parseLatest, latest => latest);
|
|
47984
|
+
}
|
|
47985
|
+
class VersionedWidgetOptionsParserBuilder {
|
|
47986
|
+
constructor(parser, migrate) {
|
|
47987
|
+
this.parser = parser;
|
|
47988
|
+
this.migrate = migrate;
|
|
47989
|
+
}
|
|
47990
|
+
|
|
47991
|
+
/**
|
|
47992
|
+
* Add a migration from an old version of the widget options.
|
|
47993
|
+
*
|
|
47994
|
+
* @returns a VersionedWidgetOptionsParserBuilder whose `parser` function
|
|
47995
|
+
* is capable of migrating the old version to the latest version. The parser
|
|
47996
|
+
* will always return the latest version of the widget options on a
|
|
47997
|
+
* successful parse.
|
|
47998
|
+
* @param parseOldVersion should be a {@link Parser} for the old options
|
|
47999
|
+
* type. It should fail if version.major isn't correct.
|
|
48000
|
+
* @param migrateToNextVersion should migrate the `Old` data to the
|
|
48001
|
+
* `Migratable` version of the current VersionedWidgetOptionsParserBuilder.
|
|
48002
|
+
* Usually, this means migrating to the next major version.
|
|
48003
|
+
*/
|
|
48004
|
+
withMigrationFrom(parseOldVersion, migrateToNextVersion) {
|
|
48005
|
+
const parseLatestVersion = this.parser;
|
|
48006
|
+
const migrateToLatest = old => this.migrate(migrateToNextVersion(old));
|
|
48007
|
+
return new VersionedWidgetOptionsParserBuilder((raw, ctx) => {
|
|
48008
|
+
const resultOfParsingLatest = parseLatestVersion(raw, ctx);
|
|
48009
|
+
if (isSuccess(resultOfParsingLatest)) {
|
|
48010
|
+
return resultOfParsingLatest;
|
|
48011
|
+
}
|
|
48012
|
+
const resultOfParsingOld = parseOldVersion(raw, ctx);
|
|
48013
|
+
if (isSuccess(resultOfParsingOld)) {
|
|
48014
|
+
return success(migrateToLatest(resultOfParsingOld.value));
|
|
48015
|
+
}
|
|
48016
|
+
|
|
48017
|
+
// If we're here, neither parse succeeded. Return the failure.
|
|
48018
|
+
return resultOfParsingOld;
|
|
48019
|
+
}, migrateToLatest);
|
|
48020
|
+
}
|
|
48021
|
+
}
|
|
48022
|
+
|
|
48023
|
+
const parsePossiblyInvalidAnswerForm = object({
|
|
48024
|
+
// `value` is the possibly invalid part of this. It should always be a
|
|
48025
|
+
// string, but some answer forms don't have it. The Expression widget
|
|
48026
|
+
// ignores invalid values, so we can safely filter them out during parsing.
|
|
48027
|
+
value: optional(string),
|
|
48028
|
+
form: defaulted(boolean, () => false),
|
|
48029
|
+
simplify: defaulted(boolean, () => false),
|
|
47953
48030
|
considered: enumeration("correct", "wrong", "ungraded"),
|
|
47954
48031
|
key: pipeParsers(optional(union(string).or(number).parser)).then((key, ctx) => ctx.success(String(key))).parser
|
|
47955
48032
|
});
|
|
47956
|
-
|
|
48033
|
+
function removeInvalidAnswerForms(possiblyInvalid) {
|
|
48034
|
+
const valid = [];
|
|
48035
|
+
for (const answerForm of possiblyInvalid) {
|
|
48036
|
+
const {
|
|
48037
|
+
value
|
|
48038
|
+
} = answerForm;
|
|
48039
|
+
if (value != null) {
|
|
48040
|
+
// Copying the object seems to be needed to make TypeScript happy
|
|
48041
|
+
valid.push(_extends({}, answerForm, {
|
|
48042
|
+
value
|
|
48043
|
+
}));
|
|
48044
|
+
}
|
|
48045
|
+
}
|
|
48046
|
+
return valid;
|
|
48047
|
+
}
|
|
48048
|
+
const version1 = object({
|
|
47957
48049
|
major: constant(1),
|
|
47958
48050
|
minor: number
|
|
47959
|
-
})
|
|
47960
|
-
|
|
48051
|
+
});
|
|
48052
|
+
const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expression"), object({
|
|
48053
|
+
answerForms: pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,
|
|
47961
48054
|
functions: array(string),
|
|
47962
48055
|
times: boolean,
|
|
47963
48056
|
visibleLabel: optional(string),
|
|
@@ -47965,10 +48058,11 @@ const parseExpressionWidgetV1 = parseWidgetWithVersion(object({
|
|
|
47965
48058
|
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
47966
48059
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
47967
48060
|
}));
|
|
47968
|
-
const
|
|
48061
|
+
const version0 = optional(object({
|
|
47969
48062
|
major: constant(0),
|
|
47970
48063
|
minor: number
|
|
47971
|
-
}))
|
|
48064
|
+
}));
|
|
48065
|
+
const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expression"), object({
|
|
47972
48066
|
functions: array(string),
|
|
47973
48067
|
times: boolean,
|
|
47974
48068
|
visibleLabel: optional(string),
|
|
@@ -47979,11 +48073,11 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(optional(object({
|
|
|
47979
48073
|
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
47980
48074
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
47981
48075
|
}));
|
|
47982
|
-
function migrateV0ToV1(widget
|
|
48076
|
+
function migrateV0ToV1(widget) {
|
|
47983
48077
|
const {
|
|
47984
48078
|
options
|
|
47985
48079
|
} = widget;
|
|
47986
|
-
return
|
|
48080
|
+
return _extends({}, widget, {
|
|
47987
48081
|
version: {
|
|
47988
48082
|
major: 1,
|
|
47989
48083
|
minor: 0
|
|
@@ -48002,9 +48096,9 @@ function migrateV0ToV1(widget, ctx) {
|
|
|
48002
48096
|
value: options.value
|
|
48003
48097
|
}]
|
|
48004
48098
|
}
|
|
48005
|
-
})
|
|
48099
|
+
});
|
|
48006
48100
|
}
|
|
48007
|
-
const parseExpressionWidget =
|
|
48101
|
+
const parseExpressionWidget = versionedWidgetOptions(parseExpressionWidgetV1).withMigrationFrom(parseExpressionWidgetV0, migrateV0ToV1).parser;
|
|
48008
48102
|
|
|
48009
48103
|
const parsePerseusRenderer = defaulted(object({
|
|
48010
48104
|
// TODO(benchristel): content is also defaulted to empty string in
|
|
@@ -48050,31 +48144,70 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
|
|
|
48050
48144
|
gradedGroups: array(parseGradedGroupWidgetOptions)
|
|
48051
48145
|
}));
|
|
48052
48146
|
|
|
48147
|
+
// discriminatedUnion() should be preferred over union() when parsing a
|
|
48148
|
+
// discriminated union type, because discriminatedUnion() produces more
|
|
48149
|
+
// understandable failure messages. It takes the discriminant as the source of
|
|
48150
|
+
// truth for which variant is to be parsed, and expects the other data to match
|
|
48151
|
+
// that variant.
|
|
48152
|
+
function discriminatedUnion(narrow, parseVariant) {
|
|
48153
|
+
return new DiscriminatedUnionBuilder(pipeParsers(narrow).then(parseVariant).parser);
|
|
48154
|
+
}
|
|
48155
|
+
class DiscriminatedUnionBuilder {
|
|
48156
|
+
constructor(parser) {
|
|
48157
|
+
this.parser = parser;
|
|
48158
|
+
}
|
|
48159
|
+
or(narrow, parseVariant) {
|
|
48160
|
+
return new DiscriminatedUnionBuilder(either(narrow, parseVariant, this.parser));
|
|
48161
|
+
}
|
|
48162
|
+
}
|
|
48163
|
+
function either(narrowToA, parseA, parseB) {
|
|
48164
|
+
return (rawValue, ctx) => {
|
|
48165
|
+
if (isSuccess(narrowToA(rawValue, ctx))) {
|
|
48166
|
+
return parseA(rawValue, ctx);
|
|
48167
|
+
}
|
|
48168
|
+
return parseB(rawValue, ctx);
|
|
48169
|
+
};
|
|
48170
|
+
}
|
|
48171
|
+
|
|
48053
48172
|
const pairOfNumbers$3 = pair(number, number);
|
|
48054
48173
|
const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
|
|
48055
48174
|
const parseGrapherWidget = parseWidget(constant("grapher"), object({
|
|
48056
48175
|
availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
|
|
48057
|
-
correct:
|
|
48176
|
+
correct: discriminatedUnion(object({
|
|
48177
|
+
type: constant("absolute_value")
|
|
48178
|
+
}), object({
|
|
48058
48179
|
type: constant("absolute_value"),
|
|
48059
48180
|
coords: pairOfPoints
|
|
48060
48181
|
})).or(object({
|
|
48182
|
+
type: constant("exponential")
|
|
48183
|
+
}), object({
|
|
48061
48184
|
type: constant("exponential"),
|
|
48062
48185
|
asymptote: pairOfPoints,
|
|
48063
48186
|
coords: pairOfPoints
|
|
48064
48187
|
})).or(object({
|
|
48188
|
+
type: constant("linear")
|
|
48189
|
+
}), object({
|
|
48065
48190
|
type: constant("linear"),
|
|
48066
48191
|
coords: pairOfPoints
|
|
48067
48192
|
})).or(object({
|
|
48193
|
+
type: constant("logarithm")
|
|
48194
|
+
}), object({
|
|
48068
48195
|
type: constant("logarithm"),
|
|
48069
48196
|
asymptote: pairOfPoints,
|
|
48070
48197
|
coords: pairOfPoints
|
|
48071
48198
|
})).or(object({
|
|
48199
|
+
type: constant("quadratic")
|
|
48200
|
+
}), object({
|
|
48072
48201
|
type: constant("quadratic"),
|
|
48073
48202
|
coords: pairOfPoints
|
|
48074
48203
|
})).or(object({
|
|
48204
|
+
type: constant("sinusoid")
|
|
48205
|
+
}), object({
|
|
48075
48206
|
type: constant("sinusoid"),
|
|
48076
48207
|
coords: pairOfPoints
|
|
48077
48208
|
})).or(object({
|
|
48209
|
+
type: constant("tangent")
|
|
48210
|
+
}), object({
|
|
48078
48211
|
type: constant("tangent"),
|
|
48079
48212
|
coords: pairOfPoints
|
|
48080
48213
|
})).parser,
|
|
@@ -48134,15 +48267,22 @@ const stringToNumber = (rawValue, ctx) => {
|
|
|
48134
48267
|
return ctx.success(parsedNumber);
|
|
48135
48268
|
};
|
|
48136
48269
|
|
|
48137
|
-
|
|
48270
|
+
function emptyToZero(x) {
|
|
48271
|
+
return x === "" ? 0 : x;
|
|
48272
|
+
}
|
|
48273
|
+
const imageDimensionToNumber = pipeParsers(union(number).or(string).parser)
|
|
48274
|
+
// In this specific case, empty string is equivalent to zero. An empty
|
|
48275
|
+
// string parses to either NaN (using parseInt) or 0 (using unary +) and
|
|
48276
|
+
// CSS will treat NaN as invalid and default to 0 instead.
|
|
48277
|
+
.then(convert(emptyToZero)).then(stringToNumber).parser;
|
|
48138
48278
|
const parsePerseusImageBackground = object({
|
|
48139
48279
|
url: optional(nullable(string)),
|
|
48140
|
-
width: optional(
|
|
48141
|
-
height: optional(
|
|
48142
|
-
top: optional(
|
|
48143
|
-
left: optional(
|
|
48144
|
-
bottom: optional(
|
|
48145
|
-
scale: optional(
|
|
48280
|
+
width: optional(imageDimensionToNumber),
|
|
48281
|
+
height: optional(imageDimensionToNumber),
|
|
48282
|
+
top: optional(imageDimensionToNumber),
|
|
48283
|
+
left: optional(imageDimensionToNumber),
|
|
48284
|
+
bottom: optional(imageDimensionToNumber),
|
|
48285
|
+
scale: optional(imageDimensionToNumber)
|
|
48146
48286
|
});
|
|
48147
48287
|
|
|
48148
48288
|
const pairOfNumbers$2 = pair(number, number);
|
|
@@ -48183,8 +48323,10 @@ const parseInputNumberWidget = parseWidget(constant("input-number"), object({
|
|
|
48183
48323
|
}));
|
|
48184
48324
|
|
|
48185
48325
|
const pairOfNumbers$1 = pair(number, number);
|
|
48326
|
+
const stringOrEmpty = defaulted(string, () => "");
|
|
48327
|
+
const parseFunctionType = constant("function");
|
|
48186
48328
|
const parseFunctionElement = object({
|
|
48187
|
-
type:
|
|
48329
|
+
type: parseFunctionType,
|
|
48188
48330
|
key: string,
|
|
48189
48331
|
options: object({
|
|
48190
48332
|
value: string,
|
|
@@ -48196,8 +48338,9 @@ const parseFunctionElement = object({
|
|
|
48196
48338
|
strokeWidth: number
|
|
48197
48339
|
})
|
|
48198
48340
|
});
|
|
48341
|
+
const parseLabelType = constant("label");
|
|
48199
48342
|
const parseLabelElement = object({
|
|
48200
|
-
type:
|
|
48343
|
+
type: parseLabelType,
|
|
48201
48344
|
key: string,
|
|
48202
48345
|
options: object({
|
|
48203
48346
|
label: string,
|
|
@@ -48206,8 +48349,9 @@ const parseLabelElement = object({
|
|
|
48206
48349
|
coordY: string
|
|
48207
48350
|
})
|
|
48208
48351
|
});
|
|
48352
|
+
const parseLineType = constant("line");
|
|
48209
48353
|
const parseLineElement = object({
|
|
48210
|
-
type:
|
|
48354
|
+
type: parseLineType,
|
|
48211
48355
|
key: string,
|
|
48212
48356
|
options: object({
|
|
48213
48357
|
color: string,
|
|
@@ -48220,8 +48364,9 @@ const parseLineElement = object({
|
|
|
48220
48364
|
arrows: string
|
|
48221
48365
|
})
|
|
48222
48366
|
});
|
|
48367
|
+
const parseMovableLineType = constant("movable-line");
|
|
48223
48368
|
const parseMovableLineElement = object({
|
|
48224
|
-
type:
|
|
48369
|
+
type: parseMovableLineType,
|
|
48225
48370
|
key: string,
|
|
48226
48371
|
options: object({
|
|
48227
48372
|
startX: string,
|
|
@@ -48239,8 +48384,9 @@ const parseMovableLineElement = object({
|
|
|
48239
48384
|
constraintYMax: string
|
|
48240
48385
|
})
|
|
48241
48386
|
});
|
|
48387
|
+
const parseMovablePointType = constant("movable-point");
|
|
48242
48388
|
const parseMovablePointElement = object({
|
|
48243
|
-
type:
|
|
48389
|
+
type: parseMovablePointType,
|
|
48244
48390
|
key: string,
|
|
48245
48391
|
options: object({
|
|
48246
48392
|
startX: string,
|
|
@@ -48249,14 +48395,15 @@ const parseMovablePointElement = object({
|
|
|
48249
48395
|
constraint: string,
|
|
48250
48396
|
snap: number,
|
|
48251
48397
|
constraintFn: string,
|
|
48252
|
-
constraintXMin:
|
|
48253
|
-
constraintXMax:
|
|
48254
|
-
constraintYMin:
|
|
48255
|
-
constraintYMax:
|
|
48398
|
+
constraintXMin: stringOrEmpty,
|
|
48399
|
+
constraintXMax: stringOrEmpty,
|
|
48400
|
+
constraintYMin: stringOrEmpty,
|
|
48401
|
+
constraintYMax: stringOrEmpty
|
|
48256
48402
|
})
|
|
48257
48403
|
});
|
|
48404
|
+
const parseParametricType = constant("parametric");
|
|
48258
48405
|
const parseParametricElement = object({
|
|
48259
|
-
type:
|
|
48406
|
+
type: parseParametricType,
|
|
48260
48407
|
key: string,
|
|
48261
48408
|
options: object({
|
|
48262
48409
|
x: string,
|
|
@@ -48268,8 +48415,9 @@ const parseParametricElement = object({
|
|
|
48268
48415
|
strokeWidth: number
|
|
48269
48416
|
})
|
|
48270
48417
|
});
|
|
48418
|
+
const parsePointType = constant("point");
|
|
48271
48419
|
const parsePointElement = object({
|
|
48272
|
-
type:
|
|
48420
|
+
type: parsePointType,
|
|
48273
48421
|
key: string,
|
|
48274
48422
|
options: object({
|
|
48275
48423
|
color: string,
|
|
@@ -48277,8 +48425,9 @@ const parsePointElement = object({
|
|
|
48277
48425
|
coordY: string
|
|
48278
48426
|
})
|
|
48279
48427
|
});
|
|
48428
|
+
const parseRectangleType = constant("rectangle");
|
|
48280
48429
|
const parseRectangleElement = object({
|
|
48281
|
-
type:
|
|
48430
|
+
type: parseRectangleType,
|
|
48282
48431
|
key: string,
|
|
48283
48432
|
options: object({
|
|
48284
48433
|
color: string,
|
|
@@ -48289,7 +48438,7 @@ const parseRectangleElement = object({
|
|
|
48289
48438
|
})
|
|
48290
48439
|
});
|
|
48291
48440
|
const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
48292
|
-
static: boolean,
|
|
48441
|
+
static: defaulted(boolean, () => false),
|
|
48293
48442
|
graph: object({
|
|
48294
48443
|
editableSettings: optional(array(enumeration("canvas", "graph"))),
|
|
48295
48444
|
box: pairOfNumbers$1,
|
|
@@ -48306,7 +48455,23 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
|
48306
48455
|
rulerTicks: optional(number),
|
|
48307
48456
|
tickStep: pairOfNumbers$1
|
|
48308
48457
|
}),
|
|
48309
|
-
elements: array(
|
|
48458
|
+
elements: array(discriminatedUnion(object({
|
|
48459
|
+
type: parseFunctionType
|
|
48460
|
+
}), parseFunctionElement).or(object({
|
|
48461
|
+
type: parseLabelType
|
|
48462
|
+
}), parseLabelElement).or(object({
|
|
48463
|
+
type: parseLineType
|
|
48464
|
+
}), parseLineElement).or(object({
|
|
48465
|
+
type: parseMovableLineType
|
|
48466
|
+
}), parseMovableLineElement).or(object({
|
|
48467
|
+
type: parseMovablePointType
|
|
48468
|
+
}), parseMovablePointElement).or(object({
|
|
48469
|
+
type: parseParametricType
|
|
48470
|
+
}), parseParametricElement).or(object({
|
|
48471
|
+
type: parsePointType
|
|
48472
|
+
}), parsePointElement).or(object({
|
|
48473
|
+
type: parseRectangleType
|
|
48474
|
+
}), parseRectangleElement).parser)
|
|
48310
48475
|
}));
|
|
48311
48476
|
|
|
48312
48477
|
// Used to represent 2-D points and ranges
|
|
@@ -48529,10 +48694,11 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
|
48529
48694
|
padding: boolean
|
|
48530
48695
|
}));
|
|
48531
48696
|
|
|
48697
|
+
const numeric = pipeParsers(union(number).or(string).parser).then(stringToNumber).parser;
|
|
48532
48698
|
const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
|
|
48533
48699
|
prefix: optional(string),
|
|
48534
48700
|
suffix: optional(string),
|
|
48535
|
-
answers: array(array(
|
|
48701
|
+
answers: array(array(numeric)),
|
|
48536
48702
|
cursorPosition: optional(array(number)),
|
|
48537
48703
|
matrixBoardSize: array(number),
|
|
48538
48704
|
static: optional(boolean)
|
|
@@ -48580,12 +48746,18 @@ const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "d
|
|
|
48580
48746
|
const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
48581
48747
|
answers: array(object({
|
|
48582
48748
|
message: string,
|
|
48583
|
-
|
|
48749
|
+
// TODO(benchristel): value should never be null or undefined,
|
|
48750
|
+
// but we have some content where it is anyway. If we backfill
|
|
48751
|
+
// the data, simplify this.
|
|
48752
|
+
value: optional(nullable(number)),
|
|
48584
48753
|
status: string,
|
|
48585
48754
|
answerForms: optional(array(parseMathFormat)),
|
|
48586
48755
|
strict: boolean,
|
|
48587
48756
|
maxError: optional(nullable(number)),
|
|
48588
|
-
|
|
48757
|
+
// TODO(benchristel): simplify should never be `true`, but we
|
|
48758
|
+
// have some content where it is anyway. If we ever backfill
|
|
48759
|
+
// the data, we should simplify `simplify`.
|
|
48760
|
+
simplify: optional(nullable(union(string).or(pipeParsers(constant(true)).then(convert(String)).parser).parser))
|
|
48589
48761
|
})),
|
|
48590
48762
|
labelText: optional(string),
|
|
48591
48763
|
size: string,
|
|
@@ -48621,7 +48793,7 @@ const parseOrdererWidget = parseWidget(constant("orderer"), object({
|
|
|
48621
48793
|
const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
48622
48794
|
passageNumber: number,
|
|
48623
48795
|
referenceNumber: number,
|
|
48624
|
-
summaryText: string
|
|
48796
|
+
summaryText: optional(string)
|
|
48625
48797
|
}));
|
|
48626
48798
|
|
|
48627
48799
|
const parsePassageWidget = parseWidget(constant("passage"), object({
|
|
@@ -48809,6 +48981,8 @@ const parseWidgetsMapEntry = ([key, widget], widgetMap, ctx) => {
|
|
|
48809
48981
|
// sequence is a deprecated widget type, and the corresponding
|
|
48810
48982
|
// widget component no longer exists.
|
|
48811
48983
|
return parseAndAssign(`sequence ${id}`, parseDeprecatedWidget);
|
|
48984
|
+
case "lights-puzzle":
|
|
48985
|
+
return parseAndAssign(`lights-puzzle ${id}`, parseDeprecatedWidget);
|
|
48812
48986
|
default:
|
|
48813
48987
|
if (getWidget(type)) {
|
|
48814
48988
|
// @ts-expect-error - 'type' is not a valid widget type
|