@khanacademy/perseus 52.0.1 → 53.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/number-input.d.ts +2 -0
- package/dist/components/range-input.d.ts +1 -0
- package/dist/components/svg-image.d.ts +2 -2
- package/dist/es/index.css +0 -30
- package/dist/es/index.js +385 -865
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +0 -30
- package/dist/index.d.ts +1 -2
- package/dist/index.js +383 -868
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +0 -1
- package/dist/renderer.d.ts +1 -1
- package/dist/styles/constants.d.ts +0 -2
- package/dist/types.d.ts +3 -20
- package/dist/util/math-utils.d.ts +1 -0
- package/dist/util/scoring.d.ts +0 -15
- package/dist/util/test-utils.d.ts +1 -1
- package/dist/widget-ai-utils/mock-widget/prompt-utils.d.ts +1 -1
- package/dist/widget-container.d.ts +1 -1
- package/dist/widgets/categorizer/categorizer.d.ts +0 -3
- package/dist/widgets/cs-program/cs-program.d.ts +2 -3
- package/dist/widgets/definition/definition.d.ts +0 -2
- package/dist/widgets/deprecated-standin/deprecated-standin.d.ts +0 -1
- package/dist/widgets/dropdown/dropdown.d.ts +2 -4
- package/dist/widgets/explanation/explanation.d.ts +0 -2
- package/dist/widgets/expression/expression.d.ts +1 -5
- package/dist/widgets/grapher/grapher.d.ts +0 -2
- package/dist/widgets/group/group.d.ts +0 -4
- package/dist/widgets/iframe/iframe.d.ts +0 -2
- package/dist/widgets/image/image.d.ts +0 -3
- package/dist/widgets/input-number/input-number.d.ts +0 -3
- package/dist/widgets/interaction/interaction.d.ts +0 -1
- package/dist/widgets/interactive-graph.d.ts +0 -2
- package/dist/widgets/interactive-graphs/backgrounds/axis-ticks.d.ts +1 -0
- package/dist/widgets/label-image/label-image.d.ts +2 -2
- package/dist/widgets/matcher/matcher.d.ts +0 -2
- package/dist/widgets/matrix/matrix.d.ts +0 -3
- package/dist/widgets/measurer/measurer.d.ts +0 -1
- package/dist/widgets/mock-widgets/mock-widget-types.d.ts +0 -6
- package/dist/widgets/mock-widgets/mock-widget.d.ts +2 -5
- package/dist/widgets/molecule/molecule.d.ts +0 -1
- package/dist/widgets/number-line/number-line.d.ts +2 -3
- package/dist/widgets/numeric-input/numeric-input.d.ts +2 -3
- package/dist/widgets/orderer/orderer.d.ts +0 -3
- package/dist/widgets/passage/passage.d.ts +0 -1
- package/dist/widgets/passage-ref/passage-ref.d.ts +0 -2
- package/dist/widgets/passage-ref-target/passage-ref-target.d.ts +0 -2
- package/dist/widgets/plotter/plotter.d.ts +0 -3
- package/dist/widgets/radio/radio.d.ts +0 -3
- package/dist/widgets/sorter/sorter.d.ts +2 -4
- package/dist/widgets/table/table.d.ts +0 -3
- package/dist/widgets/video/video.d.ts +0 -3
- package/dist/widgets.d.ts +1 -32
- package/package.json +10 -10
- package/dist/renderer-util.d.ts +0 -14
- package/dist/widgets/__shared__/score-noop.d.ts +0 -11
- package/dist/widgets/group/score-group.d.ts +0 -4
- package/dist/widgets/group/validate-group.d.ts +0 -4
- package/dist/widgets/mock-widgets/score-mock-widget.d.ts +0 -5
- package/dist/widgets/mock-widgets/validate-mock-widget.d.ts +0 -4
package/dist/es/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
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, expressionLogic, getExpressionPublicWidgetOptions, getDecimalSeparator, approximateDeepEqual, pluck,
|
|
3
|
+
import { PerseusError, Errors, expressionLogic, getExpressionPublicWidgetOptions, getDecimalSeparator, approximateDeepEqual, pluck, CoreWidgetRegistry, getUpgradedWidgetOptions, mapObject, getNumericInputPublicWidgetOptions, passageRefLogic, radioLogic, getCategorizerPublicWidgetOptions, getCSProgramPublicWidgetOptions, getDropdownPublicWidgetOptions, GrapherUtil as GrapherUtil$1, lockedFigureColors, lockedFigureFillStyles, approximateEqual, getLabelImagePublicWidgetOptions, getMatrixSize, measurerLogic, getNumberLinePublicWidgetOptions, getOrdererPublicWidgetOptions, getSorterPublicWidgetOptions, addLibraryVersionToPerseusDebug, upgradeWidgetInfoToLatestVersion, getWidgetIdsFromContentByType } from '@khanacademy/perseus-core';
|
|
4
4
|
export { isFailure, isSuccess, parseAndMigratePerseusArticle, parseAndMigratePerseusItem, parsePerseusItem } from '@khanacademy/perseus-core';
|
|
5
5
|
import * as PerseusLinter from '@khanacademy/perseus-linter';
|
|
6
6
|
import { linterContextDefault, Rule } from '@khanacademy/perseus-linter';
|
|
7
|
-
import { scoreExpression, validateExpression, KhanAnswerTypes, scoreInputNumber, inputNumberAnswerTypes, scoreNumericInput, scoreRadio, validateRadio, scoreCategorizer, validateCategorizer, scoreCSProgram, scoreDropdown, validateDropdown, scoreGrapher, scoreIframe, scoreInteractiveGraph, scoreLabelImage, scoreLabelImageMarker, scoreMatcher, scoreMatrix, validateMatrix, scoreNumberLine, validateNumberLine, scoreOrderer, validateOrderer, scorePlotter, validatePlotter, scoreSorter, validateSorter, scoreTable, validateTable } from '@khanacademy/perseus-score';
|
|
8
7
|
import { View, RenderStateRoot, Id, useOnMountEffect, useLatestRef, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
|
|
9
8
|
import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
|
|
10
9
|
import { HeadingMedium, LabelSmall, LabelLarge, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
@@ -21,6 +20,7 @@ import { color, spacing, font } from '@khanacademy/wonder-blocks-tokens';
|
|
|
21
20
|
import $ from 'jquery';
|
|
22
21
|
import { v4 } from 'uuid';
|
|
23
22
|
import PropTypes from 'prop-types';
|
|
23
|
+
import { KhanAnswerTypes, emptyWidgetsFunctional, scoreWidgetsFunctional, flattenScores, inputNumberAnswerTypes, scoreRadio, scoreLabelImageMarker } from '@khanacademy/perseus-score';
|
|
24
24
|
import { entries, UnreachableCaseError, keys } from '@khanacademy/wonder-stuff-core';
|
|
25
25
|
import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
|
|
26
26
|
import { point, KhanMath, number, vector as vector$3, line, geometry, angles, coefficients } from '@khanacademy/kmath';
|
|
@@ -1389,7 +1389,6 @@ const ClassNames = {
|
|
|
1389
1389
|
SELECTED: "perseus-radio-selected",
|
|
1390
1390
|
OPTION_CONTENT: "perseus-radio-option-content"
|
|
1391
1391
|
},
|
|
1392
|
-
INTERACTIVE: "perseus-interactive",
|
|
1393
1392
|
CORRECT: "perseus-correct",
|
|
1394
1393
|
INCORRECT: "perseus-incorrect",
|
|
1395
1394
|
UNANSWERED: "perseus-unanswered",
|
|
@@ -1637,7 +1636,6 @@ class Expression extends React.Component {
|
|
|
1637
1636
|
// eslint-disable-next-line react/no-string-refs
|
|
1638
1637
|
, {
|
|
1639
1638
|
ref: "input",
|
|
1640
|
-
className: ClassNames.INTERACTIVE,
|
|
1641
1639
|
value: this.props.value,
|
|
1642
1640
|
onChange: this.changeAndTrack,
|
|
1643
1641
|
convertDotToTimes: this.props.times,
|
|
@@ -1750,7 +1748,6 @@ var Expression$1 = {
|
|
|
1750
1748
|
name: "expression",
|
|
1751
1749
|
displayName: "Expression / Equation",
|
|
1752
1750
|
accessible: true,
|
|
1753
|
-
defaultAlignment: "inline-block",
|
|
1754
1751
|
widget: ExpressionWithDependencies,
|
|
1755
1752
|
transform: widgetOptions => {
|
|
1756
1753
|
const {
|
|
@@ -1775,12 +1772,6 @@ var Expression$1 = {
|
|
|
1775
1772
|
propUpgrades: expressionLogic.widgetOptionsUpgrades,
|
|
1776
1773
|
// For use by the editor
|
|
1777
1774
|
isLintable: true,
|
|
1778
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
1779
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
|
|
1780
|
-
scorer: scoreExpression,
|
|
1781
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
1782
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusExpressionUserInput'.
|
|
1783
|
-
validator: validateExpression,
|
|
1784
1775
|
getPublicWidgetOptions: getExpressionPublicWidgetOptions,
|
|
1785
1776
|
// TODO(LEMS-2656): remove TS suppression
|
|
1786
1777
|
// @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusExpressionRubric'.
|
|
@@ -3061,10 +3052,6 @@ const pureMdMax$1 = "1023px";
|
|
|
3061
3052
|
const pureLgMax$1 = "1279px";
|
|
3062
3053
|
// @tableBackgroundAccent: #f9f9f9; // for striping
|
|
3063
3054
|
const tableBackgroundAccent = "#F9F9F9";
|
|
3064
|
-
// @zIndexAboveScratchpad: @zIndexScratchPad + 1;
|
|
3065
|
-
const zIndexAboveScratchpad$1 = 2;
|
|
3066
|
-
// @zIndexInteractiveComponent: @zIndexAboveScratchpad + 1;
|
|
3067
|
-
const zIndexInteractiveComponent$1 = 3;
|
|
3068
3055
|
// @phoneMargin: 16px;
|
|
3069
3056
|
const phoneMargin = 16;
|
|
3070
3057
|
const negativePhoneMargin = -16;
|
|
@@ -3108,8 +3095,6 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
3108
3095
|
pureMdMax: pureMdMax$1,
|
|
3109
3096
|
pureLgMax: pureLgMax$1,
|
|
3110
3097
|
tableBackgroundAccent: tableBackgroundAccent,
|
|
3111
|
-
zIndexAboveScratchpad: zIndexAboveScratchpad$1,
|
|
3112
|
-
zIndexInteractiveComponent: zIndexInteractiveComponent$1,
|
|
3113
3098
|
phoneMargin: phoneMargin,
|
|
3114
3099
|
negativePhoneMargin: negativePhoneMargin,
|
|
3115
3100
|
hintBorderWidth: hintBorderWidth,
|
|
@@ -17334,130 +17319,162 @@ class QuestionParagraph extends React.Component {
|
|
|
17334
17319
|
}
|
|
17335
17320
|
}
|
|
17336
17321
|
|
|
17337
|
-
const noScore = {
|
|
17338
|
-
type: "points",
|
|
17339
|
-
earned: 0,
|
|
17340
|
-
total: 0,
|
|
17341
|
-
message: null
|
|
17342
|
-
};
|
|
17343
|
-
|
|
17344
|
-
/**
|
|
17345
|
-
* If a widget says that it is empty once it is graded.
|
|
17346
|
-
* Trying to encapsulate references to the score format.
|
|
17347
|
-
*/
|
|
17348
|
-
function scoreIsEmpty(score) {
|
|
17349
|
-
// HACK(benkomalo): ugh. this isn't great; the Perseus score objects
|
|
17350
|
-
// overload the type "invalid" for what should probably be three
|
|
17351
|
-
// distinct cases:
|
|
17352
|
-
// - truly empty or not fully filled out
|
|
17353
|
-
// - invalid or malformed inputs
|
|
17354
|
-
// - "almost correct" like inputs where the widget wants to give
|
|
17355
|
-
// feedback (e.g. a fraction needs to be reduced, or `pi` should
|
|
17356
|
-
// be used instead of 3.14)
|
|
17357
|
-
//
|
|
17358
|
-
// Unfortunately the coercion happens all over the place, as these
|
|
17359
|
-
// Perseus style score objects are created *everywhere* (basically
|
|
17360
|
-
// in every widget), so it's hard to change now. We assume that
|
|
17361
|
-
// anything with a "message" is not truly empty, and one of the
|
|
17362
|
-
// latter two cases for now.
|
|
17363
|
-
return score.type === "invalid" && (!score.message || score.message.length === 0);
|
|
17364
|
-
}
|
|
17365
|
-
|
|
17366
17322
|
/**
|
|
17367
|
-
*
|
|
17323
|
+
* TranslationLinter asynchronously calls Khan Academy's poentry linter.
|
|
17368
17324
|
*
|
|
17369
|
-
*
|
|
17370
|
-
*
|
|
17325
|
+
* The poentry linter checks for issues that prevent content from being
|
|
17326
|
+
* translated. This linter is KA-specific and so TranslationLinter does not do
|
|
17327
|
+
* anything in non-KA environments.
|
|
17371
17328
|
*/
|
|
17372
|
-
function combineScores(scoreA, scoreB) {
|
|
17373
|
-
let message;
|
|
17374
|
-
if (scoreA.type === "points" && scoreB.type === "points") {
|
|
17375
|
-
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
17376
|
-
// TODO(alpert): Figure out how to combine messages usefully
|
|
17377
|
-
message = null;
|
|
17378
|
-
} else {
|
|
17379
|
-
message = scoreA.message || scoreB.message;
|
|
17380
|
-
}
|
|
17381
|
-
return {
|
|
17382
|
-
type: "points",
|
|
17383
|
-
earned: scoreA.earned + scoreB.earned,
|
|
17384
|
-
total: scoreA.total + scoreB.total,
|
|
17385
|
-
message: message
|
|
17386
|
-
};
|
|
17387
|
-
}
|
|
17388
|
-
if (scoreA.type === "points" && scoreB.type === "invalid") {
|
|
17389
|
-
return scoreB;
|
|
17390
|
-
}
|
|
17391
|
-
if (scoreA.type === "invalid" && scoreB.type === "points") {
|
|
17392
|
-
return scoreA;
|
|
17393
|
-
}
|
|
17394
|
-
if (scoreA.type === "invalid" && scoreB.type === "invalid") {
|
|
17395
|
-
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
17396
|
-
// TODO(alpert): Figure out how to combine messages usefully
|
|
17397
|
-
message = null;
|
|
17398
|
-
} else {
|
|
17399
|
-
message = scoreA.message || scoreB.message;
|
|
17400
|
-
}
|
|
17401
|
-
return {
|
|
17402
|
-
type: "invalid",
|
|
17403
|
-
message: message
|
|
17404
|
-
};
|
|
17405
|
-
}
|
|
17406
17329
|
|
|
17330
|
+
// A LintCB is called once the linter has been run.
|
|
17331
|
+
|
|
17332
|
+
// msec to wait before we actually call the linter after receiving the
|
|
17333
|
+
// last call. The timer is reset if the lint is called during the wait.
|
|
17334
|
+
const DEBOUNCE_TIMEOUT = 1000;
|
|
17335
|
+
class TranslationLinter {
|
|
17336
|
+
constructor() {
|
|
17337
|
+
// To avoid linting when nothing has changed.
|
|
17338
|
+
this.previousContent = null;
|
|
17339
|
+
/**
|
|
17340
|
+
* Runs the poentry linter on the given perseus string asynchronously.
|
|
17341
|
+
*
|
|
17342
|
+
* It doesn't do anything unless we're in a KA environment.
|
|
17343
|
+
*
|
|
17344
|
+
* onLintErrorsGenerated is called even if there are no errors. It is not
|
|
17345
|
+
* called if perseusStr is unchanged from the last call.
|
|
17346
|
+
*
|
|
17347
|
+
* Linting is relatively expensive -- so we debounce linting each instance
|
|
17348
|
+
* for some amount of time.
|
|
17349
|
+
*/
|
|
17350
|
+
this.runLinter = debounce$1((perseusStr, onLintErrorsGenerated) => {
|
|
17351
|
+
// TODO(FEI-5003): Do we still need to check for 'KA'?
|
|
17352
|
+
if (typeof KA === "undefined") {
|
|
17353
|
+
return;
|
|
17354
|
+
}
|
|
17355
|
+
if (perseusStr === this.previousContent) {
|
|
17356
|
+
return;
|
|
17357
|
+
}
|
|
17358
|
+
this.previousContent = perseusStr;
|
|
17359
|
+
if (perseusStr === "") {
|
|
17360
|
+
onLintErrorsGenerated([]);
|
|
17361
|
+
return;
|
|
17362
|
+
}
|
|
17363
|
+
fetch("/api/internal/translations/lint_poentry?preview=1&lang=en", {
|
|
17364
|
+
headers: {
|
|
17365
|
+
"Content-Type": "application/json"
|
|
17366
|
+
},
|
|
17367
|
+
body: JSON.stringify({
|
|
17368
|
+
// The poentry linter verifies that the translation from
|
|
17369
|
+
// the original text to the translated text is sane. We
|
|
17370
|
+
// want to ensure that the translation from English to
|
|
17371
|
+
// English is sane, so msgid === mstr.
|
|
17372
|
+
msgid: perseusStr,
|
|
17373
|
+
// msgid is the original text
|
|
17374
|
+
msgstr: perseusStr,
|
|
17375
|
+
// msgstr is the translated text
|
|
17376
|
+
format: "perseus_text",
|
|
17377
|
+
filename: ""
|
|
17378
|
+
}),
|
|
17379
|
+
method: "POST"
|
|
17380
|
+
}).then(response => {
|
|
17381
|
+
if (response.status >= 400) {
|
|
17382
|
+
return {
|
|
17383
|
+
status: "error",
|
|
17384
|
+
message: "Could not run i18n linter."
|
|
17385
|
+
};
|
|
17386
|
+
}
|
|
17387
|
+
return response.json();
|
|
17388
|
+
}, rejection => {
|
|
17389
|
+
return {
|
|
17390
|
+
status: "error",
|
|
17391
|
+
message: "Could not run i18n linter."
|
|
17392
|
+
};
|
|
17393
|
+
}).then(json => {
|
|
17394
|
+
if (json.status === "error") {
|
|
17395
|
+
onLintErrorsGenerated(["Some part of this text makes it untranslatable. " + "The specific message from the i18n linter was: " + json.message.replace(/\n/g, " ")]);
|
|
17396
|
+
} else {
|
|
17397
|
+
onLintErrorsGenerated([]);
|
|
17398
|
+
}
|
|
17399
|
+
});
|
|
17400
|
+
}, DEBOUNCE_TIMEOUT);
|
|
17401
|
+
}
|
|
17407
17402
|
/**
|
|
17408
|
-
*
|
|
17409
|
-
*
|
|
17403
|
+
* Applies an array of errors generated by linters without position
|
|
17404
|
+
* information (TranslationLinter and the legacy getSaveWarnings()) to the
|
|
17405
|
+
* top of a Perseus tree.
|
|
17410
17406
|
*/
|
|
17411
|
-
|
|
17412
|
-
|
|
17413
|
-
|
|
17414
|
-
|
|
17407
|
+
applyLintErrors(parsedMarkdown, translationLintErrors) {
|
|
17408
|
+
// These lint errors do not have position data associated with
|
|
17409
|
+
// them, so we just plop them at the top.
|
|
17410
|
+
if (translationLintErrors.length) {
|
|
17411
|
+
const errorText = translationLintErrors.join("\n\n");
|
|
17412
|
+
parsedMarkdown.unshift({
|
|
17413
|
+
content: {
|
|
17414
|
+
type: "text",
|
|
17415
|
+
content: ""
|
|
17416
|
+
},
|
|
17417
|
+
insideTable: false,
|
|
17418
|
+
message: errorText,
|
|
17419
|
+
ruleName: "legacy-error",
|
|
17420
|
+
severity: Rule.Severity.ERROR,
|
|
17421
|
+
type: "lint"
|
|
17422
|
+
});
|
|
17415
17423
|
}
|
|
17416
|
-
}
|
|
17417
|
-
}
|
|
17418
|
-
function flattenScores(widgetScoreMap) {
|
|
17419
|
-
return Object.values(widgetScoreMap).reduce(combineScores, noScore);
|
|
17420
|
-
}
|
|
17421
|
-
function isCorrect(score) {
|
|
17422
|
-
return score.type === "points" && score.earned >= score.total;
|
|
17424
|
+
}
|
|
17423
17425
|
}
|
|
17424
|
-
|
|
17425
|
-
|
|
17426
|
-
|
|
17427
|
-
|
|
17428
|
-
|
|
17429
|
-
|
|
17430
|
-
|
|
17431
|
-
|
|
17432
|
-
|
|
17433
|
-
|
|
17434
|
-
|
|
17426
|
+
|
|
17427
|
+
/**
|
|
17428
|
+
* Preprocess TeX code to convert things that MathJax doesn't know how to handle
|
|
17429
|
+
* to things is does.
|
|
17430
|
+
*/
|
|
17431
|
+
var preprocessTex = (texCode => texCode
|
|
17432
|
+
// Replace uses of \begin{align}...\end{align} which KaTeX doesn't
|
|
17433
|
+
// support (yet) with \begin{aligned}...\end{aligned} which renders
|
|
17434
|
+
// the same is supported by KaTeX. It does the same for align*.
|
|
17435
|
+
// TODO(kevinb) update content to use aligned instead of align.
|
|
17436
|
+
// TODO(LEMS-1608) Remove this replacement as MathJax supports the
|
|
17437
|
+
// "align" macro correctly (and, in fact, it is not synonymous with
|
|
17438
|
+
// "aligned").
|
|
17439
|
+
.replace(/\{align[*]?\}/g, "{aligned}")
|
|
17440
|
+
// Replace non-breaking spaces with regular spaces.
|
|
17441
|
+
.replace(/[\u00a0]/g, " "));
|
|
17442
|
+
|
|
17443
|
+
const {
|
|
17444
|
+
interactiveSizes
|
|
17445
|
+
} = constants;
|
|
17446
|
+
|
|
17447
|
+
// Note: these size cutoffs represent content-width cutoffs as specified in
|
|
17448
|
+
// http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
|
|
17449
|
+
// TODO(benkomalo): these values aren't used in JS outside of this file, but
|
|
17450
|
+
// are coupled to the values in styles/styles.less - DRY it up at some point
|
|
17451
|
+
const smMax = articleMaxWidthTableInPx;
|
|
17452
|
+
const mdMax = articleMaxWidthInPx;
|
|
17453
|
+
const containerSizeClass = {
|
|
17454
|
+
SMALL: "small",
|
|
17455
|
+
MEDIUM: "medium",
|
|
17456
|
+
LARGE: "large",
|
|
17457
|
+
XLARGE: "xlarge"
|
|
17458
|
+
};
|
|
17459
|
+
const getClassFromWidth = width => {
|
|
17460
|
+
if (!width) {
|
|
17461
|
+
return containerSizeClass.MEDIUM;
|
|
17435
17462
|
}
|
|
17436
|
-
if (
|
|
17437
|
-
return
|
|
17438
|
-
empty: true,
|
|
17439
|
-
correct: false,
|
|
17440
|
-
message: score.message,
|
|
17441
|
-
suppressAlmostThere: score.suppressAlmostThere,
|
|
17442
|
-
guess: guess,
|
|
17443
|
-
state: state
|
|
17444
|
-
};
|
|
17463
|
+
if (width <= smMax) {
|
|
17464
|
+
return containerSizeClass.SMALL;
|
|
17445
17465
|
}
|
|
17446
|
-
|
|
17447
|
-
|
|
17448
|
-
|
|
17449
|
-
|
|
17450
|
-
|
|
17451
|
-
|
|
17452
|
-
|
|
17453
|
-
|
|
17454
|
-
}
|
|
17455
|
-
|
|
17466
|
+
if (width <= mdMax) {
|
|
17467
|
+
return containerSizeClass.MEDIUM;
|
|
17468
|
+
}
|
|
17469
|
+
return containerSizeClass.LARGE;
|
|
17470
|
+
};
|
|
17471
|
+
const getInteractiveBoxFromSizeClass = sizeClass => {
|
|
17472
|
+
if (sizeClass === containerSizeClass.SMALL) {
|
|
17473
|
+
return [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall];
|
|
17474
|
+
}
|
|
17475
|
+
return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
|
|
17476
|
+
};
|
|
17456
17477
|
|
|
17457
|
-
const DEFAULT_ALIGNMENT$1 = "block";
|
|
17458
|
-
// NOTE(kevinb): "default" is not one in `validAlignments`.
|
|
17459
|
-
const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
|
|
17460
|
-
const DEFAULT_STATIC = false;
|
|
17461
17478
|
const DEFAULT_TRACKING = "";
|
|
17462
17479
|
const DEFAULT_LINTABLE = false;
|
|
17463
17480
|
const widgets = {};
|
|
@@ -17474,7 +17491,6 @@ const registerWidgets = widgets => {
|
|
|
17474
17491
|
widgets.forEach(widget => {
|
|
17475
17492
|
registerWidget(widget.name, widget);
|
|
17476
17493
|
});
|
|
17477
|
-
validateAlignments();
|
|
17478
17494
|
};
|
|
17479
17495
|
|
|
17480
17496
|
/**
|
|
@@ -17558,17 +17574,9 @@ const getWidgetExport = name => {
|
|
|
17558
17574
|
var _widgets$name3;
|
|
17559
17575
|
return (_widgets$name3 = widgets[name]) != null ? _widgets$name3 : null;
|
|
17560
17576
|
};
|
|
17561
|
-
const getWidgetValidator = name => {
|
|
17562
|
-
var _widgets$name$validat, _widgets$name4;
|
|
17563
|
-
return (_widgets$name$validat = (_widgets$name4 = widgets[name]) == null ? void 0 : _widgets$name4.validator) != null ? _widgets$name$validat : null;
|
|
17564
|
-
};
|
|
17565
|
-
const getWidgetScorer = name => {
|
|
17566
|
-
var _widgets$name$scorer, _widgets$name5;
|
|
17567
|
-
return (_widgets$name$scorer = (_widgets$name5 = widgets[name]) == null ? void 0 : _widgets$name5.scorer) != null ? _widgets$name$scorer : null;
|
|
17568
|
-
};
|
|
17569
17577
|
const getPublicWidgetOptionsFunction = name => {
|
|
17570
|
-
var _widgets$name$getPubl, _widgets$
|
|
17571
|
-
return (_widgets$name$getPubl = (_widgets$
|
|
17578
|
+
var _widgets$name$getPubl, _widgets$name4;
|
|
17579
|
+
return (_widgets$name$getPubl = (_widgets$name4 = widgets[name]) == null ? void 0 : _widgets$name4.getPublicWidgetOptions) != null ? _widgets$name$getPubl : i => i;
|
|
17572
17580
|
};
|
|
17573
17581
|
const getEditor = name => {
|
|
17574
17582
|
return _.has(editors, name) ? editors[name] : null;
|
|
@@ -17612,109 +17620,6 @@ const isAccessible = widgetInfo => {
|
|
|
17612
17620
|
const getAllWidgetTypes = () => {
|
|
17613
17621
|
return _.keys(widgets);
|
|
17614
17622
|
};
|
|
17615
|
-
const upgradeWidgetInfoToLatestVersion = oldWidgetInfo => {
|
|
17616
|
-
const type = oldWidgetInfo.type;
|
|
17617
|
-
// NOTE(jeremy): This looks like it could be replaced by fixing types so
|
|
17618
|
-
// that `type` is non-optional. But we're seeing this in Sentry today so I
|
|
17619
|
-
// suspect we have legacy data (potentially unpublished) and we should
|
|
17620
|
-
// figure that out before depending solely on types.
|
|
17621
|
-
if (!_.isString(type)) {
|
|
17622
|
-
throw new PerseusError("widget type must be a string, but was: " + type, Errors.Internal);
|
|
17623
|
-
}
|
|
17624
|
-
const widgetExports = widgets[type];
|
|
17625
|
-
if (widgetExports == null) {
|
|
17626
|
-
// If we have a widget that isn't registered, we can't upgrade it
|
|
17627
|
-
// TODO(aria): Figure out what the best thing to do here would be
|
|
17628
|
-
return oldWidgetInfo;
|
|
17629
|
-
}
|
|
17630
|
-
|
|
17631
|
-
// Unversioned widgets (pre-July 2014) are all implicitly 0.0
|
|
17632
|
-
const initialVersion = oldWidgetInfo.version || {
|
|
17633
|
-
major: 0,
|
|
17634
|
-
minor: 0
|
|
17635
|
-
};
|
|
17636
|
-
const latestVersion = widgetExports.version || {
|
|
17637
|
-
major: 0,
|
|
17638
|
-
minor: 0
|
|
17639
|
-
};
|
|
17640
|
-
|
|
17641
|
-
// If the widget version is later than what we understand (major
|
|
17642
|
-
// version is higher than latest, or major versions are equal and minor
|
|
17643
|
-
// version is higher than latest), don't perform any upgrades.
|
|
17644
|
-
if (initialVersion.major > latestVersion.major || initialVersion.major === latestVersion.major && initialVersion.minor > latestVersion.minor) {
|
|
17645
|
-
return oldWidgetInfo;
|
|
17646
|
-
}
|
|
17647
|
-
|
|
17648
|
-
// We do a clone here so that it's safe to mutate the input parameter
|
|
17649
|
-
// in propUpgrades functions (which I will probably accidentally do at
|
|
17650
|
-
// some point, and we would like to not break when that happens).
|
|
17651
|
-
let newEditorProps = _.clone(oldWidgetInfo.options) || {};
|
|
17652
|
-
const upgradePropsMap = widgetExports.propUpgrades || {};
|
|
17653
|
-
|
|
17654
|
-
// Empty props usually mean a newly created widget by the editor,
|
|
17655
|
-
// and are always considerered up-to-date.
|
|
17656
|
-
// Mostly, we'd rather not run upgrade functions on props that are
|
|
17657
|
-
// not complete.
|
|
17658
|
-
if (_.keys(newEditorProps).length !== 0) {
|
|
17659
|
-
// We loop through all the versions after the current version of
|
|
17660
|
-
// the loaded widget, up to and including the latest version of the
|
|
17661
|
-
// loaded widget, and run the upgrade function to bring our loaded
|
|
17662
|
-
// widget's props up to that version.
|
|
17663
|
-
// There is a little subtlety here in that we call
|
|
17664
|
-
// upgradePropsMap[1] to upgrade *to* version 1,
|
|
17665
|
-
// (not from version 1).
|
|
17666
|
-
for (let nextVersion = initialVersion.major + 1; nextVersion <= latestVersion.major; nextVersion++) {
|
|
17667
|
-
if (upgradePropsMap[String(nextVersion)]) {
|
|
17668
|
-
newEditorProps = upgradePropsMap[String(nextVersion)](newEditorProps);
|
|
17669
|
-
} else {
|
|
17670
|
-
// This is a Log.error because it is unlikely to be hit in
|
|
17671
|
-
// local testing, and a Log.error is slightly less scary in
|
|
17672
|
-
// prod than a `throw new Error`
|
|
17673
|
-
Log.error("No upgrade found for widget. Cannot render.", Errors.Internal, {
|
|
17674
|
-
loggedMetadata: {
|
|
17675
|
-
type,
|
|
17676
|
-
fromMajorVersion: nextVersion - 1,
|
|
17677
|
-
toMajorVersion: nextVersion
|
|
17678
|
-
}
|
|
17679
|
-
});
|
|
17680
|
-
// But try to keep going anyways (yolo!)
|
|
17681
|
-
// (Throwing an error here would just break the page
|
|
17682
|
-
// silently anyways, so that doesn't seem much better
|
|
17683
|
-
// than a halfhearted attempt to continue, however
|
|
17684
|
-
// shallow...)
|
|
17685
|
-
}
|
|
17686
|
-
}
|
|
17687
|
-
}
|
|
17688
|
-
|
|
17689
|
-
// Minor version upgrades (eg. new optional props) don't have
|
|
17690
|
-
// transform functions. Instead, we fill in the new props with their
|
|
17691
|
-
// defaults.
|
|
17692
|
-
const defaultProps = type in editors ? editors[type].defaultProps : {};
|
|
17693
|
-
newEditorProps = _extends({}, defaultProps, newEditorProps);
|
|
17694
|
-
let alignment = oldWidgetInfo.alignment;
|
|
17695
|
-
|
|
17696
|
-
// Widgets that support multiple alignments will "lock in" the
|
|
17697
|
-
// alignment to the alignment that would be listed first in the
|
|
17698
|
-
// select box. If the widget only supports one alignment, the
|
|
17699
|
-
// alignment value will likely just end up as "default".
|
|
17700
|
-
if (alignment == null || alignment === "default") {
|
|
17701
|
-
alignment = getSupportedAlignments(type)[0];
|
|
17702
|
-
}
|
|
17703
|
-
let widgetStatic = oldWidgetInfo.static;
|
|
17704
|
-
if (widgetStatic == null) {
|
|
17705
|
-
widgetStatic = DEFAULT_STATIC;
|
|
17706
|
-
}
|
|
17707
|
-
return _.extend({}, oldWidgetInfo, {
|
|
17708
|
-
// maintain other info, like type
|
|
17709
|
-
// After upgrading we guarantee that the version is up-to-date
|
|
17710
|
-
version: latestVersion,
|
|
17711
|
-
// Default graded to true (so null/undefined becomes true):
|
|
17712
|
-
graded: oldWidgetInfo.graded != null ? oldWidgetInfo.graded : true,
|
|
17713
|
-
alignment: alignment,
|
|
17714
|
-
static: widgetStatic,
|
|
17715
|
-
options: newEditorProps
|
|
17716
|
-
});
|
|
17717
|
-
};
|
|
17718
17623
|
const getRendererPropsForWidgetInfo = (widgetInfo, strings, problemNum) => {
|
|
17719
17624
|
const type = widgetInfo.type;
|
|
17720
17625
|
const widgetExports = widgets[type];
|
|
@@ -17756,70 +17661,6 @@ const traverseChildWidgets$3 = (widgetInfo, traverseRenderer) => {
|
|
|
17756
17661
|
return widgetInfo;
|
|
17757
17662
|
};
|
|
17758
17663
|
|
|
17759
|
-
/**
|
|
17760
|
-
* Handling for the optional alignments for widgets
|
|
17761
|
-
* See widget-container.jsx for details on how alignments are implemented.
|
|
17762
|
-
*/
|
|
17763
|
-
|
|
17764
|
-
/**
|
|
17765
|
-
* Returns the list of supported alignments for the given (string) widget
|
|
17766
|
-
* type. This is used primarily at editing time to display the choices
|
|
17767
|
-
* for the user.
|
|
17768
|
-
*
|
|
17769
|
-
* Supported alignments are given as an array of strings in the exports of
|
|
17770
|
-
* a widget's module.
|
|
17771
|
-
*/
|
|
17772
|
-
const getSupportedAlignments = type => {
|
|
17773
|
-
const widgetExport = widgets[type];
|
|
17774
|
-
// @ts-expect-error - TS2322 - Type 'string[] | readonly Alignment[]' is not assignable to type 'readonly Alignment[]'.
|
|
17775
|
-
return widgetExport && widgetExport.supportedAlignments || DEFAULT_SUPPORTED_ALIGNMENTS;
|
|
17776
|
-
};
|
|
17777
|
-
|
|
17778
|
-
/**
|
|
17779
|
-
* For the given (string) widget type, determine the default alignment for
|
|
17780
|
-
* the widget. This is used at rendering time to go from "default" alignment
|
|
17781
|
-
* to the actual alignment displayed on the screen.
|
|
17782
|
-
*
|
|
17783
|
-
* The default alignment is given either as a string (called
|
|
17784
|
-
* `defaultAlignment`) or a function (called `getDefaultAlignment`) on
|
|
17785
|
-
* the exports of a widget's module.
|
|
17786
|
-
*/
|
|
17787
|
-
const getDefaultAlignment = type => {
|
|
17788
|
-
const widgetExports = widgets[type];
|
|
17789
|
-
let alignment;
|
|
17790
|
-
if (!widgetExports) {
|
|
17791
|
-
return DEFAULT_ALIGNMENT$1;
|
|
17792
|
-
}
|
|
17793
|
-
if (widgetExports.getDefaultAlignment) {
|
|
17794
|
-
alignment = widgetExports.getDefaultAlignment();
|
|
17795
|
-
} else {
|
|
17796
|
-
alignment = widgetExports.defaultAlignment;
|
|
17797
|
-
}
|
|
17798
|
-
return alignment || DEFAULT_ALIGNMENT$1;
|
|
17799
|
-
};
|
|
17800
|
-
const validAlignments = ["block", "inline-block", "inline", "float-left", "float-right", "full-width"];
|
|
17801
|
-
|
|
17802
|
-
/**
|
|
17803
|
-
* Used at startup to fail fast if an alignment given by a widget is
|
|
17804
|
-
* invalid.
|
|
17805
|
-
*/
|
|
17806
|
-
// TODO(alex): Change this to run as a testcase (vs. being run at runtime)
|
|
17807
|
-
// TODO(LP-10707): I think this can be completely removed because our TypeScript types
|
|
17808
|
-
// enforce this!
|
|
17809
|
-
const validateAlignments = () => {
|
|
17810
|
-
_.each(widgets, function (widgetInfo) {
|
|
17811
|
-
if (widgetInfo.defaultAlignment && !_.contains(validAlignments, widgetInfo.defaultAlignment)) {
|
|
17812
|
-
throw new PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid defaultAlignment value: " + widgetInfo.defaultAlignment, Errors.InvalidInput);
|
|
17813
|
-
}
|
|
17814
|
-
if (widgetInfo.supportedAlignments) {
|
|
17815
|
-
const unknownAlignments = _.difference(widgetInfo.supportedAlignments, validAlignments);
|
|
17816
|
-
if (unknownAlignments.length) {
|
|
17817
|
-
throw new PerseusError("Widget '" + widgetInfo.displayName + "' has an invalid value for supportedAlignments: " + unknownAlignments.join(" "), Errors.InvalidInput);
|
|
17818
|
-
}
|
|
17819
|
-
}
|
|
17820
|
-
});
|
|
17821
|
-
};
|
|
17822
|
-
|
|
17823
17664
|
/**
|
|
17824
17665
|
* Handling for static mode for widgets that support it.
|
|
17825
17666
|
*/
|
|
@@ -17873,8 +17714,6 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
17873
17714
|
replaceDeprecatedEditors: replaceDeprecatedEditors,
|
|
17874
17715
|
getWidget: getWidget,
|
|
17875
17716
|
getWidgetExport: getWidgetExport,
|
|
17876
|
-
getWidgetValidator: getWidgetValidator,
|
|
17877
|
-
getWidgetScorer: getWidgetScorer,
|
|
17878
17717
|
getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
|
|
17879
17718
|
getEditor: getEditor,
|
|
17880
17719
|
getTransform: getTransform,
|
|
@@ -17883,265 +17722,14 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
17883
17722
|
getPublicWidgets: getPublicWidgets,
|
|
17884
17723
|
isAccessible: isAccessible,
|
|
17885
17724
|
getAllWidgetTypes: getAllWidgetTypes,
|
|
17886
|
-
upgradeWidgetInfoToLatestVersion: upgradeWidgetInfoToLatestVersion,
|
|
17887
17725
|
getRendererPropsForWidgetInfo: getRendererPropsForWidgetInfo,
|
|
17888
17726
|
traverseChildWidgets: traverseChildWidgets$3,
|
|
17889
|
-
getSupportedAlignments: getSupportedAlignments,
|
|
17890
|
-
getDefaultAlignment: getDefaultAlignment,
|
|
17891
|
-
validateAlignments: validateAlignments,
|
|
17892
17727
|
supportsStaticMode: supportsStaticMode,
|
|
17893
17728
|
getStaticTransform: getStaticTransform,
|
|
17894
17729
|
getTracking: getTracking,
|
|
17895
17730
|
isLintable: isLintable
|
|
17896
17731
|
});
|
|
17897
17732
|
|
|
17898
|
-
function getUpgradedWidgetOptions(oldWidgetOptions) {
|
|
17899
|
-
return mapObject(oldWidgetOptions, (widgetInfo, widgetId) => {
|
|
17900
|
-
if (!widgetInfo.type || !widgetInfo.alignment) {
|
|
17901
|
-
const newValues = {};
|
|
17902
|
-
if (!widgetInfo.type) {
|
|
17903
|
-
// TODO: why does widget have no type?
|
|
17904
|
-
// We don't want to derive type from widget ID
|
|
17905
|
-
// see: LEMS-1845
|
|
17906
|
-
newValues.type = widgetId.split(" ")[0];
|
|
17907
|
-
}
|
|
17908
|
-
if (!widgetInfo.alignment) {
|
|
17909
|
-
newValues.alignment = "default";
|
|
17910
|
-
}
|
|
17911
|
-
widgetInfo = _extends({}, widgetInfo, newValues);
|
|
17912
|
-
}
|
|
17913
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
17914
|
-
return upgradeWidgetInfoToLatestVersion(widgetInfo);
|
|
17915
|
-
});
|
|
17916
|
-
}
|
|
17917
|
-
|
|
17918
|
-
/**
|
|
17919
|
-
* Checks the given user input to see if any answerable widgets have not been
|
|
17920
|
-
* "filled in" (ie. if they're empty). Another way to think about this
|
|
17921
|
-
* function is that its a check to see if we can score the provided input.
|
|
17922
|
-
*/
|
|
17923
|
-
function emptyWidgetsFunctional(widgets,
|
|
17924
|
-
// This is a port of old code, I'm not sure why
|
|
17925
|
-
// we need widgetIds vs the keys of the widgets object
|
|
17926
|
-
widgetIds, userInputMap, strings, locale) {
|
|
17927
|
-
return widgetIds.filter(id => {
|
|
17928
|
-
const widget = widgets[id];
|
|
17929
|
-
if (!widget || widget.static === true) {
|
|
17930
|
-
// Static widgets shouldn't count as empty
|
|
17931
|
-
return false;
|
|
17932
|
-
}
|
|
17933
|
-
const validator = getWidgetValidator(widget.type);
|
|
17934
|
-
const userInput = userInputMap[id];
|
|
17935
|
-
const validationData = widget.options;
|
|
17936
|
-
const score = validator == null ? void 0 : validator(userInput, validationData, strings, locale);
|
|
17937
|
-
if (score) {
|
|
17938
|
-
return scoreIsEmpty(score);
|
|
17939
|
-
}
|
|
17940
|
-
});
|
|
17941
|
-
}
|
|
17942
|
-
|
|
17943
|
-
// TODO: combine scorePerseusItem with scoreWidgetsFunctional
|
|
17944
|
-
// once scorePerseusItem is the only one calling scoreWidgetsFunctional
|
|
17945
|
-
function scorePerseusItem(perseusRenderData, userInputMap,
|
|
17946
|
-
// TODO(LEMS-2461,LEMS-2391): these probably
|
|
17947
|
-
// need to be removed before we move this to the server
|
|
17948
|
-
strings, locale) {
|
|
17949
|
-
// There seems to be a chance that PerseusRenderer.widgets might include
|
|
17950
|
-
// widget data for widgets that are not in PerseusRenderer.content,
|
|
17951
|
-
// so this checks that the widgets are being used before scoring them
|
|
17952
|
-
const usedWidgetIds = getWidgetIdsFromContent(perseusRenderData.content);
|
|
17953
|
-
const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
|
|
17954
|
-
return flattenScores(scores);
|
|
17955
|
-
}
|
|
17956
|
-
function scoreWidgetsFunctional(widgets,
|
|
17957
|
-
// This is a port of old code, I'm not sure why
|
|
17958
|
-
// we need widgetIds vs the keys of the widgets object
|
|
17959
|
-
widgetIds, userInputMap, strings, locale) {
|
|
17960
|
-
const upgradedWidgets = getUpgradedWidgetOptions(widgets);
|
|
17961
|
-
const gradedWidgetIds = widgetIds.filter(id => {
|
|
17962
|
-
const props = upgradedWidgets[id];
|
|
17963
|
-
const widgetIsGraded = (props == null ? void 0 : props.graded) == null || props.graded;
|
|
17964
|
-
const widgetIsStatic = !!(props != null && props.static);
|
|
17965
|
-
// Ungraded widgets or widgets set to static shouldn't be graded.
|
|
17966
|
-
return widgetIsGraded && !widgetIsStatic;
|
|
17967
|
-
});
|
|
17968
|
-
const widgetScores = {};
|
|
17969
|
-
gradedWidgetIds.forEach(id => {
|
|
17970
|
-
var _validator;
|
|
17971
|
-
const widget = upgradedWidgets[id];
|
|
17972
|
-
if (!widget) {
|
|
17973
|
-
return;
|
|
17974
|
-
}
|
|
17975
|
-
const userInput = userInputMap[id];
|
|
17976
|
-
const validator = getWidgetValidator(widget.type);
|
|
17977
|
-
const scorer = getWidgetScorer(widget.type);
|
|
17978
|
-
|
|
17979
|
-
// We do validation (empty checks) first and then scoring. If
|
|
17980
|
-
// validation fails, it's result is itself a PerseusScore.
|
|
17981
|
-
const score = (_validator = validator == null ? void 0 : validator(userInput, widget.options, strings, locale)) != null ? _validator : scorer == null ? void 0 : scorer(userInput, widget.options, strings, locale);
|
|
17982
|
-
if (score != null) {
|
|
17983
|
-
widgetScores[id] = score;
|
|
17984
|
-
}
|
|
17985
|
-
});
|
|
17986
|
-
return widgetScores;
|
|
17987
|
-
}
|
|
17988
|
-
|
|
17989
|
-
/**
|
|
17990
|
-
* TranslationLinter asynchronously calls Khan Academy's poentry linter.
|
|
17991
|
-
*
|
|
17992
|
-
* The poentry linter checks for issues that prevent content from being
|
|
17993
|
-
* translated. This linter is KA-specific and so TranslationLinter does not do
|
|
17994
|
-
* anything in non-KA environments.
|
|
17995
|
-
*/
|
|
17996
|
-
|
|
17997
|
-
// A LintCB is called once the linter has been run.
|
|
17998
|
-
|
|
17999
|
-
// msec to wait before we actually call the linter after receiving the
|
|
18000
|
-
// last call. The timer is reset if the lint is called during the wait.
|
|
18001
|
-
const DEBOUNCE_TIMEOUT = 1000;
|
|
18002
|
-
class TranslationLinter {
|
|
18003
|
-
constructor() {
|
|
18004
|
-
// To avoid linting when nothing has changed.
|
|
18005
|
-
this.previousContent = null;
|
|
18006
|
-
/**
|
|
18007
|
-
* Runs the poentry linter on the given perseus string asynchronously.
|
|
18008
|
-
*
|
|
18009
|
-
* It doesn't do anything unless we're in a KA environment.
|
|
18010
|
-
*
|
|
18011
|
-
* onLintErrorsGenerated is called even if there are no errors. It is not
|
|
18012
|
-
* called if perseusStr is unchanged from the last call.
|
|
18013
|
-
*
|
|
18014
|
-
* Linting is relatively expensive -- so we debounce linting each instance
|
|
18015
|
-
* for some amount of time.
|
|
18016
|
-
*/
|
|
18017
|
-
this.runLinter = debounce$1((perseusStr, onLintErrorsGenerated) => {
|
|
18018
|
-
// TODO(FEI-5003): Do we still need to check for 'KA'?
|
|
18019
|
-
if (typeof KA === "undefined") {
|
|
18020
|
-
return;
|
|
18021
|
-
}
|
|
18022
|
-
if (perseusStr === this.previousContent) {
|
|
18023
|
-
return;
|
|
18024
|
-
}
|
|
18025
|
-
this.previousContent = perseusStr;
|
|
18026
|
-
if (perseusStr === "") {
|
|
18027
|
-
onLintErrorsGenerated([]);
|
|
18028
|
-
return;
|
|
18029
|
-
}
|
|
18030
|
-
fetch("/api/internal/translations/lint_poentry?preview=1&lang=en", {
|
|
18031
|
-
headers: {
|
|
18032
|
-
"Content-Type": "application/json"
|
|
18033
|
-
},
|
|
18034
|
-
body: JSON.stringify({
|
|
18035
|
-
// The poentry linter verifies that the translation from
|
|
18036
|
-
// the original text to the translated text is sane. We
|
|
18037
|
-
// want to ensure that the translation from English to
|
|
18038
|
-
// English is sane, so msgid === mstr.
|
|
18039
|
-
msgid: perseusStr,
|
|
18040
|
-
// msgid is the original text
|
|
18041
|
-
msgstr: perseusStr,
|
|
18042
|
-
// msgstr is the translated text
|
|
18043
|
-
format: "perseus_text",
|
|
18044
|
-
filename: ""
|
|
18045
|
-
}),
|
|
18046
|
-
method: "POST"
|
|
18047
|
-
}).then(response => {
|
|
18048
|
-
if (response.status >= 400) {
|
|
18049
|
-
return {
|
|
18050
|
-
status: "error",
|
|
18051
|
-
message: "Could not run i18n linter."
|
|
18052
|
-
};
|
|
18053
|
-
}
|
|
18054
|
-
return response.json();
|
|
18055
|
-
}, rejection => {
|
|
18056
|
-
return {
|
|
18057
|
-
status: "error",
|
|
18058
|
-
message: "Could not run i18n linter."
|
|
18059
|
-
};
|
|
18060
|
-
}).then(json => {
|
|
18061
|
-
if (json.status === "error") {
|
|
18062
|
-
onLintErrorsGenerated(["Some part of this text makes it untranslatable. " + "The specific message from the i18n linter was: " + json.message.replace(/\n/g, " ")]);
|
|
18063
|
-
} else {
|
|
18064
|
-
onLintErrorsGenerated([]);
|
|
18065
|
-
}
|
|
18066
|
-
});
|
|
18067
|
-
}, DEBOUNCE_TIMEOUT);
|
|
18068
|
-
}
|
|
18069
|
-
/**
|
|
18070
|
-
* Applies an array of errors generated by linters without position
|
|
18071
|
-
* information (TranslationLinter and the legacy getSaveWarnings()) to the
|
|
18072
|
-
* top of a Perseus tree.
|
|
18073
|
-
*/
|
|
18074
|
-
applyLintErrors(parsedMarkdown, translationLintErrors) {
|
|
18075
|
-
// These lint errors do not have position data associated with
|
|
18076
|
-
// them, so we just plop them at the top.
|
|
18077
|
-
if (translationLintErrors.length) {
|
|
18078
|
-
const errorText = translationLintErrors.join("\n\n");
|
|
18079
|
-
parsedMarkdown.unshift({
|
|
18080
|
-
content: {
|
|
18081
|
-
type: "text",
|
|
18082
|
-
content: ""
|
|
18083
|
-
},
|
|
18084
|
-
insideTable: false,
|
|
18085
|
-
message: errorText,
|
|
18086
|
-
ruleName: "legacy-error",
|
|
18087
|
-
severity: Rule.Severity.ERROR,
|
|
18088
|
-
type: "lint"
|
|
18089
|
-
});
|
|
18090
|
-
}
|
|
18091
|
-
}
|
|
18092
|
-
}
|
|
18093
|
-
|
|
18094
|
-
/**
|
|
18095
|
-
* Preprocess TeX code to convert things that MathJax doesn't know how to handle
|
|
18096
|
-
* to things is does.
|
|
18097
|
-
*/
|
|
18098
|
-
var preprocessTex = (texCode => texCode
|
|
18099
|
-
// Replace uses of \begin{align}...\end{align} which KaTeX doesn't
|
|
18100
|
-
// support (yet) with \begin{aligned}...\end{aligned} which renders
|
|
18101
|
-
// the same is supported by KaTeX. It does the same for align*.
|
|
18102
|
-
// TODO(kevinb) update content to use aligned instead of align.
|
|
18103
|
-
// TODO(LEMS-1608) Remove this replacement as MathJax supports the
|
|
18104
|
-
// "align" macro correctly (and, in fact, it is not synonymous with
|
|
18105
|
-
// "aligned").
|
|
18106
|
-
.replace(/\{align[*]?\}/g, "{aligned}")
|
|
18107
|
-
// Replace non-breaking spaces with regular spaces.
|
|
18108
|
-
.replace(/[\u00a0]/g, " "));
|
|
18109
|
-
|
|
18110
|
-
const {
|
|
18111
|
-
interactiveSizes
|
|
18112
|
-
} = constants;
|
|
18113
|
-
|
|
18114
|
-
// Note: these size cutoffs represent content-width cutoffs as specified in
|
|
18115
|
-
// http://zpl.io/1mVmvU (broken link - we no longer use Zeplin)
|
|
18116
|
-
// TODO(benkomalo): these values aren't used in JS outside of this file, but
|
|
18117
|
-
// are coupled to the values in styles/styles.less - DRY it up at some point
|
|
18118
|
-
const smMax = articleMaxWidthTableInPx;
|
|
18119
|
-
const mdMax = articleMaxWidthInPx;
|
|
18120
|
-
const containerSizeClass = {
|
|
18121
|
-
SMALL: "small",
|
|
18122
|
-
MEDIUM: "medium",
|
|
18123
|
-
LARGE: "large",
|
|
18124
|
-
XLARGE: "xlarge"
|
|
18125
|
-
};
|
|
18126
|
-
const getClassFromWidth = width => {
|
|
18127
|
-
if (!width) {
|
|
18128
|
-
return containerSizeClass.MEDIUM;
|
|
18129
|
-
}
|
|
18130
|
-
if (width <= smMax) {
|
|
18131
|
-
return containerSizeClass.SMALL;
|
|
18132
|
-
}
|
|
18133
|
-
if (width <= mdMax) {
|
|
18134
|
-
return containerSizeClass.MEDIUM;
|
|
18135
|
-
}
|
|
18136
|
-
return containerSizeClass.LARGE;
|
|
18137
|
-
};
|
|
18138
|
-
const getInteractiveBoxFromSizeClass = sizeClass => {
|
|
18139
|
-
if (sizeClass === containerSizeClass.SMALL) {
|
|
18140
|
-
return [interactiveSizes.defaultBoxSizeSmall, interactiveSizes.defaultBoxSizeSmall];
|
|
18141
|
-
}
|
|
18142
|
-
return [interactiveSizes.defaultBoxSize, interactiveSizes.defaultBoxSize];
|
|
18143
|
-
};
|
|
18144
|
-
|
|
18145
17733
|
class WidgetContainer extends React.Component {
|
|
18146
17734
|
constructor(...args) {
|
|
18147
17735
|
super(...args);
|
|
@@ -18214,7 +17802,7 @@ class WidgetContainer extends React.Component {
|
|
|
18214
17802
|
}
|
|
18215
17803
|
let alignment = this.state.widgetProps.alignment;
|
|
18216
17804
|
if (alignment === "default") {
|
|
18217
|
-
alignment = getDefaultAlignment(type);
|
|
17805
|
+
alignment = CoreWidgetRegistry.getDefaultAlignment(type);
|
|
18218
17806
|
}
|
|
18219
17807
|
className += " widget-" + alignment;
|
|
18220
17808
|
const apiOptions = this.state.widgetProps.apiOptions;
|
|
@@ -18222,6 +17810,7 @@ class WidgetContainer extends React.Component {
|
|
|
18222
17810
|
// Hack to prevent interaction with static widgets: we overlay a big
|
|
18223
17811
|
// div on top of the widget and overflow: hidden the container.
|
|
18224
17812
|
// Ideally widgets themselves should know how to prevent interaction.
|
|
17813
|
+
// UPDATE HTML5: `inert` on the underlying div would be better
|
|
18225
17814
|
const isStatic = this.state.widgetProps.static || apiOptions.readOnly;
|
|
18226
17815
|
const staticContainerStyles = {
|
|
18227
17816
|
position: "relative",
|
|
@@ -18233,7 +17822,7 @@ class WidgetContainer extends React.Component {
|
|
|
18233
17822
|
position: "absolute",
|
|
18234
17823
|
top: 0,
|
|
18235
17824
|
left: 0,
|
|
18236
|
-
zIndex:
|
|
17825
|
+
zIndex: 3
|
|
18237
17826
|
};
|
|
18238
17827
|
|
|
18239
17828
|
// Some widgets may include strings of markdown that we may
|
|
@@ -19417,7 +19006,7 @@ class Renderer extends React.Component {
|
|
|
19417
19006
|
* empty if the graph is in the starting state.
|
|
19418
19007
|
*/
|
|
19419
19008
|
emptyWidgets() {
|
|
19420
|
-
return emptyWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.
|
|
19009
|
+
return emptyWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.context.locale);
|
|
19421
19010
|
}
|
|
19422
19011
|
/**
|
|
19423
19012
|
* Returns an array of the widget `.getUserInput()` results
|
|
@@ -19477,7 +19066,7 @@ class Renderer extends React.Component {
|
|
|
19477
19066
|
* @deprecated use scorePerseusItem
|
|
19478
19067
|
*/
|
|
19479
19068
|
score() {
|
|
19480
|
-
const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.
|
|
19069
|
+
const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.context.locale);
|
|
19481
19070
|
const combinedScore = flattenScores(scores);
|
|
19482
19071
|
return combinedScore;
|
|
19483
19072
|
}
|
|
@@ -20017,7 +19606,7 @@ class InputWithExamples extends React.Component {
|
|
|
20017
19606
|
};
|
|
20018
19607
|
this._getInputClassName = () => {
|
|
20019
19608
|
// Otherwise, we need to add these INPUT and FOCUSED tags here.
|
|
20020
|
-
let className = ClassNames.INPUT
|
|
19609
|
+
let className = ClassNames.INPUT;
|
|
20021
19610
|
if (this.state.focused) {
|
|
20022
19611
|
className += " " + ClassNames.FOCUSED;
|
|
20023
19612
|
}
|
|
@@ -20429,14 +20018,10 @@ const propTransform$3 = widgetOptions => {
|
|
|
20429
20018
|
var InputNumber$1 = {
|
|
20430
20019
|
name: "input-number",
|
|
20431
20020
|
displayName: "Input number (deprecated - use numeric input instead)",
|
|
20432
|
-
defaultAlignment: "inline-block",
|
|
20433
20021
|
hidden: true,
|
|
20434
20022
|
widget: InputNumber,
|
|
20435
20023
|
transform: propTransform$3,
|
|
20436
20024
|
isLintable: true,
|
|
20437
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
20438
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInputNumberUserInput'.
|
|
20439
|
-
scorer: scoreInputNumber,
|
|
20440
20025
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
20441
20026
|
if (rubric.value == null) {
|
|
20442
20027
|
return;
|
|
@@ -20693,14 +20278,11 @@ const propsTransform = function propsTransform(widgetOptions) {
|
|
|
20693
20278
|
var NumericInput$1 = {
|
|
20694
20279
|
name: "numeric-input",
|
|
20695
20280
|
displayName: "Numeric input",
|
|
20696
|
-
defaultAlignment: "inline-block",
|
|
20697
20281
|
accessible: true,
|
|
20698
20282
|
widget: NumericInput,
|
|
20699
20283
|
transform: propsTransform,
|
|
20700
20284
|
isLintable: true,
|
|
20701
|
-
|
|
20702
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumericInputUserInput'.
|
|
20703
|
-
scorer: scoreNumericInput,
|
|
20285
|
+
getPublicWidgetOptions: getNumericInputPublicWidgetOptions,
|
|
20704
20286
|
// TODO(LEMS-2656): remove TS suppression
|
|
20705
20287
|
// @ts-expect-error: Type 'Rubric' is not assignable to type 'PerseusNumericInputRubric'
|
|
20706
20288
|
getOneCorrectAnswerFromRubric(rubric) {
|
|
@@ -20761,23 +20343,6 @@ const getPromptJSON$p = renderProps => {
|
|
|
20761
20343
|
};
|
|
20762
20344
|
};
|
|
20763
20345
|
|
|
20764
|
-
/**
|
|
20765
|
-
* Several widgets don't have "right"/"wrong" scoring logic,
|
|
20766
|
-
* so this just says to move on past those widgets
|
|
20767
|
-
*
|
|
20768
|
-
* TODO(LEMS-2543) widgets that use this probably shouldn't have any
|
|
20769
|
-
* scoring logic and the thing scoring an exercise
|
|
20770
|
-
* should just know to skip these
|
|
20771
|
-
*/
|
|
20772
|
-
function scoreNoop(points = 0) {
|
|
20773
|
-
return {
|
|
20774
|
-
type: "points",
|
|
20775
|
-
earned: points,
|
|
20776
|
-
total: points,
|
|
20777
|
-
message: null
|
|
20778
|
-
};
|
|
20779
|
-
}
|
|
20780
|
-
|
|
20781
20346
|
/**
|
|
20782
20347
|
* Utility functions for manipulating ranges of highlightable content.
|
|
20783
20348
|
*/
|
|
@@ -22953,8 +22518,7 @@ var Passage$1 = {
|
|
|
22953
22518
|
transform: editorProps => {
|
|
22954
22519
|
return _.pick(editorProps, "passageTitle", "passageText", "footnotes", "showLineNumbers");
|
|
22955
22520
|
},
|
|
22956
|
-
isLintable: true
|
|
22957
|
-
scorer: () => scoreNoop()
|
|
22521
|
+
isLintable: true
|
|
22958
22522
|
};
|
|
22959
22523
|
|
|
22960
22524
|
/**
|
|
@@ -23076,16 +22640,13 @@ var PassageRef$1 = {
|
|
|
23076
22640
|
name: "passage-ref",
|
|
23077
22641
|
displayName: "PassageRef (SAT only)",
|
|
23078
22642
|
hidden: true,
|
|
23079
|
-
defaultAlignment: "inline",
|
|
23080
22643
|
widget: PassageRef,
|
|
23081
22644
|
transform: widgetOptions => ({
|
|
23082
22645
|
passageNumber: widgetOptions.passageNumber,
|
|
23083
22646
|
referenceNumber: widgetOptions.referenceNumber,
|
|
23084
22647
|
summaryText: widgetOptions.summaryText
|
|
23085
22648
|
}),
|
|
23086
|
-
version: passageRefLogic.version
|
|
23087
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
23088
|
-
scorer: () => scoreNoop()
|
|
22649
|
+
version: passageRefLogic.version
|
|
23089
22650
|
};
|
|
23090
22651
|
|
|
23091
22652
|
/**
|
|
@@ -23127,109 +22688,6 @@ var mediaQueries = {
|
|
|
23127
22688
|
xlOrLarger: `@media screen and (min-width: ${pureXlMin})`
|
|
23128
22689
|
};
|
|
23129
22690
|
|
|
23130
|
-
const {
|
|
23131
|
-
zIndexAboveScratchpad,
|
|
23132
|
-
zIndexInteractiveComponent,
|
|
23133
|
-
radioBorderColor,
|
|
23134
|
-
checkedColor,
|
|
23135
|
-
circleSize,
|
|
23136
|
-
radioMarginWidth
|
|
23137
|
-
} = constants;
|
|
23138
|
-
var sharedStyles = StyleSheet.create({
|
|
23139
|
-
perseusInteractive: {
|
|
23140
|
-
zIndex: zIndexInteractiveComponent,
|
|
23141
|
-
position: "relative"
|
|
23142
|
-
},
|
|
23143
|
-
aboveScratchpad: {
|
|
23144
|
-
position: "relative",
|
|
23145
|
-
zIndex: zIndexAboveScratchpad
|
|
23146
|
-
},
|
|
23147
|
-
blankBackground: {
|
|
23148
|
-
// TODO(emily): Use WB colors?
|
|
23149
|
-
backgroundColor: "#FDFDFD"
|
|
23150
|
-
},
|
|
23151
|
-
perseusSrOnly: {
|
|
23152
|
-
border: 0,
|
|
23153
|
-
clip: "rect(0,0,0,0)",
|
|
23154
|
-
height: 1,
|
|
23155
|
-
margin: -1,
|
|
23156
|
-
overflow: "hidden",
|
|
23157
|
-
padding: 0,
|
|
23158
|
-
position: "absolute",
|
|
23159
|
-
width: 1
|
|
23160
|
-
},
|
|
23161
|
-
responsiveLabel: {
|
|
23162
|
-
// NOTE(charlie): The values used here should be kept in sync with the
|
|
23163
|
-
// caption text sizing in Perseus' styles.less.
|
|
23164
|
-
// TODO(charlie): Migrate the captions over to using this style.
|
|
23165
|
-
[mediaQueries.smOrSmaller]: {
|
|
23166
|
-
fontSize: 14,
|
|
23167
|
-
lineHeight: 1.3
|
|
23168
|
-
},
|
|
23169
|
-
[mediaQueries.md]: {
|
|
23170
|
-
fontSize: 17,
|
|
23171
|
-
lineHeight: 1.4
|
|
23172
|
-
},
|
|
23173
|
-
[mediaQueries.lgOrLarger]: {
|
|
23174
|
-
fontSize: 20,
|
|
23175
|
-
lineHeight: 1.4
|
|
23176
|
-
}
|
|
23177
|
-
},
|
|
23178
|
-
responsiveInput: {
|
|
23179
|
-
display: "inline-block",
|
|
23180
|
-
WebkitAppearance: "none",
|
|
23181
|
-
appearance: "none",
|
|
23182
|
-
"::-ms-check": {
|
|
23183
|
-
display: "none"
|
|
23184
|
-
},
|
|
23185
|
-
backgroundColor: "#fff",
|
|
23186
|
-
border: "2px solid #fff",
|
|
23187
|
-
boxShadow: `0 0px 0px 1px ${radioBorderColor}`,
|
|
23188
|
-
outline: "none",
|
|
23189
|
-
boxSizing: "border-box",
|
|
23190
|
-
flexShrink: 0,
|
|
23191
|
-
marginBottom: 1,
|
|
23192
|
-
marginLeft: 1,
|
|
23193
|
-
marginRight: 1,
|
|
23194
|
-
marginTop: 1,
|
|
23195
|
-
height: circleSize - 2,
|
|
23196
|
-
width: circleSize - 2
|
|
23197
|
-
},
|
|
23198
|
-
responsiveRadioInput: {
|
|
23199
|
-
borderRadius: "50%",
|
|
23200
|
-
":checked": {
|
|
23201
|
-
backgroundColor: checkedColor,
|
|
23202
|
-
border: "none",
|
|
23203
|
-
borderRadius: "50%",
|
|
23204
|
-
boxShadow: `inset 0px 0px 0px 2px white, ` + `0 0px 0px 2px ${checkedColor}`,
|
|
23205
|
-
marginTop: radioMarginWidth,
|
|
23206
|
-
marginBottom: radioMarginWidth,
|
|
23207
|
-
marginLeft: radioMarginWidth,
|
|
23208
|
-
marginRight: radioMarginWidth,
|
|
23209
|
-
height: circleSize - 2 * radioMarginWidth,
|
|
23210
|
-
width: circleSize - 2 * radioMarginWidth
|
|
23211
|
-
}
|
|
23212
|
-
},
|
|
23213
|
-
responsiveRadioInputActive: {
|
|
23214
|
-
backgroundColor: "#fff",
|
|
23215
|
-
border: "2px solid #fff",
|
|
23216
|
-
borderRadius: "50%",
|
|
23217
|
-
boxShadow: `0 0px 0px 2px ${checkedColor}`,
|
|
23218
|
-
marginTop: radioMarginWidth,
|
|
23219
|
-
marginBottom: radioMarginWidth,
|
|
23220
|
-
marginLeft: radioMarginWidth,
|
|
23221
|
-
marginRight: radioMarginWidth,
|
|
23222
|
-
height: circleSize - 2 * radioMarginWidth,
|
|
23223
|
-
width: circleSize - 2 * radioMarginWidth,
|
|
23224
|
-
":checked": {
|
|
23225
|
-
backgroundColor: "#fff"
|
|
23226
|
-
}
|
|
23227
|
-
},
|
|
23228
|
-
disableTextSelection: {
|
|
23229
|
-
userSelect: "none"
|
|
23230
|
-
}
|
|
23231
|
-
});
|
|
23232
|
-
|
|
23233
22691
|
/**
|
|
23234
22692
|
* A small collection of useful scrolling utility functions.
|
|
23235
22693
|
*/
|
|
@@ -24288,7 +23746,7 @@ const BaseRadio = function BaseRadio({
|
|
|
24288
23746
|
const aphroditeClassName = checked => {
|
|
24289
23747
|
// Whether or not to show correctness borders
|
|
24290
23748
|
// for this choice and the next choice.
|
|
24291
|
-
return css(
|
|
23749
|
+
return css(styles$o.item, styles$o.responsiveItem, checked && styles$o.selectedItem, checked && choice.highlighted && styles$o.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$o.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$o.nextHighlighted);
|
|
24292
23750
|
};
|
|
24293
23751
|
|
|
24294
23752
|
// HACK(abdulrahman): Preloads the selection-state
|
|
@@ -24877,18 +24335,105 @@ var Radio = {
|
|
|
24877
24335
|
staticTransform: transform,
|
|
24878
24336
|
version: radioLogic.version,
|
|
24879
24337
|
propUpgrades: radioLogic.widgetOptionsUpgrades,
|
|
24880
|
-
isLintable: true
|
|
24881
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
24882
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
|
|
24883
|
-
scorer: scoreRadio,
|
|
24884
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
24885
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusRadioUserInput
|
|
24886
|
-
validator: validateRadio
|
|
24338
|
+
isLintable: true
|
|
24887
24339
|
};
|
|
24888
24340
|
|
|
24889
24341
|
/* eslint-disable import/no-named-as-default */
|
|
24890
24342
|
var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
|
|
24891
24343
|
|
|
24344
|
+
const {
|
|
24345
|
+
radioBorderColor,
|
|
24346
|
+
checkedColor,
|
|
24347
|
+
circleSize,
|
|
24348
|
+
radioMarginWidth
|
|
24349
|
+
} = constants;
|
|
24350
|
+
var sharedStyles = StyleSheet.create({
|
|
24351
|
+
blankBackground: {
|
|
24352
|
+
// TODO(emily): Use WB colors?
|
|
24353
|
+
backgroundColor: "#FDFDFD"
|
|
24354
|
+
},
|
|
24355
|
+
perseusSrOnly: {
|
|
24356
|
+
border: 0,
|
|
24357
|
+
clip: "rect(0,0,0,0)",
|
|
24358
|
+
height: 1,
|
|
24359
|
+
margin: -1,
|
|
24360
|
+
overflow: "hidden",
|
|
24361
|
+
padding: 0,
|
|
24362
|
+
position: "absolute",
|
|
24363
|
+
width: 1
|
|
24364
|
+
},
|
|
24365
|
+
responsiveLabel: {
|
|
24366
|
+
// NOTE(charlie): The values used here should be kept in sync with the
|
|
24367
|
+
// caption text sizing in Perseus' styles.less.
|
|
24368
|
+
// TODO(charlie): Migrate the captions over to using this style.
|
|
24369
|
+
[mediaQueries.smOrSmaller]: {
|
|
24370
|
+
fontSize: 14,
|
|
24371
|
+
lineHeight: 1.3
|
|
24372
|
+
},
|
|
24373
|
+
[mediaQueries.md]: {
|
|
24374
|
+
fontSize: 17,
|
|
24375
|
+
lineHeight: 1.4
|
|
24376
|
+
},
|
|
24377
|
+
[mediaQueries.lgOrLarger]: {
|
|
24378
|
+
fontSize: 20,
|
|
24379
|
+
lineHeight: 1.4
|
|
24380
|
+
}
|
|
24381
|
+
},
|
|
24382
|
+
responsiveInput: {
|
|
24383
|
+
display: "inline-block",
|
|
24384
|
+
WebkitAppearance: "none",
|
|
24385
|
+
appearance: "none",
|
|
24386
|
+
"::-ms-check": {
|
|
24387
|
+
display: "none"
|
|
24388
|
+
},
|
|
24389
|
+
backgroundColor: "#fff",
|
|
24390
|
+
border: "2px solid #fff",
|
|
24391
|
+
boxShadow: `0 0px 0px 1px ${radioBorderColor}`,
|
|
24392
|
+
outline: "none",
|
|
24393
|
+
boxSizing: "border-box",
|
|
24394
|
+
flexShrink: 0,
|
|
24395
|
+
marginBottom: 1,
|
|
24396
|
+
marginLeft: 1,
|
|
24397
|
+
marginRight: 1,
|
|
24398
|
+
marginTop: 1,
|
|
24399
|
+
height: circleSize - 2,
|
|
24400
|
+
width: circleSize - 2
|
|
24401
|
+
},
|
|
24402
|
+
responsiveRadioInput: {
|
|
24403
|
+
borderRadius: "50%",
|
|
24404
|
+
":checked": {
|
|
24405
|
+
backgroundColor: checkedColor,
|
|
24406
|
+
border: "none",
|
|
24407
|
+
borderRadius: "50%",
|
|
24408
|
+
boxShadow: `inset 0px 0px 0px 2px white, ` + `0 0px 0px 2px ${checkedColor}`,
|
|
24409
|
+
marginTop: radioMarginWidth,
|
|
24410
|
+
marginBottom: radioMarginWidth,
|
|
24411
|
+
marginLeft: radioMarginWidth,
|
|
24412
|
+
marginRight: radioMarginWidth,
|
|
24413
|
+
height: circleSize - 2 * radioMarginWidth,
|
|
24414
|
+
width: circleSize - 2 * radioMarginWidth
|
|
24415
|
+
}
|
|
24416
|
+
},
|
|
24417
|
+
responsiveRadioInputActive: {
|
|
24418
|
+
backgroundColor: "#fff",
|
|
24419
|
+
border: "2px solid #fff",
|
|
24420
|
+
borderRadius: "50%",
|
|
24421
|
+
boxShadow: `0 0px 0px 2px ${checkedColor}`,
|
|
24422
|
+
marginTop: radioMarginWidth,
|
|
24423
|
+
marginBottom: radioMarginWidth,
|
|
24424
|
+
marginLeft: radioMarginWidth,
|
|
24425
|
+
marginRight: radioMarginWidth,
|
|
24426
|
+
height: circleSize - 2 * radioMarginWidth,
|
|
24427
|
+
width: circleSize - 2 * radioMarginWidth,
|
|
24428
|
+
":checked": {
|
|
24429
|
+
backgroundColor: "#fff"
|
|
24430
|
+
}
|
|
24431
|
+
},
|
|
24432
|
+
disableTextSelection: {
|
|
24433
|
+
userSelect: "none"
|
|
24434
|
+
}
|
|
24435
|
+
});
|
|
24436
|
+
|
|
24892
24437
|
const getPromptJSON$n = (renderProps, userInput) => {
|
|
24893
24438
|
return {
|
|
24894
24439
|
type: "categorizer",
|
|
@@ -24979,7 +24524,6 @@ class Categorizer extends React.Component {
|
|
|
24979
24524
|
className: "category " + css(styles$n.cell),
|
|
24980
24525
|
key: catNum
|
|
24981
24526
|
}, /*#__PURE__*/React.createElement("div", {
|
|
24982
|
-
className: ClassNames.INTERACTIVE,
|
|
24983
24527
|
role: "button",
|
|
24984
24528
|
"aria-label": catName,
|
|
24985
24529
|
onClick: () => this.onChange(itemNum, catNum)
|
|
@@ -25078,12 +24622,6 @@ var Categorizer$1 = {
|
|
|
25078
24622
|
return _.pick(editorProps, "items", "categories", "values", "randomizeItems");
|
|
25079
24623
|
},
|
|
25080
24624
|
isLintable: true,
|
|
25081
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
25082
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
|
|
25083
|
-
scorer: scoreCategorizer,
|
|
25084
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
25085
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
|
|
25086
|
-
validator: validateCategorizer,
|
|
25087
24625
|
getPublicWidgetOptions: getCategorizerPublicWidgetOptions
|
|
25088
24626
|
};
|
|
25089
24627
|
|
|
@@ -25289,12 +24827,9 @@ const styles$m = StyleSheet.create({
|
|
|
25289
24827
|
var CSProgram$1 = {
|
|
25290
24828
|
name: "cs-program",
|
|
25291
24829
|
displayName: "CS Program",
|
|
25292
|
-
supportedAlignments: ["block", "full-width"],
|
|
25293
24830
|
widget: CSProgram,
|
|
25294
24831
|
hidden: true,
|
|
25295
|
-
|
|
25296
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'.
|
|
25297
|
-
scorer: scoreCSProgram
|
|
24832
|
+
getPublicWidgetOptions: getCSProgramPublicWidgetOptions
|
|
25298
24833
|
};
|
|
25299
24834
|
|
|
25300
24835
|
const getPromptJSON$l = renderProps => {
|
|
@@ -25367,11 +24902,8 @@ var Definition$1 = {
|
|
|
25367
24902
|
name: "definition",
|
|
25368
24903
|
displayName: "Definition",
|
|
25369
24904
|
accessible: true,
|
|
25370
|
-
defaultAlignment: "inline",
|
|
25371
24905
|
widget: Definition,
|
|
25372
|
-
transform: x => x
|
|
25373
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
25374
|
-
scorer: () => scoreNoop()
|
|
24906
|
+
transform: x => x
|
|
25375
24907
|
};
|
|
25376
24908
|
|
|
25377
24909
|
// There are no required props, but this component can receive props from any
|
|
@@ -25401,9 +24933,7 @@ var DeprecatedStandin$1 = {
|
|
|
25401
24933
|
name: "deprecated-standin",
|
|
25402
24934
|
displayName: "Deprecated Standin",
|
|
25403
24935
|
widget: DeprecatedStandin,
|
|
25404
|
-
hidden: true
|
|
25405
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
25406
|
-
scorer: () => scoreNoop(1)
|
|
24936
|
+
hidden: true
|
|
25407
24937
|
};
|
|
25408
24938
|
|
|
25409
24939
|
const getPromptJSON$k = (renderProps, userInput) => {
|
|
@@ -25515,16 +25045,10 @@ const optionsTransform = widgetOptions => {
|
|
|
25515
25045
|
var Dropdown$1 = {
|
|
25516
25046
|
name: "dropdown",
|
|
25517
25047
|
displayName: "Drop down",
|
|
25518
|
-
defaultAlignment: "inline-block",
|
|
25519
25048
|
accessible: true,
|
|
25520
25049
|
widget: Dropdown,
|
|
25521
25050
|
transform: optionsTransform,
|
|
25522
|
-
|
|
25523
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
|
|
25524
|
-
scorer: scoreDropdown,
|
|
25525
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
25526
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusDropdownUserInput'.
|
|
25527
|
-
validator: validateDropdown
|
|
25051
|
+
getPublicWidgetOptions: getDropdownPublicWidgetOptions
|
|
25528
25052
|
};
|
|
25529
25053
|
|
|
25530
25054
|
const getPromptJSON$j = renderProps => {
|
|
@@ -25679,12 +25203,9 @@ var Explanation$1 = {
|
|
|
25679
25203
|
name: "explanation",
|
|
25680
25204
|
displayName: "Explanation",
|
|
25681
25205
|
accessible: true,
|
|
25682
|
-
defaultAlignment: "inline",
|
|
25683
25206
|
widget: Explanation,
|
|
25684
25207
|
transform: _.identity,
|
|
25685
|
-
isLintable: true
|
|
25686
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
25687
|
-
scorer: () => scoreNoop()
|
|
25208
|
+
isLintable: true
|
|
25688
25209
|
};
|
|
25689
25210
|
|
|
25690
25211
|
const getPromptJSON$i = (title, rendererJSON, hintRendererJSON) => {
|
|
@@ -26784,7 +26305,7 @@ class FunctionGrapher extends React.Component {
|
|
|
26784
26305
|
boxSizing: "initial"
|
|
26785
26306
|
}
|
|
26786
26307
|
}, /*#__PURE__*/React.createElement("div", {
|
|
26787
|
-
className: "graphie-container
|
|
26308
|
+
className: "graphie-container blank-background",
|
|
26788
26309
|
style: {
|
|
26789
26310
|
width: box[0],
|
|
26790
26311
|
height: box[1]
|
|
@@ -26923,8 +26444,7 @@ class Grapher extends React.Component {
|
|
|
26923
26444
|
const coords = this.props.plot.coords;
|
|
26924
26445
|
const asymptote = this.props.plot.asymptote;
|
|
26925
26446
|
const typeSelector = /*#__PURE__*/React.createElement("div", {
|
|
26926
|
-
style: typeSelectorStyle
|
|
26927
|
-
className: "above-scratchpad"
|
|
26447
|
+
style: typeSelectorStyle
|
|
26928
26448
|
}, /*#__PURE__*/React.createElement(ButtonGroup, {
|
|
26929
26449
|
value: type,
|
|
26930
26450
|
allowEmpty: true,
|
|
@@ -27002,10 +26522,7 @@ var Grapher$1 = {
|
|
|
27002
26522
|
hidden: true,
|
|
27003
26523
|
widget: Grapher,
|
|
27004
26524
|
transform: propTransform$2,
|
|
27005
|
-
staticTransform: staticTransform$4
|
|
27006
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
27007
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGrapherUserInput'.
|
|
27008
|
-
scorer: scoreGrapher
|
|
26525
|
+
staticTransform: staticTransform$4
|
|
27009
26526
|
};
|
|
27010
26527
|
|
|
27011
26528
|
const getPromptJSON$f = rendererJSON => {
|
|
@@ -27021,24 +26538,6 @@ const getPromptJSON$f = rendererJSON => {
|
|
|
27021
26538
|
});
|
|
27022
26539
|
};
|
|
27023
26540
|
|
|
27024
|
-
// The `group` widget is basically a widget hosting a full Perseus system in
|
|
27025
|
-
// it. As such, scoring a group means scoring all widgets it contains.
|
|
27026
|
-
function scoreGroup(userInput, rubric, strings, locale) {
|
|
27027
|
-
const scores = scoreWidgetsFunctional(rubric.widgets, Object.keys(rubric.widgets), userInput, strings, locale);
|
|
27028
|
-
return flattenScores(scores);
|
|
27029
|
-
}
|
|
27030
|
-
|
|
27031
|
-
function validateGroup(userInput, validationData, strings, locale) {
|
|
27032
|
-
const emptyWidgets = emptyWidgetsFunctional(validationData.widgets, Object.keys(validationData.widgets), userInput, strings, locale);
|
|
27033
|
-
if (emptyWidgets.length === 0) {
|
|
27034
|
-
return null;
|
|
27035
|
-
}
|
|
27036
|
-
return {
|
|
27037
|
-
type: "invalid",
|
|
27038
|
-
message: null
|
|
27039
|
-
};
|
|
27040
|
-
}
|
|
27041
|
-
|
|
27042
26541
|
// exports has no 'transform'
|
|
27043
26542
|
|
|
27044
26543
|
class Group extends React.Component {
|
|
@@ -27193,12 +26692,6 @@ var Group$1 = {
|
|
|
27193
26692
|
displayName: "Group (SAT only)",
|
|
27194
26693
|
widget: Group,
|
|
27195
26694
|
traverseChildWidgets: traverseChildWidgets,
|
|
27196
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
27197
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
|
|
27198
|
-
scorer: scoreGroup,
|
|
27199
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
27200
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusGroupUserInput'.
|
|
27201
|
-
validator: validateGroup,
|
|
27202
26695
|
hidden: true,
|
|
27203
26696
|
isLintable: true
|
|
27204
26697
|
};
|
|
@@ -27334,10 +26827,7 @@ var Iframe$1 = {
|
|
|
27334
26827
|
displayName: "Iframe (deprecated)",
|
|
27335
26828
|
widget: Iframe,
|
|
27336
26829
|
// Let's not expose it to all content creators yet
|
|
27337
|
-
hidden: true
|
|
27338
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
27339
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusIframeUserInput'.
|
|
27340
|
-
scorer: scoreIframe
|
|
26830
|
+
hidden: true
|
|
27341
26831
|
};
|
|
27342
26832
|
|
|
27343
26833
|
const getPromptJSON$d = renderProps => {
|
|
@@ -27359,8 +26849,6 @@ const defaultBackgroundImage$2 = {
|
|
|
27359
26849
|
width: 0,
|
|
27360
26850
|
height: 0
|
|
27361
26851
|
};
|
|
27362
|
-
const editorAlignments = ["block", "full-width"];
|
|
27363
|
-
const DEFAULT_ALIGNMENT = "block";
|
|
27364
26852
|
|
|
27365
26853
|
// there is no transform as part of exports
|
|
27366
26854
|
|
|
@@ -27509,7 +26997,7 @@ class ImageWidget extends React.Component {
|
|
|
27509
26997
|
}
|
|
27510
26998
|
ImageWidget.contextType = PerseusI18nContext;
|
|
27511
26999
|
ImageWidget.defaultProps = {
|
|
27512
|
-
alignment:
|
|
27000
|
+
alignment: "block",
|
|
27513
27001
|
title: "",
|
|
27514
27002
|
range: [defaultRange, defaultRange],
|
|
27515
27003
|
box: [defaultBoxSize, defaultBoxSize],
|
|
@@ -27527,13 +27015,9 @@ var Image$1 = {
|
|
|
27527
27015
|
const bgImage = widgetOptions.backgroundImage;
|
|
27528
27016
|
return !(bgImage && bgImage.url && !widgetOptions.alt);
|
|
27529
27017
|
},
|
|
27530
|
-
defaultAlignment: DEFAULT_ALIGNMENT,
|
|
27531
|
-
supportedAlignments: editorAlignments,
|
|
27532
27018
|
displayName: "Image",
|
|
27533
27019
|
widget: ImageWidget,
|
|
27534
|
-
isLintable: true
|
|
27535
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
27536
|
-
scorer: () => scoreNoop()
|
|
27020
|
+
isLintable: true
|
|
27537
27021
|
};
|
|
27538
27022
|
|
|
27539
27023
|
const getPromptJSON$c = () => {
|
|
@@ -28131,9 +27615,7 @@ var Interactive = {
|
|
|
28131
27615
|
displayName: "Interaction",
|
|
28132
27616
|
widget: Interaction,
|
|
28133
27617
|
transform: _.identity,
|
|
28134
|
-
hidden: true
|
|
28135
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
28136
|
-
scorer: () => scoreNoop()
|
|
27618
|
+
hidden: true
|
|
28137
27619
|
};
|
|
28138
27620
|
|
|
28139
27621
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
@@ -28402,7 +27884,7 @@ class Graph extends React.Component {
|
|
|
28402
27884
|
image = null;
|
|
28403
27885
|
}
|
|
28404
27886
|
return /*#__PURE__*/React.createElement("div", {
|
|
28405
|
-
className: "graphie-container
|
|
27887
|
+
className: "graphie-container blank-background",
|
|
28406
27888
|
style: {
|
|
28407
27889
|
width: this.props.box[0],
|
|
28408
27890
|
height: this.props.box[1]
|
|
@@ -29037,7 +28519,8 @@ const tickLabelSize = 14;
|
|
|
29037
28519
|
const YGridTick = ({
|
|
29038
28520
|
y,
|
|
29039
28521
|
range,
|
|
29040
|
-
tickStep
|
|
28522
|
+
tickStep,
|
|
28523
|
+
showPi
|
|
29041
28524
|
}) => {
|
|
29042
28525
|
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
29043
28526
|
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
@@ -29073,6 +28556,7 @@ const YGridTick = ({
|
|
|
29073
28556
|
// If the graph displays both the y and x axis lines within the graph, we want
|
|
29074
28557
|
// to hide the label at -1 on the y-axis to prevent overlap with the x-axis label
|
|
29075
28558
|
const showLabel = shouldShowLabel(y, range, tickStep);
|
|
28559
|
+
const yLabel = showPi ? divideByAndShowPi(y) : y.toString();
|
|
29076
28560
|
return /*#__PURE__*/React.createElement("g", {
|
|
29077
28561
|
className: "tick",
|
|
29078
28562
|
"aria-hidden": true
|
|
@@ -29090,11 +28574,12 @@ const YGridTick = ({
|
|
|
29090
28574
|
textAnchor: "end",
|
|
29091
28575
|
x: xPositionText,
|
|
29092
28576
|
y: yPositionText
|
|
29093
|
-
},
|
|
28577
|
+
}, yLabel));
|
|
29094
28578
|
};
|
|
29095
28579
|
const XGridTick = ({
|
|
29096
28580
|
x,
|
|
29097
|
-
range
|
|
28581
|
+
range,
|
|
28582
|
+
showPi
|
|
29098
28583
|
}) => {
|
|
29099
28584
|
// If the graph requires out-of-bounds labels, we want to make sure to set the
|
|
29100
28585
|
// coordinates to the edge of the visible range of the graph. Otherwise,
|
|
@@ -29133,6 +28618,7 @@ const XGridTick = ({
|
|
|
29133
28618
|
// Apply the adjustments to the x and y positions for the text
|
|
29134
28619
|
const xPositionText = xPosition + xAdjustment;
|
|
29135
28620
|
const yPositionText = yPosition + yAdjustment;
|
|
28621
|
+
const xLabel = showPi ? divideByAndShowPi(x) : x.toString();
|
|
29136
28622
|
return /*#__PURE__*/React.createElement("g", {
|
|
29137
28623
|
className: "tick",
|
|
29138
28624
|
"aria-hidden": true
|
|
@@ -29150,7 +28636,7 @@ const XGridTick = ({
|
|
|
29150
28636
|
textAnchor: "middle",
|
|
29151
28637
|
x: xPositionText,
|
|
29152
28638
|
y: yPositionText
|
|
29153
|
-
},
|
|
28639
|
+
}, xLabel));
|
|
29154
28640
|
};
|
|
29155
28641
|
|
|
29156
28642
|
// Determines whether to show the label for the given tick
|
|
@@ -29197,6 +28683,22 @@ const countSignificantDecimals = number => {
|
|
|
29197
28683
|
}
|
|
29198
28684
|
return numStr.split(".")[1].length;
|
|
29199
28685
|
};
|
|
28686
|
+
|
|
28687
|
+
// Show the given value as a multiple of pi (already assumed to be
|
|
28688
|
+
// a multiple of pi). Exported for testing
|
|
28689
|
+
function divideByAndShowPi(value) {
|
|
28690
|
+
const dividedValue = value / Math.PI;
|
|
28691
|
+
switch (dividedValue) {
|
|
28692
|
+
case 1:
|
|
28693
|
+
return "π";
|
|
28694
|
+
case -1:
|
|
28695
|
+
return "-π";
|
|
28696
|
+
case 0:
|
|
28697
|
+
return "0";
|
|
28698
|
+
default:
|
|
28699
|
+
return dividedValue + "π";
|
|
28700
|
+
}
|
|
28701
|
+
}
|
|
29200
28702
|
const AxisTicks = () => {
|
|
29201
28703
|
const {
|
|
29202
28704
|
tickStep,
|
|
@@ -29219,6 +28721,10 @@ const AxisTicks = () => {
|
|
|
29219
28721
|
key: `y-grid-tick-${y}`,
|
|
29220
28722
|
range: range,
|
|
29221
28723
|
tickStep: tickStep[Y]
|
|
28724
|
+
// Show the tick labels as multiples of pi
|
|
28725
|
+
// if the tick step is a multiple of pi.
|
|
28726
|
+
,
|
|
28727
|
+
showPi: tickStep[Y] % Math.PI === 0
|
|
29222
28728
|
});
|
|
29223
28729
|
})), /*#__PURE__*/React.createElement("g", {
|
|
29224
28730
|
className: "x-axis-ticks"
|
|
@@ -29227,6 +28733,10 @@ const AxisTicks = () => {
|
|
|
29227
28733
|
x: x,
|
|
29228
28734
|
key: `x-grid-tick-${x}`,
|
|
29229
28735
|
range: range
|
|
28736
|
+
// Show the tick labels as multiples of pi
|
|
28737
|
+
// if the tick step is a multiple of pi.
|
|
28738
|
+
,
|
|
28739
|
+
showPi: tickStep[X] % Math.PI === 0
|
|
29230
28740
|
});
|
|
29231
28741
|
})));
|
|
29232
28742
|
};
|
|
@@ -29534,6 +29044,15 @@ const styles$d = StyleSheet.create({
|
|
|
29534
29044
|
}
|
|
29535
29045
|
});
|
|
29536
29046
|
|
|
29047
|
+
function isPiMultiple(value) {
|
|
29048
|
+
// Early return for integers that clearly aren't multiples
|
|
29049
|
+
// of pi to save some computation.
|
|
29050
|
+
if (Number.isInteger(value)) {
|
|
29051
|
+
return false;
|
|
29052
|
+
}
|
|
29053
|
+
return value % Math.PI === 0 || value % (Math.PI / 2) === 0 || value % (Math.PI / 3) === 0 || value % (Math.PI / 4) === 0 || value % (Math.PI / 6) === 0;
|
|
29054
|
+
}
|
|
29055
|
+
|
|
29537
29056
|
const _excluded$1 = ["onFormatChange", "checkValidity", "useArrowKeys"];
|
|
29538
29057
|
const {
|
|
29539
29058
|
firstNumericalParse,
|
|
@@ -29671,6 +29190,19 @@ class NumberInput extends React.Component {
|
|
|
29671
29190
|
this._getInput().value = toNumericString(val, format);
|
|
29672
29191
|
};
|
|
29673
29192
|
}
|
|
29193
|
+
componentDidMount() {
|
|
29194
|
+
// If the value is a multiple of pi, but it is not in the pi format,
|
|
29195
|
+
// then convert it to the pi format and show it as a multiple of pi.
|
|
29196
|
+
const value = this.getValue();
|
|
29197
|
+
if (this.props.allowPiTruncation && value !== null && value !== 0) {
|
|
29198
|
+
if (this.state.format !== "pi" && isPiMultiple(value)) {
|
|
29199
|
+
this._setValue(value / Math.PI, "pi");
|
|
29200
|
+
this.setState({
|
|
29201
|
+
format: "pi"
|
|
29202
|
+
});
|
|
29203
|
+
}
|
|
29204
|
+
}
|
|
29205
|
+
}
|
|
29674
29206
|
componentDidUpdate(prevProps) {
|
|
29675
29207
|
if (!number.equal(this.getValue(), this.props.value)) {
|
|
29676
29208
|
this._setValue(this.props.value, this.state.format);
|
|
@@ -29713,7 +29245,8 @@ NumberInput.propTypes = {
|
|
|
29713
29245
|
onFormatChange: PropTypes.func,
|
|
29714
29246
|
checkValidity: PropTypes.func,
|
|
29715
29247
|
size: PropTypes.oneOf(["mini", "small", "normal"]),
|
|
29716
|
-
label: PropTypes.oneOf(["put your labels outside your inputs!"])
|
|
29248
|
+
label: PropTypes.oneOf(["put your labels outside your inputs!"]),
|
|
29249
|
+
allowPiTruncation: PropTypes.bool
|
|
29717
29250
|
};
|
|
29718
29251
|
NumberInput.defaultProps = {
|
|
29719
29252
|
value: null,
|
|
@@ -29752,14 +29285,16 @@ class RangeInput extends React.Component {
|
|
|
29752
29285
|
// eslint-disable-next-line react/jsx-no-bind
|
|
29753
29286
|
,
|
|
29754
29287
|
onChange: this.onChange.bind(this, 0),
|
|
29755
|
-
placeholder: this.props.placeholder[0]
|
|
29288
|
+
placeholder: this.props.placeholder[0],
|
|
29289
|
+
allowPiTruncation: this.props.allowPiTruncation
|
|
29756
29290
|
})), /*#__PURE__*/React.createElement(NumberInput, _extends({}, this.props, {
|
|
29757
29291
|
value: value[1],
|
|
29758
29292
|
checkValidity: val => checkValidity([value[0], val])
|
|
29759
29293
|
// eslint-disable-next-line react/jsx-no-bind
|
|
29760
29294
|
,
|
|
29761
29295
|
onChange: this.onChange.bind(this, 1),
|
|
29762
|
-
placeholder: this.props.placeholder[1]
|
|
29296
|
+
placeholder: this.props.placeholder[1],
|
|
29297
|
+
allowPiTruncation: this.props.allowPiTruncation
|
|
29763
29298
|
})));
|
|
29764
29299
|
}
|
|
29765
29300
|
}
|
|
@@ -29767,7 +29302,8 @@ RangeInput.propTypes = {
|
|
|
29767
29302
|
value: PropTypes.array.isRequired,
|
|
29768
29303
|
onChange: PropTypes.func.isRequired,
|
|
29769
29304
|
placeholder: PropTypes.array,
|
|
29770
|
-
checkValidity: PropTypes.func
|
|
29305
|
+
checkValidity: PropTypes.func,
|
|
29306
|
+
allowPiTruncation: PropTypes.bool
|
|
29771
29307
|
};
|
|
29772
29308
|
RangeInput.defaultProps = {
|
|
29773
29309
|
placeholder: [null, null]
|
|
@@ -36750,10 +36286,7 @@ var InteractiveGraph$1 = {
|
|
|
36750
36286
|
name: "interactive-graph",
|
|
36751
36287
|
displayName: "Interactive graph (Assessments only)",
|
|
36752
36288
|
widget: InteractiveGraph,
|
|
36753
|
-
staticTransform: staticTransform$3
|
|
36754
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
36755
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusInteractiveGraphUserInput'.
|
|
36756
|
-
scorer: scoreInteractiveGraph
|
|
36289
|
+
staticTransform: staticTransform$3
|
|
36757
36290
|
};
|
|
36758
36291
|
|
|
36759
36292
|
// TODO: can we use WB typography?
|
|
@@ -37695,9 +37228,7 @@ var LabelImage$1 = {
|
|
|
37695
37228
|
widget: LabelImageWithDependencies,
|
|
37696
37229
|
accessible: true,
|
|
37697
37230
|
isLintable: true,
|
|
37698
|
-
|
|
37699
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusLabelImageUserInput'.
|
|
37700
|
-
scorer: scoreLabelImage
|
|
37231
|
+
getPublicWidgetOptions: getLabelImagePublicWidgetOptions
|
|
37701
37232
|
};
|
|
37702
37233
|
|
|
37703
37234
|
var ItemState = /*#__PURE__*/function (ItemState) {
|
|
@@ -37930,7 +37461,7 @@ class Draggable extends React.Component {
|
|
|
37930
37461
|
// We need to keep backwards compatbility with rules specified directly
|
|
37931
37462
|
// in CSS. Hence the hacky tacking on of manual classNames.
|
|
37932
37463
|
// See sortable.less for details.
|
|
37933
|
-
let className = css(styles$8.card, styles$8.draggable, layout === "horizontal" && styles$8.horizontalCard, layout === "vertical" && styles$8.verticalCard, type === ItemState.DRAGGING && styles$8.dragging, type === ItemState.DISABLED && styles$8.disabled, !includePadding && styles$8.unpaddedCard) + " " +
|
|
37464
|
+
let className = css(styles$8.card, styles$8.draggable, layout === "horizontal" && styles$8.horizontalCard, layout === "vertical" && styles$8.verticalCard, type === ItemState.DRAGGING && styles$8.dragging, type === ItemState.DISABLED && styles$8.disabled, !includePadding && styles$8.unpaddedCard) + " " + "perseus-sortable-draggable";
|
|
37934
37465
|
if (!includePadding) {
|
|
37935
37466
|
className += " perseus-sortable-draggable-unpadded";
|
|
37936
37467
|
}
|
|
@@ -38631,10 +38162,7 @@ var Matcher$1 = {
|
|
|
38631
38162
|
name: "matcher",
|
|
38632
38163
|
displayName: "Matcher (two column)",
|
|
38633
38164
|
widget: Matcher,
|
|
38634
|
-
isLintable: true
|
|
38635
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
38636
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatcherUserInput'.
|
|
38637
|
-
scorer: scoreMatcher
|
|
38165
|
+
isLintable: true
|
|
38638
38166
|
};
|
|
38639
38167
|
|
|
38640
38168
|
const getPromptJSON$8 = (renderProps, userInput) => {
|
|
@@ -39038,13 +38566,7 @@ var Matrix$1 = {
|
|
|
39038
38566
|
widget: Matrix,
|
|
39039
38567
|
transform: propTransform$1,
|
|
39040
38568
|
staticTransform: staticTransform$2,
|
|
39041
|
-
isLintable: true
|
|
39042
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
39043
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
|
|
39044
|
-
scorer: scoreMatrix,
|
|
39045
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
39046
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusMatrixUserInput'.
|
|
39047
|
-
validator: validateMatrix
|
|
38569
|
+
isLintable: true
|
|
39048
38570
|
};
|
|
39049
38571
|
|
|
39050
38572
|
const getPromptJSON$7 = () => {
|
|
@@ -39124,7 +38646,7 @@ class Measurer extends React.Component {
|
|
|
39124
38646
|
// TODO(scottgrant): This isn't a11y-friendly! We should insist on
|
|
39125
38647
|
// finding some valid alt text when this widget is used.
|
|
39126
38648
|
return /*#__PURE__*/React.createElement("div", {
|
|
39127
|
-
className: "perseus-widget perseus-widget-measurer " + "graphie-container
|
|
38649
|
+
className: "perseus-widget perseus-widget-measurer " + "graphie-container blank-background",
|
|
39128
38650
|
style: {
|
|
39129
38651
|
width: this.props.box[0],
|
|
39130
38652
|
height: this.props.box[1]
|
|
@@ -39161,9 +38683,7 @@ var Measurer$1 = {
|
|
|
39161
38683
|
hidden: true,
|
|
39162
38684
|
widget: Measurer,
|
|
39163
38685
|
version: measurerLogic.version,
|
|
39164
|
-
propUpgrades: measurerLogic.widgetOptionsUpgrades
|
|
39165
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
39166
|
-
scorer: () => scoreNoop(1)
|
|
38686
|
+
propUpgrades: measurerLogic.widgetOptionsUpgrades
|
|
39167
38687
|
};
|
|
39168
38688
|
|
|
39169
38689
|
/**
|
|
@@ -40051,8 +39571,7 @@ var Molecule$1 = {
|
|
|
40051
39571
|
name: "molecule-renderer",
|
|
40052
39572
|
displayName: "Molecule renderer",
|
|
40053
39573
|
hidden: true,
|
|
40054
|
-
widget: MoleculeWidget
|
|
40055
|
-
scorer: () => scoreNoop()
|
|
39574
|
+
widget: MoleculeWidget
|
|
40056
39575
|
};
|
|
40057
39576
|
|
|
40058
39577
|
const getPromptJSON$6 = (renderProps, userInput) => {
|
|
@@ -40621,12 +40140,7 @@ var NumberLine$1 = {
|
|
|
40621
40140
|
widget: NumberLine,
|
|
40622
40141
|
transform: numberLineTransform,
|
|
40623
40142
|
staticTransform: staticTransform$1,
|
|
40624
|
-
|
|
40625
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
|
|
40626
|
-
scorer: scoreNumberLine,
|
|
40627
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
40628
|
-
// @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusNumberLineUserInput'.
|
|
40629
|
-
validator: validateNumberLine
|
|
40143
|
+
getPublicWidgetOptions: getNumberLinePublicWidgetOptions
|
|
40630
40144
|
};
|
|
40631
40145
|
|
|
40632
40146
|
const getPromptJSON$5 = (renderProps, userInput) => {
|
|
@@ -40644,7 +40158,7 @@ const getPromptJSON$5 = (renderProps, userInput) => {
|
|
|
40644
40158
|
class PlaceholderCard extends React.Component {
|
|
40645
40159
|
render() {
|
|
40646
40160
|
return /*#__PURE__*/React.createElement("div", {
|
|
40647
|
-
className: "card-wrap
|
|
40161
|
+
className: "card-wrap",
|
|
40648
40162
|
style: {
|
|
40649
40163
|
width: this.props.width
|
|
40650
40164
|
}
|
|
@@ -40659,7 +40173,7 @@ class PlaceholderCard extends React.Component {
|
|
|
40659
40173
|
class DragHintCard extends React.Component {
|
|
40660
40174
|
render() {
|
|
40661
40175
|
return /*#__PURE__*/React.createElement("div", {
|
|
40662
|
-
className: "card-wrap
|
|
40176
|
+
className: "card-wrap"
|
|
40663
40177
|
}, /*#__PURE__*/React.createElement("div", {
|
|
40664
40178
|
className: "card drag-hint"
|
|
40665
40179
|
}));
|
|
@@ -40795,7 +40309,7 @@ class Card extends React.Component {
|
|
|
40795
40309
|
const rendererProps = _.pick(this.props, "content");
|
|
40796
40310
|
const onMouseDown = this.props.animating ? $.noop : this.onMouseDown;
|
|
40797
40311
|
return /*#__PURE__*/React.createElement("div", {
|
|
40798
|
-
className: "card-wrap
|
|
40312
|
+
className: "card-wrap",
|
|
40799
40313
|
style: style,
|
|
40800
40314
|
onMouseDown: onMouseDown,
|
|
40801
40315
|
onTouchStart: onMouseDown,
|
|
@@ -41166,7 +40680,7 @@ class Orderer extends React.Component {
|
|
|
41166
40680
|
});
|
|
41167
40681
|
}, this));
|
|
41168
40682
|
return /*#__PURE__*/React.createElement("div", {
|
|
41169
|
-
className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "
|
|
40683
|
+
className: "draggy-boxy-thing orderer " + "height-" + this.props.height + " " + "layout-" + this.props.layout + " " + "blank-background " + "perseus-clearfix "
|
|
41170
40684
|
// eslint-disable-next-line react/no-string-refs
|
|
41171
40685
|
,
|
|
41172
40686
|
ref: "orderer"
|
|
@@ -41187,12 +40701,6 @@ var Orderer$1 = {
|
|
|
41187
40701
|
hidden: true,
|
|
41188
40702
|
widget: Orderer,
|
|
41189
40703
|
isLintable: true,
|
|
41190
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
41191
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
|
|
41192
|
-
scorer: scoreOrderer,
|
|
41193
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
41194
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusOrdererUserInput
|
|
41195
|
-
validator: validateOrderer,
|
|
41196
40704
|
getPublicWidgetOptions: getOrdererPublicWidgetOptions
|
|
41197
40705
|
};
|
|
41198
40706
|
|
|
@@ -41231,15 +40739,12 @@ PassageRefTarget.defaultProps = {
|
|
|
41231
40739
|
var PassageRefTarget$1 = {
|
|
41232
40740
|
name: "passage-ref-target",
|
|
41233
40741
|
displayName: "PassageRefTarget",
|
|
41234
|
-
defaultAlignment: "inline",
|
|
41235
40742
|
widget: PassageRefTarget,
|
|
41236
40743
|
hidden: true,
|
|
41237
40744
|
transform: editorProps => {
|
|
41238
40745
|
return _.pick(editorProps, "content");
|
|
41239
40746
|
},
|
|
41240
|
-
isLintable: true
|
|
41241
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
41242
|
-
scorer: () => scoreNoop()
|
|
40747
|
+
isLintable: true
|
|
41243
40748
|
};
|
|
41244
40749
|
|
|
41245
40750
|
const getPromptJSON$4 = () => {
|
|
@@ -42226,7 +41731,7 @@ class Plotter extends React.Component {
|
|
|
42226
41731
|
marginBottom: this.props.labels[0] ? paddingForBottomLabel : 0
|
|
42227
41732
|
};
|
|
42228
41733
|
return /*#__PURE__*/React.createElement("div", {
|
|
42229
|
-
className: "perseus-widget-plotter graphie
|
|
41734
|
+
className: "perseus-widget-plotter graphie"
|
|
42230
41735
|
// eslint-disable-next-line react/no-string-refs
|
|
42231
41736
|
,
|
|
42232
41737
|
ref: "graphieDiv",
|
|
@@ -42256,13 +41761,7 @@ var Plotter$1 = {
|
|
|
42256
41761
|
displayName: "Plotter",
|
|
42257
41762
|
hidden: true,
|
|
42258
41763
|
widget: Plotter,
|
|
42259
|
-
staticTransform: staticTransform
|
|
42260
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
42261
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
|
|
42262
|
-
scorer: scorePlotter,
|
|
42263
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
42264
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusPlotterUserInput
|
|
42265
|
-
validator: validatePlotter
|
|
41764
|
+
staticTransform: staticTransform
|
|
42266
41765
|
};
|
|
42267
41766
|
|
|
42268
41767
|
const getPromptJSON$2 = () => {
|
|
@@ -42425,12 +41924,7 @@ var Sorter$1 = {
|
|
|
42425
41924
|
displayName: "Sorter",
|
|
42426
41925
|
widget: Sorter,
|
|
42427
41926
|
isLintable: true,
|
|
42428
|
-
|
|
42429
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
|
|
42430
|
-
scorer: scoreSorter,
|
|
42431
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
42432
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusSorterUserInput
|
|
42433
|
-
validator: validateSorter
|
|
41927
|
+
getPublicWidgetOptions: getSorterPublicWidgetOptions
|
|
42434
41928
|
};
|
|
42435
41929
|
|
|
42436
41930
|
const {
|
|
@@ -42649,13 +42143,7 @@ var Table$1 = {
|
|
|
42649
42143
|
widget: Table,
|
|
42650
42144
|
transform: propTransform,
|
|
42651
42145
|
hidden: true,
|
|
42652
|
-
isLintable: true
|
|
42653
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
42654
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
|
|
42655
|
-
scorer: scoreTable,
|
|
42656
|
-
// TODO(LEMS-2656): remove TS suppression
|
|
42657
|
-
// @ts-expect-error: Type UserInput is not assignable to type PerseusTableUserInput
|
|
42658
|
-
validator: validateTable
|
|
42146
|
+
isLintable: true
|
|
42659
42147
|
};
|
|
42660
42148
|
|
|
42661
42149
|
const getPromptJSON = () => {
|
|
@@ -42815,11 +42303,7 @@ Video.contextType = PerseusI18nContext;
|
|
|
42815
42303
|
var Video$1 = {
|
|
42816
42304
|
name: "video",
|
|
42817
42305
|
displayName: "Video",
|
|
42818
|
-
|
|
42819
|
-
supportedAlignments: ["block", "float-left", "float-right", "full-width"],
|
|
42820
|
-
widget: Video,
|
|
42821
|
-
// TODO: things that aren't interactive shouldn't need scoring functions
|
|
42822
|
-
scorer: () => scoreNoop()
|
|
42306
|
+
widget: Video
|
|
42823
42307
|
};
|
|
42824
42308
|
|
|
42825
42309
|
/* eslint-disable import/no-named-as-default */
|
|
@@ -42838,7 +42322,7 @@ const init = function init() {
|
|
|
42838
42322
|
|
|
42839
42323
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
42840
42324
|
const libName = "@khanacademy/perseus";
|
|
42841
|
-
const libVersion = "
|
|
42325
|
+
const libVersion = "53.0.0";
|
|
42842
42326
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
42843
42327
|
|
|
42844
42328
|
const apiVersion = {
|
|
@@ -43705,6 +43189,42 @@ const ref = /*#__PURE__*/React.forwardRef((props, ref) => /*#__PURE__*/React.cre
|
|
|
43705
43189
|
ref: ref
|
|
43706
43190
|
}))));
|
|
43707
43191
|
|
|
43192
|
+
function isCorrect(score) {
|
|
43193
|
+
return score.type === "points" && score.earned >= score.total;
|
|
43194
|
+
}
|
|
43195
|
+
function keScoreFromPerseusScore(score,
|
|
43196
|
+
// It's weird, but this is what we're passing it
|
|
43197
|
+
guess, state) {
|
|
43198
|
+
if (score.type === "points") {
|
|
43199
|
+
return {
|
|
43200
|
+
empty: false,
|
|
43201
|
+
correct: isCorrect(score),
|
|
43202
|
+
message: score.message,
|
|
43203
|
+
guess: guess,
|
|
43204
|
+
state: state
|
|
43205
|
+
};
|
|
43206
|
+
}
|
|
43207
|
+
if (score.type === "invalid") {
|
|
43208
|
+
return {
|
|
43209
|
+
empty: true,
|
|
43210
|
+
correct: false,
|
|
43211
|
+
message: score.message,
|
|
43212
|
+
suppressAlmostThere: score.suppressAlmostThere,
|
|
43213
|
+
guess: guess,
|
|
43214
|
+
state: state
|
|
43215
|
+
};
|
|
43216
|
+
}
|
|
43217
|
+
throw new PerseusError(
|
|
43218
|
+
// @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
|
|
43219
|
+
"Invalid score type: " + score.type, Errors.InvalidInput, {
|
|
43220
|
+
metadata: {
|
|
43221
|
+
score: JSON.stringify(score),
|
|
43222
|
+
guess: JSON.stringify(guess),
|
|
43223
|
+
state: JSON.stringify(state)
|
|
43224
|
+
}
|
|
43225
|
+
});
|
|
43226
|
+
}
|
|
43227
|
+
|
|
43708
43228
|
/**
|
|
43709
43229
|
* Traverses a {content, widgets, images} renderer props object,
|
|
43710
43230
|
* such as `itemData.question`
|
|
@@ -45081,5 +44601,5 @@ const ScoringUtil = {
|
|
|
45081
44601
|
* Types
|
|
45082
44602
|
*/
|
|
45083
44603
|
|
|
45084
|
-
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, ItemVersion as itemVersion, libVersion, makeSafeUrl, mathOnlyParser, mediaQueries, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting,
|
|
44604
|
+
export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, ItemVersion as itemVersion, libVersion, makeSafeUrl, mathOnlyParser, mediaQueries, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
45085
44605
|
//# sourceMappingURL=index.js.map
|