@khanacademy/perseus 33.3.0 → 34.0.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/sortable.d.ts +1 -4
- package/dist/es/index.js +448 -540
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +444 -542
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +1 -0
- package/dist/perseus-types.d.ts +2 -1
- package/dist/server-item-renderer.d.ts +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/util/answer-types.d.ts +1 -1
- package/dist/widgets/__shared__/noop-validator.d.ts +11 -0
- package/dist/widgets/categorizer/categorizer.d.ts +3 -1
- package/dist/widgets/cs-program/cs-program.d.ts +23 -17
- package/dist/widgets/dropdown/dropdown-validator.d.ts +4 -0
- package/dist/widgets/dropdown/dropdown.d.ts +1 -5
- package/dist/widgets/dropdown/dropdown.types.d.ts +5 -0
- package/dist/widgets/expression/expression-validator.d.ts +5 -0
- package/dist/widgets/expression/expression.d.ts +7 -6
- package/dist/widgets/expression/expression.types.d.ts +3 -0
- package/dist/widgets/expression/get-decimal-separator.d.ts +5 -0
- package/dist/widgets/iframe/iframe.d.ts +24 -15
- package/dist/widgets/interactive-graphs/graph-locked-labels-layer.d.ts +2 -0
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +5 -0
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +2 -1
- package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +2 -1
- package/dist/widgets/label-image/label-image.d.ts +2 -0
- package/dist/widgets/matcher/matcher.d.ts +17 -46
- package/dist/widgets/numeric-input/numeric-input-validator.d.ts +6 -0
- package/dist/widgets/numeric-input/numeric-input.d.ts +1 -5
- package/dist/widgets/numeric-input/numeric-input.types.d.ts +5 -0
- package/dist/widgets/orderer/orderer.d.ts +6 -5
- package/dist/widgets/sorter/sorter.d.ts +18 -44
- package/dist/widgets/table/table.d.ts +20 -24
- package/package.json +1 -1
- package/dist/mixins/widget-jsonify-deprecated.d.ts +0 -5
package/dist/es/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as KAS from '@khanacademy/kas';
|
|
2
2
|
import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
|
|
3
|
-
import { PerseusError, Errors, addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
|
|
4
3
|
import * as PerseusLinter from '@khanacademy/perseus-linter';
|
|
5
|
-
import { linterContextDefault, Rule
|
|
4
|
+
import { linterContextDefault, Rule } from '@khanacademy/perseus-linter';
|
|
6
5
|
import { View, RenderStateRoot, UniqueIDProvider, useLatestRef, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
|
|
7
6
|
import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
|
|
8
7
|
import { HeadingMedium, LabelSmall, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
@@ -18,6 +17,7 @@ import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder
|
|
|
18
17
|
import { color, spacing, font } from '@khanacademy/wonder-blocks-tokens';
|
|
19
18
|
import $ from 'jquery';
|
|
20
19
|
import { v4 } from 'uuid';
|
|
20
|
+
import { PerseusError, Errors, addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
|
|
21
21
|
import PropTypes from 'prop-types';
|
|
22
22
|
import { number, point, vector as vector$1, line } from '@khanacademy/kmath';
|
|
23
23
|
import { entries, UnreachableCaseError, keys } from '@khanacademy/wonder-stuff-core';
|
|
@@ -732,23 +732,6 @@ var dependencies = /*#__PURE__*/Object.freeze({
|
|
|
732
732
|
useDependencies: useDependencies
|
|
733
733
|
});
|
|
734
734
|
|
|
735
|
-
// TODO(LP-11481): Change the primary API we use for logging in Perseus to use
|
|
736
|
-
// getDependencies() internally... simplify usage to just using a "singleton"
|
|
737
|
-
// which get's the injected logger.
|
|
738
|
-
/**
|
|
739
|
-
* Provides logging infrastructure for Perseus
|
|
740
|
-
*/
|
|
741
|
-
const Log = {
|
|
742
|
-
// Logs a message
|
|
743
|
-
log: (message, extra) => {
|
|
744
|
-
getDependencies().Log.log(message, extra);
|
|
745
|
-
},
|
|
746
|
-
// Logs an error with a message
|
|
747
|
-
error: (message, kind, extra) => {
|
|
748
|
-
getDependencies().Log.error(message, kind, extra);
|
|
749
|
-
}
|
|
750
|
-
};
|
|
751
|
-
|
|
752
735
|
/**
|
|
753
736
|
* These are things that widgets should exclude when serializing themselves.
|
|
754
737
|
*
|
|
@@ -1192,6 +1175,23 @@ const ClassNames = {
|
|
|
1192
1175
|
MOBILE: "perseus-mobile"
|
|
1193
1176
|
};
|
|
1194
1177
|
|
|
1178
|
+
// TODO(LP-11481): Change the primary API we use for logging in Perseus to use
|
|
1179
|
+
// getDependencies() internally... simplify usage to just using a "singleton"
|
|
1180
|
+
// which get's the injected logger.
|
|
1181
|
+
/**
|
|
1182
|
+
* Provides logging infrastructure for Perseus
|
|
1183
|
+
*/
|
|
1184
|
+
const Log = {
|
|
1185
|
+
// Logs a message
|
|
1186
|
+
log: (message, extra) => {
|
|
1187
|
+
getDependencies().Log.log(message, extra);
|
|
1188
|
+
},
|
|
1189
|
+
// Logs an error with a message
|
|
1190
|
+
error: (message, kind, extra) => {
|
|
1191
|
+
getDependencies().Log.error(message, kind, extra);
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
|
|
1195
1195
|
const KhanMath = {
|
|
1196
1196
|
// Simplify formulas before display
|
|
1197
1197
|
cleanMath: function (expr) {
|
|
@@ -2106,6 +2106,176 @@ const KhanAnswerTypes = {
|
|
|
2106
2106
|
}
|
|
2107
2107
|
};
|
|
2108
2108
|
|
|
2109
|
+
/**
|
|
2110
|
+
* Get the character used for separating decimals.
|
|
2111
|
+
*/
|
|
2112
|
+
const getDecimalSeparator = locale => {
|
|
2113
|
+
var _match$;
|
|
2114
|
+
switch (locale) {
|
|
2115
|
+
// TODO(somewhatabstract): Remove this when Chrome supports the `ka`
|
|
2116
|
+
// locale properly.
|
|
2117
|
+
// https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
|
|
2118
|
+
//
|
|
2119
|
+
// Supported locales in Chrome:
|
|
2120
|
+
// https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
|
|
2121
|
+
case "ka":
|
|
2122
|
+
return ",";
|
|
2123
|
+
default:
|
|
2124
|
+
const numberWithDecimalSeparator = 1.1;
|
|
2125
|
+
// TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
|
|
2126
|
+
// support Safari 12.
|
|
2127
|
+
const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
|
|
2128
|
+
// 0x661 is ARABIC-INDIC DIGIT ONE
|
|
2129
|
+
// 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
|
|
2130
|
+
.match(/[^\d\u0661\u06F1]/);
|
|
2131
|
+
return (_match$ = match == null ? void 0 : match[0]) != null ? _match$ : ".";
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2134
|
+
|
|
2135
|
+
/* Content creators input a list of answers which are matched from top to
|
|
2136
|
+
* bottom. The intent is that they can include spcific solutions which should
|
|
2137
|
+
* be graded as correct or incorrect (or ungraded!) first, then get more
|
|
2138
|
+
* general.
|
|
2139
|
+
*
|
|
2140
|
+
* We iterate through each answer, trying to match it with the user's input
|
|
2141
|
+
* using the following angorithm:
|
|
2142
|
+
* - Try to parse the user's input. If it doesn't parse then return "not
|
|
2143
|
+
* graded".
|
|
2144
|
+
* - For each answer:
|
|
2145
|
+
* ~ Try to validate the user's input against the answer. The answer is
|
|
2146
|
+
* expected to parse.
|
|
2147
|
+
* ~ If the user's input validates (the validator judges it "correct"), we've
|
|
2148
|
+
* matched and can stop considering answers.
|
|
2149
|
+
* - If there were no matches or the matching answer is considered "ungraded",
|
|
2150
|
+
* show the user an error. TODO(joel) - what error?
|
|
2151
|
+
* - Otherwise, pass through the resulting points and message.
|
|
2152
|
+
*/
|
|
2153
|
+
function expressionValidator(userInput, rubric,
|
|
2154
|
+
// @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
|
|
2155
|
+
onInputError = function () {}, strings, locale) {
|
|
2156
|
+
const options = _.clone(rubric);
|
|
2157
|
+
_.extend(options, {
|
|
2158
|
+
decimal_separator: getDecimalSeparator(locale)
|
|
2159
|
+
});
|
|
2160
|
+
const createValidator = answer => {
|
|
2161
|
+
// We give options to KAS.parse here because it is parsing the
|
|
2162
|
+
// solution answer, not the student answer, and we don't want a
|
|
2163
|
+
// solution to work if the student is using a different language
|
|
2164
|
+
// (different from the content creation language, ie. English).
|
|
2165
|
+
const expression = KAS.parse(answer.value, rubric);
|
|
2166
|
+
// An answer may not be parsed if the expression was defined
|
|
2167
|
+
// incorrectly. For example if the answer is using a symbol defined
|
|
2168
|
+
// in the function variables list for the expression.
|
|
2169
|
+
if (!expression.parsed) {
|
|
2170
|
+
/* c8 ignore next */
|
|
2171
|
+
Log.error("Unable to parse solution answer for expression", Errors.InvalidInput, {
|
|
2172
|
+
loggedMetadata: {
|
|
2173
|
+
rubric: JSON.stringify(rubric)
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
return null;
|
|
2177
|
+
}
|
|
2178
|
+
return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, _({}).extend(options, {
|
|
2179
|
+
simplify: answer.simplify,
|
|
2180
|
+
form: answer.form
|
|
2181
|
+
}), strings);
|
|
2182
|
+
};
|
|
2183
|
+
|
|
2184
|
+
// Find the first answer form that matches the user's input and that
|
|
2185
|
+
// is considered correct. Also, track whether the input is
|
|
2186
|
+
// considered "empty" for all answer forms, and keep the validation
|
|
2187
|
+
// result for the first answer form for which the user's input was
|
|
2188
|
+
// considered "ungraded".
|
|
2189
|
+
// (Terminology reminder: the answer forms are provided by the
|
|
2190
|
+
// assessment items; they are not the user's input. Each one might
|
|
2191
|
+
// represent a correct answer, an incorrect one (if the exercise
|
|
2192
|
+
// creator has predicted certain common wrong answers and wants to
|
|
2193
|
+
// provide guidance via a message), or an ungraded one (same idea,
|
|
2194
|
+
// but without giving the user an incorrect mark for the question).
|
|
2195
|
+
let matchingAnswerForm;
|
|
2196
|
+
let matchMessage;
|
|
2197
|
+
let allEmpty = true;
|
|
2198
|
+
let firstUngradedResult;
|
|
2199
|
+
for (const answerForm of rubric.answerForms || []) {
|
|
2200
|
+
const validator = createValidator(answerForm);
|
|
2201
|
+
if (!validator) {
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
const result = validator(userInput);
|
|
2205
|
+
|
|
2206
|
+
// Short-circuit as soon as the user's input matches some answer
|
|
2207
|
+
// (independently of whether the answer is correct)
|
|
2208
|
+
if (result.correct) {
|
|
2209
|
+
matchingAnswerForm = answerForm;
|
|
2210
|
+
matchMessage = result.message || "";
|
|
2211
|
+
break;
|
|
2212
|
+
}
|
|
2213
|
+
allEmpty = allEmpty && result.empty;
|
|
2214
|
+
// If this answer form is correct and the user's input is considered
|
|
2215
|
+
// "ungraded" for it, we'll want to keep the evaluation result for
|
|
2216
|
+
// later. If the user's input doesn't match any answer forms, we'll
|
|
2217
|
+
// show the message from this validation.
|
|
2218
|
+
if (answerForm.considered === "correct" && result.ungraded && !firstUngradedResult) {
|
|
2219
|
+
firstUngradedResult = result;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
// Now check to see if we matched any answer form at all, and if
|
|
2224
|
+
// we did, whether it's considered correct, incorrect, or ungraded
|
|
2225
|
+
if (!matchingAnswerForm) {
|
|
2226
|
+
if (firstUngradedResult) {
|
|
2227
|
+
// While we didn't directly match with any answer form, we
|
|
2228
|
+
// did at some point get an "ungraded" validation result,
|
|
2229
|
+
// which might indicate e.g. a mismatch in variable casing.
|
|
2230
|
+
// We'll return "invalid", which will let the user try again
|
|
2231
|
+
// with no penalty, and the hopefully helpful validation
|
|
2232
|
+
// message.
|
|
2233
|
+
return {
|
|
2234
|
+
type: "invalid",
|
|
2235
|
+
message: firstUngradedResult.message,
|
|
2236
|
+
suppressAlmostThere: firstUngradedResult.suppressAlmostThere
|
|
2237
|
+
};
|
|
2238
|
+
}
|
|
2239
|
+
if (allEmpty) {
|
|
2240
|
+
// If everything graded as empty, it's invalid.
|
|
2241
|
+
return {
|
|
2242
|
+
type: "invalid",
|
|
2243
|
+
message: null
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
// We fell through all the possibilities and we're not empty,
|
|
2247
|
+
// so the answer is considered incorrect.
|
|
2248
|
+
return {
|
|
2249
|
+
type: "points",
|
|
2250
|
+
earned: 0,
|
|
2251
|
+
total: 1
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
if (matchingAnswerForm.considered === "ungraded") {
|
|
2255
|
+
// We matched an ungraded answer form - return "invalid", which
|
|
2256
|
+
// will let the user try again with no penalty
|
|
2257
|
+
const apiResult = onInputError(null,
|
|
2258
|
+
// Reserved for some widget identifier
|
|
2259
|
+
userInput, matchMessage);
|
|
2260
|
+
return {
|
|
2261
|
+
type: "invalid",
|
|
2262
|
+
message: apiResult === false ? null : matchMessage
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
// We matched a graded answer form, so we can now tell the user
|
|
2266
|
+
// whether their input was correct or incorrect, and hand out
|
|
2267
|
+
// points accordingly
|
|
2268
|
+
// TODO(eater): Seems silly to translate result to this
|
|
2269
|
+
// invalid/points thing and immediately translate it back in
|
|
2270
|
+
// ItemRenderer.scoreInput()
|
|
2271
|
+
return {
|
|
2272
|
+
type: "points",
|
|
2273
|
+
earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
|
|
2274
|
+
total: 1,
|
|
2275
|
+
message: matchMessage
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2109
2279
|
const _excluded$5 = ["scoring"];
|
|
2110
2280
|
// Map of international operator names to their English equivalents
|
|
2111
2281
|
const englishOperators = {
|
|
@@ -2136,32 +2306,6 @@ const deriveKeypadVersion = apiOptions => {
|
|
|
2136
2306
|
// can remove this `virtualKeypadVersion` field entirely.
|
|
2137
2307
|
return apiOptions.nativeKeypadProxy != null ? "REACT_NATIVE_KEYPAD" : "MATH_INPUT_KEYPAD_V2";
|
|
2138
2308
|
};
|
|
2139
|
-
/**
|
|
2140
|
-
* Get the character used for separating decimals.
|
|
2141
|
-
*/
|
|
2142
|
-
const getDecimalSeparator = locale => {
|
|
2143
|
-
var _match$;
|
|
2144
|
-
switch (locale) {
|
|
2145
|
-
// TODO(somewhatabstract): Remove this when Chrome supports the `ka`
|
|
2146
|
-
// locale properly.
|
|
2147
|
-
// https://github.com/formatjs/formatjs/issues/1526#issuecomment-559891201
|
|
2148
|
-
//
|
|
2149
|
-
// Supported locales in Chrome:
|
|
2150
|
-
// https://source.chromium.org/chromium/chromium/src/+/master:third_party/icu/scripts/chrome_ui_languages.list
|
|
2151
|
-
case "ka":
|
|
2152
|
-
return ",";
|
|
2153
|
-
default:
|
|
2154
|
-
const numberWithDecimalSeparator = 1.1;
|
|
2155
|
-
// TODO(FEI-3647): Update to use .formatToParts() once we no longer have to
|
|
2156
|
-
// support Safari 12.
|
|
2157
|
-
const match = new Intl.NumberFormat(locale).format(numberWithDecimalSeparator)
|
|
2158
|
-
// 0x661 is ARABIC-INDIC DIGIT ONE
|
|
2159
|
-
// 0x6F1 is EXTENDED ARABIC-INDIC DIGIT ONE
|
|
2160
|
-
.match(/[^\d\u0661\u06F1]/);
|
|
2161
|
-
return (_match$ = match == null ? void 0 : match[0]) != null ? _match$ : ".";
|
|
2162
|
-
}
|
|
2163
|
-
};
|
|
2164
|
-
|
|
2165
2309
|
// The new, MathQuill input expression widget
|
|
2166
2310
|
class Expression extends React.Component {
|
|
2167
2311
|
constructor(..._args) {
|
|
@@ -2230,7 +2374,7 @@ class Expression extends React.Component {
|
|
|
2230
2374
|
scoring
|
|
2231
2375
|
} = _ref,
|
|
2232
2376
|
rubric = _objectWithoutPropertiesLoose(_ref, _excluded$5);
|
|
2233
|
-
const score =
|
|
2377
|
+
const score = expressionValidator(this.getUserInput(), rubric, onInputError || function () {}, this.context.strings, this.context.locale);
|
|
2234
2378
|
|
|
2235
2379
|
// "scoring" is a flag that indicates when we are checking answers.
|
|
2236
2380
|
// otherwise, we may just be checking validity after changes.
|
|
@@ -2320,149 +2464,11 @@ class Expression extends React.Component {
|
|
|
2320
2464
|
}, cb);
|
|
2321
2465
|
};
|
|
2322
2466
|
}
|
|
2323
|
-
|
|
2324
|
-
/* Content creators input a list of answers which are matched from top to
|
|
2325
|
-
* bottom. The intent is that they can include spcific solutions which should
|
|
2326
|
-
* be graded as correct or incorrect (or ungraded!) first, then get more
|
|
2327
|
-
* general.
|
|
2328
|
-
*
|
|
2329
|
-
* We iterate through each answer, trying to match it with the user's input
|
|
2330
|
-
* using the following angorithm:
|
|
2331
|
-
* - Try to parse the user's input. If it doesn't parse then return "not
|
|
2332
|
-
* graded".
|
|
2333
|
-
* - For each answer:
|
|
2334
|
-
* ~ Try to validate the user's input against the answer. The answer is
|
|
2335
|
-
* expected to parse.
|
|
2336
|
-
* ~ If the user's input validates (the validator judges it "correct"), we've
|
|
2337
|
-
* matched and can stop considering answers.
|
|
2338
|
-
* - If there were no matches or the matching answer is considered "ungraded",
|
|
2339
|
-
* show the user an error. TODO(joel) - what error?
|
|
2340
|
-
* - Otherwise, pass through the resulting points and message.
|
|
2341
|
-
*/
|
|
2467
|
+
// TODO remove this in favor of just using expressionValidator
|
|
2342
2468
|
static validate(userInput, rubric,
|
|
2343
2469
|
// @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
|
|
2344
2470
|
onInputError = function () {}, strings, locale) {
|
|
2345
|
-
|
|
2346
|
-
_.extend(options, {
|
|
2347
|
-
decimal_separator: getDecimalSeparator(locale)
|
|
2348
|
-
});
|
|
2349
|
-
const createValidator = answer => {
|
|
2350
|
-
// We give options to KAS.parse here because it is parsing the
|
|
2351
|
-
// solution answer, not the student answer, and we don't want a
|
|
2352
|
-
// solution to work if the student is using a different language
|
|
2353
|
-
// (different from the content creation language, ie. English).
|
|
2354
|
-
const expression = KAS.parse(answer.value, rubric);
|
|
2355
|
-
// An answer may not be parsed if the expression was defined
|
|
2356
|
-
// incorrectly. For example if the answer is using a symbol defined
|
|
2357
|
-
// in the function variables list for the expression.
|
|
2358
|
-
if (!expression.parsed) {
|
|
2359
|
-
/* c8 ignore next */
|
|
2360
|
-
Log.error("Unable to parse solution answer for expression", Errors.InvalidInput, {
|
|
2361
|
-
loggedMetadata: {
|
|
2362
|
-
rubric: JSON.stringify(rubric)
|
|
2363
|
-
}
|
|
2364
|
-
});
|
|
2365
|
-
return null;
|
|
2366
|
-
}
|
|
2367
|
-
return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, _({}).extend(options, {
|
|
2368
|
-
simplify: answer.simplify,
|
|
2369
|
-
form: answer.form
|
|
2370
|
-
}), strings);
|
|
2371
|
-
};
|
|
2372
|
-
|
|
2373
|
-
// Find the first answer form that matches the user's input and that
|
|
2374
|
-
// is considered correct. Also, track whether the input is
|
|
2375
|
-
// considered "empty" for all answer forms, and keep the validation
|
|
2376
|
-
// result for the first answer form for which the user's input was
|
|
2377
|
-
// considered "ungraded".
|
|
2378
|
-
// (Terminology reminder: the answer forms are provided by the
|
|
2379
|
-
// assessment items; they are not the user's input. Each one might
|
|
2380
|
-
// represent a correct answer, an incorrect one (if the exercise
|
|
2381
|
-
// creator has predicted certain common wrong answers and wants to
|
|
2382
|
-
// provide guidance via a message), or an ungraded one (same idea,
|
|
2383
|
-
// but without giving the user an incorrect mark for the question).
|
|
2384
|
-
let matchingAnswerForm;
|
|
2385
|
-
let matchMessage;
|
|
2386
|
-
let allEmpty = true;
|
|
2387
|
-
let firstUngradedResult;
|
|
2388
|
-
for (const answerForm of rubric.answerForms || []) {
|
|
2389
|
-
const validate = createValidator(answerForm);
|
|
2390
|
-
if (!validate) {
|
|
2391
|
-
continue;
|
|
2392
|
-
}
|
|
2393
|
-
const result = validate(userInput);
|
|
2394
|
-
|
|
2395
|
-
// Short-circuit as soon as the user's input matches some answer
|
|
2396
|
-
// (independently of whether the answer is correct)
|
|
2397
|
-
if (result.correct) {
|
|
2398
|
-
matchingAnswerForm = answerForm;
|
|
2399
|
-
matchMessage = result.message || "";
|
|
2400
|
-
break;
|
|
2401
|
-
}
|
|
2402
|
-
allEmpty = allEmpty && result.empty;
|
|
2403
|
-
// If this answer form is correct and the user's input is considered
|
|
2404
|
-
// "ungraded" for it, we'll want to keep the evaluation result for
|
|
2405
|
-
// later. If the user's input doesn't match any answer forms, we'll
|
|
2406
|
-
// show the message from this validation.
|
|
2407
|
-
if (answerForm.considered === "correct" && result.ungraded && !firstUngradedResult) {
|
|
2408
|
-
firstUngradedResult = result;
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
|
|
2412
|
-
// Now check to see if we matched any answer form at all, and if
|
|
2413
|
-
// we did, whether it's considered correct, incorrect, or ungraded
|
|
2414
|
-
if (!matchingAnswerForm) {
|
|
2415
|
-
if (firstUngradedResult) {
|
|
2416
|
-
// While we didn't directly match with any answer form, we
|
|
2417
|
-
// did at some point get an "ungraded" validation result,
|
|
2418
|
-
// which might indicate e.g. a mismatch in variable casing.
|
|
2419
|
-
// We'll return "invalid", which will let the user try again
|
|
2420
|
-
// with no penalty, and the hopefully helpful validation
|
|
2421
|
-
// message.
|
|
2422
|
-
return {
|
|
2423
|
-
type: "invalid",
|
|
2424
|
-
message: firstUngradedResult.message,
|
|
2425
|
-
suppressAlmostThere: firstUngradedResult.suppressAlmostThere
|
|
2426
|
-
};
|
|
2427
|
-
}
|
|
2428
|
-
if (allEmpty) {
|
|
2429
|
-
// If everything graded as empty, it's invalid.
|
|
2430
|
-
return {
|
|
2431
|
-
type: "invalid",
|
|
2432
|
-
message: null
|
|
2433
|
-
};
|
|
2434
|
-
}
|
|
2435
|
-
// We fell through all the possibilities and we're not empty,
|
|
2436
|
-
// so the answer is considered incorrect.
|
|
2437
|
-
return {
|
|
2438
|
-
type: "points",
|
|
2439
|
-
earned: 0,
|
|
2440
|
-
total: 1
|
|
2441
|
-
};
|
|
2442
|
-
}
|
|
2443
|
-
if (matchingAnswerForm.considered === "ungraded") {
|
|
2444
|
-
// We matched an ungraded answer form - return "invalid", which
|
|
2445
|
-
// will let the user try again with no penalty
|
|
2446
|
-
const apiResult = onInputError(null,
|
|
2447
|
-
// Reserved for some widget identifier
|
|
2448
|
-
userInput, matchMessage);
|
|
2449
|
-
return {
|
|
2450
|
-
type: "invalid",
|
|
2451
|
-
message: apiResult === false ? null : matchMessage
|
|
2452
|
-
};
|
|
2453
|
-
}
|
|
2454
|
-
// We matched a graded answer form, so we can now tell the user
|
|
2455
|
-
// whether their input was correct or incorrect, and hand out
|
|
2456
|
-
// points accordingly
|
|
2457
|
-
// TODO(eater): Seems silly to translate result to this
|
|
2458
|
-
// invalid/points thing and immediately translate it back in
|
|
2459
|
-
// ItemRenderer.scoreInput()
|
|
2460
|
-
return {
|
|
2461
|
-
type: "points",
|
|
2462
|
-
earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
|
|
2463
|
-
total: 1,
|
|
2464
|
-
message: matchMessage
|
|
2465
|
-
};
|
|
2471
|
+
return expressionValidator(userInput, rubric, onInputError, strings, locale);
|
|
2466
2472
|
}
|
|
2467
2473
|
static getUserInputFromProps(props) {
|
|
2468
2474
|
return normalizeTex(props.value);
|
|
@@ -2670,10 +2676,10 @@ const ExpressionWithDependencies = /*#__PURE__*/React.forwardRef((props, ref) =>
|
|
|
2670
2676
|
|
|
2671
2677
|
// HACK: Propogate "static" methods onto our wrapper component.
|
|
2672
2678
|
// In the future we should adjust client apps to not depend on these static
|
|
2673
|
-
// methods and instead adjust
|
|
2679
|
+
// methods and instead adjust Perseus to provide these facilities through
|
|
2674
2680
|
// instance methods on our Renderers.
|
|
2675
2681
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
2676
|
-
ExpressionWithDependencies.validate =
|
|
2682
|
+
ExpressionWithDependencies.validate = expressionValidator;
|
|
2677
2683
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
2678
2684
|
ExpressionWithDependencies.getUserInputFromProps = Expression.getUserInputFromProps;
|
|
2679
2685
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
@@ -19620,9 +19626,7 @@ const isIdPathPrefix = function isIdPathPrefix(prefixArray, wholeArray) {
|
|
|
19620
19626
|
|
|
19621
19627
|
// The return type for getUserInput. Widgets have full control of what is
|
|
19622
19628
|
// returned so it's not easily typed (some widgets return a scalar (string),
|
|
19623
|
-
// some return a custom-built object
|
|
19624
|
-
// `WidgetJsonifyDeprecated` which returns an object containing widget props
|
|
19625
|
-
// (filtered by deprecated keys)).
|
|
19629
|
+
// some return a custom-built object
|
|
19626
19630
|
/**
|
|
19627
19631
|
* The Widget type represents the common API that the Renderer uses to interact
|
|
19628
19632
|
* with all widgets. All widgets must implement the methods in this API, unless
|
|
@@ -22024,6 +22028,131 @@ const answerFormButtons = [{
|
|
|
22024
22028
|
value: "pi",
|
|
22025
22029
|
content: "\u03C0"
|
|
22026
22030
|
}];
|
|
22031
|
+
|
|
22032
|
+
// This function checks if the user inputted a percent value, parsing
|
|
22033
|
+
// it as a number (and maybe scaling) so that it can be graded.
|
|
22034
|
+
// NOTE(michaelpolyak): Unlike `KhanAnswerTypes.number.percent()` which
|
|
22035
|
+
// can accept several input forms with or without "%", the decision
|
|
22036
|
+
// to parse based on the presence of "%" in the input, is so that we
|
|
22037
|
+
// don't accidently scale the user typed value before grading, CP-930.
|
|
22038
|
+
function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
|
|
22039
|
+
// If the input value is not a string ending with "%", then there's
|
|
22040
|
+
// nothing more to do. The value will be graded as inputted by user.
|
|
22041
|
+
if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
|
|
22042
|
+
return inputValue;
|
|
22043
|
+
}
|
|
22044
|
+
const value = parseFloat(inputValue.slice(0, -1));
|
|
22045
|
+
// If the input value stripped of the "%" cannot be parsed as a
|
|
22046
|
+
// number (the slice is not really necessary for parseFloat to work
|
|
22047
|
+
// if the string starts with a number) then return the original
|
|
22048
|
+
// input for grading.
|
|
22049
|
+
if (isNaN(value)) {
|
|
22050
|
+
return inputValue;
|
|
22051
|
+
}
|
|
22052
|
+
|
|
22053
|
+
// Next, if all correct answers are in the range of |0,1| then we
|
|
22054
|
+
// scale the user typed value. We assume this is the correct thing
|
|
22055
|
+
// to do since the input value ends with "%".
|
|
22056
|
+
if (normalizedAnswerExpected) {
|
|
22057
|
+
return value / 100;
|
|
22058
|
+
}
|
|
22059
|
+
|
|
22060
|
+
// Otherwise, we return input valuåe (number) stripped of the "%".
|
|
22061
|
+
return value;
|
|
22062
|
+
}
|
|
22063
|
+
function numericInputValidator(userInput, rubric, strings) {
|
|
22064
|
+
const defaultAnswerForms = answerFormButtons.map(e => e["value"])
|
|
22065
|
+
// Don't default to validating the answer as a pi answer
|
|
22066
|
+
// if answerForm isn't set on the answer
|
|
22067
|
+
// https://khanacademy.atlassian.net/browse/LC-691
|
|
22068
|
+
.filter(e => e !== "pi");
|
|
22069
|
+
const createValidator = answer => {
|
|
22070
|
+
var _answer$answerForms;
|
|
22071
|
+
const stringAnswer = `${answer.value}`;
|
|
22072
|
+
|
|
22073
|
+
// Always validate against the provided answer forms (pi, decimal, etc.)
|
|
22074
|
+
const validatorForms = [...((_answer$answerForms = answer.answerForms) != null ? _answer$answerForms : [])];
|
|
22075
|
+
|
|
22076
|
+
// When an answer is set to strict, we validate using ONLY
|
|
22077
|
+
// the provided answerForms. If strict is false, or if there
|
|
22078
|
+
// were no provided answer forms, we will include all
|
|
22079
|
+
// of the default answer forms in our validator.
|
|
22080
|
+
if (!answer.strict || validatorForms.length === 0) {
|
|
22081
|
+
validatorForms.push(...defaultAnswerForms);
|
|
22082
|
+
}
|
|
22083
|
+
return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
|
|
22084
|
+
message: answer.message,
|
|
22085
|
+
simplify: answer.status === "correct" ? answer.simplify : "optional",
|
|
22086
|
+
inexact: true,
|
|
22087
|
+
// TODO(merlob) backfill / delete
|
|
22088
|
+
maxError: answer.maxError,
|
|
22089
|
+
forms: validatorForms
|
|
22090
|
+
}, strings);
|
|
22091
|
+
};
|
|
22092
|
+
|
|
22093
|
+
// We may have received TeX; try to parse it before grading.
|
|
22094
|
+
// If `currentValue` is not TeX, this should be a no-op.
|
|
22095
|
+
const currentValue = ParseTex(userInput.currentValue);
|
|
22096
|
+
const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
22097
|
+
const normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1);
|
|
22098
|
+
|
|
22099
|
+
// Look through all correct answers for one that matches either
|
|
22100
|
+
// precisely or approximately and return the appropriate message:
|
|
22101
|
+
// - if precise, return the message that the answer came with
|
|
22102
|
+
// - if it needs to be simplified, etc., show that message
|
|
22103
|
+
let result = correctAnswers.map(answer => {
|
|
22104
|
+
// The coefficient is an attribute of the widget
|
|
22105
|
+
let localValue = currentValue;
|
|
22106
|
+
if (rubric.coefficient) {
|
|
22107
|
+
if (!localValue) {
|
|
22108
|
+
localValue = 1;
|
|
22109
|
+
} else if (localValue === "-") {
|
|
22110
|
+
localValue = -1;
|
|
22111
|
+
}
|
|
22112
|
+
}
|
|
22113
|
+
const validate = createValidator(answer);
|
|
22114
|
+
return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
|
|
22115
|
+
}).find(match => match.correct || match.empty);
|
|
22116
|
+
if (!result) {
|
|
22117
|
+
// Otherwise, if the guess is not correct
|
|
22118
|
+
const otherAnswers = [].concat(
|
|
22119
|
+
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
22120
|
+
rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong"));
|
|
22121
|
+
|
|
22122
|
+
// Look through all other answers and if one matches either
|
|
22123
|
+
// precisely or approximately return the answer's message
|
|
22124
|
+
const match = otherAnswers.find(answer => {
|
|
22125
|
+
const validate = createValidator(answer);
|
|
22126
|
+
return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
|
|
22127
|
+
});
|
|
22128
|
+
result = {
|
|
22129
|
+
// @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
|
|
22130
|
+
empty: match ? match.status === "ungraded" : false,
|
|
22131
|
+
// @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
|
|
22132
|
+
correct: match ? match.status === "correct" : false,
|
|
22133
|
+
// @ts-expect-error - TS2339 - Property 'message' does not exist on type 'never'.
|
|
22134
|
+
message: match ? match.message : null,
|
|
22135
|
+
guess: currentValue
|
|
22136
|
+
};
|
|
22137
|
+
}
|
|
22138
|
+
|
|
22139
|
+
// TODO(eater): Seems silly to translate result to this
|
|
22140
|
+
// invalid/points thing and immediately translate it
|
|
22141
|
+
// back in ItemRenderer.scoreInput()
|
|
22142
|
+
if (result.empty) {
|
|
22143
|
+
return {
|
|
22144
|
+
type: "invalid",
|
|
22145
|
+
message: result.message
|
|
22146
|
+
};
|
|
22147
|
+
}
|
|
22148
|
+
return {
|
|
22149
|
+
type: "points",
|
|
22150
|
+
earned: result.correct ? 1 : 0,
|
|
22151
|
+
total: 1,
|
|
22152
|
+
message: result.message
|
|
22153
|
+
};
|
|
22154
|
+
}
|
|
22155
|
+
|
|
22027
22156
|
const formExamples = {
|
|
22028
22157
|
integer: (form, strings) => strings.integerExample,
|
|
22029
22158
|
proper: (form, strings) => form.simplify === "optional" ? strings.properExample : strings.simplifiedProperExample,
|
|
@@ -22070,7 +22199,7 @@ class NumericInput extends React.Component {
|
|
|
22070
22199
|
return !noFormsAccepted && !allFormsAccepted;
|
|
22071
22200
|
};
|
|
22072
22201
|
this.simpleValidate = rubric => {
|
|
22073
|
-
return
|
|
22202
|
+
return numericInputValidator(this.getUserInput(), rubric, this.context.strings);
|
|
22074
22203
|
};
|
|
22075
22204
|
this.focus = () => {
|
|
22076
22205
|
var _this$inputRef;
|
|
@@ -22151,98 +22280,7 @@ class NumericInput extends React.Component {
|
|
|
22151
22280
|
return answerStrings[0];
|
|
22152
22281
|
}
|
|
22153
22282
|
static validate(userInput, rubric, strings) {
|
|
22154
|
-
|
|
22155
|
-
// Don't default to validating the answer as a pi answer
|
|
22156
|
-
// if answerForm isn't set on the answer
|
|
22157
|
-
// https://khanacademy.atlassian.net/browse/LC-691
|
|
22158
|
-
.filter(e => e !== "pi");
|
|
22159
|
-
const createValidator = answer => {
|
|
22160
|
-
var _answer$answerForms;
|
|
22161
|
-
const stringAnswer = `${answer.value}`;
|
|
22162
|
-
|
|
22163
|
-
// Always validate against the provided answer forms (pi, decimal, etc.)
|
|
22164
|
-
const validatorForms = [...((_answer$answerForms = answer.answerForms) != null ? _answer$answerForms : [])];
|
|
22165
|
-
|
|
22166
|
-
// When an answer is set to strict, we validate using ONLY
|
|
22167
|
-
// the provided answerForms. If strict is false, or if there
|
|
22168
|
-
// were no provided answer forms, we will include all
|
|
22169
|
-
// of the default answer forms in our validator.
|
|
22170
|
-
if (!answer.strict || validatorForms.length === 0) {
|
|
22171
|
-
validatorForms.push(...defaultAnswerForms);
|
|
22172
|
-
}
|
|
22173
|
-
return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
|
|
22174
|
-
message: answer.message,
|
|
22175
|
-
simplify: answer.status === "correct" ? answer.simplify : "optional",
|
|
22176
|
-
inexact: true,
|
|
22177
|
-
// TODO(merlob) backfill / delete
|
|
22178
|
-
maxError: answer.maxError,
|
|
22179
|
-
forms: validatorForms
|
|
22180
|
-
}, strings);
|
|
22181
|
-
};
|
|
22182
|
-
|
|
22183
|
-
// We may have received TeX; try to parse it before grading.
|
|
22184
|
-
// If `currentValue` is not TeX, this should be a no-op.
|
|
22185
|
-
const currentValue = ParseTex(userInput.currentValue);
|
|
22186
|
-
const correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
22187
|
-
const normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1);
|
|
22188
|
-
|
|
22189
|
-
// Look through all correct answers for one that matches either
|
|
22190
|
-
// precisely or approximately and return the appropriate message:
|
|
22191
|
-
// - if precise, return the message that the answer came with
|
|
22192
|
-
// - if it needs to be simplified, etc., show that message
|
|
22193
|
-
let result = correctAnswers.map(answer => {
|
|
22194
|
-
// The coefficient is an attribute of the widget
|
|
22195
|
-
let localValue = currentValue;
|
|
22196
|
-
if (rubric.coefficient) {
|
|
22197
|
-
if (!localValue) {
|
|
22198
|
-
// @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'string'.
|
|
22199
|
-
localValue = 1;
|
|
22200
|
-
} else if (localValue === "-") {
|
|
22201
|
-
// @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'string'.
|
|
22202
|
-
localValue = -1;
|
|
22203
|
-
}
|
|
22204
|
-
}
|
|
22205
|
-
const validate = createValidator(answer);
|
|
22206
|
-
return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
|
|
22207
|
-
}).find(match => match.correct || match.empty);
|
|
22208
|
-
if (!result) {
|
|
22209
|
-
// Otherwise, if the guess is not correct
|
|
22210
|
-
const otherAnswers = [].concat(
|
|
22211
|
-
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
22212
|
-
rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong"));
|
|
22213
|
-
|
|
22214
|
-
// Look through all other answers and if one matches either
|
|
22215
|
-
// precisely or approximately return the answer's message
|
|
22216
|
-
const match = otherAnswers.find(answer => {
|
|
22217
|
-
const validate = createValidator(answer);
|
|
22218
|
-
return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
|
|
22219
|
-
});
|
|
22220
|
-
result = {
|
|
22221
|
-
// @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
|
|
22222
|
-
empty: match ? match.status === "ungraded" : false,
|
|
22223
|
-
// @ts-expect-error - TS2339 - Property 'status' does not exist on type 'never'.
|
|
22224
|
-
correct: match ? match.status === "correct" : false,
|
|
22225
|
-
// @ts-expect-error - TS2339 - Property 'message' does not exist on type 'never'.
|
|
22226
|
-
message: match ? match.message : null,
|
|
22227
|
-
guess: currentValue
|
|
22228
|
-
};
|
|
22229
|
-
}
|
|
22230
|
-
|
|
22231
|
-
// TODO(eater): Seems silly to translate result to this
|
|
22232
|
-
// invalid/points thing and immediately translate it
|
|
22233
|
-
// back in ItemRenderer.scoreInput()
|
|
22234
|
-
if (result.empty) {
|
|
22235
|
-
return {
|
|
22236
|
-
type: "invalid",
|
|
22237
|
-
message: result.message
|
|
22238
|
-
};
|
|
22239
|
-
}
|
|
22240
|
-
return {
|
|
22241
|
-
type: "points",
|
|
22242
|
-
earned: result.correct ? 1 : 0,
|
|
22243
|
-
total: 1,
|
|
22244
|
-
message: result.message
|
|
22245
|
-
};
|
|
22283
|
+
return numericInputValidator(userInput, rubric, strings);
|
|
22246
22284
|
}
|
|
22247
22285
|
render() {
|
|
22248
22286
|
let labelText = this.props.labelText;
|
|
@@ -22343,37 +22381,6 @@ const unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
|
22343
22381
|
return formExampleKeys.indexOf(form.name);
|
|
22344
22382
|
});
|
|
22345
22383
|
};
|
|
22346
|
-
// This function checks if the user inputted a percent value, parsing
|
|
22347
|
-
// it as a number (and maybe scaling) so that it can be graded.
|
|
22348
|
-
// NOTE(michaelpolyak): Unlike `KhanAnswerTypes.number.percent()` which
|
|
22349
|
-
// can accept several input forms with or without "%", the decision
|
|
22350
|
-
// to parse based on the presence of "%" in the input, is so that we
|
|
22351
|
-
// don't accidently scale the user typed value before grading, CP-930.
|
|
22352
|
-
function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
|
|
22353
|
-
// If the input value is not a string ending with "%", then there's
|
|
22354
|
-
// nothing more to do. The value will be graded as inputted by user.
|
|
22355
|
-
if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
|
|
22356
|
-
return inputValue;
|
|
22357
|
-
}
|
|
22358
|
-
const value = parseFloat(inputValue.slice(0, -1));
|
|
22359
|
-
// If the input value stripped of the "%" cannot be parsed as a
|
|
22360
|
-
// number (the slice is not really necessary for parseFloat to work
|
|
22361
|
-
// if the string starts with a number) then return the original
|
|
22362
|
-
// input for grading.
|
|
22363
|
-
if (isNaN(value)) {
|
|
22364
|
-
return inputValue;
|
|
22365
|
-
}
|
|
22366
|
-
|
|
22367
|
-
// Next, if all correct answers are in the range of |0,1| then we
|
|
22368
|
-
// scale the user typed value. We assume this is the correct thing
|
|
22369
|
-
// to do since the input value ends with "%".
|
|
22370
|
-
if (normalizedAnswerExpected) {
|
|
22371
|
-
return value / 100;
|
|
22372
|
-
}
|
|
22373
|
-
|
|
22374
|
-
// Otherwise, we return input valuåe (number) stripped of the "%".
|
|
22375
|
-
return value;
|
|
22376
|
-
}
|
|
22377
22384
|
const propsTransform = function propsTransform(widgetOptions) {
|
|
22378
22385
|
const rendererProps = _.extend(_.omit(widgetOptions, "answers"), {
|
|
22379
22386
|
answerForms: unionAnswerForms(
|
|
@@ -22401,6 +22408,23 @@ var NumericInput$1 = {
|
|
|
22401
22408
|
isLintable: true
|
|
22402
22409
|
};
|
|
22403
22410
|
|
|
22411
|
+
/**
|
|
22412
|
+
* Several widgets don't have "right"/"wrong" validation logic,
|
|
22413
|
+
* so this just says to move on past those widgets
|
|
22414
|
+
*
|
|
22415
|
+
* TODO: widgets that use this probably shouldn't have any
|
|
22416
|
+
* validation logic and the thing validating an exercise
|
|
22417
|
+
* should just know to skip these
|
|
22418
|
+
*/
|
|
22419
|
+
function noopValidator() {
|
|
22420
|
+
return {
|
|
22421
|
+
type: "points",
|
|
22422
|
+
earned: 0,
|
|
22423
|
+
total: 0,
|
|
22424
|
+
message: null
|
|
22425
|
+
};
|
|
22426
|
+
}
|
|
22427
|
+
|
|
22404
22428
|
const EN_DASH$1 = "\u2013";
|
|
22405
22429
|
class PassageRef extends React.Component {
|
|
22406
22430
|
constructor(..._args) {
|
|
@@ -22453,12 +22477,7 @@ class PassageRef extends React.Component {
|
|
|
22453
22477
|
};
|
|
22454
22478
|
}
|
|
22455
22479
|
static validate(userInput, rubric) {
|
|
22456
|
-
return
|
|
22457
|
-
type: "points",
|
|
22458
|
-
earned: 0,
|
|
22459
|
-
total: 0,
|
|
22460
|
-
message: null
|
|
22461
|
-
};
|
|
22480
|
+
return noopValidator();
|
|
22462
22481
|
}
|
|
22463
22482
|
componentDidMount() {
|
|
22464
22483
|
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
@@ -22521,6 +22540,7 @@ PassageRef.defaultProps = {
|
|
|
22521
22540
|
var PassageRef$1 = {
|
|
22522
22541
|
name: "passage-ref",
|
|
22523
22542
|
displayName: "PassageRef (SAT only)",
|
|
22543
|
+
hidden: true,
|
|
22524
22544
|
defaultAlignment: "inline",
|
|
22525
22545
|
widget: PassageRef,
|
|
22526
22546
|
transform: widgetOptions => ({
|
|
@@ -24406,23 +24426,6 @@ var Radio = {
|
|
|
24406
24426
|
/* eslint-disable import/no-named-as-default */
|
|
24407
24427
|
var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
|
|
24408
24428
|
|
|
24409
|
-
/* Free implementation of getUserInput. This should be used sparingly, since it
|
|
24410
|
-
* just returns all the widget's props rather than picking out those which were
|
|
24411
|
-
* input by the user.
|
|
24412
|
-
*/
|
|
24413
|
-
const WidgetJsonifyDeprecated = {
|
|
24414
|
-
getUserInput: function () {
|
|
24415
|
-
// Omit props that get passed to all widgets
|
|
24416
|
-
// @ts-expect-error - TS2339 - Property 'props' does not exist on type '{ readonly getUserInput: () => any; readonly getUserInputFromProps: (props: any) => any; }'.
|
|
24417
|
-
return _.omit(this.props, denylist);
|
|
24418
|
-
},
|
|
24419
|
-
// Static version of `WidgetJsonifyDeprecated.getUserInput`
|
|
24420
|
-
getUserInputFromProps: function (props) {
|
|
24421
|
-
// Omit props that get passed to all widgets
|
|
24422
|
-
return _.omit(props, denylist);
|
|
24423
|
-
}
|
|
24424
|
-
};
|
|
24425
|
-
|
|
24426
24429
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
24427
24430
|
class Categorizer extends React.Component {
|
|
24428
24431
|
constructor(..._args) {
|
|
@@ -24466,7 +24469,9 @@ class Categorizer extends React.Component {
|
|
|
24466
24469
|
};
|
|
24467
24470
|
}
|
|
24468
24471
|
static getUserInputFromProps(props) {
|
|
24469
|
-
return
|
|
24472
|
+
return {
|
|
24473
|
+
values: props.values
|
|
24474
|
+
};
|
|
24470
24475
|
}
|
|
24471
24476
|
onChange(itemNum, catNum) {
|
|
24472
24477
|
const values = [...this.props.values];
|
|
@@ -24612,6 +24617,7 @@ const styles$s = StyleSheet.create({
|
|
|
24612
24617
|
var Categorizer$1 = {
|
|
24613
24618
|
name: "categorizer",
|
|
24614
24619
|
displayName: "Categorizer",
|
|
24620
|
+
hidden: true,
|
|
24615
24621
|
widget: Categorizer,
|
|
24616
24622
|
transform: widgetOptions => {
|
|
24617
24623
|
return _.pick(widgetOptions, "items", "categories", "randomizeItems");
|
|
@@ -24669,6 +24675,9 @@ const parseUrl = url => new URL(url, location().origin);
|
|
|
24669
24675
|
*/
|
|
24670
24676
|
const toAbsoluteUrl = url => parseUrl(url).href;
|
|
24671
24677
|
|
|
24678
|
+
/**
|
|
24679
|
+
* This widget is for embedding Khan Academy CS programs.
|
|
24680
|
+
*/
|
|
24672
24681
|
const {
|
|
24673
24682
|
updateQueryString: updateQueryString$1
|
|
24674
24683
|
} = Util;
|
|
@@ -24714,12 +24723,6 @@ class CSProgram extends React.Component {
|
|
|
24714
24723
|
// @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
|
|
24715
24724
|
return change.apply(this, args);
|
|
24716
24725
|
};
|
|
24717
|
-
this.simpleValidate = rubric => {
|
|
24718
|
-
return CSProgram.validate({
|
|
24719
|
-
status: this.props.status,
|
|
24720
|
-
message: this.props.message
|
|
24721
|
-
}, rubric);
|
|
24722
|
-
};
|
|
24723
24726
|
}
|
|
24724
24727
|
// The widget's grading function
|
|
24725
24728
|
static validate(state, rubric) {
|
|
@@ -24752,6 +24755,12 @@ class CSProgram extends React.Component {
|
|
|
24752
24755
|
componentWillUnmount() {
|
|
24753
24756
|
$(window).off("message", this.handleMessageEvent);
|
|
24754
24757
|
}
|
|
24758
|
+
simpleValidate(rubric) {
|
|
24759
|
+
return CSProgram.validate({
|
|
24760
|
+
status: this.props.status,
|
|
24761
|
+
message: this.props.message
|
|
24762
|
+
}, rubric);
|
|
24763
|
+
}
|
|
24755
24764
|
render() {
|
|
24756
24765
|
if (!this.props.programID) {
|
|
24757
24766
|
return /*#__PURE__*/React.createElement("div", null);
|
|
@@ -24811,18 +24820,6 @@ class CSProgram extends React.Component {
|
|
|
24811
24820
|
}));
|
|
24812
24821
|
}
|
|
24813
24822
|
}
|
|
24814
|
-
CSProgram.propTypes = _extends({}, propTypes, {
|
|
24815
|
-
programID: PropTypes.string,
|
|
24816
|
-
programType: PropTypes.oneOf(["pjs", "sql", "webpage"]),
|
|
24817
|
-
width: PropTypes.number,
|
|
24818
|
-
height: PropTypes.number,
|
|
24819
|
-
// eslint-disable-next-line react/forbid-prop-types
|
|
24820
|
-
settings: PropTypes.array,
|
|
24821
|
-
showEditor: PropTypes.bool,
|
|
24822
|
-
showButtons: PropTypes.bool,
|
|
24823
|
-
status: PropTypes.oneOf(["incomplete", "incorrect", "correct"]),
|
|
24824
|
-
message: PropTypes.string
|
|
24825
|
-
});
|
|
24826
24823
|
CSProgram.defaultProps = {
|
|
24827
24824
|
showEditor: false,
|
|
24828
24825
|
showButtons: false,
|
|
@@ -24859,12 +24856,7 @@ class Definition extends React.Component {
|
|
|
24859
24856
|
};
|
|
24860
24857
|
}
|
|
24861
24858
|
static validate(userInput, rubric) {
|
|
24862
|
-
return
|
|
24863
|
-
type: "points",
|
|
24864
|
-
earned: 0,
|
|
24865
|
-
total: 0,
|
|
24866
|
-
message: null
|
|
24867
|
-
};
|
|
24859
|
+
return noopValidator();
|
|
24868
24860
|
}
|
|
24869
24861
|
render() {
|
|
24870
24862
|
return /*#__PURE__*/React.createElement(DefinitionConsumer, null, ({
|
|
@@ -24966,6 +24958,23 @@ var DeprecatedStandin$1 = {
|
|
|
24966
24958
|
hidden: true
|
|
24967
24959
|
};
|
|
24968
24960
|
|
|
24961
|
+
function dropdownValidator(userInput, rubric) {
|
|
24962
|
+
const selected = userInput.value;
|
|
24963
|
+
if (selected === 0) {
|
|
24964
|
+
return {
|
|
24965
|
+
type: "invalid",
|
|
24966
|
+
message: null
|
|
24967
|
+
};
|
|
24968
|
+
}
|
|
24969
|
+
const correct = rubric.choices[selected - 1].correct;
|
|
24970
|
+
return {
|
|
24971
|
+
type: "points",
|
|
24972
|
+
earned: correct ? 1 : 0,
|
|
24973
|
+
total: 1,
|
|
24974
|
+
message: null
|
|
24975
|
+
};
|
|
24976
|
+
}
|
|
24977
|
+
|
|
24969
24978
|
class Dropdown extends React.Component {
|
|
24970
24979
|
constructor(...args) {
|
|
24971
24980
|
super(...args);
|
|
@@ -24992,24 +25001,11 @@ class Dropdown extends React.Component {
|
|
|
24992
25001
|
};
|
|
24993
25002
|
};
|
|
24994
25003
|
this.simpleValidate = rubric => {
|
|
24995
|
-
return
|
|
25004
|
+
return dropdownValidator(this.getUserInput(), rubric);
|
|
24996
25005
|
};
|
|
24997
25006
|
}
|
|
24998
25007
|
static validate(userInput, rubric) {
|
|
24999
|
-
|
|
25000
|
-
if (selected === 0) {
|
|
25001
|
-
return {
|
|
25002
|
-
type: "invalid",
|
|
25003
|
-
message: null
|
|
25004
|
-
};
|
|
25005
|
-
}
|
|
25006
|
-
const correct = rubric.choices[selected - 1].correct;
|
|
25007
|
-
return {
|
|
25008
|
-
type: "points",
|
|
25009
|
-
earned: correct ? 1 : 0,
|
|
25010
|
-
total: 1,
|
|
25011
|
-
message: null
|
|
25012
|
-
};
|
|
25008
|
+
return dropdownValidator(userInput, rubric);
|
|
25013
25009
|
}
|
|
25014
25010
|
render() {
|
|
25015
25011
|
const children = [/*#__PURE__*/React.createElement(OptionItem, {
|
|
@@ -25092,12 +25088,7 @@ class Explanation extends React.Component {
|
|
|
25092
25088
|
};
|
|
25093
25089
|
}
|
|
25094
25090
|
static validate(userInput, rubric) {
|
|
25095
|
-
return
|
|
25096
|
-
type: "points",
|
|
25097
|
-
earned: 0,
|
|
25098
|
-
total: 0,
|
|
25099
|
-
message: null
|
|
25100
|
-
};
|
|
25091
|
+
return noopValidator();
|
|
25101
25092
|
}
|
|
25102
25093
|
componentDidMount() {
|
|
25103
25094
|
this._mounted = true;
|
|
@@ -26925,6 +26916,7 @@ const staticTransform$4 = editorProps => {
|
|
|
26925
26916
|
var Grapher$1 = {
|
|
26926
26917
|
name: "grapher",
|
|
26927
26918
|
displayName: "Grapher",
|
|
26919
|
+
hidden: true,
|
|
26928
26920
|
widget: Grapher,
|
|
26929
26921
|
transform: propTransform$2,
|
|
26930
26922
|
staticTransform: staticTransform$4
|
|
@@ -27074,21 +27066,25 @@ var Group$1 = {
|
|
|
27074
27066
|
displayName: "Group (SAT only)",
|
|
27075
27067
|
widget: Group,
|
|
27076
27068
|
traverseChildWidgets: traverseChildWidgets,
|
|
27077
|
-
hidden:
|
|
27069
|
+
hidden: true,
|
|
27078
27070
|
isLintable: true
|
|
27079
27071
|
};
|
|
27080
27072
|
|
|
27073
|
+
/**
|
|
27074
|
+
* This is an iframe widget. It is used for rendering an iframe that
|
|
27075
|
+
* then communicates its state via window.postMessage
|
|
27076
|
+
* This is useful for embedding arbitrary visualizations/simulations with
|
|
27077
|
+
* completed conditions, such as the mazes and games in Algorithms.
|
|
27078
|
+
* It's particularly well suited for embedding our ProcessingJS programs,
|
|
27079
|
+
* but could also be used for embedding viz's hosted elsewhere.
|
|
27080
|
+
*/
|
|
27081
27081
|
const {
|
|
27082
27082
|
updateQueryString
|
|
27083
27083
|
} = Util;
|
|
27084
|
-
|
|
27085
27084
|
/* This renders the iframe and handles validation via window.postMessage */
|
|
27086
27085
|
class Iframe extends React.Component {
|
|
27087
27086
|
constructor(..._args) {
|
|
27088
27087
|
super(..._args);
|
|
27089
|
-
this.getUserInput = () => {
|
|
27090
|
-
return WidgetJsonifyDeprecated.getUserInput.call(this);
|
|
27091
|
-
};
|
|
27092
27088
|
this.handleMessageEvent = e => {
|
|
27093
27089
|
// We receive data from the iframe that contains {passed: true/false}
|
|
27094
27090
|
// and use that to set the status
|
|
@@ -27123,6 +27119,12 @@ class Iframe extends React.Component {
|
|
|
27123
27119
|
componentWillUnmount() {
|
|
27124
27120
|
$(window).off("message", this.handleMessageEvent);
|
|
27125
27121
|
}
|
|
27122
|
+
getUserInput() {
|
|
27123
|
+
return {
|
|
27124
|
+
status: this.props.status,
|
|
27125
|
+
message: this.props.message
|
|
27126
|
+
};
|
|
27127
|
+
}
|
|
27126
27128
|
render() {
|
|
27127
27129
|
const style = {
|
|
27128
27130
|
width: String(this.props.width),
|
|
@@ -27188,16 +27190,6 @@ class Iframe extends React.Component {
|
|
|
27188
27190
|
/**
|
|
27189
27191
|
* This is the widget's grading function
|
|
27190
27192
|
*/
|
|
27191
|
-
Iframe.propTypes = _extends({}, propTypes, {
|
|
27192
|
-
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
27193
|
-
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
27194
|
-
url: PropTypes.string,
|
|
27195
|
-
settings: PropTypes.array,
|
|
27196
|
-
status: PropTypes.oneOf(["incomplete", "incorrect", "correct"]),
|
|
27197
|
-
message: PropTypes.string,
|
|
27198
|
-
allowFullScreen: PropTypes.bool,
|
|
27199
|
-
allowTopNavigation: PropTypes.bool
|
|
27200
|
-
});
|
|
27201
27193
|
Iframe.defaultProps = {
|
|
27202
27194
|
status: "incomplete",
|
|
27203
27195
|
// optional message
|
|
@@ -27266,12 +27258,7 @@ class ImageWidget extends React.Component {
|
|
|
27266
27258
|
this.focus = () => {};
|
|
27267
27259
|
}
|
|
27268
27260
|
static validate(userInput, rubric) {
|
|
27269
|
-
return
|
|
27270
|
-
type: "points",
|
|
27271
|
-
earned: 0,
|
|
27272
|
-
total: 0,
|
|
27273
|
-
message: null
|
|
27274
|
-
};
|
|
27261
|
+
return noopValidator();
|
|
27275
27262
|
}
|
|
27276
27263
|
// no-op
|
|
27277
27264
|
|
|
@@ -27592,12 +27579,7 @@ class Interaction extends React.Component {
|
|
|
27592
27579
|
};
|
|
27593
27580
|
}
|
|
27594
27581
|
static validate(state, rubric) {
|
|
27595
|
-
return
|
|
27596
|
-
type: "points",
|
|
27597
|
-
earned: 0,
|
|
27598
|
-
total: 0,
|
|
27599
|
-
message: null
|
|
27600
|
-
};
|
|
27582
|
+
return noopValidator();
|
|
27601
27583
|
}
|
|
27602
27584
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
27603
27585
|
// HACK(michaelpolyak): This widget is not wired to use the Perseus
|
|
@@ -29663,17 +29645,23 @@ function LockedLabel(props) {
|
|
|
29663
29645
|
|
|
29664
29646
|
function GraphLockedLabelsLayer(props) {
|
|
29665
29647
|
const {
|
|
29648
|
+
flags,
|
|
29666
29649
|
lockedFigures
|
|
29667
29650
|
} = props;
|
|
29668
29651
|
return lockedFigures.map((figure, i) => {
|
|
29652
|
+
var _flags$mafs;
|
|
29669
29653
|
if (figure.type === "label") {
|
|
29670
29654
|
return /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
29671
29655
|
key: `label-${i}`
|
|
29672
29656
|
}, figure));
|
|
29673
29657
|
}
|
|
29674
|
-
|
|
29675
|
-
|
|
29676
|
-
|
|
29658
|
+
if (flags != null && (_flags$mafs = flags["mafs"]) != null && _flags$mafs["locked-point-labels"] && figure.type === "point") {
|
|
29659
|
+
return /*#__PURE__*/React.createElement(React.Fragment, {
|
|
29660
|
+
key: `point-${i}`
|
|
29661
|
+
}, figure.labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
29662
|
+
key: `locked-figure-${i}-label-${j}`
|
|
29663
|
+
}, label))));
|
|
29664
|
+
}
|
|
29677
29665
|
return null;
|
|
29678
29666
|
});
|
|
29679
29667
|
}
|
|
@@ -31884,6 +31872,7 @@ function useDraggablePx(args) {
|
|
|
31884
31872
|
*/
|
|
31885
31873
|
const REMOVE_BUTTON_ID = "perseus_mafs_remove_button";
|
|
31886
31874
|
const MafsGraph = props => {
|
|
31875
|
+
var _props$flags;
|
|
31887
31876
|
const {
|
|
31888
31877
|
state,
|
|
31889
31878
|
dispatch,
|
|
@@ -31991,7 +31980,8 @@ const MafsGraph = props => {
|
|
|
31991
31980
|
props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
|
|
31992
31981
|
lockedFigures: props.lockedFigures,
|
|
31993
31982
|
range: state.range
|
|
31994
|
-
}))), props.
|
|
31983
|
+
}))), ((_props$flags = props.flags) == null || (_props$flags = _props$flags["mafs"]) == null ? void 0 : _props$flags["interactive-graph-locked-features-labels"]) && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
|
|
31984
|
+
flags: props.flags,
|
|
31995
31985
|
lockedFigures: props.lockedFigures
|
|
31996
31986
|
}), /*#__PURE__*/React.createElement(View, {
|
|
31997
31987
|
style: {
|
|
@@ -34600,7 +34590,7 @@ class InteractiveGraph extends React.Component {
|
|
|
34600
34590
|
const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
|
|
34601
34591
|
const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
|
|
34602
34592
|
return /*#__PURE__*/React.createElement(StatefulMafsGraph, _extends({}, this.props, {
|
|
34603
|
-
|
|
34593
|
+
flags: (_this$props$apiOption2 = this.props.apiOptions) == null ? void 0 : _this$props$apiOption2.flags,
|
|
34604
34594
|
ref: this.mafsRef,
|
|
34605
34595
|
gridStep: gridStep,
|
|
34606
34596
|
snapStep: snapStep,
|
|
@@ -35211,7 +35201,7 @@ function shouldUseMafs(mafsFlags, graph) {
|
|
|
35211
35201
|
const staticTransform$3 = _.identity;
|
|
35212
35202
|
var InteractiveGraph$1 = {
|
|
35213
35203
|
name: "interactive-graph",
|
|
35214
|
-
displayName: "Interactive graph",
|
|
35204
|
+
displayName: "Interactive graph (Assessments only)",
|
|
35215
35205
|
widget: InteractiveGraph,
|
|
35216
35206
|
staticTransform: staticTransform$3
|
|
35217
35207
|
};
|
|
@@ -37147,11 +37137,6 @@ var LabelImage$1 = {
|
|
|
37147
37137
|
isLintable: true
|
|
37148
37138
|
};
|
|
37149
37139
|
|
|
37150
|
-
let Layout = /*#__PURE__*/function (Layout) {
|
|
37151
|
-
Layout["HORIZONTAL"] = "horizontal";
|
|
37152
|
-
Layout["VERTICAL"] = "vertical";
|
|
37153
|
-
return Layout;
|
|
37154
|
-
}({});
|
|
37155
37140
|
var ItemState = /*#__PURE__*/function (ItemState) {
|
|
37156
37141
|
ItemState["STATIC"] = "static";
|
|
37157
37142
|
ItemState["DRAGGING"] = "dragging";
|
|
@@ -37178,7 +37163,7 @@ class Placeholder extends React.Component {
|
|
|
37178
37163
|
const {
|
|
37179
37164
|
layout
|
|
37180
37165
|
} = this.props;
|
|
37181
|
-
const className = css(styles$d.card, styles$d.placeholder, layout ===
|
|
37166
|
+
const className = css(styles$d.card, styles$d.placeholder, layout === "horizontal" && styles$d.horizontalCard);
|
|
37182
37167
|
const style = {
|
|
37183
37168
|
width: this.props.width,
|
|
37184
37169
|
height: this.props.height
|
|
@@ -37382,7 +37367,7 @@ class Draggable extends React.Component {
|
|
|
37382
37367
|
// We need to keep backwards compatbility with rules specified directly
|
|
37383
37368
|
// in CSS. Hence the hacky tacking on of manual classNames.
|
|
37384
37369
|
// See sortable.less for details.
|
|
37385
|
-
let className = css(styles$d.card, styles$d.draggable, layout ===
|
|
37370
|
+
let className = css(styles$d.card, styles$d.draggable, layout === "horizontal" && styles$d.horizontalCard, layout === "vertical" && styles$d.verticalCard, type === ItemState.DRAGGING && styles$d.dragging, type === ItemState.DISABLED && styles$d.disabled, !includePadding && styles$d.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
|
|
37386
37371
|
if (!includePadding) {
|
|
37387
37372
|
className += " perseus-sortable-draggable-unpadded";
|
|
37388
37373
|
}
|
|
@@ -37511,7 +37496,7 @@ class Sortable extends React.Component {
|
|
|
37511
37496
|
if (constraints != null && constraints.width) {
|
|
37512
37497
|
// Items must be at least as wide as the specified constraint
|
|
37513
37498
|
syncWidth = _.max(widths.concat(constraints.width));
|
|
37514
|
-
} else if (layout ===
|
|
37499
|
+
} else if (layout === "vertical") {
|
|
37515
37500
|
// Sync widths to get a clean column
|
|
37516
37501
|
syncWidth = _.max(widths);
|
|
37517
37502
|
}
|
|
@@ -37519,7 +37504,7 @@ class Sortable extends React.Component {
|
|
|
37519
37504
|
if (constraints != null && constraints.height) {
|
|
37520
37505
|
// Items must be at least as high as the specified constraint
|
|
37521
37506
|
syncHeight = _.max(heights.concat(constraints.height));
|
|
37522
|
-
} else if (layout ===
|
|
37507
|
+
} else if (layout === "horizontal") {
|
|
37523
37508
|
// Sync widths to get a clean row
|
|
37524
37509
|
syncHeight = _.max(heights);
|
|
37525
37510
|
}
|
|
@@ -37601,7 +37586,7 @@ class Sortable extends React.Component {
|
|
|
37601
37586
|
|
|
37602
37587
|
// @ts-expect-error - TS2551 - Property 'splice' does not exist on type 'readonly SortableItem[]'. Did you mean 'slice'?
|
|
37603
37588
|
items.splice(currentIndex, 1);
|
|
37604
|
-
if (this.props.layout ===
|
|
37589
|
+
if (this.props.layout === "horizontal") {
|
|
37605
37590
|
// @ts-expect-error - TS2339 - Property 'offset' does not exist on type 'JQueryStatic'. | TS2339 - Property 'offset' does not exist on type 'JQueryStatic'.
|
|
37606
37591
|
const midWidth = $draggable.offset().left - $sortable.offset().left;
|
|
37607
37592
|
let sumWidth = 0;
|
|
@@ -37716,19 +37701,19 @@ class Sortable extends React.Component {
|
|
|
37716
37701
|
// We need to keep backwards compatbility with rules specified directly
|
|
37717
37702
|
// in CSS. See sortable.less for details.
|
|
37718
37703
|
const className = css(styles$d.sortable) + " perseus-sortable";
|
|
37719
|
-
const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout ===
|
|
37720
|
-
const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout ===
|
|
37704
|
+
const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout === "vertical";
|
|
37705
|
+
const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout === "horizontal";
|
|
37721
37706
|
_.each(this.state.items, function (item, i, items) {
|
|
37722
37707
|
const isLast = i === items.length - 1;
|
|
37723
37708
|
const isStatic = item.state === ItemState.STATIC || item.state === ItemState.DISABLED;
|
|
37724
37709
|
let margin;
|
|
37725
37710
|
|
|
37726
37711
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
37727
|
-
if (this.props.layout ===
|
|
37712
|
+
if (this.props.layout === "horizontal") {
|
|
37728
37713
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
37729
37714
|
margin = "0 " + this.props.margin + "px 0 0"; // right
|
|
37730
37715
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
37731
|
-
} else if (this.props.layout ===
|
|
37716
|
+
} else if (this.props.layout === "vertical") {
|
|
37732
37717
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
37733
37718
|
margin = "0 0 " + this.props.margin + "px 0"; // bottom
|
|
37734
37719
|
}
|
|
@@ -37802,7 +37787,7 @@ class Sortable extends React.Component {
|
|
|
37802
37787
|
}
|
|
37803
37788
|
}
|
|
37804
37789
|
Sortable.defaultProps = {
|
|
37805
|
-
layout:
|
|
37790
|
+
layout: "horizontal",
|
|
37806
37791
|
padding: true,
|
|
37807
37792
|
disabled: false,
|
|
37808
37793
|
constraints: {},
|
|
@@ -37861,7 +37846,6 @@ const styles$d = StyleSheet.create({
|
|
|
37861
37846
|
}
|
|
37862
37847
|
});
|
|
37863
37848
|
|
|
37864
|
-
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
37865
37849
|
const {
|
|
37866
37850
|
shuffle: shuffle$1,
|
|
37867
37851
|
seededRNG
|
|
@@ -37995,7 +37979,7 @@ class Matcher extends React.Component {
|
|
|
37995
37979
|
className: css(styles$c.column)
|
|
37996
37980
|
}, /*#__PURE__*/React.createElement(Sortable, {
|
|
37997
37981
|
options: left,
|
|
37998
|
-
layout:
|
|
37982
|
+
layout: "vertical",
|
|
37999
37983
|
padding: this.props.padding,
|
|
38000
37984
|
disabled: !this.props.orderMatters,
|
|
38001
37985
|
constraints: constraints,
|
|
@@ -38008,11 +37992,9 @@ class Matcher extends React.Component {
|
|
|
38008
37992
|
ref: "left"
|
|
38009
37993
|
})), /*#__PURE__*/React.createElement("td", {
|
|
38010
37994
|
className: css(styles$c.column, styles$c.columnRight)
|
|
38011
|
-
}, /*#__PURE__*/React.createElement(Sortable
|
|
38012
|
-
// @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
|
|
38013
|
-
, {
|
|
37995
|
+
}, /*#__PURE__*/React.createElement(Sortable, {
|
|
38014
37996
|
options: right,
|
|
38015
|
-
layout:
|
|
37997
|
+
layout: "vertical",
|
|
38016
37998
|
padding: this.props.padding,
|
|
38017
37999
|
constraints: constraints,
|
|
38018
38000
|
onMeasure: this.onMeasureRight,
|
|
@@ -38026,18 +38008,6 @@ class Matcher extends React.Component {
|
|
|
38026
38008
|
}
|
|
38027
38009
|
}
|
|
38028
38010
|
Matcher.contextType = PerseusI18nContext;
|
|
38029
|
-
Matcher.propTypes = {
|
|
38030
|
-
apiOptions: ApiOptions.propTypes,
|
|
38031
|
-
labels: PropTypes.array,
|
|
38032
|
-
left: PropTypes.array,
|
|
38033
|
-
onChange: PropTypes.func,
|
|
38034
|
-
orderMatters: PropTypes.bool,
|
|
38035
|
-
padding: PropTypes.bool,
|
|
38036
|
-
problemNum: PropTypes.number,
|
|
38037
|
-
right: PropTypes.array,
|
|
38038
|
-
trackInteraction: PropTypes.func.isRequired,
|
|
38039
|
-
linterContext: linterContextProps
|
|
38040
|
-
};
|
|
38041
38011
|
Matcher.defaultProps = {
|
|
38042
38012
|
left: [],
|
|
38043
38013
|
right: [],
|
|
@@ -38557,6 +38527,7 @@ const staticTransform$2 = editorProps => {
|
|
|
38557
38527
|
var Matrix$1 = {
|
|
38558
38528
|
name: "matrix",
|
|
38559
38529
|
displayName: "Matrix",
|
|
38530
|
+
hidden: true,
|
|
38560
38531
|
widget: Matrix,
|
|
38561
38532
|
transform: propTransform$1,
|
|
38562
38533
|
staticTransform: staticTransform$2,
|
|
@@ -38713,6 +38684,7 @@ const propUpgrades = {
|
|
|
38713
38684
|
var Measurer$1 = {
|
|
38714
38685
|
name: "measurer",
|
|
38715
38686
|
displayName: "Measurer",
|
|
38687
|
+
hidden: true,
|
|
38716
38688
|
widget: Measurer,
|
|
38717
38689
|
version: {
|
|
38718
38690
|
major: 1,
|
|
@@ -39602,12 +39574,7 @@ class MoleculeWidget extends React.Component {
|
|
|
39602
39574
|
// TODO(colin): this is here as part of the interface for a component.
|
|
39603
39575
|
// Figure out if there is something more appropriate that this should
|
|
39604
39576
|
// return.
|
|
39605
|
-
return
|
|
39606
|
-
type: "points",
|
|
39607
|
-
earned: 0,
|
|
39608
|
-
total: 0,
|
|
39609
|
-
message: null
|
|
39610
|
-
};
|
|
39577
|
+
return noopValidator();
|
|
39611
39578
|
};
|
|
39612
39579
|
}
|
|
39613
39580
|
render() {
|
|
@@ -40214,15 +40181,11 @@ class PlaceholderCard extends React.Component {
|
|
|
40214
40181
|
}, /*#__PURE__*/React.createElement("div", {
|
|
40215
40182
|
className: "card placeholder",
|
|
40216
40183
|
style: {
|
|
40217
|
-
height: this.props.
|
|
40184
|
+
height: this.props.width
|
|
40218
40185
|
}
|
|
40219
40186
|
}));
|
|
40220
40187
|
}
|
|
40221
40188
|
}
|
|
40222
|
-
PlaceholderCard.propTypes = {
|
|
40223
|
-
width: PropTypes.number.isRequired,
|
|
40224
|
-
height: PropTypes.number.isRequired
|
|
40225
|
-
};
|
|
40226
40189
|
class DragHintCard extends React.Component {
|
|
40227
40190
|
render() {
|
|
40228
40191
|
return /*#__PURE__*/React.createElement("div", {
|
|
@@ -40232,10 +40195,6 @@ class DragHintCard extends React.Component {
|
|
|
40232
40195
|
}));
|
|
40233
40196
|
}
|
|
40234
40197
|
}
|
|
40235
|
-
const PropTypePosition = PropTypes.shape({
|
|
40236
|
-
left: PropTypes.number,
|
|
40237
|
-
top: PropTypes.number
|
|
40238
|
-
});
|
|
40239
40198
|
class Card extends React.Component {
|
|
40240
40199
|
constructor(...args) {
|
|
40241
40200
|
super(...args);
|
|
@@ -40316,7 +40275,7 @@ class Card extends React.Component {
|
|
|
40316
40275
|
this.props.fakeRef !== nextProps.fakeRef;
|
|
40317
40276
|
}
|
|
40318
40277
|
componentDidUpdate(prevProps, prevState) {
|
|
40319
|
-
if (this.props.animating && !prevProps.animating) {
|
|
40278
|
+
if (this.props.animating && !prevProps.animating && this.props.animateTo && this.props.startOffset) {
|
|
40320
40279
|
// If we just were changed into animating, start the animation.
|
|
40321
40280
|
// We pick the animation speed based on the distance that the card
|
|
40322
40281
|
// needs to travel. (Why sqrt? Just because it looks nice -- with a
|
|
@@ -40339,10 +40298,11 @@ class Card extends React.Component {
|
|
|
40339
40298
|
render() {
|
|
40340
40299
|
let style = {};
|
|
40341
40300
|
if (this.props.floating) {
|
|
40301
|
+
var _this$props$startOffs, _this$props$startOffs2;
|
|
40342
40302
|
style = {
|
|
40343
40303
|
position: "absolute",
|
|
40344
|
-
left: this.props.startOffset.left,
|
|
40345
|
-
top: this.props.startOffset.top
|
|
40304
|
+
left: (_this$props$startOffs = this.props.startOffset) == null ? void 0 : _this$props$startOffs.left,
|
|
40305
|
+
top: (_this$props$startOffs2 = this.props.startOffset) == null ? void 0 : _this$props$startOffs2.top
|
|
40346
40306
|
};
|
|
40347
40307
|
}
|
|
40348
40308
|
if (this.props.width) {
|
|
@@ -40352,7 +40312,7 @@ class Card extends React.Component {
|
|
|
40352
40312
|
if (this.props.stack) {
|
|
40353
40313
|
className.push("stack");
|
|
40354
40314
|
}
|
|
40355
|
-
if (this.props.floating && !this.props.animating) {
|
|
40315
|
+
if (this.props.floating && !this.props.animating && this.props.mouse && this.props.startMouse) {
|
|
40356
40316
|
className.push("dragging");
|
|
40357
40317
|
style.left += this.props.mouse.left - this.props.startMouse.left;
|
|
40358
40318
|
style.top += this.props.mouse.top - this.props.startMouse.top;
|
|
@@ -40377,28 +40337,13 @@ class Card extends React.Component {
|
|
|
40377
40337
|
}
|
|
40378
40338
|
}
|
|
40379
40339
|
Card.contextType = PerseusI18nContext;
|
|
40380
|
-
Card.propTypes = {
|
|
40381
|
-
floating: PropTypes.bool.isRequired,
|
|
40382
|
-
animating: PropTypes.bool,
|
|
40383
|
-
width: PropTypes.number,
|
|
40384
|
-
stack: PropTypes.bool,
|
|
40385
|
-
onMouseDown: PropTypes.func,
|
|
40386
|
-
onMouseMove: PropTypes.func,
|
|
40387
|
-
onMouseUp: PropTypes.func,
|
|
40388
|
-
// Used only for floating/animating cards
|
|
40389
|
-
startMouse: PropTypePosition,
|
|
40390
|
-
startOffset: PropTypePosition,
|
|
40391
|
-
animateTo: PropTypePosition,
|
|
40392
|
-
onAnimationEnd: PropTypes.func,
|
|
40393
|
-
linterContext: linterContextProps
|
|
40394
|
-
};
|
|
40395
40340
|
Card.defaultProps = {
|
|
40396
40341
|
stack: false,
|
|
40397
40342
|
animating: false,
|
|
40398
40343
|
linterContext: linterContextDefault
|
|
40399
40344
|
};
|
|
40400
40345
|
const NORMAL = "normal",
|
|
40401
|
-
HORIZONTAL
|
|
40346
|
+
HORIZONTAL = "horizontal";
|
|
40402
40347
|
class Orderer extends React.Component {
|
|
40403
40348
|
constructor(...args) {
|
|
40404
40349
|
super(...args);
|
|
@@ -40545,7 +40490,7 @@ class Orderer extends React.Component {
|
|
|
40545
40490
|
};
|
|
40546
40491
|
this.findCorrectIndex = (draggable, list) => {
|
|
40547
40492
|
// Find the correct index for a card given the current cards.
|
|
40548
|
-
const isHorizontal = this.props.layout === HORIZONTAL
|
|
40493
|
+
const isHorizontal = this.props.layout === HORIZONTAL;
|
|
40549
40494
|
// eslint-disable-next-line react/no-string-refs
|
|
40550
40495
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
40551
40496
|
const $dragList = $(ReactDOM__default.findDOMNode(this.refs.dragList));
|
|
@@ -40595,7 +40540,7 @@ class Orderer extends React.Component {
|
|
|
40595
40540
|
if (draggable == null) {
|
|
40596
40541
|
return false;
|
|
40597
40542
|
}
|
|
40598
|
-
const isHorizontal = this.props.layout === HORIZONTAL
|
|
40543
|
+
const isHorizontal = this.props.layout === HORIZONTAL,
|
|
40599
40544
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
40600
40545
|
$draggable = $(ReactDOM__default.findDOMNode(draggable)),
|
|
40601
40546
|
// eslint-disable-next-line react/no-string-refs
|
|
@@ -40767,7 +40712,7 @@ Orderer.defaultProps = {
|
|
|
40767
40712
|
options: [],
|
|
40768
40713
|
correctOptions: [],
|
|
40769
40714
|
height: NORMAL,
|
|
40770
|
-
layout: HORIZONTAL
|
|
40715
|
+
layout: HORIZONTAL,
|
|
40771
40716
|
linterContext: linterContextDefault
|
|
40772
40717
|
};
|
|
40773
40718
|
_.extend(Orderer, {
|
|
@@ -40790,6 +40735,7 @@ _.extend(Orderer, {
|
|
|
40790
40735
|
var Orderer$1 = {
|
|
40791
40736
|
name: "orderer",
|
|
40792
40737
|
displayName: "Orderer",
|
|
40738
|
+
hidden: true,
|
|
40793
40739
|
widget: Orderer,
|
|
40794
40740
|
isLintable: true
|
|
40795
40741
|
};
|
|
@@ -42594,12 +42540,7 @@ class Passage extends React.Component {
|
|
|
42594
42540
|
};
|
|
42595
42541
|
}
|
|
42596
42542
|
static validate(state, rubric) {
|
|
42597
|
-
return
|
|
42598
|
-
type: "points",
|
|
42599
|
-
earned: 0,
|
|
42600
|
-
total: 0,
|
|
42601
|
-
message: null
|
|
42602
|
-
};
|
|
42543
|
+
return noopValidator();
|
|
42603
42544
|
}
|
|
42604
42545
|
componentDidMount() {
|
|
42605
42546
|
this._updateState();
|
|
@@ -42965,6 +42906,7 @@ Passage.defaultProps = {
|
|
|
42965
42906
|
var Passage$1 = {
|
|
42966
42907
|
name: "passage",
|
|
42967
42908
|
displayName: "Passage (SAT only)",
|
|
42909
|
+
hidden: true,
|
|
42968
42910
|
widget: Passage,
|
|
42969
42911
|
transform: editorProps => {
|
|
42970
42912
|
return _.pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
|
|
@@ -42976,23 +42918,21 @@ var Passage$1 = {
|
|
|
42976
42918
|
class PassageRefTarget extends React.Component {
|
|
42977
42919
|
constructor(..._args) {
|
|
42978
42920
|
super(..._args);
|
|
42921
|
+
// TODO passage-ref-target isn't interactive; remove
|
|
42979
42922
|
this.getUserInput = () => {
|
|
42980
|
-
return
|
|
42923
|
+
return;
|
|
42981
42924
|
};
|
|
42925
|
+
// TODO passage-ref-target isn't interactive; remove
|
|
42982
42926
|
this.change = (...args) => {
|
|
42983
42927
|
return change.apply(this, args);
|
|
42984
42928
|
};
|
|
42929
|
+
// TODO passage-ref-target isn't interactive; remove
|
|
42985
42930
|
this.simpleValidate = rubric => {
|
|
42986
42931
|
return PassageRefTarget.validate(this.getUserInput(), rubric);
|
|
42987
42932
|
};
|
|
42988
42933
|
}
|
|
42989
42934
|
static validate(state, rubric) {
|
|
42990
|
-
return
|
|
42991
|
-
type: "points",
|
|
42992
|
-
earned: 0,
|
|
42993
|
-
total: 0,
|
|
42994
|
-
message: null
|
|
42995
|
-
};
|
|
42935
|
+
return noopValidator();
|
|
42996
42936
|
}
|
|
42997
42937
|
render() {
|
|
42998
42938
|
return /*#__PURE__*/React.createElement(Renderer, {
|
|
@@ -44054,6 +43994,7 @@ const staticTransform = _.identity;
|
|
|
44054
43994
|
var Plotter$1 = {
|
|
44055
43995
|
name: "plotter",
|
|
44056
43996
|
displayName: "Plotter",
|
|
43997
|
+
hidden: true,
|
|
44057
43998
|
widget: Plotter,
|
|
44058
43999
|
staticTransform: staticTransform
|
|
44059
44000
|
};
|
|
@@ -44104,17 +44045,13 @@ var PythonProgram$1 = {
|
|
|
44104
44045
|
widget: PythonProgram
|
|
44105
44046
|
};
|
|
44106
44047
|
|
|
44107
|
-
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
44108
44048
|
const {
|
|
44109
44049
|
shuffle
|
|
44110
44050
|
} = Util;
|
|
44111
|
-
const HORIZONTAL = "horizontal";
|
|
44112
|
-
const VERTICAL = "vertical";
|
|
44113
44051
|
class Sorter extends React.Component {
|
|
44114
44052
|
constructor(...args) {
|
|
44115
44053
|
super(...args);
|
|
44116
|
-
|
|
44117
|
-
this._isMounted = void 0;
|
|
44054
|
+
this._isMounted = false;
|
|
44118
44055
|
this.state = {
|
|
44119
44056
|
changed: false
|
|
44120
44057
|
};
|
|
@@ -44188,9 +44125,7 @@ class Sorter extends React.Component {
|
|
|
44188
44125
|
const marginPx = apiOptions.isMobile ? 8 : 5;
|
|
44189
44126
|
return /*#__PURE__*/React.createElement("div", {
|
|
44190
44127
|
className: "perseus-widget-sorter perseus-clearfix"
|
|
44191
|
-
}, /*#__PURE__*/React.createElement(Sortable
|
|
44192
|
-
// @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
|
|
44193
|
-
, {
|
|
44128
|
+
}, /*#__PURE__*/React.createElement(Sortable, {
|
|
44194
44129
|
options: options,
|
|
44195
44130
|
layout: this.props.layout,
|
|
44196
44131
|
margin: marginPx,
|
|
@@ -44203,20 +44138,9 @@ class Sorter extends React.Component {
|
|
|
44203
44138
|
}));
|
|
44204
44139
|
}
|
|
44205
44140
|
}
|
|
44206
|
-
Sorter.propTypes = {
|
|
44207
|
-
apiOptions: ApiOptions.propTypes,
|
|
44208
|
-
// eslint-disable-next-line react/forbid-prop-types
|
|
44209
|
-
correct: PropTypes.array,
|
|
44210
|
-
layout: PropTypes.oneOf([HORIZONTAL, VERTICAL]),
|
|
44211
|
-
onChange: PropTypes.func,
|
|
44212
|
-
padding: PropTypes.bool,
|
|
44213
|
-
problemNum: PropTypes.number,
|
|
44214
|
-
trackInteraction: PropTypes.func.isRequired,
|
|
44215
|
-
linterContext: linterContextProps
|
|
44216
|
-
};
|
|
44217
44141
|
Sorter.defaultProps = {
|
|
44218
44142
|
correct: [],
|
|
44219
|
-
layout:
|
|
44143
|
+
layout: "horizontal",
|
|
44220
44144
|
padding: true,
|
|
44221
44145
|
problemNum: 0,
|
|
44222
44146
|
onChange: function () {},
|
|
@@ -44232,7 +44156,6 @@ var Sorter$1 = {
|
|
|
44232
44156
|
const {
|
|
44233
44157
|
assert
|
|
44234
44158
|
} = InteractiveUtil;
|
|
44235
|
-
|
|
44236
44159
|
/* Input handling: Maps a (row, column) pair to a unique ref used by React,
|
|
44237
44160
|
* and extracts (row, column) pairs from input paths, used to allow outsiders
|
|
44238
44161
|
* to focus, blur, set input values, etc. */
|
|
@@ -44419,16 +44342,6 @@ class Table extends React.Component {
|
|
|
44419
44342
|
}
|
|
44420
44343
|
}
|
|
44421
44344
|
Table.contextType = PerseusI18nContext;
|
|
44422
|
-
Table.propTypes = {
|
|
44423
|
-
answers: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
|
|
44424
|
-
editableHeaders: PropTypes.bool,
|
|
44425
|
-
// The editor to use when editableHeaders is enabled
|
|
44426
|
-
Editor: PropTypes.func,
|
|
44427
|
-
headers: PropTypes.arrayOf(PropTypes.string),
|
|
44428
|
-
keypadElement: keypadElementPropType,
|
|
44429
|
-
trackInteraction: PropTypes.func.isRequired,
|
|
44430
|
-
linterContext: linterContextProps
|
|
44431
|
-
};
|
|
44432
44345
|
Table.defaultProps = function () {
|
|
44433
44346
|
const defaultRows = 4;
|
|
44434
44347
|
const defaultColumns = 1;
|
|
@@ -44630,12 +44543,7 @@ class Video extends React.Component {
|
|
|
44630
44543
|
* of Khan Academy videos.
|
|
44631
44544
|
*/
|
|
44632
44545
|
static validate(userInput, rubric) {
|
|
44633
|
-
return
|
|
44634
|
-
type: "points",
|
|
44635
|
-
earned: 0,
|
|
44636
|
-
total: 0,
|
|
44637
|
-
message: null
|
|
44638
|
-
};
|
|
44546
|
+
return noopValidator();
|
|
44639
44547
|
}
|
|
44640
44548
|
render() {
|
|
44641
44549
|
const {
|
|
@@ -44740,7 +44648,7 @@ const init = function init(options) {
|
|
|
44740
44648
|
|
|
44741
44649
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44742
44650
|
const libName = "@khanacademy/perseus";
|
|
44743
|
-
const libVersion = "
|
|
44651
|
+
const libVersion = "34.0.0";
|
|
44744
44652
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44745
44653
|
|
|
44746
44654
|
const apiVersion = {
|
|
@@ -48203,5 +48111,5 @@ const EditorJsonify = {
|
|
|
48203
48111
|
}
|
|
48204
48112
|
};
|
|
48205
48113
|
|
|
48206
|
-
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST,
|
|
48114
|
+
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
48207
48115
|
//# sourceMappingURL=index.js.map
|