@khanacademy/perseus 26.0.0 → 27.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/es/index.js CHANGED
@@ -501,6 +501,7 @@ class InnerMathInput extends React.Component {
501
501
  }), /*#__PURE__*/React.createElement(Popover, {
502
502
  opened: this.state.keypadOpen,
503
503
  onClose: () => this.closeKeypad(),
504
+ portal: false,
504
505
  dismissEnabled: true,
505
506
  content: () => {
506
507
  var _this$props$keypadBut, _this$props;
@@ -730,7 +731,7 @@ const denylist = [
730
731
  // (technically the renderer still adds them)
731
732
  "key", "ref",
732
733
  // added by src/renderer.jsx
733
- "containerSizeClass", "widgetId", "onChange", "problemNum", "apiOptions", "questionCompleted", "findWidgets",
734
+ "containerSizeClass", "widgetId", "onChange", "problemNum", "apiOptions", "questionCompleted", "findWidgets", "hintMode",
734
735
  // added by src/editor.jsx, for widgets removing themselves
735
736
  // this is soooo not the right place for this, but alas.
736
737
  "onRemove",
@@ -19014,6 +19015,7 @@ const replaceDeprecatedWidgets = () => {
19014
19015
  replaceWidget("reaction-diagram", "deprecated-standin");
19015
19016
  replaceWidget("sequence", "deprecated-standin");
19016
19017
  replaceWidget("simulator", "deprecated-standin");
19018
+ replaceWidget("unit-input", "deprecated-standin");
19017
19019
  };
19018
19020
  const registerEditors = editorsToRegister => {
19019
19021
  editorsToRegister.forEach(editor => {
@@ -19047,6 +19049,7 @@ const replaceDeprecatedEditors = () => {
19047
19049
  replaceEditor("reaction-diagram", "deprecated-standin");
19048
19050
  replaceEditor("sequence", "deprecated-standin");
19049
19051
  replaceEditor("simulator", "deprecated-standin");
19052
+ replaceEditor("unit-input", "deprecated-standin");
19050
19053
  };
19051
19054
  const getWidget = name => {
19052
19055
  var _widgets$name;
@@ -19731,6 +19734,7 @@ class Renderer extends React.Component {
19731
19734
  onBlur: _.partial(this._onWidgetBlur, id),
19732
19735
  findWidgets: this.findWidgets,
19733
19736
  reviewModeRubric: reviewModeRubric,
19737
+ hintMode: this.props.hintMode,
19734
19738
  onChange: (newProps, cb, silent = false) => {
19735
19739
  this._setWidgetProps(id, newProps, cb, silent);
19736
19740
  },
@@ -20930,6 +20934,7 @@ Renderer.defaultProps = {
20930
20934
  findExternalWidgets: () => [],
20931
20935
  alwaysUpdate: false,
20932
20936
  reviewMode: false,
20937
+ hintMode: false,
20933
20938
  serializedState: null,
20934
20939
  onSerializedStateUpdated: () => {},
20935
20940
  linterContext: PerseusLinter.linterContextDefault
@@ -28298,7 +28303,8 @@ const defaultGraphConfig = {
28298
28303
  graphDimensionsInPixels: [1, 1],
28299
28304
  width: 0,
28300
28305
  height: 0,
28301
- labels: []
28306
+ labels: [],
28307
+ hintMode: false
28302
28308
  };
28303
28309
  const GraphConfigContext = /*#__PURE__*/createContext(defaultGraphConfig);
28304
28310
  function useGraphConfig() {
@@ -29766,8 +29772,6 @@ const ItemExtras = [
29766
29772
 
29767
29773
  // prettier-ignore
29768
29774
 
29769
- // prettier-ignore
29770
-
29771
29775
  //prettier-ignore
29772
29776
 
29773
29777
  // A background image applied to various widgets.
@@ -30292,6 +30296,16 @@ function useDraggable(args) {
30292
30296
  const isKeyboard = type.includes("key");
30293
30297
  if (isKeyboard) {
30294
30298
  event == null || event.preventDefault();
30299
+
30300
+ // When a key is held down, we see multiple "keydown" events,
30301
+ // followed by a single "keyup" event.
30302
+ // For a single keypress, we only see a "keydown" event, no
30303
+ // "keyup".
30304
+ // We never want to process the keyup event as an intent to
30305
+ // move so we bail on further processing here.
30306
+ if (type === "keyup") {
30307
+ return;
30308
+ }
30295
30309
  const {
30296
30310
  direction: yDownDirection,
30297
30311
  altKey,
@@ -30392,7 +30406,8 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
30392
30406
  const {
30393
30407
  range,
30394
30408
  markings,
30395
- showTooltips
30409
+ showTooltips,
30410
+ hintMode
30396
30411
  } = useGraphConfig();
30397
30412
  const {
30398
30413
  point,
@@ -30434,7 +30449,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
30434
30449
  cursor
30435
30450
  },
30436
30451
  "data-testid": "movable-point",
30437
- tabIndex: tabIndex(focusBehavior)
30452
+ tabIndex: hintMode ? -1 : tabIndex(focusBehavior)
30438
30453
  }, /*#__PURE__*/React.createElement("circle", {
30439
30454
  className: "movable-point-hitbox",
30440
30455
  r: hitboxSizePx / 2,
@@ -30522,7 +30537,8 @@ const MovableLine = props => {
30522
30537
  };
30523
30538
  function useControlPoint(point, color, onMovePoint) {
30524
30539
  const {
30525
- snapStep
30540
+ snapStep,
30541
+ hintMode
30526
30542
  } = useGraphConfig();
30527
30543
  const [focused, setFocused] = useState(false);
30528
30544
  const keyboardHandleRef = useRef(null);
@@ -30544,7 +30560,7 @@ function useControlPoint(point, color, onMovePoint) {
30544
30560
  const focusableHandle = /*#__PURE__*/React.createElement("g", {
30545
30561
  "data-testid": "movable-point__focusable-handle",
30546
30562
  className: "movable-point__focusable-handle",
30547
- tabIndex: 0,
30563
+ tabIndex: hintMode ? -1 : 0,
30548
30564
  ref: keyboardHandleRef,
30549
30565
  onFocus: () => setFocused(true),
30550
30566
  onBlur: () => setFocused(false)
@@ -30577,7 +30593,8 @@ const Line$1 = props => {
30577
30593
  const {
30578
30594
  range,
30579
30595
  graphDimensionsInPixels,
30580
- snapStep
30596
+ snapStep,
30597
+ hintMode
30581
30598
  } = useGraphConfig();
30582
30599
  let startExtend = undefined;
30583
30600
  let endExtend = undefined;
@@ -30599,7 +30616,7 @@ const Line$1 = props => {
30599
30616
  });
30600
30617
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("g", {
30601
30618
  ref: line,
30602
- tabIndex: 0,
30619
+ tabIndex: hintMode ? -1 : 0,
30603
30620
  className: "movable-line",
30604
30621
  "data-testid": "movable-line",
30605
30622
  style: {
@@ -31110,6 +31127,9 @@ const PolygonGraph = props => {
31110
31127
  snapStep,
31111
31128
  snapTo
31112
31129
  } = props.graphState;
31130
+ const {
31131
+ hintMode
31132
+ } = useGraphConfig();
31113
31133
 
31114
31134
  // TODO(benchristel): can the default set of points be removed here? I don't
31115
31135
  // think coords can be null.
@@ -31129,12 +31149,16 @@ const PolygonGraph = props => {
31129
31149
  constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
31130
31150
  });
31131
31151
  const active = hovered || focused || dragging;
31152
+ const lastMoveTime = React.useRef(0);
31132
31153
  const lines = getLines(points);
31133
31154
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Polygon, {
31134
31155
  points: [...points],
31135
31156
  color: "var(--movable-line-stroke-color)",
31136
31157
  svgPolygonProps: {
31137
- strokeWidth: active ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)"
31158
+ strokeWidth: active ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)",
31159
+ style: {
31160
+ fill: "transparent"
31161
+ }
31138
31162
  }
31139
31163
  }), points.map((point, i) => {
31140
31164
  const pt1 = points.at(i - 1);
@@ -31165,10 +31189,11 @@ const PolygonGraph = props => {
31165
31189
  color: "transparent",
31166
31190
  svgPolygonProps: {
31167
31191
  ref,
31168
- tabIndex: 0,
31192
+ tabIndex: hintMode ? -1 : 0,
31169
31193
  strokeWidth: TARGET_SIZE,
31170
31194
  style: {
31171
- cursor: dragging ? "grabbing" : "grab"
31195
+ cursor: dragging ? "grabbing" : "grab",
31196
+ fill: hovered ? "var(--mafs-blue)" : "transparent"
31172
31197
  },
31173
31198
  onFocus: () => setFocused(true),
31174
31199
  onBlur: () => setFocused(false),
@@ -31180,7 +31205,15 @@ const PolygonGraph = props => {
31180
31205
  key: "point-" + i,
31181
31206
  snapTo: snapTo,
31182
31207
  point: point,
31183
- onMove: destination => dispatch(actions.polygon.movePoint(i, destination))
31208
+ onMove: destination => {
31209
+ const now = Date.now();
31210
+ const targetFPS = 40;
31211
+ const moveThresholdTime = 1000 / targetFPS;
31212
+ if (now - lastMoveTime.current > moveThresholdTime) {
31213
+ dispatch(actions.polygon.movePoint(i, destination));
31214
+ lastMoveTime.current = now;
31215
+ }
31216
+ }
31184
31217
  })));
31185
31218
  };
31186
31219
  function getLines(points) {
@@ -31289,7 +31322,8 @@ function MovableCircle(props) {
31289
31322
  onMove
31290
31323
  } = props;
31291
31324
  const {
31292
- snapStep
31325
+ snapStep,
31326
+ hintMode
31293
31327
  } = useGraphConfig();
31294
31328
  const draggableRef = useRef(null);
31295
31329
  const {
@@ -31304,7 +31338,7 @@ function MovableCircle(props) {
31304
31338
  const [radiiPx] = useTransformDimensionsToPixels([radius, radius]);
31305
31339
  return /*#__PURE__*/React.createElement("g", {
31306
31340
  ref: draggableRef,
31307
- tabIndex: 0,
31341
+ tabIndex: hintMode ? -1 : 0,
31308
31342
  className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`
31309
31343
  }, /*#__PURE__*/React.createElement("ellipse", {
31310
31344
  className: "focus-ring",
@@ -31707,7 +31741,8 @@ const MafsGraph = props => {
31707
31741
  const {
31708
31742
  state,
31709
31743
  dispatch,
31710
- labels
31744
+ labels,
31745
+ hintMode
31711
31746
  } = props;
31712
31747
  const [width, height] = props.box;
31713
31748
  const tickStep = props.step;
@@ -31722,7 +31757,8 @@ const MafsGraph = props => {
31722
31757
  graphDimensionsInPixels: props.box,
31723
31758
  width,
31724
31759
  height,
31725
- labels
31760
+ labels,
31761
+ hintMode
31726
31762
  }
31727
31763
  }, /*#__PURE__*/React.createElement(View, {
31728
31764
  className: "mafs-graph",
@@ -31733,7 +31769,8 @@ const MafsGraph = props => {
31733
31769
  padding: "25px 25px 0 0",
31734
31770
  boxSizing: "content-box",
31735
31771
  marginLeft: "20px",
31736
- marginBottom: "20px"
31772
+ marginBottom: "20px",
31773
+ pointerEvents: hintMode ? "none" : "auto"
31737
31774
  }
31738
31775
  }, /*#__PURE__*/React.createElement(LegacyGrid, {
31739
31776
  box: props.box,
@@ -31764,7 +31801,7 @@ const MafsGraph = props => {
31764
31801
  }), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
31765
31802
  lockedFigures: props.lockedFigures,
31766
31803
  range: state.range
31767
- }), "/* Protractor */", props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
31804
+ }), props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
31768
31805
  state,
31769
31806
  dispatch
31770
31807
  })))));
@@ -32793,7 +32830,6 @@ function mafsStateToInteractiveGraph(state) {
32793
32830
  return _extends({}, state);
32794
32831
  }
32795
32832
  const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
32796
- var _graph$startCoords;
32797
32833
  const {
32798
32834
  onChange,
32799
32835
  graph
@@ -32828,7 +32864,6 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
32828
32864
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
32829
32865
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
32830
32866
  const showSides = graph.type === "polygon" ? graph.showSides : null;
32831
- const startCoords = (_graph$startCoords = graph.startCoords) != null ? _graph$startCoords : null;
32832
32867
  const originalPropsRef = useRef(props);
32833
32868
  const latestPropsRef = useLatestRef(props);
32834
32869
  useEffect(() => {
@@ -32839,7 +32874,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
32839
32874
  if (latestPropsRef.current !== originalPropsRef.current) {
32840
32875
  dispatch(reinitialize(latestPropsRef.current));
32841
32876
  }
32842
- }, [graph.type, numSegments, numSides, snapTo, showAngles, showSides, startCoords, latestPropsRef, startCoords]);
32877
+ }, [graph.type, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, graph.startCoords]);
32843
32878
  return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, props, {
32844
32879
  state: state,
32845
32880
  dispatch: dispatch
@@ -34100,9 +34135,6 @@ class LegacyInteractiveGraph extends React.Component {
34100
34135
  markings: this.props.markings,
34101
34136
  backgroundImage: this.props.backgroundImage,
34102
34137
  showProtractor: this.props.showProtractor,
34103
- showRuler: this.props.showRuler,
34104
- rulerLabel: this.props.rulerLabel,
34105
- rulerTicks: this.props.rulerTicks,
34106
34138
  onMouseDown: onMouseDown,
34107
34139
  onGraphieUpdated: this.setGraphie,
34108
34140
  setDrawingAreaAvailable: this.props.apiOptions.setDrawingAreaAvailable,
@@ -34119,9 +34151,6 @@ LegacyInteractiveGraph.defaultProps = {
34119
34151
  markings: "graph",
34120
34152
  showTooltips: false,
34121
34153
  showProtractor: false,
34122
- showRuler: false,
34123
- rulerLabel: "",
34124
- rulerTicks: 10,
34125
34154
  graph: {
34126
34155
  type: "linear"
34127
34156
  }
@@ -34159,7 +34188,8 @@ class InteractiveGraph extends React.Component {
34159
34188
  gridStep: gridStep,
34160
34189
  snapStep: snapStep,
34161
34190
  box: box,
34162
- showTooltips: !!this.props.showTooltips
34191
+ showTooltips: !!this.props.showTooltips,
34192
+ hintMode: this.props.hintMode
34163
34193
  }));
34164
34194
  }
34165
34195
  return /*#__PURE__*/React.createElement(LegacyInteractiveGraph, _extends({
@@ -34719,9 +34749,6 @@ InteractiveGraph.defaultProps = {
34719
34749
  markings: "graph",
34720
34750
  showTooltips: false,
34721
34751
  showProtractor: false,
34722
- showRuler: false,
34723
- rulerLabel: "",
34724
- rulerTicks: 10,
34725
34752
  graph: {
34726
34753
  type: "linear"
34727
34754
  }
@@ -43850,914 +43877,6 @@ var Table$1 = {
43850
43877
  isLintable: true
43851
43878
  };
43852
43879
 
43853
- var isObject = function isObject(obj) {
43854
- return obj === Object(obj);
43855
- };
43856
- var merge = function merge() {
43857
- var obj = {};
43858
- for (var i = 0; i < arguments.length; i++) {
43859
- var source = arguments[i];
43860
- if (source) {
43861
- for (var prop in source) {
43862
- obj[prop] = source[prop];
43863
- }
43864
- }
43865
- }
43866
- return obj;
43867
- };
43868
- var clone = function clone(obj) {
43869
- if (!isObject(obj)) {
43870
- return obj;
43871
- }
43872
- return Array.isArray(obj) ? obj.slice() : merge(obj);
43873
- };
43874
-
43875
- /* TODO batch *all* mutations
43876
- * idea: freeze / thaw implementations for all types
43877
- * lens constructor thaws, freeze delegates to type's freeze
43878
- */
43879
-
43880
- // equivalents, without requiring it
43881
- // find the implementation to use for a given object
43882
- var dispatch = function dispatch(x) {
43883
- if (Array.isArray(x)) {
43884
- return arr;
43885
- } else if (isObject(x)) {
43886
- return obj;
43887
- } else if (typeof x === "string") {
43888
- return str;
43889
- }
43890
- };
43891
-
43892
- // This is underscore with a different name
43893
- var lens = function lens(obj) {
43894
- if (obj instanceof lens) {
43895
- return obj;
43896
- }
43897
- if (!(this instanceof lens)) {
43898
- return new lens(obj);
43899
- }
43900
- var ops = dispatch(obj);
43901
- this._wrapped = ops.thaw ? ops.thaw(obj) : obj;
43902
- };
43903
- lens.prototype.freeze = function () {
43904
- var obj = this._wrapped;
43905
- var ops = dispatch(obj);
43906
- return ops.freeze ? ops.freeze(obj) : obj;
43907
- };
43908
- lens.prototype.zoom = function (lensArr) {
43909
- if (this._zoomStack === undefined) {
43910
- this._zoomStack = [];
43911
- }
43912
- this._zoomStack.push({
43913
- zoom: lensArr,
43914
- wrapped: this._wrapped
43915
- });
43916
- this._wrapped = lens(this._wrapped).get(lensArr);
43917
- return this;
43918
- };
43919
- lens.prototype.deZoom = function () {
43920
- var frame = this._zoomStack.pop();
43921
- this._wrapped = lens(frame.wrapped).set(frame.zoom, this._wrapped).freeze();
43922
- return this;
43923
- };
43924
- lens.prototype.get = function (lensArr) {
43925
- var obj = this._wrapped;
43926
- for (var i = 0; i < lensArr.length; i++) {
43927
- obj = dispatch(obj).get(obj, lensArr[i]);
43928
- }
43929
- return obj;
43930
- };
43931
- lens.prototype.mod = function (lensArr, f) {
43932
- var obj = this._wrapped;
43933
- var newObj = clone(obj);
43934
- var ops = dispatch(obj);
43935
- if (lensArr.length === 0) {
43936
- this._wrapped = f(this._wrapped);
43937
- } else if (lensArr.length === 1) {
43938
- this._wrapped = ops.mod(newObj, lensArr[0], f);
43939
- } else {
43940
- var monocle = lensArr[0];
43941
- var shortLens = lensArr.slice(1);
43942
-
43943
- // newObj = ops.mod(obj[monocle], shortLens, f);
43944
-
43945
- newObj[monocle] = lens(obj[monocle]).mod(shortLens, f).freeze();
43946
- this._wrapped = newObj;
43947
- }
43948
- return this;
43949
- };
43950
-
43951
- // TODO - move to individual files
43952
- lens.prototype.merge = function (lensArr, props) {
43953
- this._wrapped = lens(this._wrapped).mod(lensArr, function (oldProps) {
43954
- return merge(oldProps, props);
43955
- }).freeze();
43956
- return this;
43957
- };
43958
-
43959
- // Lens must have length >= 1 or there would be nothing to return
43960
- lens.prototype.del = function (lensArr) {
43961
- var obj = this._wrapped;
43962
- var ops = dispatch(obj);
43963
- if (lensArr.length === 1) {
43964
- this._wrapped = ops.del(obj, lensArr[0]);
43965
- } else {
43966
- var monocle = lensArr[0];
43967
- var shortLens = lensArr.slice(1);
43968
- var newObj = clone(obj);
43969
- newObj[monocle] = lens(obj[monocle]).del(shortLens).freeze();
43970
- this._wrapped = newObj;
43971
- }
43972
- return this;
43973
- };
43974
- lens.prototype.set = function (lensArr, set) {
43975
- return this.mod(lensArr, function () {
43976
- return set;
43977
- });
43978
- };
43979
-
43980
- // ARRAY FUNCTIONS
43981
-
43982
- const arr = {
43983
- get: function (arr, monocle) {
43984
- return arr[monocle];
43985
- },
43986
- set: function (arr, monocle, val) {
43987
- var newArr = arr.splice();
43988
- newArr[monocle] = val;
43989
- return newArr;
43990
- },
43991
- mod: function (arr, monocle, f) {
43992
- var newArr = arr.slice();
43993
- newArr[monocle] = f(arr[monocle]);
43994
- return newArr;
43995
- },
43996
- del: function (arr, monocle) {
43997
- var newArr = arr.slice();
43998
- newArr.splice(monocle, 1);
43999
- return newArr;
44000
- }
44001
- };
44002
-
44003
- /*
44004
- // Lens must point to a member of an array. We'll insert into that array.
44005
- lens.prototype.insertAt = function(lensArr, toInsert) {
44006
- var obj = this._wrapped;
44007
-
44008
- var arrLens = lensArr.slice(0, -1);
44009
- var arr = lens(obj).get(arrLens).slice(); // slice to copy
44010
-
44011
- var arrIdx = lensArr[lensArr.length-1];
44012
- arr.splice(arrIdx, 0, toInsert);
44013
- return lens(obj).set(arrLens, arr);
44014
- };
44015
-
44016
- lens.prototype.insertBefore = lens.prototype.insertAt;
44017
- lens.prototype.insertAfter = function(lensArr, toInsert) {
44018
- var newLens = lensArr.slice();
44019
- newLens[newLens.length-1] += 1;
44020
- return lens(this._wrapped).insertAt(newLens, toInsert);
44021
- };
44022
- */
44023
-
44024
- // OBJECT FUNCTIONS
44025
-
44026
- const obj = {
44027
- get: function (obj, monocle) {
44028
- return obj[monocle];
44029
- },
44030
- set: function (obj, monocle, val) {
44031
- var newObj = clone(obj);
44032
- newObj[monocle] = val;
44033
- return newObj;
44034
- },
44035
- mod: function (obj, monocle, f) {
44036
- var newObj = clone(obj);
44037
- newObj[monocle] = f(obj[monocle]);
44038
- return newObj;
44039
- },
44040
- del: function (obj, monocle) {
44041
- var newObj = clone(obj);
44042
- delete newObj[monocle];
44043
- return newObj;
44044
- }
44045
- };
44046
-
44047
- // STRING FUNCTIONS
44048
-
44049
- const str = {
44050
- get: function (arr, monocle) {
44051
- return arr[monocle];
44052
- },
44053
- set: function (arr, monocle, val) {
44054
- var newArr = arr.splice();
44055
- newArr[monocle] = val;
44056
- return newArr;
44057
- },
44058
- mod: function (arr, monocle, f) {
44059
- var newArr = arr.splice();
44060
- newArr[monocle] = f(arr[monocle]);
44061
- return newArr;
44062
- },
44063
- del: function (arr, monocle) {
44064
- var newArr = arr.slice();
44065
- newArr.splice(monocle);
44066
- return newArr;
44067
- }
44068
- };
44069
-
44070
- /* eslint-disable eqeqeq, @typescript-eslint/no-unused-vars */
44071
- /*
44072
- * A class for formatting number to significant digits.
44073
- * Copyright (C) 2001 Stephen Ostermiller
44074
- * http://ostermiller.org/contact.pl?regarding?JavaScript+Significant+Figures
44075
- *
44076
- * This program is free software; you can redistribute it and/or modify
44077
- * it under the terms of the GNU General Public License as published by
44078
- * the Free Software Foundation; either version 2 of the License, or
44079
- * (at your option) any later version.
44080
- *
44081
- * This program is distributed in the hope that it will be useful,
44082
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
44083
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44084
- * GNU General Public License for more details.
44085
- */
44086
-
44087
- /**
44088
- * An object tho both scans floating points to
44089
- * determine the number of significant figures.
44090
- * and can display a floating point using any number
44091
- * of significant figures.
44092
- *
44093
- * @param s A string representation of a floating point.
44094
- */
44095
- class SignificantFigures {
44096
- constructor(s) {
44097
- this.order = void 0;
44098
- this.mantissa = void 0;
44099
- this.positive = void 0;
44100
- this.sigFigs = void 0;
44101
- this.sigDecs = void 0;
44102
- this.order = parseOrder(s);
44103
- this.mantissa = parseMantissa(s);
44104
- this.positive = parseSign(s);
44105
-
44106
- /**
44107
- * Get the number of significant figures this object uses.
44108
- * Leading zeros are not significant. Traling zeros up to
44109
- * and after the decimal point are significant.
44110
- * Significant figures is good to know when the number is
44111
- * used in multiplication.
44112
- *
44113
- * @return the number of significant figures.
44114
- */
44115
- this.sigFigs = this.mantissa.length;
44116
-
44117
- /**
44118
- * Get the least significant decimal this object uses.
44119
- * This is useful to know if a number is being used
44120
- * in addition.
44121
- * 400 - 2 the hundreds place (10^2) is the least significant decimal.
44122
- * 75 - 0 the ones place (10^0) is the least significant decimal.
44123
- * .543 - -3 the 1/1000's place (10^-3) is the least significant decimal.
44124
- *
44125
- * @return an integer representing the least significant decimal place.
44126
- */
44127
- this.sigDecs = this.order - this.mantissa.length;
44128
- }
44129
- }
44130
-
44131
- /**
44132
- * Format a floating point for display using the specified
44133
- * number of significant figures and least significant decimal.
44134
- * Scientific notation may used by this method if this
44135
- * object is very small, very large, has many significant
44136
- * figures, or the number of significant figures would be
44137
- * ambiguous in the output if scientific notation were not
44138
- * used.
44139
- *
44140
- * @param f A floating point number that should be displayed
44141
- * @param sigFigs desired number of significant figures (integer).
44142
- * @param sigDecs the least significant decimal place (integer).
44143
- * @param scientific true iff scientific notation should always be used.
44144
- * @return a string of this object formatted correctly.
44145
- */
44146
- function displaySigFigs(f, sigFigs, sigDecs, scientific) {
44147
- const s = "" + f;
44148
- let order = parseOrder(s);
44149
- let mantissa = parseMantissa(s);
44150
- let positive = parseSign(s);
44151
- let add;
44152
- let decAdd;
44153
- let sigAdd;
44154
- let zeroScientific = false;
44155
- if (f == 0 || mantissa == "" || mantissa == "0") {
44156
- mantissa = "";
44157
- for (let i = 0; i < sigFigs; i++) {
44158
- mantissa += "0";
44159
- }
44160
- order = sigFigs + sigDecs;
44161
- if (sigDecs < 0 && -sigDecs >= sigFigs) {
44162
- zeroScientific = true;
44163
- }
44164
- } else {
44165
- decAdd = order - mantissa.length - sigDecs;
44166
- sigAdd = sigFigs - mantissa.length;
44167
- add = Math.min(sigAdd, decAdd);
44168
- if (add < 0) {
44169
- let rounded = round(mantissa, -add);
44170
- if (rounded.length > mantissa.length + add) {
44171
- order++;
44172
- if (decAdd > sigAdd) {
44173
- rounded = round(rounded, 1);
44174
- }
44175
- }
44176
- mantissa = rounded;
44177
- } else if (add > 0) {
44178
- for (let i = 0; i < add; i++) {
44179
- mantissa += "0";
44180
- }
44181
- }
44182
- if (mantissa == "" || mantissa == "0") {
44183
- mantissa = "0";
44184
- positive = true;
44185
- order = 1 + sigDecs;
44186
- if (order != 0) {
44187
- zeroScientific = true;
44188
- }
44189
- }
44190
- }
44191
- const useScientific = scientific || mantissa.length > 20 || order > 4 || order < -2 || order - mantissa.length > 0 && trailingZeros(mantissa) > 0 || zeroScientific;
44192
- let returnVal = "";
44193
- if (!positive) {
44194
- returnVal += "-";
44195
- }
44196
- if (useScientific) {
44197
- returnVal += mantissa.charAt(0);
44198
- if (mantissa.length > 1) {
44199
- returnVal += "." + mantissa.substring(1, mantissa.length);
44200
- }
44201
- if (order - 1 != 0) {
44202
- returnVal += " x 10^" + (order - 1);
44203
- }
44204
- } else {
44205
- let wholePart = "";
44206
- let fractPart = "";
44207
- let needDot = true;
44208
- if (order > 0) {
44209
- if (mantissa.length > order) {
44210
- wholePart = mantissa.substring(0, order);
44211
- fractPart = mantissa.substring(order, mantissa.length);
44212
- } else {
44213
- wholePart = mantissa;
44214
- needDot = trailingZeros(mantissa) != 0;
44215
- for (let i = 0; i < order - mantissa.length; i++) {
44216
- wholePart += "0";
44217
- }
44218
- }
44219
- } else {
44220
- for (let i = 0; i < -order; i++) {
44221
- fractPart += "0";
44222
- }
44223
- fractPart += mantissa;
44224
- }
44225
- returnVal += (wholePart == "" ? "0" : wholePart) + (needDot ? "." : "") + fractPart;
44226
- }
44227
- return returnVal;
44228
- }
44229
-
44230
- /**
44231
- * Count the significant trailing zeros on this object.
44232
- *
44233
- * @return the number of trailing zeros
44234
- */
44235
- function trailingZeros(mantissa) {
44236
- let zeros = 0;
44237
- for (let i = mantissa.length - 1; i >= 0; i--) {
44238
- const c = mantissa.charAt(i);
44239
- if (c == "0") {
44240
- zeros++;
44241
- } else {
44242
- return zeros;
44243
- }
44244
- }
44245
- return zeros;
44246
- }
44247
-
44248
- /**
44249
- * Parse a string representation of a floating point
44250
- * and pull out the sign.
44251
- *
44252
- * @param s the string representation of a floating point.
44253
- * @return true iff this is a positive number
44254
- */
44255
- function parseSign(s) {
44256
- let beginning = true;
44257
- let seenDot = false;
44258
- let seenSomething = false;
44259
- let zeros = "";
44260
- let all = "";
44261
- let pos = true;
44262
- for (let i = 0; i < s.length; i++) {
44263
- const c = s.charAt(i);
44264
- if (c >= "1" && c <= "9") {
44265
- all += zeros + c;
44266
- zeros = "";
44267
- seenSomething = true;
44268
- beginning = false;
44269
- } else if (c == "0") {
44270
- if (seenDot) {
44271
- if (seenSomething) {
44272
- all += zeros + c;
44273
- zeros = "";
44274
- }
44275
- } else {
44276
- if (seenSomething) {
44277
- zeros += c;
44278
- }
44279
- }
44280
- beginning = false;
44281
- } else if (!seenDot && c == ".") {
44282
- all += zeros;
44283
- zeros = "";
44284
- seenDot = true;
44285
- beginning = false;
44286
- } else if (c == "e" || c == "E" && i + 1 < s.length) {
44287
- parseInt(s.substring(i + 1, s.length));
44288
- i = s.length;
44289
- } else if (beginning && (c == "+" || c == "-")) {
44290
- if (c == "-") {
44291
- pos = !pos;
44292
- }
44293
- }
44294
- }
44295
- if (all == "") {
44296
- return true;
44297
- }
44298
- return pos;
44299
- }
44300
-
44301
- /**
44302
- * Parse a string representation of a floating point
44303
- * and pull out the mantissa.
44304
- *
44305
- * @param s the string representation of a floating point.
44306
- * @return the mantissa of this number.
44307
- */
44308
- function parseMantissa(s) {
44309
- let seenDot = false;
44310
- let seenSomething = false;
44311
- let zeros = "";
44312
- let leadZeros = "";
44313
- let all = "";
44314
- for (let i = 0; i < s.length; i++) {
44315
- const c = s.charAt(i);
44316
- if (c >= "1" && c <= "9") {
44317
- all += zeros + c;
44318
- zeros = "";
44319
- seenSomething = true;
44320
- } else if (c == "0") {
44321
- if (seenDot) {
44322
- if (seenSomething) {
44323
- all += zeros + c;
44324
- zeros = "";
44325
- } else {
44326
- leadZeros += c;
44327
- }
44328
- } else {
44329
- if (seenSomething) {
44330
- leadZeros += c;
44331
- zeros += c;
44332
- } else {
44333
- leadZeros += c;
44334
- }
44335
- }
44336
- } else if (!seenDot && c == ".") {
44337
- all += zeros;
44338
- zeros = "";
44339
- seenDot = true;
44340
- } else if (c == "e" || c == "E" && i + 1 < s.length) {
44341
- parseInt(s.substring(i + 1, s.length));
44342
- i = s.length;
44343
- } else ;
44344
- }
44345
- if (all == "") {
44346
- return leadZeros;
44347
- }
44348
- return all;
44349
- }
44350
-
44351
- /**
44352
- * Parse a string representation of a floating point
44353
- * and pull out the exponent.
44354
- *
44355
- * @param s the string representation of a floating point.
44356
- * @return (integer) the number after the e.
44357
- */
44358
- function parseOrder(s) {
44359
- let seenDot = false;
44360
- let seenSomething = false;
44361
- let zeros = "";
44362
- let all = "";
44363
- let decPlaces = 0;
44364
- let totalDecs = 0;
44365
- for (let i = 0; i < s.length; i++) {
44366
- const c = s.charAt(i);
44367
- if (c >= "1" && c <= "9") {
44368
- all += zeros + c;
44369
- zeros = "";
44370
- seenSomething = true;
44371
- if (!seenDot) {
44372
- totalDecs++;
44373
- decPlaces++;
44374
- }
44375
- } else if (c == "0") {
44376
- if (seenDot) {
44377
- if (seenSomething) {
44378
- all += zeros + c;
44379
- zeros = "";
44380
- } else {
44381
- decPlaces--;
44382
- }
44383
- } else {
44384
- totalDecs++;
44385
- if (seenSomething) {
44386
- decPlaces++;
44387
- zeros += c;
44388
- }
44389
- }
44390
- } else if (!seenDot && c == ".") {
44391
- all += zeros;
44392
- zeros = "";
44393
- seenDot = true;
44394
- } else if (c == "e" || c == "E" && i + 1 < s.length) {
44395
- const raised = parseInt(s.substring(i + 1, s.length));
44396
- decPlaces += raised;
44397
- totalDecs += raised;
44398
- i = s.length;
44399
- } else ;
44400
- }
44401
- if (all == "") {
44402
- return totalDecs;
44403
- }
44404
- return decPlaces;
44405
- }
44406
-
44407
- /**
44408
- * Remove the specified number of digits from string by
44409
- * rounding. Proper rounding rules for scientific purposes
44410
- * are followed.
44411
- * This method may cause an extra significant figure
44412
- * to be added to the number. For example, if 999999
44413
- * were rounded, A one would carry over and become
44414
- * a significant figure. Those who call this method
44415
- * should check for this and call round again if needed.
44416
- *
44417
- * @param mantissa A string representing an whole number of arbitrary length.
44418
- * @param digits A number of digits to remove
44419
- * @return A string represted the rounded version of mantissa
44420
- */
44421
- function round(mantissa, digits) {
44422
- const last = mantissa.length - digits - 1;
44423
- if (last < 0) {
44424
- return "";
44425
- }
44426
- if (last >= mantissa.length - 1) {
44427
- return mantissa;
44428
- }
44429
- const nextToLast = mantissa.charAt(last + 1);
44430
- const lastChar = mantissa.charAt(last);
44431
- let roundUp = false;
44432
- if (nextToLast > "5") {
44433
- roundUp = true;
44434
- } else if (nextToLast == "5") {
44435
- for (let j = last + 2; j < mantissa.length; j++) {
44436
- if (mantissa.charAt(j) != "0") {
44437
- roundUp = true;
44438
- }
44439
- }
44440
- // @ts-expect-error - TS2362 - The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
44441
- if (lastChar % 2 == 1) {
44442
- roundUp = true;
44443
- }
44444
- }
44445
- let result = "";
44446
- for (let i = last; i >= 0; i--) {
44447
- const c = mantissa.charAt(i);
44448
- if (roundUp) {
44449
- let nextChar;
44450
- if (c == "9") {
44451
- nextChar = "0";
44452
- } else {
44453
- switch (c) {
44454
- case "0":
44455
- nextChar = "1";
44456
- break;
44457
- case "1":
44458
- nextChar = "2";
44459
- break;
44460
- case "2":
44461
- nextChar = "3";
44462
- break;
44463
- case "3":
44464
- nextChar = "4";
44465
- break;
44466
- case "4":
44467
- nextChar = "5";
44468
- break;
44469
- case "5":
44470
- nextChar = "6";
44471
- break;
44472
- case "6":
44473
- nextChar = "7";
44474
- break;
44475
- case "7":
44476
- nextChar = "8";
44477
- break;
44478
- case "8":
44479
- nextChar = "9";
44480
- break;
44481
- }
44482
- roundUp = false;
44483
- }
44484
- result = nextChar + result;
44485
- } else {
44486
- result = c + result;
44487
- }
44488
- }
44489
- if (roundUp) {
44490
- result = "1" + result;
44491
- }
44492
- return result;
44493
- }
44494
-
44495
- // TODO(joel): teach KAS how to accept an answer only if it's expressed in
44496
- const ALL = "all";
44497
- const countSigfigs = function countSigfigs(value) {
44498
- return new SignificantFigures(value).sigFigs;
44499
- };
44500
- /* I just wrote this, but it's old by analogy to `OldExpression`, in that it's
44501
- * the version that non-mathquill platforms get stuck with. Constructed with an
44502
- * <input>, a parser, popsicle sticks, and glue.
44503
- *
44504
- * In the same way as OldExpression, this parses continuously as you type, then
44505
- * shows and hides an error buddy. The error message is only shown after a
44506
- * rolling two second delay, but hidden immediately on further typing.
44507
- */
44508
- class OldUnitInput extends React.Component {
44509
- constructor(..._args) {
44510
- super(..._args);
44511
- this._errorTimeout = void 0;
44512
- this._showError = () => {
44513
- if (this.props.value === "") {
44514
- return;
44515
- }
44516
-
44517
- // eslint-disable-next-line react/no-string-refs
44518
- // @ts-expect-error - TS2769 - No overload matches this call.
44519
- const $error = $(ReactDOM__default.findDOMNode(this.refs.error));
44520
- // @ts-expect-error - TS2339 - Property 'is' does not exist on type 'JQueryStatic'.
44521
- if (!$error.is(":visible")) {
44522
- $error
44523
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
44524
- .css({
44525
- top: 50,
44526
- opacity: 0.1
44527
- }).show().animate({
44528
- top: 0,
44529
- opacity: 1.0
44530
- }, 300);
44531
- }
44532
- };
44533
- this._hideError = () => {
44534
- // eslint-disable-next-line react/no-string-refs
44535
- // @ts-expect-error - TS2769 - No overload matches this call.
44536
- const $error = $(ReactDOM__default.findDOMNode(this.refs.error));
44537
- // @ts-expect-error - TS2339 - Property 'is' does not exist on type 'JQueryStatic'.
44538
- if ($error.is(":visible")) {
44539
- // @ts-expect-error - TS2339 - Property 'animate' does not exist on type 'JQueryStatic'.
44540
- $error.animate({
44541
- top: 50,
44542
- opacity: 0.1
44543
- }, 300, function () {
44544
- // eslint-disable-next-line @babel/no-invalid-this
44545
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
44546
- // eslint-disable-next-line @babel/no-invalid-this
44547
- $(this).hide();
44548
- });
44549
- }
44550
- };
44551
- this.change = (...args) => {
44552
- // @ts-expect-error - TS2345 - Argument of type 'readonly any[]' is not assignable to parameter of type 'any[]'.
44553
- return change.apply(this, args);
44554
- };
44555
- this.handleBlur = () => {
44556
- this.props.onBlur([]);
44557
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
44558
- // eslint-disable-next-line no-restricted-syntax
44559
- // @ts-expect-error - TS2769 - No overload matches this call.
44560
- clearTimeout(this._errorTimeout);
44561
- if (!KAS.unitParse(this.props.value).parsed) {
44562
- this._showError();
44563
- }
44564
- };
44565
- this.handleChange = event => {
44566
- this._hideError();
44567
- this.props.onChange({
44568
- value: event.target.value
44569
- });
44570
- };
44571
- this.simpleValidate = (rubric, onInputError) => {
44572
- return OldUnitInput.validate(this.getUserInput(), rubric, this.context.strings);
44573
- };
44574
- this.getUserInput = () => {
44575
- return this.props.value;
44576
- };
44577
- // begin mobile stuff
44578
- this.getInputPaths = () => {
44579
- // The widget itself is an input, so we return a single empty list to
44580
- // indicate this.
44581
- return [[]];
44582
- };
44583
- this.focusInputPath = inputPath => {
44584
- // eslint-disable-next-line react/no-string-refs
44585
- const input = ReactDOM__default.findDOMNode(this.refs.input);
44586
- if (input) {
44587
- /**
44588
- * TODO(somewhatabstract, JIRA-XXXX):
44589
- * Change to using a ref callback so that focus() can be
44590
- * accessed.
44591
- */
44592
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element | Text'.
44593
- input.focus();
44594
- }
44595
- };
44596
- this.handleFocus = () => {
44597
- this.props.onFocus([]);
44598
- };
44599
- this.blurInputPath = inputPath => {
44600
- // eslint-disable-next-line react/no-string-refs
44601
- const input = ReactDOM__default.findDOMNode(this.refs.input);
44602
- if (input) {
44603
- /**
44604
- * TODO(somewhatabstract, JIRA-XXXX):
44605
- * Change to using a ref callback so that focus() can be
44606
- * accessed.
44607
- */
44608
- // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'Element | Text'.
44609
- input.blur();
44610
- }
44611
- };
44612
- this.setInputValue = (path, newValue, cb) => {
44613
- this.props.onChange({
44614
- value: newValue
44615
- }, cb);
44616
- };
44617
- this.getDOMNodeForPath = () => {
44618
- // eslint-disable-next-line react/no-string-refs
44619
- return ReactDOM__default.findDOMNode(this.refs.input);
44620
- };
44621
- this.getGrammarTypeForPath = inputPath => {
44622
- return "unit";
44623
- };
44624
- }
44625
- static validate(userInput, rubric, strings) {
44626
- const answer = KAS.unitParse(rubric.value).expr;
44627
- const guess = KAS.unitParse(userInput);
44628
- if (!guess.parsed) {
44629
- return {
44630
- type: "invalid",
44631
- message: strings.dontUnderstandUnits
44632
- };
44633
- }
44634
-
44635
- // Note: we check sigfigs, then numerical correctness, then units, so
44636
- // the most significant things come last, that way the user will see
44637
- // the most important message.
44638
- let message = null;
44639
-
44640
- // did the user specify the right number of sigfigs?
44641
- // TODO(joel) - add a grading mode where the wrong number of sigfigs
44642
- // isn't marked wrong
44643
- const sigfigs = rubric.sigfigs;
44644
- const sigfigsCorrect = countSigfigs(guess.coefficient) === sigfigs;
44645
- if (!sigfigsCorrect) {
44646
- message = strings.checkSigFigs;
44647
- }
44648
-
44649
- // now we need to check that the answer is correct to the precision we
44650
- // require.
44651
- let numericallyCorrect;
44652
- try {
44653
- const x = new KAS.Var("x");
44654
- const equality = new KAS.Eq(answer.simplify(), "=", new KAS.Mul(x, guess.expr.simplify()));
44655
- const conversion = equality.solveLinearEquationForVariable(x);
44656
-
44657
- // Make sure the conversion factor between the user's input answer
44658
- // and the canonical answer is 1, to sigfig places.
44659
- // TODO(joel) is this sound?
44660
- numericallyCorrect = Number(conversion.eval()).toPrecision(sigfigs) === Number(1).toPrecision(sigfigs);
44661
- } catch (e) {
44662
- numericallyCorrect = false;
44663
- }
44664
- if (!numericallyCorrect) {
44665
- message = strings.answerNumericallyIncorrect;
44666
- }
44667
- let kasCorrect;
44668
- const guessUnit = primUnits(guess.expr.simplify());
44669
- const answerUnit = primUnits(answer.simplify());
44670
- if (rubric.accepting === ALL) {
44671
- // We're accepting all units - KAS does the hard work of figuring
44672
- // out if the user's unit is equivalent to the author's unit.
44673
- kasCorrect = KAS.compare(guessUnit, answerUnit).equal;
44674
- } else {
44675
- // Are any of the accepted units the same as what the user entered?
44676
- kasCorrect = _(rubric.acceptingUnits).any(unit => {
44677
- const thisAnswerUnit = primUnits(KAS.unitParse(unit).unit.simplify());
44678
- return KAS.compare(thisAnswerUnit, guessUnit
44679
- // TODO(joel) - make this work as intended.
44680
- // { form: true }
44681
- ).equal;
44682
- });
44683
- }
44684
- if (!kasCorrect) {
44685
- message = strings.checkUnits;
44686
- }
44687
- const correct = kasCorrect && numericallyCorrect && sigfigsCorrect;
44688
- return {
44689
- type: "points",
44690
- earned: correct ? 1 : 0,
44691
- total: 1,
44692
- message
44693
- };
44694
- }
44695
- componentDidUpdate() {
44696
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
44697
- // eslint-disable-next-line no-restricted-syntax
44698
- // @ts-expect-error - TS2769 - No overload matches this call.
44699
- clearTimeout(this._errorTimeout);
44700
- if (KAS.unitParse(this.props.value).parsed) {
44701
- this._hideError();
44702
- } else {
44703
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
44704
- // eslint-disable-next-line no-restricted-syntax
44705
- // @ts-expect-error - TS2322 - Type 'Timeout' is not assignable to type 'number'.
44706
- this._errorTimeout = setTimeout(this._showError, 2000);
44707
- }
44708
- }
44709
- componentWillUnmount() {
44710
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
44711
- // eslint-disable-next-line no-restricted-syntax
44712
- // @ts-expect-error - TS2769 - No overload matches this call.
44713
- clearTimeout(this._errorTimeout);
44714
- }
44715
- // end mobile stuff
44716
- // TODO(joel) think about showing the error buddy
44717
- render() {
44718
- return /*#__PURE__*/React.createElement("div", {
44719
- className: "old-unit-input"
44720
- }, /*#__PURE__*/React.createElement("input", {
44721
- onChange: this.handleChange,
44722
- ref: "input" // eslint-disable-line react/no-string-refs
44723
- ,
44724
- className: ClassNames.INTERACTIVE,
44725
- value: this.props.value,
44726
- onFocus: this.handleFocus,
44727
- onBlur: this.handleBlur
44728
- }), /*#__PURE__*/React.createElement("div", {
44729
- ref: "error",
44730
- className: "error",
44731
- style: {
44732
- display: "none"
44733
- }
44734
- }, this.context.strings.dontUnderstand));
44735
- }
44736
- }
44737
-
44738
- // Extract the primitive units from a unit expression. This first simplifies
44739
- // `expr` to a `Mul` like "5 kg m / s^2" then removes the first term.
44740
- OldUnitInput.contextType = PerseusI18nContext;
44741
- OldUnitInput.defaultProps = {
44742
- apiOptions: ApiOptions.defaults,
44743
- value: ""
44744
- };
44745
- const primUnits = function primUnits(expr) {
44746
- return expr.simplify().asMul().partition()[1].flatten().simplify();
44747
- };
44748
- var Unit = {
44749
- name: "unit-input",
44750
- displayName: "Unit input",
44751
- defaultAlignment: "inline-block",
44752
- widget: OldUnitInput,
44753
- transform: x => lens(x).del(["value"]).freeze(),
44754
- version: {
44755
- major: 0,
44756
- minor: 1
44757
- },
44758
- hidden: true
44759
- };
44760
-
44761
43880
  /**
44762
43881
  * This is a component that adds a link to the transcript for embedded videos in articles.
44763
43882
  */
@@ -44930,7 +44049,7 @@ var Video$1 = {
44930
44049
  };
44931
44050
 
44932
44051
  /* eslint-disable import/no-named-as-default */
44933
- var extraWidgets = [Categorizer$1, CSProgram$1, Dropdown$1, Explanation$1, Definition$1, Grapher$1, GradedGroup$1, GradedGroupSet$1, Group$1, Iframe$1, Image$1, Interactive, InteractiveGraph$1, LabelImage$1, Matrix$1, Matcher$1, Measurer$1, Molecule$1, NumberLine$1, Orderer$1, Passage$1, PassageRef$1, PassageRefTarget$1, Plotter$1, PythonProgram$1, Sorter$1, Table$1, Unit, Video$1, DeprecatedStandin$1];
44052
+ var extraWidgets = [Categorizer$1, CSProgram$1, Dropdown$1, Explanation$1, Definition$1, Grapher$1, GradedGroup$1, GradedGroupSet$1, Group$1, Iframe$1, Image$1, Interactive, InteractiveGraph$1, LabelImage$1, Matrix$1, Matcher$1, Measurer$1, Molecule$1, NumberLine$1, Orderer$1, Passage$1, PassageRef$1, PassageRefTarget$1, Plotter$1, PythonProgram$1, Sorter$1, Table$1, Video$1, DeprecatedStandin$1];
44934
44053
 
44935
44054
  /**
44936
44055
  * This should be called by all clients, specifying whether extra widgets are
@@ -44970,7 +44089,7 @@ const init = function init(options) {
44970
44089
 
44971
44090
  // This file is processed by a Rollup plugin (replace) to inject the production
44972
44091
  const libName = "@khanacademy/perseus";
44973
- const libVersion = "26.0.0";
44092
+ const libVersion = "27.0.0";
44974
44093
  addLibraryVersionToPerseusDebug(libName, libVersion);
44975
44094
 
44976
44095
  const apiVersion = {
@@ -45212,6 +44331,7 @@ class HintRenderer extends React.Component {
45212
44331
  // eslint-disable-next-line react/no-string-refs
45213
44332
  , {
45214
44333
  ref: "renderer",
44334
+ hintMode: true,
45215
44335
  widgets: hint.widgets,
45216
44336
  content: hint.content || "",
45217
44337
  images: hint.images,
@@ -46519,6 +45639,223 @@ function treeToItem(node) {
46519
45639
  };
46520
45640
  }
46521
45641
 
45642
+ var isObject = function isObject(obj) {
45643
+ return obj === Object(obj);
45644
+ };
45645
+ var merge = function merge() {
45646
+ var obj = {};
45647
+ for (var i = 0; i < arguments.length; i++) {
45648
+ var source = arguments[i];
45649
+ if (source) {
45650
+ for (var prop in source) {
45651
+ obj[prop] = source[prop];
45652
+ }
45653
+ }
45654
+ }
45655
+ return obj;
45656
+ };
45657
+ var clone = function clone(obj) {
45658
+ if (!isObject(obj)) {
45659
+ return obj;
45660
+ }
45661
+ return Array.isArray(obj) ? obj.slice() : merge(obj);
45662
+ };
45663
+
45664
+ /* TODO batch *all* mutations
45665
+ * idea: freeze / thaw implementations for all types
45666
+ * lens constructor thaws, freeze delegates to type's freeze
45667
+ */
45668
+
45669
+ // equivalents, without requiring it
45670
+ // find the implementation to use for a given object
45671
+ var dispatch = function dispatch(x) {
45672
+ if (Array.isArray(x)) {
45673
+ return arr;
45674
+ } else if (isObject(x)) {
45675
+ return obj;
45676
+ } else if (typeof x === "string") {
45677
+ return str;
45678
+ }
45679
+ };
45680
+
45681
+ // This is underscore with a different name
45682
+ var lens = function lens(obj) {
45683
+ if (obj instanceof lens) {
45684
+ return obj;
45685
+ }
45686
+ if (!(this instanceof lens)) {
45687
+ return new lens(obj);
45688
+ }
45689
+ var ops = dispatch(obj);
45690
+ this._wrapped = ops.thaw ? ops.thaw(obj) : obj;
45691
+ };
45692
+ lens.prototype.freeze = function () {
45693
+ var obj = this._wrapped;
45694
+ var ops = dispatch(obj);
45695
+ return ops.freeze ? ops.freeze(obj) : obj;
45696
+ };
45697
+ lens.prototype.zoom = function (lensArr) {
45698
+ if (this._zoomStack === undefined) {
45699
+ this._zoomStack = [];
45700
+ }
45701
+ this._zoomStack.push({
45702
+ zoom: lensArr,
45703
+ wrapped: this._wrapped
45704
+ });
45705
+ this._wrapped = lens(this._wrapped).get(lensArr);
45706
+ return this;
45707
+ };
45708
+ lens.prototype.deZoom = function () {
45709
+ var frame = this._zoomStack.pop();
45710
+ this._wrapped = lens(frame.wrapped).set(frame.zoom, this._wrapped).freeze();
45711
+ return this;
45712
+ };
45713
+ lens.prototype.get = function (lensArr) {
45714
+ var obj = this._wrapped;
45715
+ for (var i = 0; i < lensArr.length; i++) {
45716
+ obj = dispatch(obj).get(obj, lensArr[i]);
45717
+ }
45718
+ return obj;
45719
+ };
45720
+ lens.prototype.mod = function (lensArr, f) {
45721
+ var obj = this._wrapped;
45722
+ var newObj = clone(obj);
45723
+ var ops = dispatch(obj);
45724
+ if (lensArr.length === 0) {
45725
+ this._wrapped = f(this._wrapped);
45726
+ } else if (lensArr.length === 1) {
45727
+ this._wrapped = ops.mod(newObj, lensArr[0], f);
45728
+ } else {
45729
+ var monocle = lensArr[0];
45730
+ var shortLens = lensArr.slice(1);
45731
+
45732
+ // newObj = ops.mod(obj[monocle], shortLens, f);
45733
+
45734
+ newObj[monocle] = lens(obj[monocle]).mod(shortLens, f).freeze();
45735
+ this._wrapped = newObj;
45736
+ }
45737
+ return this;
45738
+ };
45739
+
45740
+ // TODO - move to individual files
45741
+ lens.prototype.merge = function (lensArr, props) {
45742
+ this._wrapped = lens(this._wrapped).mod(lensArr, function (oldProps) {
45743
+ return merge(oldProps, props);
45744
+ }).freeze();
45745
+ return this;
45746
+ };
45747
+
45748
+ // Lens must have length >= 1 or there would be nothing to return
45749
+ lens.prototype.del = function (lensArr) {
45750
+ var obj = this._wrapped;
45751
+ var ops = dispatch(obj);
45752
+ if (lensArr.length === 1) {
45753
+ this._wrapped = ops.del(obj, lensArr[0]);
45754
+ } else {
45755
+ var monocle = lensArr[0];
45756
+ var shortLens = lensArr.slice(1);
45757
+ var newObj = clone(obj);
45758
+ newObj[monocle] = lens(obj[monocle]).del(shortLens).freeze();
45759
+ this._wrapped = newObj;
45760
+ }
45761
+ return this;
45762
+ };
45763
+ lens.prototype.set = function (lensArr, set) {
45764
+ return this.mod(lensArr, function () {
45765
+ return set;
45766
+ });
45767
+ };
45768
+
45769
+ // ARRAY FUNCTIONS
45770
+
45771
+ const arr = {
45772
+ get: function (arr, monocle) {
45773
+ return arr[monocle];
45774
+ },
45775
+ set: function (arr, monocle, val) {
45776
+ var newArr = arr.splice();
45777
+ newArr[monocle] = val;
45778
+ return newArr;
45779
+ },
45780
+ mod: function (arr, monocle, f) {
45781
+ var newArr = arr.slice();
45782
+ newArr[monocle] = f(arr[monocle]);
45783
+ return newArr;
45784
+ },
45785
+ del: function (arr, monocle) {
45786
+ var newArr = arr.slice();
45787
+ newArr.splice(monocle, 1);
45788
+ return newArr;
45789
+ }
45790
+ };
45791
+
45792
+ /*
45793
+ // Lens must point to a member of an array. We'll insert into that array.
45794
+ lens.prototype.insertAt = function(lensArr, toInsert) {
45795
+ var obj = this._wrapped;
45796
+
45797
+ var arrLens = lensArr.slice(0, -1);
45798
+ var arr = lens(obj).get(arrLens).slice(); // slice to copy
45799
+
45800
+ var arrIdx = lensArr[lensArr.length-1];
45801
+ arr.splice(arrIdx, 0, toInsert);
45802
+ return lens(obj).set(arrLens, arr);
45803
+ };
45804
+
45805
+ lens.prototype.insertBefore = lens.prototype.insertAt;
45806
+ lens.prototype.insertAfter = function(lensArr, toInsert) {
45807
+ var newLens = lensArr.slice();
45808
+ newLens[newLens.length-1] += 1;
45809
+ return lens(this._wrapped).insertAt(newLens, toInsert);
45810
+ };
45811
+ */
45812
+
45813
+ // OBJECT FUNCTIONS
45814
+
45815
+ const obj = {
45816
+ get: function (obj, monocle) {
45817
+ return obj[monocle];
45818
+ },
45819
+ set: function (obj, monocle, val) {
45820
+ var newObj = clone(obj);
45821
+ newObj[monocle] = val;
45822
+ return newObj;
45823
+ },
45824
+ mod: function (obj, monocle, f) {
45825
+ var newObj = clone(obj);
45826
+ newObj[monocle] = f(obj[monocle]);
45827
+ return newObj;
45828
+ },
45829
+ del: function (obj, monocle) {
45830
+ var newObj = clone(obj);
45831
+ delete newObj[monocle];
45832
+ return newObj;
45833
+ }
45834
+ };
45835
+
45836
+ // STRING FUNCTIONS
45837
+
45838
+ const str = {
45839
+ get: function (arr, monocle) {
45840
+ return arr[monocle];
45841
+ },
45842
+ set: function (arr, monocle, val) {
45843
+ var newArr = arr.splice();
45844
+ newArr[monocle] = val;
45845
+ return newArr;
45846
+ },
45847
+ mod: function (arr, monocle, f) {
45848
+ var newArr = arr.splice();
45849
+ newArr[monocle] = f(arr[monocle]);
45850
+ return newArr;
45851
+ },
45852
+ del: function (arr, monocle) {
45853
+ var newArr = arr.slice();
45854
+ newArr.splice(monocle);
45855
+ return newArr;
45856
+ }
45857
+ };
45858
+
46522
45859
  const _excluded = ["item", "children", "shape", "serializedState", "onSerializedStateUpdated"];
46523
45860
 
46524
45861
  // TODO(mdr)
@@ -47118,6 +46455,372 @@ function violatingWidgets(itemData) {
47118
46455
  return _.uniq(widgets);
47119
46456
  }
47120
46457
 
46458
+ /* eslint-disable eqeqeq, @typescript-eslint/no-unused-vars */
46459
+
46460
+ /**
46461
+ * Format a floating point for display using the specified
46462
+ * number of significant figures and least significant decimal.
46463
+ * Scientific notation may used by this method if this
46464
+ * object is very small, very large, has many significant
46465
+ * figures, or the number of significant figures would be
46466
+ * ambiguous in the output if scientific notation were not
46467
+ * used.
46468
+ *
46469
+ * @param f A floating point number that should be displayed
46470
+ * @param sigFigs desired number of significant figures (integer).
46471
+ * @param sigDecs the least significant decimal place (integer).
46472
+ * @param scientific true iff scientific notation should always be used.
46473
+ * @return a string of this object formatted correctly.
46474
+ */
46475
+ function displaySigFigs(f, sigFigs, sigDecs, scientific) {
46476
+ const s = "" + f;
46477
+ let order = parseOrder(s);
46478
+ let mantissa = parseMantissa(s);
46479
+ let positive = parseSign(s);
46480
+ let add;
46481
+ let decAdd;
46482
+ let sigAdd;
46483
+ let zeroScientific = false;
46484
+ if (f == 0 || mantissa == "" || mantissa == "0") {
46485
+ mantissa = "";
46486
+ for (let i = 0; i < sigFigs; i++) {
46487
+ mantissa += "0";
46488
+ }
46489
+ order = sigFigs + sigDecs;
46490
+ if (sigDecs < 0 && -sigDecs >= sigFigs) {
46491
+ zeroScientific = true;
46492
+ }
46493
+ } else {
46494
+ decAdd = order - mantissa.length - sigDecs;
46495
+ sigAdd = sigFigs - mantissa.length;
46496
+ add = Math.min(sigAdd, decAdd);
46497
+ if (add < 0) {
46498
+ let rounded = round(mantissa, -add);
46499
+ if (rounded.length > mantissa.length + add) {
46500
+ order++;
46501
+ if (decAdd > sigAdd) {
46502
+ rounded = round(rounded, 1);
46503
+ }
46504
+ }
46505
+ mantissa = rounded;
46506
+ } else if (add > 0) {
46507
+ for (let i = 0; i < add; i++) {
46508
+ mantissa += "0";
46509
+ }
46510
+ }
46511
+ if (mantissa == "" || mantissa == "0") {
46512
+ mantissa = "0";
46513
+ positive = true;
46514
+ order = 1 + sigDecs;
46515
+ if (order != 0) {
46516
+ zeroScientific = true;
46517
+ }
46518
+ }
46519
+ }
46520
+ const useScientific = scientific || mantissa.length > 20 || order > 4 || order < -2 || order - mantissa.length > 0 && trailingZeros(mantissa) > 0 || zeroScientific;
46521
+ let returnVal = "";
46522
+ if (!positive) {
46523
+ returnVal += "-";
46524
+ }
46525
+ if (useScientific) {
46526
+ returnVal += mantissa.charAt(0);
46527
+ if (mantissa.length > 1) {
46528
+ returnVal += "." + mantissa.substring(1, mantissa.length);
46529
+ }
46530
+ if (order - 1 != 0) {
46531
+ returnVal += " x 10^" + (order - 1);
46532
+ }
46533
+ } else {
46534
+ let wholePart = "";
46535
+ let fractPart = "";
46536
+ let needDot = true;
46537
+ if (order > 0) {
46538
+ if (mantissa.length > order) {
46539
+ wholePart = mantissa.substring(0, order);
46540
+ fractPart = mantissa.substring(order, mantissa.length);
46541
+ } else {
46542
+ wholePart = mantissa;
46543
+ needDot = trailingZeros(mantissa) != 0;
46544
+ for (let i = 0; i < order - mantissa.length; i++) {
46545
+ wholePart += "0";
46546
+ }
46547
+ }
46548
+ } else {
46549
+ for (let i = 0; i < -order; i++) {
46550
+ fractPart += "0";
46551
+ }
46552
+ fractPart += mantissa;
46553
+ }
46554
+ returnVal += (wholePart == "" ? "0" : wholePart) + (needDot ? "." : "") + fractPart;
46555
+ }
46556
+ return returnVal;
46557
+ }
46558
+
46559
+ /**
46560
+ * Count the significant trailing zeros on this object.
46561
+ *
46562
+ * @return the number of trailing zeros
46563
+ */
46564
+ function trailingZeros(mantissa) {
46565
+ let zeros = 0;
46566
+ for (let i = mantissa.length - 1; i >= 0; i--) {
46567
+ const c = mantissa.charAt(i);
46568
+ if (c == "0") {
46569
+ zeros++;
46570
+ } else {
46571
+ return zeros;
46572
+ }
46573
+ }
46574
+ return zeros;
46575
+ }
46576
+
46577
+ /**
46578
+ * Parse a string representation of a floating point
46579
+ * and pull out the sign.
46580
+ *
46581
+ * @param s the string representation of a floating point.
46582
+ * @return true iff this is a positive number
46583
+ */
46584
+ function parseSign(s) {
46585
+ let beginning = true;
46586
+ let seenDot = false;
46587
+ let seenSomething = false;
46588
+ let zeros = "";
46589
+ let all = "";
46590
+ let pos = true;
46591
+ for (let i = 0; i < s.length; i++) {
46592
+ const c = s.charAt(i);
46593
+ if (c >= "1" && c <= "9") {
46594
+ all += zeros + c;
46595
+ zeros = "";
46596
+ seenSomething = true;
46597
+ beginning = false;
46598
+ } else if (c == "0") {
46599
+ if (seenDot) {
46600
+ if (seenSomething) {
46601
+ all += zeros + c;
46602
+ zeros = "";
46603
+ }
46604
+ } else {
46605
+ if (seenSomething) {
46606
+ zeros += c;
46607
+ }
46608
+ }
46609
+ beginning = false;
46610
+ } else if (!seenDot && c == ".") {
46611
+ all += zeros;
46612
+ zeros = "";
46613
+ seenDot = true;
46614
+ beginning = false;
46615
+ } else if (c == "e" || c == "E" && i + 1 < s.length) {
46616
+ parseInt(s.substring(i + 1, s.length));
46617
+ i = s.length;
46618
+ } else if (beginning && (c == "+" || c == "-")) {
46619
+ if (c == "-") {
46620
+ pos = !pos;
46621
+ }
46622
+ }
46623
+ }
46624
+ if (all == "") {
46625
+ return true;
46626
+ }
46627
+ return pos;
46628
+ }
46629
+
46630
+ /**
46631
+ * Parse a string representation of a floating point
46632
+ * and pull out the mantissa.
46633
+ *
46634
+ * @param s the string representation of a floating point.
46635
+ * @return the mantissa of this number.
46636
+ */
46637
+ function parseMantissa(s) {
46638
+ let seenDot = false;
46639
+ let seenSomething = false;
46640
+ let zeros = "";
46641
+ let leadZeros = "";
46642
+ let all = "";
46643
+ for (let i = 0; i < s.length; i++) {
46644
+ const c = s.charAt(i);
46645
+ if (c >= "1" && c <= "9") {
46646
+ all += zeros + c;
46647
+ zeros = "";
46648
+ seenSomething = true;
46649
+ } else if (c == "0") {
46650
+ if (seenDot) {
46651
+ if (seenSomething) {
46652
+ all += zeros + c;
46653
+ zeros = "";
46654
+ } else {
46655
+ leadZeros += c;
46656
+ }
46657
+ } else {
46658
+ if (seenSomething) {
46659
+ leadZeros += c;
46660
+ zeros += c;
46661
+ } else {
46662
+ leadZeros += c;
46663
+ }
46664
+ }
46665
+ } else if (!seenDot && c == ".") {
46666
+ all += zeros;
46667
+ zeros = "";
46668
+ seenDot = true;
46669
+ } else if (c == "e" || c == "E" && i + 1 < s.length) {
46670
+ parseInt(s.substring(i + 1, s.length));
46671
+ i = s.length;
46672
+ } else ;
46673
+ }
46674
+ if (all == "") {
46675
+ return leadZeros;
46676
+ }
46677
+ return all;
46678
+ }
46679
+
46680
+ /**
46681
+ * Parse a string representation of a floating point
46682
+ * and pull out the exponent.
46683
+ *
46684
+ * @param s the string representation of a floating point.
46685
+ * @return (integer) the number after the e.
46686
+ */
46687
+ function parseOrder(s) {
46688
+ let seenDot = false;
46689
+ let seenSomething = false;
46690
+ let zeros = "";
46691
+ let all = "";
46692
+ let decPlaces = 0;
46693
+ let totalDecs = 0;
46694
+ for (let i = 0; i < s.length; i++) {
46695
+ const c = s.charAt(i);
46696
+ if (c >= "1" && c <= "9") {
46697
+ all += zeros + c;
46698
+ zeros = "";
46699
+ seenSomething = true;
46700
+ if (!seenDot) {
46701
+ totalDecs++;
46702
+ decPlaces++;
46703
+ }
46704
+ } else if (c == "0") {
46705
+ if (seenDot) {
46706
+ if (seenSomething) {
46707
+ all += zeros + c;
46708
+ zeros = "";
46709
+ } else {
46710
+ decPlaces--;
46711
+ }
46712
+ } else {
46713
+ totalDecs++;
46714
+ if (seenSomething) {
46715
+ decPlaces++;
46716
+ zeros += c;
46717
+ }
46718
+ }
46719
+ } else if (!seenDot && c == ".") {
46720
+ all += zeros;
46721
+ zeros = "";
46722
+ seenDot = true;
46723
+ } else if (c == "e" || c == "E" && i + 1 < s.length) {
46724
+ const raised = parseInt(s.substring(i + 1, s.length));
46725
+ decPlaces += raised;
46726
+ totalDecs += raised;
46727
+ i = s.length;
46728
+ } else ;
46729
+ }
46730
+ if (all == "") {
46731
+ return totalDecs;
46732
+ }
46733
+ return decPlaces;
46734
+ }
46735
+
46736
+ /**
46737
+ * Remove the specified number of digits from string by
46738
+ * rounding. Proper rounding rules for scientific purposes
46739
+ * are followed.
46740
+ * This method may cause an extra significant figure
46741
+ * to be added to the number. For example, if 999999
46742
+ * were rounded, A one would carry over and become
46743
+ * a significant figure. Those who call this method
46744
+ * should check for this and call round again if needed.
46745
+ *
46746
+ * @param mantissa A string representing an whole number of arbitrary length.
46747
+ * @param digits A number of digits to remove
46748
+ * @return A string represted the rounded version of mantissa
46749
+ */
46750
+ function round(mantissa, digits) {
46751
+ const last = mantissa.length - digits - 1;
46752
+ if (last < 0) {
46753
+ return "";
46754
+ }
46755
+ if (last >= mantissa.length - 1) {
46756
+ return mantissa;
46757
+ }
46758
+ const nextToLast = mantissa.charAt(last + 1);
46759
+ const lastChar = mantissa.charAt(last);
46760
+ let roundUp = false;
46761
+ if (nextToLast > "5") {
46762
+ roundUp = true;
46763
+ } else if (nextToLast == "5") {
46764
+ for (let j = last + 2; j < mantissa.length; j++) {
46765
+ if (mantissa.charAt(j) != "0") {
46766
+ roundUp = true;
46767
+ }
46768
+ }
46769
+ // @ts-expect-error - TS2362 - The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
46770
+ if (lastChar % 2 == 1) {
46771
+ roundUp = true;
46772
+ }
46773
+ }
46774
+ let result = "";
46775
+ for (let i = last; i >= 0; i--) {
46776
+ const c = mantissa.charAt(i);
46777
+ if (roundUp) {
46778
+ let nextChar;
46779
+ if (c == "9") {
46780
+ nextChar = "0";
46781
+ } else {
46782
+ switch (c) {
46783
+ case "0":
46784
+ nextChar = "1";
46785
+ break;
46786
+ case "1":
46787
+ nextChar = "2";
46788
+ break;
46789
+ case "2":
46790
+ nextChar = "3";
46791
+ break;
46792
+ case "3":
46793
+ nextChar = "4";
46794
+ break;
46795
+ case "4":
46796
+ nextChar = "5";
46797
+ break;
46798
+ case "5":
46799
+ nextChar = "6";
46800
+ break;
46801
+ case "6":
46802
+ nextChar = "7";
46803
+ break;
46804
+ case "7":
46805
+ nextChar = "8";
46806
+ break;
46807
+ case "8":
46808
+ nextChar = "9";
46809
+ break;
46810
+ }
46811
+ roundUp = false;
46812
+ }
46813
+ result = nextChar + result;
46814
+ } else {
46815
+ result = c + result;
46816
+ }
46817
+ }
46818
+ if (roundUp) {
46819
+ result = "1" + result;
46820
+ }
46821
+ return result;
46822
+ }
46823
+
47121
46824
  /**
47122
46825
  * Some tests require some or all of the widgets and editors to be registered
47123
46826
  * in order for them to work. Requiring this file will register all of the
@@ -47837,9 +47540,6 @@ function generateTestInteractiveGraphWidget() {
47837
47540
  markings: "graph",
47838
47541
  labels: ["x", "y"],
47839
47542
  showProtractor: false,
47840
- showRuler: false,
47841
- rulerLabel: "mm",
47842
- rulerTicks: 2,
47843
47543
  range: [[-10, 10], [-10, 10]],
47844
47544
  graph: {
47845
47545
  type: "angle"
@@ -47951,7 +47651,6 @@ const widgetNameToEnum = {
47951
47651
  simulator: "SIMULATOR",
47952
47652
  sorter: "SORTER",
47953
47653
  table: "TABLE",
47954
- "unit-input": "UNIT",
47955
47654
  video: "VIDEO"
47956
47655
  };
47957
47656
  function convertWidgetNameToEnum(name) {
@@ -47970,5 +47669,5 @@ const EditorJsonify = {
47970
47669
  }
47971
47670
  };
47972
47671
 
47973
- export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getLineCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
47672
+ export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getSegmentCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
47974
47673
  //# sourceMappingURL=index.js.map