@khanacademy/perseus 23.5.0 → 23.6.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/input-with-examples.d.ts +0 -4
- package/dist/es/index.css +0 -17
- package/dist/es/index.js +214 -327
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +0 -17
- package/dist/index.js +214 -324
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +1 -0
- package/dist/server-item-renderer.d.ts +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/widgets/expression.d.ts +6 -0
- package/dist/widgets/interactive-graphs/graphs/sinusoid.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/use-draggable.d.ts +13 -0
- package/dist/widgets/interactive-graphs/{locked-ellipse.d.ts → locked-figures/locked-ellipse.d.ts} +1 -1
- package/dist/widgets/interactive-graphs/{locked-line.d.ts → locked-figures/locked-line.d.ts} +1 -1
- package/dist/widgets/interactive-graphs/{locked-point.d.ts → locked-figures/locked-point.d.ts} +1 -1
- package/dist/widgets/interactive-graphs/{locked-polygon.d.ts → locked-figures/locked-polygon.d.ts} +1 -1
- package/dist/widgets/interactive-graphs/{locked-vector.d.ts → locked-figures/locked-vector.d.ts} +1 -1
- package/dist/widgets/label-image.d.ts +2 -0
- package/package.json +1 -1
- package/dist/components/math-output.d.ts +0 -27
package/dist/index.js
CHANGED
|
@@ -21203,251 +21203,6 @@ const styles$B = {
|
|
|
21203
21203
|
}
|
|
21204
21204
|
};
|
|
21205
21205
|
|
|
21206
|
-
/*
|
|
21207
|
-
* In this file, an `expression` is some portion of valid TeX enclosed in
|
|
21208
|
-
* curly brackets.
|
|
21209
|
-
*/
|
|
21210
|
-
|
|
21211
|
-
/*
|
|
21212
|
-
* Find the index at which an expression ends, i.e., has an unmatched
|
|
21213
|
-
* closing curly bracket. This method assumes that we start with a non-open
|
|
21214
|
-
* bracket character and end when we've seen more left than right brackets
|
|
21215
|
-
* (rather than assuming that we start with a bracket character and wait for
|
|
21216
|
-
* bracket equality).
|
|
21217
|
-
*/
|
|
21218
|
-
function findEndpoint(tex, currentIndex) {
|
|
21219
|
-
let bracketDepth = 0;
|
|
21220
|
-
for (let i = currentIndex, len = tex.length; i < len; i++) {
|
|
21221
|
-
const c = tex[i];
|
|
21222
|
-
if (c === "{") {
|
|
21223
|
-
bracketDepth++;
|
|
21224
|
-
} else if (c === "}") {
|
|
21225
|
-
bracketDepth--;
|
|
21226
|
-
}
|
|
21227
|
-
if (bracketDepth < 0) {
|
|
21228
|
-
return i;
|
|
21229
|
-
}
|
|
21230
|
-
}
|
|
21231
|
-
// If we never see unbalanced curly brackets, default to the
|
|
21232
|
-
// entire string
|
|
21233
|
-
return tex.length;
|
|
21234
|
-
}
|
|
21235
|
-
|
|
21236
|
-
/*
|
|
21237
|
-
* Parses an individual set of curly brackets into TeX.
|
|
21238
|
-
*/
|
|
21239
|
-
function parseNextExpression(tex, currentIndex, handler) {
|
|
21240
|
-
// Find the first '{' and grab subsequent TeX
|
|
21241
|
-
// Ex) tex: '{3}{7}', and we want the '3'
|
|
21242
|
-
const openBracketIndex = tex.indexOf("{", currentIndex);
|
|
21243
|
-
const nextExpIndex = openBracketIndex + 1;
|
|
21244
|
-
|
|
21245
|
-
// Truncate to only contain remaining TeX
|
|
21246
|
-
const endpoint = findEndpoint(tex, nextExpIndex);
|
|
21247
|
-
const expressionTeX = tex.substring(nextExpIndex, endpoint);
|
|
21248
|
-
const parsedExp = walkTex(expressionTeX, handler);
|
|
21249
|
-
return {
|
|
21250
|
-
endpoint: endpoint,
|
|
21251
|
-
expression: parsedExp
|
|
21252
|
-
};
|
|
21253
|
-
}
|
|
21254
|
-
function getNextFracIndex(tex, currentIndex) {
|
|
21255
|
-
const dfrac = "\\dfrac";
|
|
21256
|
-
const frac = "\\frac";
|
|
21257
|
-
const nextFrac = tex.indexOf(frac, currentIndex);
|
|
21258
|
-
const nextDFrac = tex.indexOf(dfrac, currentIndex);
|
|
21259
|
-
if (nextFrac > -1 && nextDFrac > -1) {
|
|
21260
|
-
return Math.min(nextFrac, nextDFrac);
|
|
21261
|
-
}
|
|
21262
|
-
if (nextFrac > -1) {
|
|
21263
|
-
return nextFrac;
|
|
21264
|
-
}
|
|
21265
|
-
if (nextDFrac > -1) {
|
|
21266
|
-
return nextDFrac;
|
|
21267
|
-
}
|
|
21268
|
-
return -1;
|
|
21269
|
-
}
|
|
21270
|
-
function walkTex(tex, handler) {
|
|
21271
|
-
if (!tex) {
|
|
21272
|
-
return "";
|
|
21273
|
-
}
|
|
21274
|
-
|
|
21275
|
-
// Ex) tex: '2 \dfrac {3}{7}'
|
|
21276
|
-
let parsedString = "";
|
|
21277
|
-
let currentIndex = 0;
|
|
21278
|
-
let nextFrac = getNextFracIndex(tex, currentIndex);
|
|
21279
|
-
|
|
21280
|
-
// For each \dfrac, find the two expressions (wrapped in {}) and recur
|
|
21281
|
-
while (nextFrac > -1) {
|
|
21282
|
-
// Gather first fragment, preceding \dfrac
|
|
21283
|
-
// Ex) parsedString: '2 '
|
|
21284
|
-
parsedString += tex.substring(currentIndex, nextFrac);
|
|
21285
|
-
|
|
21286
|
-
// Remove everything preceding \dfrac, which has been parsed
|
|
21287
|
-
currentIndex = nextFrac;
|
|
21288
|
-
|
|
21289
|
-
// Parse first expression and move index past it
|
|
21290
|
-
// Ex) firstParsedExpression.expression: '3'
|
|
21291
|
-
const firstParsedExpression = parseNextExpression(tex, currentIndex, handler);
|
|
21292
|
-
currentIndex = firstParsedExpression.endpoint + 1;
|
|
21293
|
-
|
|
21294
|
-
// Parse second expression
|
|
21295
|
-
// Ex) secondParsedExpression.expression: '7'
|
|
21296
|
-
const secondParsedExpression = parseNextExpression(tex, currentIndex, handler);
|
|
21297
|
-
currentIndex = secondParsedExpression.endpoint + 1;
|
|
21298
|
-
|
|
21299
|
-
// Add expressions to running total of parsed expressions
|
|
21300
|
-
if (parsedString.length) {
|
|
21301
|
-
parsedString += " ";
|
|
21302
|
-
}
|
|
21303
|
-
|
|
21304
|
-
// Apply a custom handler based on the parsed subexpressions
|
|
21305
|
-
parsedString += handler(firstParsedExpression.expression, secondParsedExpression.expression);
|
|
21306
|
-
|
|
21307
|
-
// Find next DFrac, relative to currentIndex
|
|
21308
|
-
nextFrac = getNextFracIndex(tex, currentIndex);
|
|
21309
|
-
}
|
|
21310
|
-
|
|
21311
|
-
// Add remaining TeX, which is \dfrac-free
|
|
21312
|
-
parsedString += tex.slice(currentIndex);
|
|
21313
|
-
return parsedString;
|
|
21314
|
-
}
|
|
21315
|
-
|
|
21316
|
-
/*
|
|
21317
|
-
* Modify a TeX expression, returning another TeX expression. The resulting
|
|
21318
|
-
* expression will have its innermost fractions stubbed out with \fracs
|
|
21319
|
-
* (as opposed to \dfracs). All other content will remain untouched.
|
|
21320
|
-
*/
|
|
21321
|
-
function modifyTex(tex) {
|
|
21322
|
-
function isNestedFraction(tex) {
|
|
21323
|
-
return tex.indexOf("\\frac") > -1 || tex.indexOf("\\dfrac") > -1;
|
|
21324
|
-
}
|
|
21325
|
-
const handler = function (exp1, exp2) {
|
|
21326
|
-
let prefix;
|
|
21327
|
-
if (isNestedFraction(exp1) || isNestedFraction(exp2)) {
|
|
21328
|
-
prefix = "\\dfrac";
|
|
21329
|
-
} else {
|
|
21330
|
-
prefix = "\\frac";
|
|
21331
|
-
}
|
|
21332
|
-
return prefix + " {" + exp1 + "}{" + exp2 + "}";
|
|
21333
|
-
};
|
|
21334
|
-
return walkTex(tex, handler);
|
|
21335
|
-
}
|
|
21336
|
-
|
|
21337
|
-
/*
|
|
21338
|
-
* Parse a TeX expression into something interpretable by input-number.
|
|
21339
|
-
* The process is concerned with: (1) parsing fractions, i.e., \dfracs; and
|
|
21340
|
-
* (2) removing backslash-escaping from certain characters (right now, only
|
|
21341
|
-
* percent signs).
|
|
21342
|
-
*
|
|
21343
|
-
* The basic algorithm for handling \dfracs splits on \dfracs and then recurs
|
|
21344
|
-
* on the subsequent "expressions", i.e., the {} pairs that follow \dfrac. The
|
|
21345
|
-
* recursion is to allow for nested \dfrac elements.
|
|
21346
|
-
*
|
|
21347
|
-
* Backslash-escapes are removed with a simple search-and-replace.
|
|
21348
|
-
*/
|
|
21349
|
-
function parseTex(tex) {
|
|
21350
|
-
const handler = function (exp1, exp2) {
|
|
21351
|
-
return exp1 + "/" + exp2;
|
|
21352
|
-
};
|
|
21353
|
-
const texWithoutFracs = walkTex(tex, handler);
|
|
21354
|
-
return texWithoutFracs.replace("\\%", "%");
|
|
21355
|
-
}
|
|
21356
|
-
var TexWrangler = {
|
|
21357
|
-
parseTex,
|
|
21358
|
-
modifyTex
|
|
21359
|
-
};
|
|
21360
|
-
|
|
21361
|
-
/* eslint-disable react/sort-comp */
|
|
21362
|
-
const ModifyTex = TexWrangler.modifyTex;
|
|
21363
|
-
class MathOutput extends React__namespace.Component {
|
|
21364
|
-
static defaultProps = {
|
|
21365
|
-
value: "",
|
|
21366
|
-
onFocus: function () {},
|
|
21367
|
-
onBlur: function () {}
|
|
21368
|
-
};
|
|
21369
|
-
state = {
|
|
21370
|
-
focused: false,
|
|
21371
|
-
selectorNamespace: ___default["default"].uniqueId("math-output")
|
|
21372
|
-
};
|
|
21373
|
-
_getInputClassName = () => {
|
|
21374
|
-
let className = "math-output " + ClassNames.INPUT + " " + ClassNames.INTERACTIVE;
|
|
21375
|
-
if (this.state.focused) {
|
|
21376
|
-
className += " " + ClassNames.FOCUSED;
|
|
21377
|
-
}
|
|
21378
|
-
if (this.props.className) {
|
|
21379
|
-
className += " " + this.props.className;
|
|
21380
|
-
}
|
|
21381
|
-
return className;
|
|
21382
|
-
};
|
|
21383
|
-
_getDisplayValue = value => {
|
|
21384
|
-
// Cast from (potentially a) number to string
|
|
21385
|
-
let displayText;
|
|
21386
|
-
if (value != null) {
|
|
21387
|
-
displayText = "" + value;
|
|
21388
|
-
} else {
|
|
21389
|
-
displayText = "";
|
|
21390
|
-
}
|
|
21391
|
-
return ModifyTex(displayText);
|
|
21392
|
-
};
|
|
21393
|
-
render() {
|
|
21394
|
-
const divStyle = {
|
|
21395
|
-
textAlign: "center"
|
|
21396
|
-
};
|
|
21397
|
-
const {
|
|
21398
|
-
TeX
|
|
21399
|
-
} = getDependencies();
|
|
21400
|
-
return /*#__PURE__*/React__namespace.createElement("span", {
|
|
21401
|
-
// eslint-disable-next-line react/no-string-refs
|
|
21402
|
-
ref: "input",
|
|
21403
|
-
className: this._getInputClassName(),
|
|
21404
|
-
"aria-label": this.props.labelText,
|
|
21405
|
-
onMouseDown: this.focus,
|
|
21406
|
-
onTouchStart: this.focus
|
|
21407
|
-
}, /*#__PURE__*/React__namespace.createElement("div", {
|
|
21408
|
-
style: divStyle
|
|
21409
|
-
}, /*#__PURE__*/React__namespace.createElement(TeX, null, this._getDisplayValue(this.props.value))));
|
|
21410
|
-
}
|
|
21411
|
-
getValue = () => {
|
|
21412
|
-
return this.props.value;
|
|
21413
|
-
};
|
|
21414
|
-
focus = () => {
|
|
21415
|
-
if (!this.state.focused) {
|
|
21416
|
-
this.props.onFocus();
|
|
21417
|
-
this._bindBlurHandler();
|
|
21418
|
-
this.setState({
|
|
21419
|
-
focused: true
|
|
21420
|
-
});
|
|
21421
|
-
}
|
|
21422
|
-
};
|
|
21423
|
-
blur = () => {
|
|
21424
|
-
if (this.state.focused) {
|
|
21425
|
-
this.props.onBlur();
|
|
21426
|
-
this._unbindBlurHandler();
|
|
21427
|
-
this.setState({
|
|
21428
|
-
focused: false
|
|
21429
|
-
});
|
|
21430
|
-
}
|
|
21431
|
-
};
|
|
21432
|
-
_bindBlurHandler = () => {
|
|
21433
|
-
$__default["default"](document).bind("vclick." + this.state.selectorNamespace, e => {
|
|
21434
|
-
// Detect whether the target has our React DOM node as a parent
|
|
21435
|
-
const $closestWidget = $__default["default"](e.target).closest(
|
|
21436
|
-
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
21437
|
-
ReactDOM__default["default"].findDOMNode(this));
|
|
21438
|
-
if (!$closestWidget.length) {
|
|
21439
|
-
this.blur();
|
|
21440
|
-
}
|
|
21441
|
-
});
|
|
21442
|
-
};
|
|
21443
|
-
_unbindBlurHandler = () => {
|
|
21444
|
-
$__default["default"](document).unbind("." + this.state.selectorNamespace);
|
|
21445
|
-
};
|
|
21446
|
-
componentWillUnmount() {
|
|
21447
|
-
this._unbindBlurHandler();
|
|
21448
|
-
}
|
|
21449
|
-
}
|
|
21450
|
-
|
|
21451
21206
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
21452
21207
|
let lastId = 0;
|
|
21453
21208
|
function uniqueIdForInput() {
|
|
@@ -21788,7 +21543,6 @@ const {
|
|
|
21788
21543
|
class InputWithExamples extends React__namespace.Component {
|
|
21789
21544
|
static contextType = PerseusI18nContext;
|
|
21790
21545
|
static defaultProps = {
|
|
21791
|
-
type: "text",
|
|
21792
21546
|
shouldShowExamples: true,
|
|
21793
21547
|
onFocus: function () {},
|
|
21794
21548
|
onBlur: function () {},
|
|
@@ -21804,12 +21558,6 @@ class InputWithExamples extends React__namespace.Component {
|
|
|
21804
21558
|
return `input-with-examples-${btoa(this.props.id).replace(/=/g, "")}`;
|
|
21805
21559
|
};
|
|
21806
21560
|
_getInputClassName = () => {
|
|
21807
|
-
// <MathOutput> is a special component that manages its own class and
|
|
21808
|
-
// state, as it's a <span> that wants to act like an <input>.
|
|
21809
|
-
if (this.props.type === "tex") {
|
|
21810
|
-
return this.props.className;
|
|
21811
|
-
}
|
|
21812
|
-
|
|
21813
21561
|
// Otherwise, we need to add these INPUT and FOCUSED tags here.
|
|
21814
21562
|
let className = ClassNames.INPUT + " " + ClassNames.INTERACTIVE;
|
|
21815
21563
|
if (this.state.focused) {
|
|
@@ -21820,8 +21568,7 @@ class InputWithExamples extends React__namespace.Component {
|
|
|
21820
21568
|
}
|
|
21821
21569
|
return className;
|
|
21822
21570
|
};
|
|
21823
|
-
|
|
21824
|
-
// Minimal set of props, used by each input type
|
|
21571
|
+
_renderInput = () => {
|
|
21825
21572
|
const id = this._getUniqueId();
|
|
21826
21573
|
const inputProps = {
|
|
21827
21574
|
id: id,
|
|
@@ -21833,63 +21580,18 @@ class InputWithExamples extends React__namespace.Component {
|
|
|
21833
21580
|
onFocus: this._handleFocus,
|
|
21834
21581
|
onBlur: this._handleBlur,
|
|
21835
21582
|
disabled: this.props.disabled,
|
|
21836
|
-
style: this.props.style
|
|
21837
|
-
};
|
|
21838
|
-
if (this.props.type === "tex") {
|
|
21839
|
-
return inputProps;
|
|
21840
|
-
}
|
|
21841
|
-
|
|
21842
|
-
// Add useful props required for MATH and TEXT modes
|
|
21843
|
-
___default["default"].extend(inputProps, {
|
|
21583
|
+
style: this.props.style,
|
|
21844
21584
|
onChange: this.props.onChange,
|
|
21845
|
-
onTouchStart: captureScratchpadTouchStart$2
|
|
21846
|
-
|
|
21847
|
-
|
|
21848
|
-
|
|
21849
|
-
|
|
21850
|
-
|
|
21851
|
-
|
|
21852
|
-
buttonsVisible: this.props.buttonsVisible,
|
|
21853
|
-
convertDotToTimes: this.props.convertDotToTimes
|
|
21854
|
-
}, inputProps);
|
|
21855
|
-
}
|
|
21856
|
-
if (this.props.type === "text") {
|
|
21857
|
-
return ___default["default"].extend({
|
|
21858
|
-
autoCapitalize: "off",
|
|
21859
|
-
autoComplete: "off",
|
|
21860
|
-
autoCorrect: "off",
|
|
21861
|
-
spellCheck: "false"
|
|
21862
|
-
}, inputProps);
|
|
21863
|
-
}
|
|
21864
|
-
};
|
|
21865
|
-
_getComponentForInputType = () => {
|
|
21866
|
-
switch (this.props.type) {
|
|
21867
|
-
case "tex":
|
|
21868
|
-
return MathOutput;
|
|
21869
|
-
case "math":
|
|
21870
|
-
return MathInput;
|
|
21871
|
-
case "text":
|
|
21872
|
-
return TextInput;
|
|
21873
|
-
default:
|
|
21874
|
-
this.props.type;
|
|
21875
|
-
return null;
|
|
21876
|
-
}
|
|
21877
|
-
};
|
|
21878
|
-
_renderInput = () => {
|
|
21879
|
-
const inputProps = this._getPropsForInputType();
|
|
21880
|
-
const InputComponent = this._getComponentForInputType();
|
|
21881
|
-
return /*#__PURE__*/React__namespace.createElement(InputComponent, inputProps);
|
|
21585
|
+
onTouchStart: captureScratchpadTouchStart$2,
|
|
21586
|
+
autoCapitalize: "off",
|
|
21587
|
+
autoComplete: "off",
|
|
21588
|
+
autoCorrect: "off",
|
|
21589
|
+
spellCheck: "false"
|
|
21590
|
+
};
|
|
21591
|
+
return /*#__PURE__*/React__namespace.createElement(TextInput, inputProps);
|
|
21882
21592
|
};
|
|
21883
21593
|
render() {
|
|
21884
21594
|
const input = this._renderInput();
|
|
21885
|
-
|
|
21886
|
-
// Static rendering, which doesn't include the 'tooltip' logic that the
|
|
21887
|
-
// other types require, and is hence handled separately.
|
|
21888
|
-
if (this.props.type === "tex") {
|
|
21889
|
-
return input;
|
|
21890
|
-
}
|
|
21891
|
-
|
|
21892
|
-
// Else, we need to be able to show examples
|
|
21893
21595
|
const examplesContent = ___default["default"].map(this.props.examples, example => {
|
|
21894
21596
|
return "- " + example;
|
|
21895
21597
|
}).join("\n");
|
|
@@ -22025,6 +21727,161 @@ SimpleKeypadInput.propTypes = {
|
|
|
22025
21727
|
value: PropTypes__default["default"].oneOfType([PropTypes__default["default"].string, PropTypes__default["default"].number])
|
|
22026
21728
|
};
|
|
22027
21729
|
|
|
21730
|
+
/*
|
|
21731
|
+
* In this file, an `expression` is some portion of valid TeX enclosed in
|
|
21732
|
+
* curly brackets.
|
|
21733
|
+
*/
|
|
21734
|
+
|
|
21735
|
+
/*
|
|
21736
|
+
* Find the index at which an expression ends, i.e., has an unmatched
|
|
21737
|
+
* closing curly bracket. This method assumes that we start with a non-open
|
|
21738
|
+
* bracket character and end when we've seen more left than right brackets
|
|
21739
|
+
* (rather than assuming that we start with a bracket character and wait for
|
|
21740
|
+
* bracket equality).
|
|
21741
|
+
*/
|
|
21742
|
+
function findEndpoint(tex, currentIndex) {
|
|
21743
|
+
let bracketDepth = 0;
|
|
21744
|
+
for (let i = currentIndex, len = tex.length; i < len; i++) {
|
|
21745
|
+
const c = tex[i];
|
|
21746
|
+
if (c === "{") {
|
|
21747
|
+
bracketDepth++;
|
|
21748
|
+
} else if (c === "}") {
|
|
21749
|
+
bracketDepth--;
|
|
21750
|
+
}
|
|
21751
|
+
if (bracketDepth < 0) {
|
|
21752
|
+
return i;
|
|
21753
|
+
}
|
|
21754
|
+
}
|
|
21755
|
+
// If we never see unbalanced curly brackets, default to the
|
|
21756
|
+
// entire string
|
|
21757
|
+
return tex.length;
|
|
21758
|
+
}
|
|
21759
|
+
|
|
21760
|
+
/*
|
|
21761
|
+
* Parses an individual set of curly brackets into TeX.
|
|
21762
|
+
*/
|
|
21763
|
+
function parseNextExpression(tex, currentIndex, handler) {
|
|
21764
|
+
// Find the first '{' and grab subsequent TeX
|
|
21765
|
+
// Ex) tex: '{3}{7}', and we want the '3'
|
|
21766
|
+
const openBracketIndex = tex.indexOf("{", currentIndex);
|
|
21767
|
+
const nextExpIndex = openBracketIndex + 1;
|
|
21768
|
+
|
|
21769
|
+
// Truncate to only contain remaining TeX
|
|
21770
|
+
const endpoint = findEndpoint(tex, nextExpIndex);
|
|
21771
|
+
const expressionTeX = tex.substring(nextExpIndex, endpoint);
|
|
21772
|
+
const parsedExp = walkTex(expressionTeX, handler);
|
|
21773
|
+
return {
|
|
21774
|
+
endpoint: endpoint,
|
|
21775
|
+
expression: parsedExp
|
|
21776
|
+
};
|
|
21777
|
+
}
|
|
21778
|
+
function getNextFracIndex(tex, currentIndex) {
|
|
21779
|
+
const dfrac = "\\dfrac";
|
|
21780
|
+
const frac = "\\frac";
|
|
21781
|
+
const nextFrac = tex.indexOf(frac, currentIndex);
|
|
21782
|
+
const nextDFrac = tex.indexOf(dfrac, currentIndex);
|
|
21783
|
+
if (nextFrac > -1 && nextDFrac > -1) {
|
|
21784
|
+
return Math.min(nextFrac, nextDFrac);
|
|
21785
|
+
}
|
|
21786
|
+
if (nextFrac > -1) {
|
|
21787
|
+
return nextFrac;
|
|
21788
|
+
}
|
|
21789
|
+
if (nextDFrac > -1) {
|
|
21790
|
+
return nextDFrac;
|
|
21791
|
+
}
|
|
21792
|
+
return -1;
|
|
21793
|
+
}
|
|
21794
|
+
function walkTex(tex, handler) {
|
|
21795
|
+
if (!tex) {
|
|
21796
|
+
return "";
|
|
21797
|
+
}
|
|
21798
|
+
|
|
21799
|
+
// Ex) tex: '2 \dfrac {3}{7}'
|
|
21800
|
+
let parsedString = "";
|
|
21801
|
+
let currentIndex = 0;
|
|
21802
|
+
let nextFrac = getNextFracIndex(tex, currentIndex);
|
|
21803
|
+
|
|
21804
|
+
// For each \dfrac, find the two expressions (wrapped in {}) and recur
|
|
21805
|
+
while (nextFrac > -1) {
|
|
21806
|
+
// Gather first fragment, preceding \dfrac
|
|
21807
|
+
// Ex) parsedString: '2 '
|
|
21808
|
+
parsedString += tex.substring(currentIndex, nextFrac);
|
|
21809
|
+
|
|
21810
|
+
// Remove everything preceding \dfrac, which has been parsed
|
|
21811
|
+
currentIndex = nextFrac;
|
|
21812
|
+
|
|
21813
|
+
// Parse first expression and move index past it
|
|
21814
|
+
// Ex) firstParsedExpression.expression: '3'
|
|
21815
|
+
const firstParsedExpression = parseNextExpression(tex, currentIndex, handler);
|
|
21816
|
+
currentIndex = firstParsedExpression.endpoint + 1;
|
|
21817
|
+
|
|
21818
|
+
// Parse second expression
|
|
21819
|
+
// Ex) secondParsedExpression.expression: '7'
|
|
21820
|
+
const secondParsedExpression = parseNextExpression(tex, currentIndex, handler);
|
|
21821
|
+
currentIndex = secondParsedExpression.endpoint + 1;
|
|
21822
|
+
|
|
21823
|
+
// Add expressions to running total of parsed expressions
|
|
21824
|
+
if (parsedString.length) {
|
|
21825
|
+
parsedString += " ";
|
|
21826
|
+
}
|
|
21827
|
+
|
|
21828
|
+
// Apply a custom handler based on the parsed subexpressions
|
|
21829
|
+
parsedString += handler(firstParsedExpression.expression, secondParsedExpression.expression);
|
|
21830
|
+
|
|
21831
|
+
// Find next DFrac, relative to currentIndex
|
|
21832
|
+
nextFrac = getNextFracIndex(tex, currentIndex);
|
|
21833
|
+
}
|
|
21834
|
+
|
|
21835
|
+
// Add remaining TeX, which is \dfrac-free
|
|
21836
|
+
parsedString += tex.slice(currentIndex);
|
|
21837
|
+
return parsedString;
|
|
21838
|
+
}
|
|
21839
|
+
|
|
21840
|
+
/*
|
|
21841
|
+
* Modify a TeX expression, returning another TeX expression. The resulting
|
|
21842
|
+
* expression will have its innermost fractions stubbed out with \fracs
|
|
21843
|
+
* (as opposed to \dfracs). All other content will remain untouched.
|
|
21844
|
+
*/
|
|
21845
|
+
function modifyTex(tex) {
|
|
21846
|
+
function isNestedFraction(tex) {
|
|
21847
|
+
return tex.indexOf("\\frac") > -1 || tex.indexOf("\\dfrac") > -1;
|
|
21848
|
+
}
|
|
21849
|
+
const handler = function (exp1, exp2) {
|
|
21850
|
+
let prefix;
|
|
21851
|
+
if (isNestedFraction(exp1) || isNestedFraction(exp2)) {
|
|
21852
|
+
prefix = "\\dfrac";
|
|
21853
|
+
} else {
|
|
21854
|
+
prefix = "\\frac";
|
|
21855
|
+
}
|
|
21856
|
+
return prefix + " {" + exp1 + "}{" + exp2 + "}";
|
|
21857
|
+
};
|
|
21858
|
+
return walkTex(tex, handler);
|
|
21859
|
+
}
|
|
21860
|
+
|
|
21861
|
+
/*
|
|
21862
|
+
* Parse a TeX expression into something interpretable by input-number.
|
|
21863
|
+
* The process is concerned with: (1) parsing fractions, i.e., \dfracs; and
|
|
21864
|
+
* (2) removing backslash-escaping from certain characters (right now, only
|
|
21865
|
+
* percent signs).
|
|
21866
|
+
*
|
|
21867
|
+
* The basic algorithm for handling \dfracs splits on \dfracs and then recurs
|
|
21868
|
+
* on the subsequent "expressions", i.e., the {} pairs that follow \dfrac. The
|
|
21869
|
+
* recursion is to allow for nested \dfrac elements.
|
|
21870
|
+
*
|
|
21871
|
+
* Backslash-escapes are removed with a simple search-and-replace.
|
|
21872
|
+
*/
|
|
21873
|
+
function parseTex(tex) {
|
|
21874
|
+
const handler = function (exp1, exp2) {
|
|
21875
|
+
return exp1 + "/" + exp2;
|
|
21876
|
+
};
|
|
21877
|
+
const texWithoutFracs = walkTex(tex, handler);
|
|
21878
|
+
return texWithoutFracs.replace("\\%", "%");
|
|
21879
|
+
}
|
|
21880
|
+
var TexWrangler = {
|
|
21881
|
+
parseTex,
|
|
21882
|
+
modifyTex
|
|
21883
|
+
};
|
|
21884
|
+
|
|
22028
21885
|
/* eslint-disable react/sort-comp */
|
|
22029
21886
|
const ParseTex$1 = TexWrangler.parseTex;
|
|
22030
21887
|
const answerTypes = {
|
|
@@ -22145,7 +22002,6 @@ class InputNumber extends React__namespace.Component {
|
|
|
22145
22002
|
value: this.props.currentValue,
|
|
22146
22003
|
onChange: this.handleChange,
|
|
22147
22004
|
style: inputStyles,
|
|
22148
|
-
type: "text",
|
|
22149
22005
|
examples: this.examples(),
|
|
22150
22006
|
shouldShowExamples: this.shouldShowExamples(),
|
|
22151
22007
|
onFocus: this._handleFocus,
|
|
@@ -22612,7 +22468,6 @@ class NumericInput extends React__namespace.Component {
|
|
|
22612
22468
|
value: this.props.currentValue,
|
|
22613
22469
|
onChange: this.handleChange,
|
|
22614
22470
|
labelText: labelText,
|
|
22615
|
-
type: "text",
|
|
22616
22471
|
examples: this.examples(),
|
|
22617
22472
|
shouldShowExamples: this.shouldShowExamples(),
|
|
22618
22473
|
onFocus: this._handleFocus,
|
|
@@ -29410,6 +29265,10 @@ function snap(snapStep, point) {
|
|
|
29410
29265
|
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
29411
29266
|
}
|
|
29412
29267
|
|
|
29268
|
+
function useDraggable(params) {
|
|
29269
|
+
return mafs.useMovable(params);
|
|
29270
|
+
}
|
|
29271
|
+
|
|
29413
29272
|
// The hitbox size of 48px by 48px is preserved from the legacy interactive
|
|
29414
29273
|
// graph.
|
|
29415
29274
|
const hitboxSizePx = 48;
|
|
@@ -29565,7 +29424,7 @@ function useControlPoint(point, color, onMovePoint) {
|
|
|
29565
29424
|
} = useGraphConfig();
|
|
29566
29425
|
const [focused, setFocused] = React.useState(false);
|
|
29567
29426
|
const keyboardHandleRef = React.useRef(null);
|
|
29568
|
-
|
|
29427
|
+
useDraggable({
|
|
29569
29428
|
gestureTarget: keyboardHandleRef,
|
|
29570
29429
|
point,
|
|
29571
29430
|
onMove: onMovePoint,
|
|
@@ -29574,7 +29433,7 @@ function useControlPoint(point, color, onMovePoint) {
|
|
|
29574
29433
|
const visiblePointRef = React.useRef(null);
|
|
29575
29434
|
const {
|
|
29576
29435
|
dragging
|
|
29577
|
-
} =
|
|
29436
|
+
} = useDraggable({
|
|
29578
29437
|
gestureTarget: visiblePointRef,
|
|
29579
29438
|
point,
|
|
29580
29439
|
onMove: onMovePoint,
|
|
@@ -29628,7 +29487,7 @@ const Line$1 = props => {
|
|
|
29628
29487
|
const line = React.useRef(null);
|
|
29629
29488
|
const {
|
|
29630
29489
|
dragging
|
|
29631
|
-
} =
|
|
29490
|
+
} = useDraggable({
|
|
29632
29491
|
gestureTarget: line,
|
|
29633
29492
|
point: start,
|
|
29634
29493
|
onMove: newPoint => {
|
|
@@ -29976,7 +29835,7 @@ const StyledMovablePoint = props => {
|
|
|
29976
29835
|
const snapToValue = snapTo ?? "grid";
|
|
29977
29836
|
const {
|
|
29978
29837
|
dragging
|
|
29979
|
-
} =
|
|
29838
|
+
} = useDraggable({
|
|
29980
29839
|
gestureTarget: elementRef,
|
|
29981
29840
|
point,
|
|
29982
29841
|
onMove,
|
|
@@ -30018,7 +29877,7 @@ const PolygonGraph = props => {
|
|
|
30018
29877
|
const snapToValue = snapTo ?? "grid";
|
|
30019
29878
|
const {
|
|
30020
29879
|
dragging
|
|
30021
|
-
} =
|
|
29880
|
+
} = useDraggable({
|
|
30022
29881
|
gestureTarget: ref,
|
|
30023
29882
|
point: dragReferencePoint,
|
|
30024
29883
|
onMove: newPoint => {
|
|
@@ -30193,7 +30052,7 @@ function MovableCircle(props) {
|
|
|
30193
30052
|
const draggableRef = React.useRef(null);
|
|
30194
30053
|
const {
|
|
30195
30054
|
dragging
|
|
30196
|
-
} =
|
|
30055
|
+
} = useDraggable({
|
|
30197
30056
|
gestureTarget: draggableRef,
|
|
30198
30057
|
point: center,
|
|
30199
30058
|
onMove,
|
|
@@ -30324,10 +30183,24 @@ function SinusoidGraph(props) {
|
|
|
30324
30183
|
coords
|
|
30325
30184
|
} = graphState;
|
|
30326
30185
|
|
|
30327
|
-
//
|
|
30186
|
+
// The coefficients are used to calculate the sinusoid equation, plot the graph, and to indicate
|
|
30187
|
+
// to content creators the currently selected "correct answer" in the Content Editor.
|
|
30188
|
+
// While we should technically never have invalid coordinates, we want to ensure that
|
|
30189
|
+
// we have a fallback so that the graph can still be plotted without crashing.
|
|
30190
|
+
const coeffRef = React__namespace.useRef({
|
|
30191
|
+
amplitude: 1,
|
|
30192
|
+
angularFrequency: 1,
|
|
30193
|
+
phase: 1,
|
|
30194
|
+
verticalOffset: 0
|
|
30195
|
+
});
|
|
30328
30196
|
const coeffs = getSinusoidCoefficients(coords);
|
|
30197
|
+
|
|
30198
|
+
// If the coefficients are valid, update the reference
|
|
30199
|
+
if (coeffs !== undefined) {
|
|
30200
|
+
coeffRef.current = coeffs;
|
|
30201
|
+
}
|
|
30329
30202
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, {
|
|
30330
|
-
y: x => computeSine(x,
|
|
30203
|
+
y: x => computeSine(x, coeffRef.current),
|
|
30331
30204
|
color: wonderBlocksTokens.color.blue
|
|
30332
30205
|
}), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
30333
30206
|
key: "point-" + i,
|
|
@@ -30354,6 +30227,11 @@ const getSinusoidCoefficients = coords => {
|
|
|
30354
30227
|
const p1 = coords[0];
|
|
30355
30228
|
const p2 = coords[1];
|
|
30356
30229
|
|
|
30230
|
+
// If the x-coordinates are the same, we are unable to calculate the coefficients
|
|
30231
|
+
if (p2[0] === p1[0]) {
|
|
30232
|
+
return;
|
|
30233
|
+
}
|
|
30234
|
+
|
|
30357
30235
|
// Resulting coefficients are canonical for this sine curve
|
|
30358
30236
|
const amplitude = p2[1] - p1[1];
|
|
30359
30237
|
const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
|
|
@@ -31847,11 +31725,14 @@ function doMovePoint(state, action) {
|
|
|
31847
31725
|
}
|
|
31848
31726
|
case "sinusoid":
|
|
31849
31727
|
{
|
|
31850
|
-
// First, we need to
|
|
31851
|
-
// If they are, we don't want to move the point
|
|
31728
|
+
// First, we need to make sure to bound the new coordinates to the graph range
|
|
31852
31729
|
const destination = action.destination;
|
|
31730
|
+
const boundDestination = boundAndSnapToGrid(destination, state);
|
|
31731
|
+
|
|
31732
|
+
// Then, we need to verify that the new coordinates are not on the same
|
|
31733
|
+
// vertical line. If they are, then we don't want to move the point
|
|
31853
31734
|
const newCoords = [...state.coords];
|
|
31854
|
-
newCoords[action.index] =
|
|
31735
|
+
newCoords[action.index] = boundDestination;
|
|
31855
31736
|
if (newCoords[0][0] === newCoords[1][0]) {
|
|
31856
31737
|
return state;
|
|
31857
31738
|
}
|
|
@@ -31861,7 +31742,7 @@ function doMovePoint(state, action) {
|
|
|
31861
31742
|
coords: setAtIndex({
|
|
31862
31743
|
array: state.coords,
|
|
31863
31744
|
index: action.index,
|
|
31864
|
-
newValue:
|
|
31745
|
+
newValue: boundDestination
|
|
31865
31746
|
})
|
|
31866
31747
|
};
|
|
31867
31748
|
}
|
|
@@ -31869,7 +31750,10 @@ function doMovePoint(state, action) {
|
|
|
31869
31750
|
{
|
|
31870
31751
|
// Set up the new coords and check if the quadratic coefficients are valid
|
|
31871
31752
|
const newCoords = [...state.coords];
|
|
31872
|
-
|
|
31753
|
+
|
|
31754
|
+
// Bind the new destination to the graph range/snapStep and then get the quadratic coefficients
|
|
31755
|
+
const boundDestination = boundAndSnapToGrid(action.destination, state);
|
|
31756
|
+
newCoords[action.index] = boundDestination;
|
|
31873
31757
|
const QuadraticCoefficients = getQuadraticCoefficients(newCoords);
|
|
31874
31758
|
|
|
31875
31759
|
// If the new destination results in an invalid quadratic equation, we don't want to move the point
|
|
@@ -31882,7 +31766,7 @@ function doMovePoint(state, action) {
|
|
|
31882
31766
|
coords: setAtIndex({
|
|
31883
31767
|
array: state.coords,
|
|
31884
31768
|
index: action.index,
|
|
31885
|
-
newValue:
|
|
31769
|
+
newValue: boundDestination
|
|
31886
31770
|
})
|
|
31887
31771
|
};
|
|
31888
31772
|
}
|
|
@@ -33698,6 +33582,12 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
33698
33582
|
return InteractiveGraph.validate(this.getUserInput?.(), rubric, this);
|
|
33699
33583
|
}
|
|
33700
33584
|
render() {
|
|
33585
|
+
if (this.props.graph.type === "polygon" && this.props.graph.numSides === "unlimited") {
|
|
33586
|
+
return /*#__PURE__*/React__namespace.createElement(LegacyInteractiveGraph, _extends({
|
|
33587
|
+
ref: this.legacyGraphRef
|
|
33588
|
+
}, this.props));
|
|
33589
|
+
}
|
|
33590
|
+
|
|
33701
33591
|
// Mafs shim
|
|
33702
33592
|
if (this.props.apiOptions?.flags?.["mafs"]?.[this.props.graph.type]) {
|
|
33703
33593
|
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
@@ -44497,7 +44387,7 @@ const init = function (options) {
|
|
|
44497
44387
|
|
|
44498
44388
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44499
44389
|
const libName = "@khanacademy/perseus";
|
|
44500
|
-
const libVersion = "23.
|
|
44390
|
+
const libVersion = "23.6.0";
|
|
44501
44391
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44502
44392
|
|
|
44503
44393
|
const apiVersion = {
|