@khanacademy/perseus 49.1.6 → 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 +231 -59
- 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 +232 -59
- 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;
|
|
@@ -27427,8 +27434,7 @@ class Dropdown extends React.Component {
|
|
|
27427
27434
|
disabled: true,
|
|
27428
27435
|
label: /*#__PURE__*/React.createElement(Renderer, {
|
|
27429
27436
|
content: this.props.placeholder,
|
|
27430
|
-
strings: this.context.strings
|
|
27431
|
-
inline: true
|
|
27437
|
+
strings: this.context.strings
|
|
27432
27438
|
}),
|
|
27433
27439
|
labelAsText: this.props.placeholder
|
|
27434
27440
|
}), ...this.props.choices.map((choice, i) => /*#__PURE__*/React.createElement(OptionItem, {
|
|
@@ -27436,8 +27442,7 @@ class Dropdown extends React.Component {
|
|
|
27436
27442
|
value: String(i + 1),
|
|
27437
27443
|
label: /*#__PURE__*/React.createElement(Renderer, {
|
|
27438
27444
|
content: choice,
|
|
27439
|
-
strings: this.context.strings
|
|
27440
|
-
inline: true
|
|
27445
|
+
strings: this.context.strings
|
|
27441
27446
|
}),
|
|
27442
27447
|
labelAsText: choice
|
|
27443
27448
|
}))];
|
|
@@ -27457,6 +27462,7 @@ class Dropdown extends React.Component {
|
|
|
27457
27462
|
}, this.props.visibleLabel), /*#__PURE__*/React.createElement(SingleSelect, {
|
|
27458
27463
|
id: dropdownId,
|
|
27459
27464
|
placeholder: "",
|
|
27465
|
+
className: "perseus-dropdown",
|
|
27460
27466
|
onChange: value => this._handleChange(parseInt(value)),
|
|
27461
27467
|
selectedValue: String(this.props.selected),
|
|
27462
27468
|
disabled: this.props.apiOptions.readOnly,
|
|
@@ -34133,10 +34139,10 @@ function AngleGraph(props) {
|
|
|
34133
34139
|
angleMeasure,
|
|
34134
34140
|
vertexX: srFormatNumber(coords[1][X], locale),
|
|
34135
34141
|
vertexY: srFormatNumber(coords[1][Y], locale),
|
|
34136
|
-
|
|
34137
|
-
|
|
34138
|
-
|
|
34139
|
-
|
|
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)
|
|
34140
34146
|
});
|
|
34141
34147
|
const formatCoordinates = (x, y) => ({
|
|
34142
34148
|
x: srFormatNumber(x, locale),
|
|
@@ -45554,7 +45560,7 @@ const init = function init() {
|
|
|
45554
45560
|
|
|
45555
45561
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45556
45562
|
const libName = "@khanacademy/perseus";
|
|
45557
|
-
const libVersion = "49.
|
|
45563
|
+
const libVersion = "49.2.0";
|
|
45558
45564
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45559
45565
|
|
|
45560
45566
|
const apiVersion = {
|
|
@@ -47839,10 +47845,10 @@ class UnionBuilder {
|
|
|
47839
47845
|
this.parser = parser;
|
|
47840
47846
|
}
|
|
47841
47847
|
or(newBranch) {
|
|
47842
|
-
return new UnionBuilder(either(this.parser, newBranch));
|
|
47848
|
+
return new UnionBuilder(either$1(this.parser, newBranch));
|
|
47843
47849
|
}
|
|
47844
47850
|
}
|
|
47845
|
-
function either(parseA, parseB) {
|
|
47851
|
+
function either$1(parseA, parseB) {
|
|
47846
47852
|
return (rawValue, ctx) => {
|
|
47847
47853
|
const resultA = parseA(rawValue, ctx);
|
|
47848
47854
|
if (isSuccess(resultA)) {
|
|
@@ -47944,22 +47950,107 @@ const parseExplanationWidget = parseWidget(constant("explanation"), object({
|
|
|
47944
47950
|
// We wrap parseWidgetsMap in a function here to make sure it is not
|
|
47945
47951
|
// referenced before it is defined. There is an import cycle between
|
|
47946
47952
|
// this file and widgets-map.ts that could cause it to be undefined.
|
|
47947
|
-
widgets: (rawVal, ctx) => parseWidgetsMap(rawVal, ctx),
|
|
47948
|
-
static: boolean
|
|
47953
|
+
widgets: defaulted((rawVal, ctx) => parseWidgetsMap(rawVal, ctx), () => ({})),
|
|
47954
|
+
static: defaulted(boolean, () => false)
|
|
47949
47955
|
}));
|
|
47950
47956
|
|
|
47951
|
-
|
|
47952
|
-
|
|
47953
|
-
|
|
47954
|
-
|
|
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),
|
|
47955
48030
|
considered: enumeration("correct", "wrong", "ungraded"),
|
|
47956
48031
|
key: pipeParsers(optional(union(string).or(number).parser)).then((key, ctx) => ctx.success(String(key))).parser
|
|
47957
48032
|
});
|
|
47958
|
-
|
|
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({
|
|
47959
48049
|
major: constant(1),
|
|
47960
48050
|
minor: number
|
|
47961
|
-
})
|
|
47962
|
-
|
|
48051
|
+
});
|
|
48052
|
+
const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expression"), object({
|
|
48053
|
+
answerForms: pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,
|
|
47963
48054
|
functions: array(string),
|
|
47964
48055
|
times: boolean,
|
|
47965
48056
|
visibleLabel: optional(string),
|
|
@@ -47967,10 +48058,11 @@ const parseExpressionWidgetV1 = parseWidgetWithVersion(object({
|
|
|
47967
48058
|
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
47968
48059
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
47969
48060
|
}));
|
|
47970
|
-
const
|
|
48061
|
+
const version0 = optional(object({
|
|
47971
48062
|
major: constant(0),
|
|
47972
48063
|
minor: number
|
|
47973
|
-
}))
|
|
48064
|
+
}));
|
|
48065
|
+
const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expression"), object({
|
|
47974
48066
|
functions: array(string),
|
|
47975
48067
|
times: boolean,
|
|
47976
48068
|
visibleLabel: optional(string),
|
|
@@ -47981,11 +48073,11 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(optional(object({
|
|
|
47981
48073
|
buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
|
|
47982
48074
|
buttonsVisible: optional(enumeration("always", "never", "focused"))
|
|
47983
48075
|
}));
|
|
47984
|
-
function migrateV0ToV1(widget
|
|
48076
|
+
function migrateV0ToV1(widget) {
|
|
47985
48077
|
const {
|
|
47986
48078
|
options
|
|
47987
48079
|
} = widget;
|
|
47988
|
-
return
|
|
48080
|
+
return _extends({}, widget, {
|
|
47989
48081
|
version: {
|
|
47990
48082
|
major: 1,
|
|
47991
48083
|
minor: 0
|
|
@@ -48004,9 +48096,9 @@ function migrateV0ToV1(widget, ctx) {
|
|
|
48004
48096
|
value: options.value
|
|
48005
48097
|
}]
|
|
48006
48098
|
}
|
|
48007
|
-
})
|
|
48099
|
+
});
|
|
48008
48100
|
}
|
|
48009
|
-
const parseExpressionWidget =
|
|
48101
|
+
const parseExpressionWidget = versionedWidgetOptions(parseExpressionWidgetV1).withMigrationFrom(parseExpressionWidgetV0, migrateV0ToV1).parser;
|
|
48010
48102
|
|
|
48011
48103
|
const parsePerseusRenderer = defaulted(object({
|
|
48012
48104
|
// TODO(benchristel): content is also defaulted to empty string in
|
|
@@ -48052,31 +48144,70 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
|
|
|
48052
48144
|
gradedGroups: array(parseGradedGroupWidgetOptions)
|
|
48053
48145
|
}));
|
|
48054
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
|
+
|
|
48055
48172
|
const pairOfNumbers$3 = pair(number, number);
|
|
48056
48173
|
const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
|
|
48057
48174
|
const parseGrapherWidget = parseWidget(constant("grapher"), object({
|
|
48058
48175
|
availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
|
|
48059
|
-
correct:
|
|
48176
|
+
correct: discriminatedUnion(object({
|
|
48177
|
+
type: constant("absolute_value")
|
|
48178
|
+
}), object({
|
|
48060
48179
|
type: constant("absolute_value"),
|
|
48061
48180
|
coords: pairOfPoints
|
|
48062
48181
|
})).or(object({
|
|
48182
|
+
type: constant("exponential")
|
|
48183
|
+
}), object({
|
|
48063
48184
|
type: constant("exponential"),
|
|
48064
48185
|
asymptote: pairOfPoints,
|
|
48065
48186
|
coords: pairOfPoints
|
|
48066
48187
|
})).or(object({
|
|
48188
|
+
type: constant("linear")
|
|
48189
|
+
}), object({
|
|
48067
48190
|
type: constant("linear"),
|
|
48068
48191
|
coords: pairOfPoints
|
|
48069
48192
|
})).or(object({
|
|
48193
|
+
type: constant("logarithm")
|
|
48194
|
+
}), object({
|
|
48070
48195
|
type: constant("logarithm"),
|
|
48071
48196
|
asymptote: pairOfPoints,
|
|
48072
48197
|
coords: pairOfPoints
|
|
48073
48198
|
})).or(object({
|
|
48199
|
+
type: constant("quadratic")
|
|
48200
|
+
}), object({
|
|
48074
48201
|
type: constant("quadratic"),
|
|
48075
48202
|
coords: pairOfPoints
|
|
48076
48203
|
})).or(object({
|
|
48204
|
+
type: constant("sinusoid")
|
|
48205
|
+
}), object({
|
|
48077
48206
|
type: constant("sinusoid"),
|
|
48078
48207
|
coords: pairOfPoints
|
|
48079
48208
|
})).or(object({
|
|
48209
|
+
type: constant("tangent")
|
|
48210
|
+
}), object({
|
|
48080
48211
|
type: constant("tangent"),
|
|
48081
48212
|
coords: pairOfPoints
|
|
48082
48213
|
})).parser,
|
|
@@ -48136,15 +48267,22 @@ const stringToNumber = (rawValue, ctx) => {
|
|
|
48136
48267
|
return ctx.success(parsedNumber);
|
|
48137
48268
|
};
|
|
48138
48269
|
|
|
48139
|
-
|
|
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;
|
|
48140
48278
|
const parsePerseusImageBackground = object({
|
|
48141
48279
|
url: optional(nullable(string)),
|
|
48142
|
-
width: optional(
|
|
48143
|
-
height: optional(
|
|
48144
|
-
top: optional(
|
|
48145
|
-
left: optional(
|
|
48146
|
-
bottom: optional(
|
|
48147
|
-
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)
|
|
48148
48286
|
});
|
|
48149
48287
|
|
|
48150
48288
|
const pairOfNumbers$2 = pair(number, number);
|
|
@@ -48185,8 +48323,10 @@ const parseInputNumberWidget = parseWidget(constant("input-number"), object({
|
|
|
48185
48323
|
}));
|
|
48186
48324
|
|
|
48187
48325
|
const pairOfNumbers$1 = pair(number, number);
|
|
48326
|
+
const stringOrEmpty = defaulted(string, () => "");
|
|
48327
|
+
const parseFunctionType = constant("function");
|
|
48188
48328
|
const parseFunctionElement = object({
|
|
48189
|
-
type:
|
|
48329
|
+
type: parseFunctionType,
|
|
48190
48330
|
key: string,
|
|
48191
48331
|
options: object({
|
|
48192
48332
|
value: string,
|
|
@@ -48198,8 +48338,9 @@ const parseFunctionElement = object({
|
|
|
48198
48338
|
strokeWidth: number
|
|
48199
48339
|
})
|
|
48200
48340
|
});
|
|
48341
|
+
const parseLabelType = constant("label");
|
|
48201
48342
|
const parseLabelElement = object({
|
|
48202
|
-
type:
|
|
48343
|
+
type: parseLabelType,
|
|
48203
48344
|
key: string,
|
|
48204
48345
|
options: object({
|
|
48205
48346
|
label: string,
|
|
@@ -48208,8 +48349,9 @@ const parseLabelElement = object({
|
|
|
48208
48349
|
coordY: string
|
|
48209
48350
|
})
|
|
48210
48351
|
});
|
|
48352
|
+
const parseLineType = constant("line");
|
|
48211
48353
|
const parseLineElement = object({
|
|
48212
|
-
type:
|
|
48354
|
+
type: parseLineType,
|
|
48213
48355
|
key: string,
|
|
48214
48356
|
options: object({
|
|
48215
48357
|
color: string,
|
|
@@ -48222,8 +48364,9 @@ const parseLineElement = object({
|
|
|
48222
48364
|
arrows: string
|
|
48223
48365
|
})
|
|
48224
48366
|
});
|
|
48367
|
+
const parseMovableLineType = constant("movable-line");
|
|
48225
48368
|
const parseMovableLineElement = object({
|
|
48226
|
-
type:
|
|
48369
|
+
type: parseMovableLineType,
|
|
48227
48370
|
key: string,
|
|
48228
48371
|
options: object({
|
|
48229
48372
|
startX: string,
|
|
@@ -48241,8 +48384,9 @@ const parseMovableLineElement = object({
|
|
|
48241
48384
|
constraintYMax: string
|
|
48242
48385
|
})
|
|
48243
48386
|
});
|
|
48387
|
+
const parseMovablePointType = constant("movable-point");
|
|
48244
48388
|
const parseMovablePointElement = object({
|
|
48245
|
-
type:
|
|
48389
|
+
type: parseMovablePointType,
|
|
48246
48390
|
key: string,
|
|
48247
48391
|
options: object({
|
|
48248
48392
|
startX: string,
|
|
@@ -48251,14 +48395,15 @@ const parseMovablePointElement = object({
|
|
|
48251
48395
|
constraint: string,
|
|
48252
48396
|
snap: number,
|
|
48253
48397
|
constraintFn: string,
|
|
48254
|
-
constraintXMin:
|
|
48255
|
-
constraintXMax:
|
|
48256
|
-
constraintYMin:
|
|
48257
|
-
constraintYMax:
|
|
48398
|
+
constraintXMin: stringOrEmpty,
|
|
48399
|
+
constraintXMax: stringOrEmpty,
|
|
48400
|
+
constraintYMin: stringOrEmpty,
|
|
48401
|
+
constraintYMax: stringOrEmpty
|
|
48258
48402
|
})
|
|
48259
48403
|
});
|
|
48404
|
+
const parseParametricType = constant("parametric");
|
|
48260
48405
|
const parseParametricElement = object({
|
|
48261
|
-
type:
|
|
48406
|
+
type: parseParametricType,
|
|
48262
48407
|
key: string,
|
|
48263
48408
|
options: object({
|
|
48264
48409
|
x: string,
|
|
@@ -48270,8 +48415,9 @@ const parseParametricElement = object({
|
|
|
48270
48415
|
strokeWidth: number
|
|
48271
48416
|
})
|
|
48272
48417
|
});
|
|
48418
|
+
const parsePointType = constant("point");
|
|
48273
48419
|
const parsePointElement = object({
|
|
48274
|
-
type:
|
|
48420
|
+
type: parsePointType,
|
|
48275
48421
|
key: string,
|
|
48276
48422
|
options: object({
|
|
48277
48423
|
color: string,
|
|
@@ -48279,8 +48425,9 @@ const parsePointElement = object({
|
|
|
48279
48425
|
coordY: string
|
|
48280
48426
|
})
|
|
48281
48427
|
});
|
|
48428
|
+
const parseRectangleType = constant("rectangle");
|
|
48282
48429
|
const parseRectangleElement = object({
|
|
48283
|
-
type:
|
|
48430
|
+
type: parseRectangleType,
|
|
48284
48431
|
key: string,
|
|
48285
48432
|
options: object({
|
|
48286
48433
|
color: string,
|
|
@@ -48291,7 +48438,7 @@ const parseRectangleElement = object({
|
|
|
48291
48438
|
})
|
|
48292
48439
|
});
|
|
48293
48440
|
const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
48294
|
-
static: boolean,
|
|
48441
|
+
static: defaulted(boolean, () => false),
|
|
48295
48442
|
graph: object({
|
|
48296
48443
|
editableSettings: optional(array(enumeration("canvas", "graph"))),
|
|
48297
48444
|
box: pairOfNumbers$1,
|
|
@@ -48308,7 +48455,23 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
|
|
|
48308
48455
|
rulerTicks: optional(number),
|
|
48309
48456
|
tickStep: pairOfNumbers$1
|
|
48310
48457
|
}),
|
|
48311
|
-
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)
|
|
48312
48475
|
}));
|
|
48313
48476
|
|
|
48314
48477
|
// Used to represent 2-D points and ranges
|
|
@@ -48531,10 +48694,11 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
|
|
|
48531
48694
|
padding: boolean
|
|
48532
48695
|
}));
|
|
48533
48696
|
|
|
48697
|
+
const numeric = pipeParsers(union(number).or(string).parser).then(stringToNumber).parser;
|
|
48534
48698
|
const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
|
|
48535
48699
|
prefix: optional(string),
|
|
48536
48700
|
suffix: optional(string),
|
|
48537
|
-
answers: array(array(
|
|
48701
|
+
answers: array(array(numeric)),
|
|
48538
48702
|
cursorPosition: optional(array(number)),
|
|
48539
48703
|
matrixBoardSize: array(number),
|
|
48540
48704
|
static: optional(boolean)
|
|
@@ -48582,12 +48746,18 @@ const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "d
|
|
|
48582
48746
|
const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
|
|
48583
48747
|
answers: array(object({
|
|
48584
48748
|
message: string,
|
|
48585
|
-
|
|
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)),
|
|
48586
48753
|
status: string,
|
|
48587
48754
|
answerForms: optional(array(parseMathFormat)),
|
|
48588
48755
|
strict: boolean,
|
|
48589
48756
|
maxError: optional(nullable(number)),
|
|
48590
|
-
|
|
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))
|
|
48591
48761
|
})),
|
|
48592
48762
|
labelText: optional(string),
|
|
48593
48763
|
size: string,
|
|
@@ -48623,7 +48793,7 @@ const parseOrdererWidget = parseWidget(constant("orderer"), object({
|
|
|
48623
48793
|
const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
|
|
48624
48794
|
passageNumber: number,
|
|
48625
48795
|
referenceNumber: number,
|
|
48626
|
-
summaryText: string
|
|
48796
|
+
summaryText: optional(string)
|
|
48627
48797
|
}));
|
|
48628
48798
|
|
|
48629
48799
|
const parsePassageWidget = parseWidget(constant("passage"), object({
|
|
@@ -48811,6 +48981,8 @@ const parseWidgetsMapEntry = ([key, widget], widgetMap, ctx) => {
|
|
|
48811
48981
|
// sequence is a deprecated widget type, and the corresponding
|
|
48812
48982
|
// widget component no longer exists.
|
|
48813
48983
|
return parseAndAssign(`sequence ${id}`, parseDeprecatedWidget);
|
|
48984
|
+
case "lights-puzzle":
|
|
48985
|
+
return parseAndAssign(`lights-puzzle ${id}`, parseDeprecatedWidget);
|
|
48814
48986
|
default:
|
|
48815
48987
|
if (getWidget(type)) {
|
|
48816
48988
|
// @ts-expect-error - 'type' is not a valid widget type
|