@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/es/index.js
CHANGED
|
@@ -20964,254 +20964,6 @@ const styles$B = {
|
|
|
20964
20964
|
}
|
|
20965
20965
|
};
|
|
20966
20966
|
|
|
20967
|
-
/*
|
|
20968
|
-
* In this file, an `expression` is some portion of valid TeX enclosed in
|
|
20969
|
-
* curly brackets.
|
|
20970
|
-
*/
|
|
20971
|
-
|
|
20972
|
-
/*
|
|
20973
|
-
* Find the index at which an expression ends, i.e., has an unmatched
|
|
20974
|
-
* closing curly bracket. This method assumes that we start with a non-open
|
|
20975
|
-
* bracket character and end when we've seen more left than right brackets
|
|
20976
|
-
* (rather than assuming that we start with a bracket character and wait for
|
|
20977
|
-
* bracket equality).
|
|
20978
|
-
*/
|
|
20979
|
-
function findEndpoint(tex, currentIndex) {
|
|
20980
|
-
let bracketDepth = 0;
|
|
20981
|
-
for (let i = currentIndex, len = tex.length; i < len; i++) {
|
|
20982
|
-
const c = tex[i];
|
|
20983
|
-
if (c === "{") {
|
|
20984
|
-
bracketDepth++;
|
|
20985
|
-
} else if (c === "}") {
|
|
20986
|
-
bracketDepth--;
|
|
20987
|
-
}
|
|
20988
|
-
if (bracketDepth < 0) {
|
|
20989
|
-
return i;
|
|
20990
|
-
}
|
|
20991
|
-
}
|
|
20992
|
-
// If we never see unbalanced curly brackets, default to the
|
|
20993
|
-
// entire string
|
|
20994
|
-
return tex.length;
|
|
20995
|
-
}
|
|
20996
|
-
|
|
20997
|
-
/*
|
|
20998
|
-
* Parses an individual set of curly brackets into TeX.
|
|
20999
|
-
*/
|
|
21000
|
-
function parseNextExpression(tex, currentIndex, handler) {
|
|
21001
|
-
// Find the first '{' and grab subsequent TeX
|
|
21002
|
-
// Ex) tex: '{3}{7}', and we want the '3'
|
|
21003
|
-
const openBracketIndex = tex.indexOf("{", currentIndex);
|
|
21004
|
-
const nextExpIndex = openBracketIndex + 1;
|
|
21005
|
-
|
|
21006
|
-
// Truncate to only contain remaining TeX
|
|
21007
|
-
const endpoint = findEndpoint(tex, nextExpIndex);
|
|
21008
|
-
const expressionTeX = tex.substring(nextExpIndex, endpoint);
|
|
21009
|
-
const parsedExp = walkTex(expressionTeX, handler);
|
|
21010
|
-
return {
|
|
21011
|
-
endpoint: endpoint,
|
|
21012
|
-
expression: parsedExp
|
|
21013
|
-
};
|
|
21014
|
-
}
|
|
21015
|
-
function getNextFracIndex(tex, currentIndex) {
|
|
21016
|
-
const dfrac = "\\dfrac";
|
|
21017
|
-
const frac = "\\frac";
|
|
21018
|
-
const nextFrac = tex.indexOf(frac, currentIndex);
|
|
21019
|
-
const nextDFrac = tex.indexOf(dfrac, currentIndex);
|
|
21020
|
-
if (nextFrac > -1 && nextDFrac > -1) {
|
|
21021
|
-
return Math.min(nextFrac, nextDFrac);
|
|
21022
|
-
}
|
|
21023
|
-
if (nextFrac > -1) {
|
|
21024
|
-
return nextFrac;
|
|
21025
|
-
}
|
|
21026
|
-
if (nextDFrac > -1) {
|
|
21027
|
-
return nextDFrac;
|
|
21028
|
-
}
|
|
21029
|
-
return -1;
|
|
21030
|
-
}
|
|
21031
|
-
function walkTex(tex, handler) {
|
|
21032
|
-
if (!tex) {
|
|
21033
|
-
return "";
|
|
21034
|
-
}
|
|
21035
|
-
|
|
21036
|
-
// Ex) tex: '2 \dfrac {3}{7}'
|
|
21037
|
-
let parsedString = "";
|
|
21038
|
-
let currentIndex = 0;
|
|
21039
|
-
let nextFrac = getNextFracIndex(tex, currentIndex);
|
|
21040
|
-
|
|
21041
|
-
// For each \dfrac, find the two expressions (wrapped in {}) and recur
|
|
21042
|
-
while (nextFrac > -1) {
|
|
21043
|
-
// Gather first fragment, preceding \dfrac
|
|
21044
|
-
// Ex) parsedString: '2 '
|
|
21045
|
-
parsedString += tex.substring(currentIndex, nextFrac);
|
|
21046
|
-
|
|
21047
|
-
// Remove everything preceding \dfrac, which has been parsed
|
|
21048
|
-
currentIndex = nextFrac;
|
|
21049
|
-
|
|
21050
|
-
// Parse first expression and move index past it
|
|
21051
|
-
// Ex) firstParsedExpression.expression: '3'
|
|
21052
|
-
const firstParsedExpression = parseNextExpression(tex, currentIndex, handler);
|
|
21053
|
-
currentIndex = firstParsedExpression.endpoint + 1;
|
|
21054
|
-
|
|
21055
|
-
// Parse second expression
|
|
21056
|
-
// Ex) secondParsedExpression.expression: '7'
|
|
21057
|
-
const secondParsedExpression = parseNextExpression(tex, currentIndex, handler);
|
|
21058
|
-
currentIndex = secondParsedExpression.endpoint + 1;
|
|
21059
|
-
|
|
21060
|
-
// Add expressions to running total of parsed expressions
|
|
21061
|
-
if (parsedString.length) {
|
|
21062
|
-
parsedString += " ";
|
|
21063
|
-
}
|
|
21064
|
-
|
|
21065
|
-
// Apply a custom handler based on the parsed subexpressions
|
|
21066
|
-
parsedString += handler(firstParsedExpression.expression, secondParsedExpression.expression);
|
|
21067
|
-
|
|
21068
|
-
// Find next DFrac, relative to currentIndex
|
|
21069
|
-
nextFrac = getNextFracIndex(tex, currentIndex);
|
|
21070
|
-
}
|
|
21071
|
-
|
|
21072
|
-
// Add remaining TeX, which is \dfrac-free
|
|
21073
|
-
parsedString += tex.slice(currentIndex);
|
|
21074
|
-
return parsedString;
|
|
21075
|
-
}
|
|
21076
|
-
|
|
21077
|
-
/*
|
|
21078
|
-
* Modify a TeX expression, returning another TeX expression. The resulting
|
|
21079
|
-
* expression will have its innermost fractions stubbed out with \fracs
|
|
21080
|
-
* (as opposed to \dfracs). All other content will remain untouched.
|
|
21081
|
-
*/
|
|
21082
|
-
function modifyTex(tex) {
|
|
21083
|
-
function isNestedFraction(tex) {
|
|
21084
|
-
return tex.indexOf("\\frac") > -1 || tex.indexOf("\\dfrac") > -1;
|
|
21085
|
-
}
|
|
21086
|
-
const handler = function handler(exp1, exp2) {
|
|
21087
|
-
let prefix;
|
|
21088
|
-
if (isNestedFraction(exp1) || isNestedFraction(exp2)) {
|
|
21089
|
-
prefix = "\\dfrac";
|
|
21090
|
-
} else {
|
|
21091
|
-
prefix = "\\frac";
|
|
21092
|
-
}
|
|
21093
|
-
return prefix + " {" + exp1 + "}{" + exp2 + "}";
|
|
21094
|
-
};
|
|
21095
|
-
return walkTex(tex, handler);
|
|
21096
|
-
}
|
|
21097
|
-
|
|
21098
|
-
/*
|
|
21099
|
-
* Parse a TeX expression into something interpretable by input-number.
|
|
21100
|
-
* The process is concerned with: (1) parsing fractions, i.e., \dfracs; and
|
|
21101
|
-
* (2) removing backslash-escaping from certain characters (right now, only
|
|
21102
|
-
* percent signs).
|
|
21103
|
-
*
|
|
21104
|
-
* The basic algorithm for handling \dfracs splits on \dfracs and then recurs
|
|
21105
|
-
* on the subsequent "expressions", i.e., the {} pairs that follow \dfrac. The
|
|
21106
|
-
* recursion is to allow for nested \dfrac elements.
|
|
21107
|
-
*
|
|
21108
|
-
* Backslash-escapes are removed with a simple search-and-replace.
|
|
21109
|
-
*/
|
|
21110
|
-
function parseTex(tex) {
|
|
21111
|
-
const handler = function handler(exp1, exp2) {
|
|
21112
|
-
return exp1 + "/" + exp2;
|
|
21113
|
-
};
|
|
21114
|
-
const texWithoutFracs = walkTex(tex, handler);
|
|
21115
|
-
return texWithoutFracs.replace("\\%", "%");
|
|
21116
|
-
}
|
|
21117
|
-
var TexWrangler = {
|
|
21118
|
-
parseTex,
|
|
21119
|
-
modifyTex
|
|
21120
|
-
};
|
|
21121
|
-
|
|
21122
|
-
/* eslint-disable react/sort-comp */
|
|
21123
|
-
const ModifyTex = TexWrangler.modifyTex;
|
|
21124
|
-
class MathOutput extends React.Component {
|
|
21125
|
-
constructor(...args) {
|
|
21126
|
-
super(...args);
|
|
21127
|
-
this.state = {
|
|
21128
|
-
focused: false,
|
|
21129
|
-
selectorNamespace: _.uniqueId("math-output")
|
|
21130
|
-
};
|
|
21131
|
-
this._getInputClassName = () => {
|
|
21132
|
-
let className = "math-output " + ClassNames.INPUT + " " + ClassNames.INTERACTIVE;
|
|
21133
|
-
if (this.state.focused) {
|
|
21134
|
-
className += " " + ClassNames.FOCUSED;
|
|
21135
|
-
}
|
|
21136
|
-
if (this.props.className) {
|
|
21137
|
-
className += " " + this.props.className;
|
|
21138
|
-
}
|
|
21139
|
-
return className;
|
|
21140
|
-
};
|
|
21141
|
-
this._getDisplayValue = value => {
|
|
21142
|
-
// Cast from (potentially a) number to string
|
|
21143
|
-
let displayText;
|
|
21144
|
-
if (value != null) {
|
|
21145
|
-
displayText = "" + value;
|
|
21146
|
-
} else {
|
|
21147
|
-
displayText = "";
|
|
21148
|
-
}
|
|
21149
|
-
return ModifyTex(displayText);
|
|
21150
|
-
};
|
|
21151
|
-
this.getValue = () => {
|
|
21152
|
-
return this.props.value;
|
|
21153
|
-
};
|
|
21154
|
-
this.focus = () => {
|
|
21155
|
-
if (!this.state.focused) {
|
|
21156
|
-
this.props.onFocus();
|
|
21157
|
-
this._bindBlurHandler();
|
|
21158
|
-
this.setState({
|
|
21159
|
-
focused: true
|
|
21160
|
-
});
|
|
21161
|
-
}
|
|
21162
|
-
};
|
|
21163
|
-
this.blur = () => {
|
|
21164
|
-
if (this.state.focused) {
|
|
21165
|
-
this.props.onBlur();
|
|
21166
|
-
this._unbindBlurHandler();
|
|
21167
|
-
this.setState({
|
|
21168
|
-
focused: false
|
|
21169
|
-
});
|
|
21170
|
-
}
|
|
21171
|
-
};
|
|
21172
|
-
this._bindBlurHandler = () => {
|
|
21173
|
-
$(document).bind("vclick." + this.state.selectorNamespace, e => {
|
|
21174
|
-
// Detect whether the target has our React DOM node as a parent
|
|
21175
|
-
const $closestWidget = $(e.target).closest(
|
|
21176
|
-
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
21177
|
-
ReactDOM__default.findDOMNode(this));
|
|
21178
|
-
if (!$closestWidget.length) {
|
|
21179
|
-
this.blur();
|
|
21180
|
-
}
|
|
21181
|
-
});
|
|
21182
|
-
};
|
|
21183
|
-
this._unbindBlurHandler = () => {
|
|
21184
|
-
$(document).unbind("." + this.state.selectorNamespace);
|
|
21185
|
-
};
|
|
21186
|
-
}
|
|
21187
|
-
render() {
|
|
21188
|
-
const divStyle = {
|
|
21189
|
-
textAlign: "center"
|
|
21190
|
-
};
|
|
21191
|
-
const {
|
|
21192
|
-
TeX
|
|
21193
|
-
} = getDependencies();
|
|
21194
|
-
return /*#__PURE__*/React.createElement("span", {
|
|
21195
|
-
// eslint-disable-next-line react/no-string-refs
|
|
21196
|
-
ref: "input",
|
|
21197
|
-
className: this._getInputClassName(),
|
|
21198
|
-
"aria-label": this.props.labelText,
|
|
21199
|
-
onMouseDown: this.focus,
|
|
21200
|
-
onTouchStart: this.focus
|
|
21201
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
21202
|
-
style: divStyle
|
|
21203
|
-
}, /*#__PURE__*/React.createElement(TeX, null, this._getDisplayValue(this.props.value))));
|
|
21204
|
-
}
|
|
21205
|
-
componentWillUnmount() {
|
|
21206
|
-
this._unbindBlurHandler();
|
|
21207
|
-
}
|
|
21208
|
-
}
|
|
21209
|
-
MathOutput.defaultProps = {
|
|
21210
|
-
value: "",
|
|
21211
|
-
onFocus: function () {},
|
|
21212
|
-
onBlur: function () {}
|
|
21213
|
-
};
|
|
21214
|
-
|
|
21215
20967
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
21216
20968
|
let lastId = 0;
|
|
21217
20969
|
function uniqueIdForInput(prefix = "input-") {
|
|
@@ -21566,12 +21318,6 @@ class InputWithExamples extends React.Component {
|
|
|
21566
21318
|
return `input-with-examples-${btoa(this.props.id).replace(/=/g, "")}`;
|
|
21567
21319
|
};
|
|
21568
21320
|
this._getInputClassName = () => {
|
|
21569
|
-
// <MathOutput> is a special component that manages its own class and
|
|
21570
|
-
// state, as it's a <span> that wants to act like an <input>.
|
|
21571
|
-
if (this.props.type === "tex") {
|
|
21572
|
-
return this.props.className;
|
|
21573
|
-
}
|
|
21574
|
-
|
|
21575
21321
|
// Otherwise, we need to add these INPUT and FOCUSED tags here.
|
|
21576
21322
|
let className = ClassNames.INPUT + " " + ClassNames.INTERACTIVE;
|
|
21577
21323
|
if (this.state.focused) {
|
|
@@ -21582,8 +21328,7 @@ class InputWithExamples extends React.Component {
|
|
|
21582
21328
|
}
|
|
21583
21329
|
return className;
|
|
21584
21330
|
};
|
|
21585
|
-
this.
|
|
21586
|
-
// Minimal set of props, used by each input type
|
|
21331
|
+
this._renderInput = () => {
|
|
21587
21332
|
const id = this._getUniqueId();
|
|
21588
21333
|
const inputProps = {
|
|
21589
21334
|
id: id,
|
|
@@ -21595,52 +21340,15 @@ class InputWithExamples extends React.Component {
|
|
|
21595
21340
|
onFocus: this._handleFocus,
|
|
21596
21341
|
onBlur: this._handleBlur,
|
|
21597
21342
|
disabled: this.props.disabled,
|
|
21598
|
-
style: this.props.style
|
|
21599
|
-
};
|
|
21600
|
-
if (this.props.type === "tex") {
|
|
21601
|
-
return inputProps;
|
|
21602
|
-
}
|
|
21603
|
-
|
|
21604
|
-
// Add useful props required for MATH and TEXT modes
|
|
21605
|
-
_.extend(inputProps, {
|
|
21343
|
+
style: this.props.style,
|
|
21606
21344
|
onChange: this.props.onChange,
|
|
21607
|
-
onTouchStart: captureScratchpadTouchStart$2
|
|
21608
|
-
|
|
21609
|
-
|
|
21610
|
-
|
|
21611
|
-
|
|
21612
|
-
|
|
21613
|
-
|
|
21614
|
-
buttonsVisible: this.props.buttonsVisible,
|
|
21615
|
-
convertDotToTimes: this.props.convertDotToTimes
|
|
21616
|
-
}, inputProps);
|
|
21617
|
-
}
|
|
21618
|
-
if (this.props.type === "text") {
|
|
21619
|
-
return _.extend({
|
|
21620
|
-
autoCapitalize: "off",
|
|
21621
|
-
autoComplete: "off",
|
|
21622
|
-
autoCorrect: "off",
|
|
21623
|
-
spellCheck: "false"
|
|
21624
|
-
}, inputProps);
|
|
21625
|
-
}
|
|
21626
|
-
};
|
|
21627
|
-
this._getComponentForInputType = () => {
|
|
21628
|
-
switch (this.props.type) {
|
|
21629
|
-
case "tex":
|
|
21630
|
-
return MathOutput;
|
|
21631
|
-
case "math":
|
|
21632
|
-
return MathInput;
|
|
21633
|
-
case "text":
|
|
21634
|
-
return TextInput;
|
|
21635
|
-
default:
|
|
21636
|
-
this.props.type;
|
|
21637
|
-
return null;
|
|
21638
|
-
}
|
|
21639
|
-
};
|
|
21640
|
-
this._renderInput = () => {
|
|
21641
|
-
const inputProps = this._getPropsForInputType();
|
|
21642
|
-
const InputComponent = this._getComponentForInputType();
|
|
21643
|
-
return /*#__PURE__*/React.createElement(InputComponent, inputProps);
|
|
21345
|
+
onTouchStart: captureScratchpadTouchStart$2,
|
|
21346
|
+
autoCapitalize: "off",
|
|
21347
|
+
autoComplete: "off",
|
|
21348
|
+
autoCorrect: "off",
|
|
21349
|
+
spellCheck: "false"
|
|
21350
|
+
};
|
|
21351
|
+
return /*#__PURE__*/React.createElement(TextInput, inputProps);
|
|
21644
21352
|
};
|
|
21645
21353
|
this._handleFocus = () => {
|
|
21646
21354
|
this.props.onFocus();
|
|
@@ -21682,14 +21390,6 @@ class InputWithExamples extends React.Component {
|
|
|
21682
21390
|
}
|
|
21683
21391
|
render() {
|
|
21684
21392
|
const input = this._renderInput();
|
|
21685
|
-
|
|
21686
|
-
// Static rendering, which doesn't include the 'tooltip' logic that the
|
|
21687
|
-
// other types require, and is hence handled separately.
|
|
21688
|
-
if (this.props.type === "tex") {
|
|
21689
|
-
return input;
|
|
21690
|
-
}
|
|
21691
|
-
|
|
21692
|
-
// Else, we need to be able to show examples
|
|
21693
21393
|
const examplesContent = _.map(this.props.examples, example => {
|
|
21694
21394
|
return "- " + example;
|
|
21695
21395
|
}).join("\n");
|
|
@@ -21716,7 +21416,6 @@ class InputWithExamples extends React.Component {
|
|
|
21716
21416
|
}
|
|
21717
21417
|
InputWithExamples.contextType = PerseusI18nContext;
|
|
21718
21418
|
InputWithExamples.defaultProps = {
|
|
21719
|
-
type: "text",
|
|
21720
21419
|
shouldShowExamples: true,
|
|
21721
21420
|
onFocus: function () {},
|
|
21722
21421
|
onBlur: function () {},
|
|
@@ -21805,6 +21504,161 @@ SimpleKeypadInput.propTypes = {
|
|
|
21805
21504
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
|
21806
21505
|
};
|
|
21807
21506
|
|
|
21507
|
+
/*
|
|
21508
|
+
* In this file, an `expression` is some portion of valid TeX enclosed in
|
|
21509
|
+
* curly brackets.
|
|
21510
|
+
*/
|
|
21511
|
+
|
|
21512
|
+
/*
|
|
21513
|
+
* Find the index at which an expression ends, i.e., has an unmatched
|
|
21514
|
+
* closing curly bracket. This method assumes that we start with a non-open
|
|
21515
|
+
* bracket character and end when we've seen more left than right brackets
|
|
21516
|
+
* (rather than assuming that we start with a bracket character and wait for
|
|
21517
|
+
* bracket equality).
|
|
21518
|
+
*/
|
|
21519
|
+
function findEndpoint(tex, currentIndex) {
|
|
21520
|
+
let bracketDepth = 0;
|
|
21521
|
+
for (let i = currentIndex, len = tex.length; i < len; i++) {
|
|
21522
|
+
const c = tex[i];
|
|
21523
|
+
if (c === "{") {
|
|
21524
|
+
bracketDepth++;
|
|
21525
|
+
} else if (c === "}") {
|
|
21526
|
+
bracketDepth--;
|
|
21527
|
+
}
|
|
21528
|
+
if (bracketDepth < 0) {
|
|
21529
|
+
return i;
|
|
21530
|
+
}
|
|
21531
|
+
}
|
|
21532
|
+
// If we never see unbalanced curly brackets, default to the
|
|
21533
|
+
// entire string
|
|
21534
|
+
return tex.length;
|
|
21535
|
+
}
|
|
21536
|
+
|
|
21537
|
+
/*
|
|
21538
|
+
* Parses an individual set of curly brackets into TeX.
|
|
21539
|
+
*/
|
|
21540
|
+
function parseNextExpression(tex, currentIndex, handler) {
|
|
21541
|
+
// Find the first '{' and grab subsequent TeX
|
|
21542
|
+
// Ex) tex: '{3}{7}', and we want the '3'
|
|
21543
|
+
const openBracketIndex = tex.indexOf("{", currentIndex);
|
|
21544
|
+
const nextExpIndex = openBracketIndex + 1;
|
|
21545
|
+
|
|
21546
|
+
// Truncate to only contain remaining TeX
|
|
21547
|
+
const endpoint = findEndpoint(tex, nextExpIndex);
|
|
21548
|
+
const expressionTeX = tex.substring(nextExpIndex, endpoint);
|
|
21549
|
+
const parsedExp = walkTex(expressionTeX, handler);
|
|
21550
|
+
return {
|
|
21551
|
+
endpoint: endpoint,
|
|
21552
|
+
expression: parsedExp
|
|
21553
|
+
};
|
|
21554
|
+
}
|
|
21555
|
+
function getNextFracIndex(tex, currentIndex) {
|
|
21556
|
+
const dfrac = "\\dfrac";
|
|
21557
|
+
const frac = "\\frac";
|
|
21558
|
+
const nextFrac = tex.indexOf(frac, currentIndex);
|
|
21559
|
+
const nextDFrac = tex.indexOf(dfrac, currentIndex);
|
|
21560
|
+
if (nextFrac > -1 && nextDFrac > -1) {
|
|
21561
|
+
return Math.min(nextFrac, nextDFrac);
|
|
21562
|
+
}
|
|
21563
|
+
if (nextFrac > -1) {
|
|
21564
|
+
return nextFrac;
|
|
21565
|
+
}
|
|
21566
|
+
if (nextDFrac > -1) {
|
|
21567
|
+
return nextDFrac;
|
|
21568
|
+
}
|
|
21569
|
+
return -1;
|
|
21570
|
+
}
|
|
21571
|
+
function walkTex(tex, handler) {
|
|
21572
|
+
if (!tex) {
|
|
21573
|
+
return "";
|
|
21574
|
+
}
|
|
21575
|
+
|
|
21576
|
+
// Ex) tex: '2 \dfrac {3}{7}'
|
|
21577
|
+
let parsedString = "";
|
|
21578
|
+
let currentIndex = 0;
|
|
21579
|
+
let nextFrac = getNextFracIndex(tex, currentIndex);
|
|
21580
|
+
|
|
21581
|
+
// For each \dfrac, find the two expressions (wrapped in {}) and recur
|
|
21582
|
+
while (nextFrac > -1) {
|
|
21583
|
+
// Gather first fragment, preceding \dfrac
|
|
21584
|
+
// Ex) parsedString: '2 '
|
|
21585
|
+
parsedString += tex.substring(currentIndex, nextFrac);
|
|
21586
|
+
|
|
21587
|
+
// Remove everything preceding \dfrac, which has been parsed
|
|
21588
|
+
currentIndex = nextFrac;
|
|
21589
|
+
|
|
21590
|
+
// Parse first expression and move index past it
|
|
21591
|
+
// Ex) firstParsedExpression.expression: '3'
|
|
21592
|
+
const firstParsedExpression = parseNextExpression(tex, currentIndex, handler);
|
|
21593
|
+
currentIndex = firstParsedExpression.endpoint + 1;
|
|
21594
|
+
|
|
21595
|
+
// Parse second expression
|
|
21596
|
+
// Ex) secondParsedExpression.expression: '7'
|
|
21597
|
+
const secondParsedExpression = parseNextExpression(tex, currentIndex, handler);
|
|
21598
|
+
currentIndex = secondParsedExpression.endpoint + 1;
|
|
21599
|
+
|
|
21600
|
+
// Add expressions to running total of parsed expressions
|
|
21601
|
+
if (parsedString.length) {
|
|
21602
|
+
parsedString += " ";
|
|
21603
|
+
}
|
|
21604
|
+
|
|
21605
|
+
// Apply a custom handler based on the parsed subexpressions
|
|
21606
|
+
parsedString += handler(firstParsedExpression.expression, secondParsedExpression.expression);
|
|
21607
|
+
|
|
21608
|
+
// Find next DFrac, relative to currentIndex
|
|
21609
|
+
nextFrac = getNextFracIndex(tex, currentIndex);
|
|
21610
|
+
}
|
|
21611
|
+
|
|
21612
|
+
// Add remaining TeX, which is \dfrac-free
|
|
21613
|
+
parsedString += tex.slice(currentIndex);
|
|
21614
|
+
return parsedString;
|
|
21615
|
+
}
|
|
21616
|
+
|
|
21617
|
+
/*
|
|
21618
|
+
* Modify a TeX expression, returning another TeX expression. The resulting
|
|
21619
|
+
* expression will have its innermost fractions stubbed out with \fracs
|
|
21620
|
+
* (as opposed to \dfracs). All other content will remain untouched.
|
|
21621
|
+
*/
|
|
21622
|
+
function modifyTex(tex) {
|
|
21623
|
+
function isNestedFraction(tex) {
|
|
21624
|
+
return tex.indexOf("\\frac") > -1 || tex.indexOf("\\dfrac") > -1;
|
|
21625
|
+
}
|
|
21626
|
+
const handler = function handler(exp1, exp2) {
|
|
21627
|
+
let prefix;
|
|
21628
|
+
if (isNestedFraction(exp1) || isNestedFraction(exp2)) {
|
|
21629
|
+
prefix = "\\dfrac";
|
|
21630
|
+
} else {
|
|
21631
|
+
prefix = "\\frac";
|
|
21632
|
+
}
|
|
21633
|
+
return prefix + " {" + exp1 + "}{" + exp2 + "}";
|
|
21634
|
+
};
|
|
21635
|
+
return walkTex(tex, handler);
|
|
21636
|
+
}
|
|
21637
|
+
|
|
21638
|
+
/*
|
|
21639
|
+
* Parse a TeX expression into something interpretable by input-number.
|
|
21640
|
+
* The process is concerned with: (1) parsing fractions, i.e., \dfracs; and
|
|
21641
|
+
* (2) removing backslash-escaping from certain characters (right now, only
|
|
21642
|
+
* percent signs).
|
|
21643
|
+
*
|
|
21644
|
+
* The basic algorithm for handling \dfracs splits on \dfracs and then recurs
|
|
21645
|
+
* on the subsequent "expressions", i.e., the {} pairs that follow \dfrac. The
|
|
21646
|
+
* recursion is to allow for nested \dfrac elements.
|
|
21647
|
+
*
|
|
21648
|
+
* Backslash-escapes are removed with a simple search-and-replace.
|
|
21649
|
+
*/
|
|
21650
|
+
function parseTex(tex) {
|
|
21651
|
+
const handler = function handler(exp1, exp2) {
|
|
21652
|
+
return exp1 + "/" + exp2;
|
|
21653
|
+
};
|
|
21654
|
+
const texWithoutFracs = walkTex(tex, handler);
|
|
21655
|
+
return texWithoutFracs.replace("\\%", "%");
|
|
21656
|
+
}
|
|
21657
|
+
var TexWrangler = {
|
|
21658
|
+
parseTex,
|
|
21659
|
+
modifyTex
|
|
21660
|
+
};
|
|
21661
|
+
|
|
21808
21662
|
/* eslint-disable react/sort-comp */
|
|
21809
21663
|
const ParseTex$1 = TexWrangler.parseTex;
|
|
21810
21664
|
const answerTypes = {
|
|
@@ -21983,7 +21837,6 @@ class InputNumber extends React.Component {
|
|
|
21983
21837
|
value: this.props.currentValue,
|
|
21984
21838
|
onChange: this.handleChange,
|
|
21985
21839
|
style: inputStyles,
|
|
21986
|
-
type: "text",
|
|
21987
21840
|
examples: this.examples(),
|
|
21988
21841
|
shouldShowExamples: this.shouldShowExamples(),
|
|
21989
21842
|
onFocus: this._handleFocus,
|
|
@@ -22392,7 +22245,6 @@ class NumericInput extends React.Component {
|
|
|
22392
22245
|
value: this.props.currentValue,
|
|
22393
22246
|
onChange: this.handleChange,
|
|
22394
22247
|
labelText: labelText,
|
|
22395
|
-
type: "text",
|
|
22396
22248
|
examples: this.examples(),
|
|
22397
22249
|
shouldShowExamples: this.shouldShowExamples(),
|
|
22398
22250
|
onFocus: this._handleFocus,
|
|
@@ -29120,6 +28972,10 @@ function snap(snapStep, point) {
|
|
|
29120
28972
|
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
29121
28973
|
}
|
|
29122
28974
|
|
|
28975
|
+
function useDraggable(params) {
|
|
28976
|
+
return useMovable(params);
|
|
28977
|
+
}
|
|
28978
|
+
|
|
29123
28979
|
// The hitbox size of 48px by 48px is preserved from the legacy interactive
|
|
29124
28980
|
// graph.
|
|
29125
28981
|
const hitboxSizePx = 48;
|
|
@@ -29273,7 +29129,7 @@ function useControlPoint(point, color, onMovePoint) {
|
|
|
29273
29129
|
} = useGraphConfig();
|
|
29274
29130
|
const [focused, setFocused] = useState(false);
|
|
29275
29131
|
const keyboardHandleRef = useRef(null);
|
|
29276
|
-
|
|
29132
|
+
useDraggable({
|
|
29277
29133
|
gestureTarget: keyboardHandleRef,
|
|
29278
29134
|
point,
|
|
29279
29135
|
onMove: onMovePoint,
|
|
@@ -29282,7 +29138,7 @@ function useControlPoint(point, color, onMovePoint) {
|
|
|
29282
29138
|
const visiblePointRef = useRef(null);
|
|
29283
29139
|
const {
|
|
29284
29140
|
dragging
|
|
29285
|
-
} =
|
|
29141
|
+
} = useDraggable({
|
|
29286
29142
|
gestureTarget: visiblePointRef,
|
|
29287
29143
|
point,
|
|
29288
29144
|
onMove: onMovePoint,
|
|
@@ -29336,7 +29192,7 @@ const Line$1 = props => {
|
|
|
29336
29192
|
const line = useRef(null);
|
|
29337
29193
|
const {
|
|
29338
29194
|
dragging
|
|
29339
|
-
} =
|
|
29195
|
+
} = useDraggable({
|
|
29340
29196
|
gestureTarget: line,
|
|
29341
29197
|
point: start,
|
|
29342
29198
|
onMove: newPoint => {
|
|
@@ -29676,7 +29532,7 @@ const StyledMovablePoint = props => {
|
|
|
29676
29532
|
const snapToValue = snapTo != null ? snapTo : "grid";
|
|
29677
29533
|
const {
|
|
29678
29534
|
dragging
|
|
29679
|
-
} =
|
|
29535
|
+
} = useDraggable({
|
|
29680
29536
|
gestureTarget: elementRef,
|
|
29681
29537
|
point,
|
|
29682
29538
|
onMove,
|
|
@@ -29718,7 +29574,7 @@ const PolygonGraph = props => {
|
|
|
29718
29574
|
const snapToValue = snapTo != null ? snapTo : "grid";
|
|
29719
29575
|
const {
|
|
29720
29576
|
dragging
|
|
29721
|
-
} =
|
|
29577
|
+
} = useDraggable({
|
|
29722
29578
|
gestureTarget: ref,
|
|
29723
29579
|
point: dragReferencePoint,
|
|
29724
29580
|
onMove: newPoint => {
|
|
@@ -29881,7 +29737,7 @@ function MovableCircle(props) {
|
|
|
29881
29737
|
const draggableRef = useRef(null);
|
|
29882
29738
|
const {
|
|
29883
29739
|
dragging
|
|
29884
|
-
} =
|
|
29740
|
+
} = useDraggable({
|
|
29885
29741
|
gestureTarget: draggableRef,
|
|
29886
29742
|
point: center,
|
|
29887
29743
|
onMove,
|
|
@@ -30012,10 +29868,24 @@ function SinusoidGraph(props) {
|
|
|
30012
29868
|
coords
|
|
30013
29869
|
} = graphState;
|
|
30014
29870
|
|
|
30015
|
-
//
|
|
29871
|
+
// The coefficients are used to calculate the sinusoid equation, plot the graph, and to indicate
|
|
29872
|
+
// to content creators the currently selected "correct answer" in the Content Editor.
|
|
29873
|
+
// While we should technically never have invalid coordinates, we want to ensure that
|
|
29874
|
+
// we have a fallback so that the graph can still be plotted without crashing.
|
|
29875
|
+
const coeffRef = React.useRef({
|
|
29876
|
+
amplitude: 1,
|
|
29877
|
+
angularFrequency: 1,
|
|
29878
|
+
phase: 1,
|
|
29879
|
+
verticalOffset: 0
|
|
29880
|
+
});
|
|
30016
29881
|
const coeffs = getSinusoidCoefficients(coords);
|
|
29882
|
+
|
|
29883
|
+
// If the coefficients are valid, update the reference
|
|
29884
|
+
if (coeffs !== undefined) {
|
|
29885
|
+
coeffRef.current = coeffs;
|
|
29886
|
+
}
|
|
30017
29887
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Plot$3.OfX, {
|
|
30018
|
-
y: x => computeSine(x,
|
|
29888
|
+
y: x => computeSine(x, coeffRef.current),
|
|
30019
29889
|
color: color.blue
|
|
30020
29890
|
}), coords.map((coord, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
30021
29891
|
key: "point-" + i,
|
|
@@ -30042,6 +29912,11 @@ const getSinusoidCoefficients = coords => {
|
|
|
30042
29912
|
const p1 = coords[0];
|
|
30043
29913
|
const p2 = coords[1];
|
|
30044
29914
|
|
|
29915
|
+
// If the x-coordinates are the same, we are unable to calculate the coefficients
|
|
29916
|
+
if (p2[0] === p1[0]) {
|
|
29917
|
+
return;
|
|
29918
|
+
}
|
|
29919
|
+
|
|
30045
29920
|
// Resulting coefficients are canonical for this sine curve
|
|
30046
29921
|
const amplitude = p2[1] - p1[1];
|
|
30047
29922
|
const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
|
|
@@ -31515,11 +31390,14 @@ function doMovePoint(state, action) {
|
|
|
31515
31390
|
}
|
|
31516
31391
|
case "sinusoid":
|
|
31517
31392
|
{
|
|
31518
|
-
// First, we need to
|
|
31519
|
-
// If they are, we don't want to move the point
|
|
31393
|
+
// First, we need to make sure to bound the new coordinates to the graph range
|
|
31520
31394
|
const destination = action.destination;
|
|
31395
|
+
const boundDestination = boundAndSnapToGrid(destination, state);
|
|
31396
|
+
|
|
31397
|
+
// Then, we need to verify that the new coordinates are not on the same
|
|
31398
|
+
// vertical line. If they are, then we don't want to move the point
|
|
31521
31399
|
const _newCoords = [...state.coords];
|
|
31522
|
-
_newCoords[action.index] =
|
|
31400
|
+
_newCoords[action.index] = boundDestination;
|
|
31523
31401
|
if (_newCoords[0][0] === _newCoords[1][0]) {
|
|
31524
31402
|
return state;
|
|
31525
31403
|
}
|
|
@@ -31528,7 +31406,7 @@ function doMovePoint(state, action) {
|
|
|
31528
31406
|
coords: setAtIndex({
|
|
31529
31407
|
array: state.coords,
|
|
31530
31408
|
index: action.index,
|
|
31531
|
-
newValue:
|
|
31409
|
+
newValue: boundDestination
|
|
31532
31410
|
})
|
|
31533
31411
|
});
|
|
31534
31412
|
}
|
|
@@ -31536,7 +31414,10 @@ function doMovePoint(state, action) {
|
|
|
31536
31414
|
{
|
|
31537
31415
|
// Set up the new coords and check if the quadratic coefficients are valid
|
|
31538
31416
|
const _newCoords2 = [...state.coords];
|
|
31539
|
-
|
|
31417
|
+
|
|
31418
|
+
// Bind the new destination to the graph range/snapStep and then get the quadratic coefficients
|
|
31419
|
+
const boundDestination = boundAndSnapToGrid(action.destination, state);
|
|
31420
|
+
_newCoords2[action.index] = boundDestination;
|
|
31540
31421
|
const QuadraticCoefficients = getQuadraticCoefficients(_newCoords2);
|
|
31541
31422
|
|
|
31542
31423
|
// If the new destination results in an invalid quadratic equation, we don't want to move the point
|
|
@@ -31548,7 +31429,7 @@ function doMovePoint(state, action) {
|
|
|
31548
31429
|
coords: setAtIndex({
|
|
31549
31430
|
array: state.coords,
|
|
31550
31431
|
index: action.index,
|
|
31551
|
-
newValue:
|
|
31432
|
+
newValue: boundDestination
|
|
31552
31433
|
})
|
|
31553
31434
|
});
|
|
31554
31435
|
}
|
|
@@ -33360,6 +33241,12 @@ class InteractiveGraph extends React.Component {
|
|
|
33360
33241
|
}
|
|
33361
33242
|
render() {
|
|
33362
33243
|
var _this$props$apiOption;
|
|
33244
|
+
if (this.props.graph.type === "polygon" && this.props.graph.numSides === "unlimited") {
|
|
33245
|
+
return /*#__PURE__*/React.createElement(LegacyInteractiveGraph, _extends({
|
|
33246
|
+
ref: this.legacyGraphRef
|
|
33247
|
+
}, this.props));
|
|
33248
|
+
}
|
|
33249
|
+
|
|
33363
33250
|
// Mafs shim
|
|
33364
33251
|
if ((_this$props$apiOption = this.props.apiOptions) != null && (_this$props$apiOption = _this$props$apiOption.flags) != null && (_this$props$apiOption = _this$props$apiOption["mafs"]) != null && _this$props$apiOption[this.props.graph.type]) {
|
|
33365
33252
|
const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
|
|
@@ -44152,7 +44039,7 @@ const init = function init(options) {
|
|
|
44152
44039
|
|
|
44153
44040
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44154
44041
|
const libName = "@khanacademy/perseus";
|
|
44155
|
-
const libVersion = "23.
|
|
44042
|
+
const libVersion = "23.6.0";
|
|
44156
44043
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44157
44044
|
|
|
44158
44045
|
const apiVersion = {
|