@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/index.js CHANGED
@@ -618,6 +618,7 @@ class InnerMathInput extends React__namespace.Component {
618
618
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.Popover, {
619
619
  opened: this.state.keypadOpen,
620
620
  onClose: () => this.closeKeypad(),
621
+ portal: false,
621
622
  dismissEnabled: true,
622
623
  content: () => /*#__PURE__*/React__namespace.createElement(wonderBlocksPopover.PopoverContentCore, {
623
624
  closeButtonVisible: true,
@@ -834,7 +835,7 @@ const denylist = [
834
835
  // (technically the renderer still adds them)
835
836
  "key", "ref",
836
837
  // added by src/renderer.jsx
837
- "containerSizeClass", "widgetId", "onChange", "problemNum", "apiOptions", "questionCompleted", "findWidgets",
838
+ "containerSizeClass", "widgetId", "onChange", "problemNum", "apiOptions", "questionCompleted", "findWidgets", "hintMode",
838
839
  // added by src/editor.jsx, for widgets removing themselves
839
840
  // this is soooo not the right place for this, but alas.
840
841
  "onRemove",
@@ -19194,6 +19195,7 @@ const replaceDeprecatedWidgets = () => {
19194
19195
  replaceWidget("reaction-diagram", "deprecated-standin");
19195
19196
  replaceWidget("sequence", "deprecated-standin");
19196
19197
  replaceWidget("simulator", "deprecated-standin");
19198
+ replaceWidget("unit-input", "deprecated-standin");
19197
19199
  };
19198
19200
  const registerEditors = editorsToRegister => {
19199
19201
  editorsToRegister.forEach(editor => {
@@ -19227,6 +19229,7 @@ const replaceDeprecatedEditors = () => {
19227
19229
  replaceEditor("reaction-diagram", "deprecated-standin");
19228
19230
  replaceEditor("sequence", "deprecated-standin");
19229
19231
  replaceEditor("simulator", "deprecated-standin");
19232
+ replaceEditor("unit-input", "deprecated-standin");
19230
19233
  };
19231
19234
  const getWidget = name => {
19232
19235
  // TODO(alex): Consider referring to these as renderers to avoid
@@ -19789,6 +19792,7 @@ class Renderer extends React__namespace.Component {
19789
19792
  findExternalWidgets: () => [],
19790
19793
  alwaysUpdate: false,
19791
19794
  reviewMode: false,
19795
+ hintMode: false,
19792
19796
  serializedState: null,
19793
19797
  onSerializedStateUpdated: () => {},
19794
19798
  linterContext: PerseusLinter__namespace.linterContextDefault
@@ -20079,6 +20083,7 @@ class Renderer extends React__namespace.Component {
20079
20083
  onBlur: ___default["default"].partial(this._onWidgetBlur, id),
20080
20084
  findWidgets: this.findWidgets,
20081
20085
  reviewModeRubric: reviewModeRubric,
20086
+ hintMode: this.props.hintMode,
20082
20087
  onChange: function (newProps, cb) {
20083
20088
  let silent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
20084
20089
  _this._setWidgetProps(id, newProps, cb, silent);
@@ -28585,7 +28590,8 @@ const defaultGraphConfig = {
28585
28590
  graphDimensionsInPixels: [1, 1],
28586
28591
  width: 0,
28587
28592
  height: 0,
28588
- labels: []
28593
+ labels: [],
28594
+ hintMode: false
28589
28595
  };
28590
28596
  const GraphConfigContext = /*#__PURE__*/React.createContext(defaultGraphConfig);
28591
28597
  function useGraphConfig() {
@@ -30070,8 +30076,6 @@ const ItemExtras = [
30070
30076
 
30071
30077
  // prettier-ignore
30072
30078
 
30073
- // prettier-ignore
30074
-
30075
30079
  //prettier-ignore
30076
30080
 
30077
30081
  // A background image applied to various widgets.
@@ -30597,6 +30601,16 @@ function useDraggable(args) {
30597
30601
  const isKeyboard = type.includes("key");
30598
30602
  if (isKeyboard) {
30599
30603
  event?.preventDefault();
30604
+
30605
+ // When a key is held down, we see multiple "keydown" events,
30606
+ // followed by a single "keyup" event.
30607
+ // For a single keypress, we only see a "keydown" event, no
30608
+ // "keyup".
30609
+ // We never want to process the keyup event as an intent to
30610
+ // move so we bail on further processing here.
30611
+ if (type === "keyup") {
30612
+ return;
30613
+ }
30600
30614
  const {
30601
30615
  direction: yDownDirection,
30602
30616
  altKey,
@@ -30697,7 +30711,8 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
30697
30711
  const {
30698
30712
  range,
30699
30713
  markings,
30700
- showTooltips
30714
+ showTooltips,
30715
+ hintMode
30701
30716
  } = useGraphConfig();
30702
30717
  const {
30703
30718
  point,
@@ -30742,7 +30757,7 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
30742
30757
  cursor
30743
30758
  },
30744
30759
  "data-testid": "movable-point",
30745
- tabIndex: tabIndex(focusBehavior)
30760
+ tabIndex: hintMode ? -1 : tabIndex(focusBehavior)
30746
30761
  }, /*#__PURE__*/React__namespace.createElement("circle", {
30747
30762
  className: "movable-point-hitbox",
30748
30763
  r: hitboxSizePx / 2,
@@ -30830,7 +30845,8 @@ const MovableLine = props => {
30830
30845
  };
30831
30846
  function useControlPoint(point, color, onMovePoint) {
30832
30847
  const {
30833
- snapStep
30848
+ snapStep,
30849
+ hintMode
30834
30850
  } = useGraphConfig();
30835
30851
  const [focused, setFocused] = React.useState(false);
30836
30852
  const keyboardHandleRef = React.useRef(null);
@@ -30852,7 +30868,7 @@ function useControlPoint(point, color, onMovePoint) {
30852
30868
  const focusableHandle = /*#__PURE__*/React__namespace.createElement("g", {
30853
30869
  "data-testid": "movable-point__focusable-handle",
30854
30870
  className: "movable-point__focusable-handle",
30855
- tabIndex: 0,
30871
+ tabIndex: hintMode ? -1 : 0,
30856
30872
  ref: keyboardHandleRef,
30857
30873
  onFocus: () => setFocused(true),
30858
30874
  onBlur: () => setFocused(false)
@@ -30885,7 +30901,8 @@ const Line$1 = props => {
30885
30901
  const {
30886
30902
  range,
30887
30903
  graphDimensionsInPixels,
30888
- snapStep
30904
+ snapStep,
30905
+ hintMode
30889
30906
  } = useGraphConfig();
30890
30907
  let startExtend = undefined;
30891
30908
  let endExtend = undefined;
@@ -30907,7 +30924,7 @@ const Line$1 = props => {
30907
30924
  });
30908
30925
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("g", {
30909
30926
  ref: line,
30910
- tabIndex: 0,
30927
+ tabIndex: hintMode ? -1 : 0,
30911
30928
  className: "movable-line",
30912
30929
  "data-testid": "movable-line",
30913
30930
  style: {
@@ -31426,6 +31443,9 @@ const PolygonGraph = props => {
31426
31443
  snapStep,
31427
31444
  snapTo
31428
31445
  } = props.graphState;
31446
+ const {
31447
+ hintMode
31448
+ } = useGraphConfig();
31429
31449
 
31430
31450
  // TODO(benchristel): can the default set of points be removed here? I don't
31431
31451
  // think coords can be null.
@@ -31445,12 +31465,16 @@ const PolygonGraph = props => {
31445
31465
  constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
31446
31466
  });
31447
31467
  const active = hovered || focused || dragging;
31468
+ const lastMoveTime = React__namespace.useRef(0);
31448
31469
  const lines = getLines(points);
31449
31470
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
31450
31471
  points: [...points],
31451
31472
  color: "var(--movable-line-stroke-color)",
31452
31473
  svgPolygonProps: {
31453
- strokeWidth: active ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)"
31474
+ strokeWidth: active ? "var(--movable-line-stroke-weight-active)" : "var(--movable-line-stroke-weight)",
31475
+ style: {
31476
+ fill: "transparent"
31477
+ }
31454
31478
  }
31455
31479
  }), points.map((point, i) => {
31456
31480
  const pt1 = points.at(i - 1);
@@ -31482,10 +31506,11 @@ const PolygonGraph = props => {
31482
31506
  color: "transparent",
31483
31507
  svgPolygonProps: {
31484
31508
  ref,
31485
- tabIndex: 0,
31509
+ tabIndex: hintMode ? -1 : 0,
31486
31510
  strokeWidth: TARGET_SIZE,
31487
31511
  style: {
31488
- cursor: dragging ? "grabbing" : "grab"
31512
+ cursor: dragging ? "grabbing" : "grab",
31513
+ fill: hovered ? "var(--mafs-blue)" : "transparent"
31489
31514
  },
31490
31515
  onFocus: () => setFocused(true),
31491
31516
  onBlur: () => setFocused(false),
@@ -31497,7 +31522,15 @@ const PolygonGraph = props => {
31497
31522
  key: "point-" + i,
31498
31523
  snapTo: snapTo,
31499
31524
  point: point,
31500
- onMove: destination => dispatch(actions.polygon.movePoint(i, destination))
31525
+ onMove: destination => {
31526
+ const now = Date.now();
31527
+ const targetFPS = 40;
31528
+ const moveThresholdTime = 1000 / targetFPS;
31529
+ if (now - lastMoveTime.current > moveThresholdTime) {
31530
+ dispatch(actions.polygon.movePoint(i, destination));
31531
+ lastMoveTime.current = now;
31532
+ }
31533
+ }
31501
31534
  })));
31502
31535
  };
31503
31536
  function getLines(points) {
@@ -31618,7 +31651,8 @@ function MovableCircle(props) {
31618
31651
  onMove
31619
31652
  } = props;
31620
31653
  const {
31621
- snapStep
31654
+ snapStep,
31655
+ hintMode
31622
31656
  } = useGraphConfig();
31623
31657
  const draggableRef = React.useRef(null);
31624
31658
  const {
@@ -31633,7 +31667,7 @@ function MovableCircle(props) {
31633
31667
  const [radiiPx] = useTransformDimensionsToPixels([radius, radius]);
31634
31668
  return /*#__PURE__*/React__namespace.createElement("g", {
31635
31669
  ref: draggableRef,
31636
- tabIndex: 0,
31670
+ tabIndex: hintMode ? -1 : 0,
31637
31671
  className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`
31638
31672
  }, /*#__PURE__*/React__namespace.createElement("ellipse", {
31639
31673
  className: "focus-ring",
@@ -32037,7 +32071,8 @@ const MafsGraph = props => {
32037
32071
  const {
32038
32072
  state,
32039
32073
  dispatch,
32040
- labels
32074
+ labels,
32075
+ hintMode
32041
32076
  } = props;
32042
32077
  const [width, height] = props.box;
32043
32078
  const tickStep = props.step;
@@ -32052,7 +32087,8 @@ const MafsGraph = props => {
32052
32087
  graphDimensionsInPixels: props.box,
32053
32088
  width,
32054
32089
  height,
32055
- labels
32090
+ labels,
32091
+ hintMode
32056
32092
  }
32057
32093
  }, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
32058
32094
  className: "mafs-graph",
@@ -32063,7 +32099,8 @@ const MafsGraph = props => {
32063
32099
  padding: "25px 25px 0 0",
32064
32100
  boxSizing: "content-box",
32065
32101
  marginLeft: "20px",
32066
- marginBottom: "20px"
32102
+ marginBottom: "20px",
32103
+ pointerEvents: hintMode ? "none" : "auto"
32067
32104
  }
32068
32105
  }, /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
32069
32106
  box: props.box,
@@ -32094,7 +32131,7 @@ const MafsGraph = props => {
32094
32131
  }), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
32095
32132
  lockedFigures: props.lockedFigures,
32096
32133
  range: state.range
32097
- }), "/* Protractor */", props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
32134
+ }), props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
32098
32135
  state,
32099
32136
  dispatch
32100
32137
  })))));
@@ -33195,7 +33232,6 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
33195
33232
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
33196
33233
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
33197
33234
  const showSides = graph.type === "polygon" ? graph.showSides : null;
33198
- const startCoords = graph.startCoords ?? null;
33199
33235
  const originalPropsRef = React.useRef(props);
33200
33236
  const latestPropsRef = wonderBlocksCore.useLatestRef(props);
33201
33237
  React.useEffect(() => {
@@ -33206,7 +33242,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
33206
33242
  if (latestPropsRef.current !== originalPropsRef.current) {
33207
33243
  dispatch(reinitialize(latestPropsRef.current));
33208
33244
  }
33209
- }, [graph.type, numSegments, numSides, snapTo, showAngles, showSides, startCoords, latestPropsRef, startCoords]);
33245
+ }, [graph.type, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, graph.startCoords]);
33210
33246
  return /*#__PURE__*/React__namespace.createElement(MafsGraph, _extends({}, props, {
33211
33247
  state: state,
33212
33248
  dispatch: dispatch
@@ -33297,9 +33333,6 @@ class LegacyInteractiveGraph extends React__namespace.Component {
33297
33333
  markings: "graph",
33298
33334
  showTooltips: false,
33299
33335
  showProtractor: false,
33300
- showRuler: false,
33301
- rulerLabel: "",
33302
- rulerTicks: 10,
33303
33336
  graph: {
33304
33337
  type: "linear"
33305
33338
  }
@@ -34462,9 +34495,6 @@ class LegacyInteractiveGraph extends React__namespace.Component {
34462
34495
  markings: this.props.markings,
34463
34496
  backgroundImage: this.props.backgroundImage,
34464
34497
  showProtractor: this.props.showProtractor,
34465
- showRuler: this.props.showRuler,
34466
- rulerLabel: this.props.rulerLabel,
34467
- rulerTicks: this.props.rulerTicks,
34468
34498
  onMouseDown: onMouseDown,
34469
34499
  onGraphieUpdated: this.setGraphie,
34470
34500
  setDrawingAreaAvailable: this.props.apiOptions.setDrawingAreaAvailable,
@@ -34483,9 +34513,6 @@ class InteractiveGraph extends React__namespace.Component {
34483
34513
  markings: "graph",
34484
34514
  showTooltips: false,
34485
34515
  showProtractor: false,
34486
- showRuler: false,
34487
- rulerLabel: "",
34488
- rulerTicks: 10,
34489
34516
  graph: {
34490
34517
  type: "linear"
34491
34518
  }
@@ -34514,7 +34541,8 @@ class InteractiveGraph extends React__namespace.Component {
34514
34541
  gridStep: gridStep,
34515
34542
  snapStep: snapStep,
34516
34543
  box: box,
34517
- showTooltips: !!this.props.showTooltips
34544
+ showTooltips: !!this.props.showTooltips,
34545
+ hintMode: this.props.hintMode
34518
34546
  }));
34519
34547
  }
34520
34548
  return /*#__PURE__*/React__namespace.createElement(LegacyInteractiveGraph, _extends({
@@ -44203,921 +44231,6 @@ var Table$1 = {
44203
44231
  isLintable: true
44204
44232
  };
44205
44233
 
44206
- var isObject = function (obj) {
44207
- return obj === Object(obj);
44208
- };
44209
- var merge = function () {
44210
- var obj = {};
44211
- for (var i = 0; i < arguments.length; i++) {
44212
- var source = arguments[i];
44213
- if (source) {
44214
- for (var prop in source) {
44215
- obj[prop] = source[prop];
44216
- }
44217
- }
44218
- }
44219
- return obj;
44220
- };
44221
- var clone = function (obj) {
44222
- if (!isObject(obj)) {
44223
- return obj;
44224
- }
44225
- return Array.isArray(obj) ? obj.slice() : merge(obj);
44226
- };
44227
-
44228
- /* TODO batch *all* mutations
44229
- * idea: freeze / thaw implementations for all types
44230
- * lens constructor thaws, freeze delegates to type's freeze
44231
- */
44232
-
44233
- // equivalents, without requiring it
44234
- // find the implementation to use for a given object
44235
- var dispatch = function (x) {
44236
- if (Array.isArray(x)) {
44237
- return arr;
44238
- } else if (isObject(x)) {
44239
- return obj;
44240
- } else if (typeof x === "string") {
44241
- return str;
44242
- }
44243
- };
44244
-
44245
- // This is underscore with a different name
44246
- var lens = function (obj) {
44247
- if (obj instanceof lens) {
44248
- return obj;
44249
- }
44250
- if (!(this instanceof lens)) {
44251
- return new lens(obj);
44252
- }
44253
- var ops = dispatch(obj);
44254
- this._wrapped = ops.thaw ? ops.thaw(obj) : obj;
44255
- };
44256
- lens.prototype.freeze = function () {
44257
- var obj = this._wrapped;
44258
- var ops = dispatch(obj);
44259
- return ops.freeze ? ops.freeze(obj) : obj;
44260
- };
44261
- lens.prototype.zoom = function (lensArr) {
44262
- if (this._zoomStack === undefined) {
44263
- this._zoomStack = [];
44264
- }
44265
- this._zoomStack.push({
44266
- zoom: lensArr,
44267
- wrapped: this._wrapped
44268
- });
44269
- this._wrapped = lens(this._wrapped).get(lensArr);
44270
- return this;
44271
- };
44272
- lens.prototype.deZoom = function () {
44273
- var frame = this._zoomStack.pop();
44274
- this._wrapped = lens(frame.wrapped).set(frame.zoom, this._wrapped).freeze();
44275
- return this;
44276
- };
44277
- lens.prototype.get = function (lensArr) {
44278
- var obj = this._wrapped;
44279
- for (var i = 0; i < lensArr.length; i++) {
44280
- obj = dispatch(obj).get(obj, lensArr[i]);
44281
- }
44282
- return obj;
44283
- };
44284
- lens.prototype.mod = function (lensArr, f) {
44285
- var obj = this._wrapped;
44286
- var newObj = clone(obj);
44287
- var ops = dispatch(obj);
44288
- if (lensArr.length === 0) {
44289
- this._wrapped = f(this._wrapped);
44290
- } else if (lensArr.length === 1) {
44291
- this._wrapped = ops.mod(newObj, lensArr[0], f);
44292
- } else {
44293
- var monocle = lensArr[0];
44294
- var shortLens = lensArr.slice(1);
44295
-
44296
- // newObj = ops.mod(obj[monocle], shortLens, f);
44297
-
44298
- newObj[monocle] = lens(obj[monocle]).mod(shortLens, f).freeze();
44299
- this._wrapped = newObj;
44300
- }
44301
- return this;
44302
- };
44303
-
44304
- // TODO - move to individual files
44305
- lens.prototype.merge = function (lensArr, props) {
44306
- this._wrapped = lens(this._wrapped).mod(lensArr, function (oldProps) {
44307
- return merge(oldProps, props);
44308
- }).freeze();
44309
- return this;
44310
- };
44311
-
44312
- // Lens must have length >= 1 or there would be nothing to return
44313
- lens.prototype.del = function (lensArr) {
44314
- var obj = this._wrapped;
44315
- var ops = dispatch(obj);
44316
- if (lensArr.length === 1) {
44317
- this._wrapped = ops.del(obj, lensArr[0]);
44318
- } else {
44319
- var monocle = lensArr[0];
44320
- var shortLens = lensArr.slice(1);
44321
- var newObj = clone(obj);
44322
- newObj[monocle] = lens(obj[monocle]).del(shortLens).freeze();
44323
- this._wrapped = newObj;
44324
- }
44325
- return this;
44326
- };
44327
- lens.prototype.set = function (lensArr, set) {
44328
- return this.mod(lensArr, function () {
44329
- return set;
44330
- });
44331
- };
44332
-
44333
- // ARRAY FUNCTIONS
44334
-
44335
- const arr = {
44336
- get: function (arr, monocle) {
44337
- return arr[monocle];
44338
- },
44339
- set: function (arr, monocle, val) {
44340
- var newArr = arr.splice();
44341
- newArr[monocle] = val;
44342
- return newArr;
44343
- },
44344
- mod: function (arr, monocle, f) {
44345
- var newArr = arr.slice();
44346
- newArr[monocle] = f(arr[monocle]);
44347
- return newArr;
44348
- },
44349
- del: function (arr, monocle) {
44350
- var newArr = arr.slice();
44351
- newArr.splice(monocle, 1);
44352
- return newArr;
44353
- }
44354
- };
44355
-
44356
- /*
44357
- // Lens must point to a member of an array. We'll insert into that array.
44358
- lens.prototype.insertAt = function(lensArr, toInsert) {
44359
- var obj = this._wrapped;
44360
-
44361
- var arrLens = lensArr.slice(0, -1);
44362
- var arr = lens(obj).get(arrLens).slice(); // slice to copy
44363
-
44364
- var arrIdx = lensArr[lensArr.length-1];
44365
- arr.splice(arrIdx, 0, toInsert);
44366
- return lens(obj).set(arrLens, arr);
44367
- };
44368
-
44369
- lens.prototype.insertBefore = lens.prototype.insertAt;
44370
- lens.prototype.insertAfter = function(lensArr, toInsert) {
44371
- var newLens = lensArr.slice();
44372
- newLens[newLens.length-1] += 1;
44373
- return lens(this._wrapped).insertAt(newLens, toInsert);
44374
- };
44375
- */
44376
-
44377
- // OBJECT FUNCTIONS
44378
-
44379
- const obj = {
44380
- get: function (obj, monocle) {
44381
- return obj[monocle];
44382
- },
44383
- set: function (obj, monocle, val) {
44384
- var newObj = clone(obj);
44385
- newObj[monocle] = val;
44386
- return newObj;
44387
- },
44388
- mod: function (obj, monocle, f) {
44389
- var newObj = clone(obj);
44390
- newObj[monocle] = f(obj[monocle]);
44391
- return newObj;
44392
- },
44393
- del: function (obj, monocle) {
44394
- var newObj = clone(obj);
44395
- delete newObj[monocle];
44396
- return newObj;
44397
- }
44398
- };
44399
-
44400
- // STRING FUNCTIONS
44401
-
44402
- const str = {
44403
- get: function (arr, monocle) {
44404
- return arr[monocle];
44405
- },
44406
- set: function (arr, monocle, val) {
44407
- var newArr = arr.splice();
44408
- newArr[monocle] = val;
44409
- return newArr;
44410
- },
44411
- mod: function (arr, monocle, f) {
44412
- var newArr = arr.splice();
44413
- newArr[monocle] = f(arr[monocle]);
44414
- return newArr;
44415
- },
44416
- del: function (arr, monocle) {
44417
- var newArr = arr.slice();
44418
- newArr.splice(monocle);
44419
- return newArr;
44420
- }
44421
- };
44422
-
44423
- /* eslint-disable eqeqeq, @typescript-eslint/no-unused-vars */
44424
- /*
44425
- * A class for formatting number to significant digits.
44426
- * Copyright (C) 2001 Stephen Ostermiller
44427
- * http://ostermiller.org/contact.pl?regarding?JavaScript+Significant+Figures
44428
- *
44429
- * This program is free software; you can redistribute it and/or modify
44430
- * it under the terms of the GNU General Public License as published by
44431
- * the Free Software Foundation; either version 2 of the License, or
44432
- * (at your option) any later version.
44433
- *
44434
- * This program is distributed in the hope that it will be useful,
44435
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
44436
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44437
- * GNU General Public License for more details.
44438
- */
44439
-
44440
- /**
44441
- * An object tho both scans floating points to
44442
- * determine the number of significant figures.
44443
- * and can display a floating point using any number
44444
- * of significant figures.
44445
- *
44446
- * @param s A string representation of a floating point.
44447
- */
44448
- class SignificantFigures {
44449
- order;
44450
- mantissa;
44451
- positive;
44452
- sigFigs;
44453
- sigDecs;
44454
- constructor(s) {
44455
- this.order = parseOrder(s);
44456
- this.mantissa = parseMantissa(s);
44457
- this.positive = parseSign(s);
44458
-
44459
- /**
44460
- * Get the number of significant figures this object uses.
44461
- * Leading zeros are not significant. Traling zeros up to
44462
- * and after the decimal point are significant.
44463
- * Significant figures is good to know when the number is
44464
- * used in multiplication.
44465
- *
44466
- * @return the number of significant figures.
44467
- */
44468
- this.sigFigs = this.mantissa.length;
44469
-
44470
- /**
44471
- * Get the least significant decimal this object uses.
44472
- * This is useful to know if a number is being used
44473
- * in addition.
44474
- * 400 - 2 the hundreds place (10^2) is the least significant decimal.
44475
- * 75 - 0 the ones place (10^0) is the least significant decimal.
44476
- * .543 - -3 the 1/1000's place (10^-3) is the least significant decimal.
44477
- *
44478
- * @return an integer representing the least significant decimal place.
44479
- */
44480
- this.sigDecs = this.order - this.mantissa.length;
44481
- }
44482
- }
44483
-
44484
- /**
44485
- * Format a floating point for display using the specified
44486
- * number of significant figures and least significant decimal.
44487
- * Scientific notation may used by this method if this
44488
- * object is very small, very large, has many significant
44489
- * figures, or the number of significant figures would be
44490
- * ambiguous in the output if scientific notation were not
44491
- * used.
44492
- *
44493
- * @param f A floating point number that should be displayed
44494
- * @param sigFigs desired number of significant figures (integer).
44495
- * @param sigDecs the least significant decimal place (integer).
44496
- * @param scientific true iff scientific notation should always be used.
44497
- * @return a string of this object formatted correctly.
44498
- */
44499
- function displaySigFigs(f, sigFigs, sigDecs, scientific) {
44500
- const s = "" + f;
44501
- let order = parseOrder(s);
44502
- let mantissa = parseMantissa(s);
44503
- let positive = parseSign(s);
44504
- let add;
44505
- let decAdd;
44506
- let sigAdd;
44507
- let zeroScientific = false;
44508
- if (f == 0 || mantissa == "" || mantissa == "0") {
44509
- mantissa = "";
44510
- for (let i = 0; i < sigFigs; i++) {
44511
- mantissa += "0";
44512
- }
44513
- order = sigFigs + sigDecs;
44514
- if (sigDecs < 0 && -sigDecs >= sigFigs) {
44515
- zeroScientific = true;
44516
- }
44517
- } else {
44518
- decAdd = order - mantissa.length - sigDecs;
44519
- sigAdd = sigFigs - mantissa.length;
44520
- add = Math.min(sigAdd, decAdd);
44521
- if (add < 0) {
44522
- let rounded = round(mantissa, -add);
44523
- if (rounded.length > mantissa.length + add) {
44524
- order++;
44525
- if (decAdd > sigAdd) {
44526
- rounded = round(rounded, 1);
44527
- }
44528
- }
44529
- mantissa = rounded;
44530
- } else if (add > 0) {
44531
- for (let i = 0; i < add; i++) {
44532
- mantissa += "0";
44533
- }
44534
- }
44535
- if (mantissa == "" || mantissa == "0") {
44536
- mantissa = "0";
44537
- positive = true;
44538
- order = 1 + sigDecs;
44539
- if (order != 0) {
44540
- zeroScientific = true;
44541
- }
44542
- }
44543
- }
44544
- const useScientific = scientific || mantissa.length > 20 || order > 4 || order < -2 || order - mantissa.length > 0 && trailingZeros(mantissa) > 0 || zeroScientific;
44545
- let returnVal = "";
44546
- if (!positive) {
44547
- returnVal += "-";
44548
- }
44549
- if (useScientific) {
44550
- returnVal += mantissa.charAt(0);
44551
- if (mantissa.length > 1) {
44552
- returnVal += "." + mantissa.substring(1, mantissa.length);
44553
- }
44554
- if (order - 1 != 0) {
44555
- returnVal += " x 10^" + (order - 1);
44556
- }
44557
- } else {
44558
- let wholePart = "";
44559
- let fractPart = "";
44560
- let needDot = true;
44561
- if (order > 0) {
44562
- if (mantissa.length > order) {
44563
- wholePart = mantissa.substring(0, order);
44564
- fractPart = mantissa.substring(order, mantissa.length);
44565
- } else {
44566
- wholePart = mantissa;
44567
- needDot = trailingZeros(mantissa) != 0;
44568
- for (let i = 0; i < order - mantissa.length; i++) {
44569
- wholePart += "0";
44570
- }
44571
- }
44572
- } else {
44573
- for (let i = 0; i < -order; i++) {
44574
- fractPart += "0";
44575
- }
44576
- fractPart += mantissa;
44577
- }
44578
- returnVal += (wholePart == "" ? "0" : wholePart) + (needDot ? "." : "") + fractPart;
44579
- }
44580
- return returnVal;
44581
- }
44582
-
44583
- /**
44584
- * Count the significant trailing zeros on this object.
44585
- *
44586
- * @return the number of trailing zeros
44587
- */
44588
- function trailingZeros(mantissa) {
44589
- let zeros = 0;
44590
- for (let i = mantissa.length - 1; i >= 0; i--) {
44591
- const c = mantissa.charAt(i);
44592
- if (c == "0") {
44593
- zeros++;
44594
- } else {
44595
- return zeros;
44596
- }
44597
- }
44598
- return zeros;
44599
- }
44600
-
44601
- /**
44602
- * Parse a string representation of a floating point
44603
- * and pull out the sign.
44604
- *
44605
- * @param s the string representation of a floating point.
44606
- * @return true iff this is a positive number
44607
- */
44608
- function parseSign(s) {
44609
- let beginning = true;
44610
- let seenDot = false;
44611
- let seenSomething = false;
44612
- let zeros = "";
44613
- let all = "";
44614
- let pos = true;
44615
- for (let i = 0; i < s.length; i++) {
44616
- const c = s.charAt(i);
44617
- if (c >= "1" && c <= "9") {
44618
- all += zeros + c;
44619
- zeros = "";
44620
- seenSomething = true;
44621
- beginning = false;
44622
- } else if (c == "0") {
44623
- if (seenDot) {
44624
- if (seenSomething) {
44625
- all += zeros + c;
44626
- zeros = "";
44627
- }
44628
- } else {
44629
- if (seenSomething) {
44630
- zeros += c;
44631
- }
44632
- }
44633
- beginning = false;
44634
- } else if (!seenDot && c == ".") {
44635
- all += zeros;
44636
- zeros = "";
44637
- seenDot = true;
44638
- beginning = false;
44639
- } else if (c == "e" || c == "E" && i + 1 < s.length) {
44640
- parseInt(s.substring(i + 1, s.length));
44641
- i = s.length;
44642
- } else if (beginning && (c == "+" || c == "-")) {
44643
- if (c == "-") {
44644
- pos = !pos;
44645
- }
44646
- }
44647
- }
44648
- if (all == "") {
44649
- return true;
44650
- }
44651
- return pos;
44652
- }
44653
-
44654
- /**
44655
- * Parse a string representation of a floating point
44656
- * and pull out the mantissa.
44657
- *
44658
- * @param s the string representation of a floating point.
44659
- * @return the mantissa of this number.
44660
- */
44661
- function parseMantissa(s) {
44662
- let seenDot = false;
44663
- let seenSomething = false;
44664
- let zeros = "";
44665
- let leadZeros = "";
44666
- let all = "";
44667
- for (let i = 0; i < s.length; i++) {
44668
- const c = s.charAt(i);
44669
- if (c >= "1" && c <= "9") {
44670
- all += zeros + c;
44671
- zeros = "";
44672
- seenSomething = true;
44673
- } else if (c == "0") {
44674
- if (seenDot) {
44675
- if (seenSomething) {
44676
- all += zeros + c;
44677
- zeros = "";
44678
- } else {
44679
- leadZeros += c;
44680
- }
44681
- } else {
44682
- if (seenSomething) {
44683
- leadZeros += c;
44684
- zeros += c;
44685
- } else {
44686
- leadZeros += c;
44687
- }
44688
- }
44689
- } else if (!seenDot && c == ".") {
44690
- all += zeros;
44691
- zeros = "";
44692
- seenDot = true;
44693
- } else if (c == "e" || c == "E" && i + 1 < s.length) {
44694
- parseInt(s.substring(i + 1, s.length));
44695
- i = s.length;
44696
- } else ;
44697
- }
44698
- if (all == "") {
44699
- return leadZeros;
44700
- }
44701
- return all;
44702
- }
44703
-
44704
- /**
44705
- * Parse a string representation of a floating point
44706
- * and pull out the exponent.
44707
- *
44708
- * @param s the string representation of a floating point.
44709
- * @return (integer) the number after the e.
44710
- */
44711
- function parseOrder(s) {
44712
- let seenDot = false;
44713
- let seenSomething = false;
44714
- let zeros = "";
44715
- let all = "";
44716
- let decPlaces = 0;
44717
- let totalDecs = 0;
44718
- for (let i = 0; i < s.length; i++) {
44719
- const c = s.charAt(i);
44720
- if (c >= "1" && c <= "9") {
44721
- all += zeros + c;
44722
- zeros = "";
44723
- seenSomething = true;
44724
- if (!seenDot) {
44725
- totalDecs++;
44726
- decPlaces++;
44727
- }
44728
- } else if (c == "0") {
44729
- if (seenDot) {
44730
- if (seenSomething) {
44731
- all += zeros + c;
44732
- zeros = "";
44733
- } else {
44734
- decPlaces--;
44735
- }
44736
- } else {
44737
- totalDecs++;
44738
- if (seenSomething) {
44739
- decPlaces++;
44740
- zeros += c;
44741
- }
44742
- }
44743
- } else if (!seenDot && c == ".") {
44744
- all += zeros;
44745
- zeros = "";
44746
- seenDot = true;
44747
- } else if (c == "e" || c == "E" && i + 1 < s.length) {
44748
- const raised = parseInt(s.substring(i + 1, s.length));
44749
- decPlaces += raised;
44750
- totalDecs += raised;
44751
- i = s.length;
44752
- } else ;
44753
- }
44754
- if (all == "") {
44755
- return totalDecs;
44756
- }
44757
- return decPlaces;
44758
- }
44759
-
44760
- /**
44761
- * Remove the specified number of digits from string by
44762
- * rounding. Proper rounding rules for scientific purposes
44763
- * are followed.
44764
- * This method may cause an extra significant figure
44765
- * to be added to the number. For example, if 999999
44766
- * were rounded, A one would carry over and become
44767
- * a significant figure. Those who call this method
44768
- * should check for this and call round again if needed.
44769
- *
44770
- * @param mantissa A string representing an whole number of arbitrary length.
44771
- * @param digits A number of digits to remove
44772
- * @return A string represted the rounded version of mantissa
44773
- */
44774
- function round(mantissa, digits) {
44775
- const last = mantissa.length - digits - 1;
44776
- if (last < 0) {
44777
- return "";
44778
- }
44779
- if (last >= mantissa.length - 1) {
44780
- return mantissa;
44781
- }
44782
- const nextToLast = mantissa.charAt(last + 1);
44783
- const lastChar = mantissa.charAt(last);
44784
- let roundUp = false;
44785
- if (nextToLast > "5") {
44786
- roundUp = true;
44787
- } else if (nextToLast == "5") {
44788
- for (let j = last + 2; j < mantissa.length; j++) {
44789
- if (mantissa.charAt(j) != "0") {
44790
- roundUp = true;
44791
- }
44792
- }
44793
- // @ts-expect-error - TS2362 - The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
44794
- if (lastChar % 2 == 1) {
44795
- roundUp = true;
44796
- }
44797
- }
44798
- let result = "";
44799
- for (let i = last; i >= 0; i--) {
44800
- const c = mantissa.charAt(i);
44801
- if (roundUp) {
44802
- let nextChar;
44803
- if (c == "9") {
44804
- nextChar = "0";
44805
- } else {
44806
- switch (c) {
44807
- case "0":
44808
- nextChar = "1";
44809
- break;
44810
- case "1":
44811
- nextChar = "2";
44812
- break;
44813
- case "2":
44814
- nextChar = "3";
44815
- break;
44816
- case "3":
44817
- nextChar = "4";
44818
- break;
44819
- case "4":
44820
- nextChar = "5";
44821
- break;
44822
- case "5":
44823
- nextChar = "6";
44824
- break;
44825
- case "6":
44826
- nextChar = "7";
44827
- break;
44828
- case "7":
44829
- nextChar = "8";
44830
- break;
44831
- case "8":
44832
- nextChar = "9";
44833
- break;
44834
- }
44835
- roundUp = false;
44836
- }
44837
- result = nextChar + result;
44838
- } else {
44839
- result = c + result;
44840
- }
44841
- }
44842
- if (roundUp) {
44843
- result = "1" + result;
44844
- }
44845
- return result;
44846
- }
44847
-
44848
- // TODO(joel): teach KAS how to accept an answer only if it's expressed in
44849
- const ALL = "all";
44850
- const countSigfigs = function (value) {
44851
- return new SignificantFigures(value).sigFigs;
44852
- };
44853
- /* I just wrote this, but it's old by analogy to `OldExpression`, in that it's
44854
- * the version that non-mathquill platforms get stuck with. Constructed with an
44855
- * <input>, a parser, popsicle sticks, and glue.
44856
- *
44857
- * In the same way as OldExpression, this parses continuously as you type, then
44858
- * shows and hides an error buddy. The error message is only shown after a
44859
- * rolling two second delay, but hidden immediately on further typing.
44860
- */
44861
- class OldUnitInput extends React__namespace.Component {
44862
- static contextType = PerseusI18nContext;
44863
- _errorTimeout;
44864
- static defaultProps = {
44865
- apiOptions: ApiOptions.defaults,
44866
- value: ""
44867
- };
44868
- static validate(userInput, rubric, strings) {
44869
- const answer = KAS__namespace.unitParse(rubric.value).expr;
44870
- const guess = KAS__namespace.unitParse(userInput);
44871
- if (!guess.parsed) {
44872
- return {
44873
- type: "invalid",
44874
- message: strings.dontUnderstandUnits
44875
- };
44876
- }
44877
-
44878
- // Note: we check sigfigs, then numerical correctness, then units, so
44879
- // the most significant things come last, that way the user will see
44880
- // the most important message.
44881
- let message = null;
44882
-
44883
- // did the user specify the right number of sigfigs?
44884
- // TODO(joel) - add a grading mode where the wrong number of sigfigs
44885
- // isn't marked wrong
44886
- const sigfigs = rubric.sigfigs;
44887
- const sigfigsCorrect = countSigfigs(guess.coefficient) === sigfigs;
44888
- if (!sigfigsCorrect) {
44889
- message = strings.checkSigFigs;
44890
- }
44891
-
44892
- // now we need to check that the answer is correct to the precision we
44893
- // require.
44894
- let numericallyCorrect;
44895
- try {
44896
- const x = new KAS__namespace.Var("x");
44897
- const equality = new KAS__namespace.Eq(answer.simplify(), "=", new KAS__namespace.Mul(x, guess.expr.simplify()));
44898
- const conversion = equality.solveLinearEquationForVariable(x);
44899
-
44900
- // Make sure the conversion factor between the user's input answer
44901
- // and the canonical answer is 1, to sigfig places.
44902
- // TODO(joel) is this sound?
44903
- numericallyCorrect = Number(conversion.eval()).toPrecision(sigfigs) === Number(1).toPrecision(sigfigs);
44904
- } catch (e) {
44905
- numericallyCorrect = false;
44906
- }
44907
- if (!numericallyCorrect) {
44908
- message = strings.answerNumericallyIncorrect;
44909
- }
44910
- let kasCorrect;
44911
- const guessUnit = primUnits(guess.expr.simplify());
44912
- const answerUnit = primUnits(answer.simplify());
44913
- if (rubric.accepting === ALL) {
44914
- // We're accepting all units - KAS does the hard work of figuring
44915
- // out if the user's unit is equivalent to the author's unit.
44916
- kasCorrect = KAS__namespace.compare(guessUnit, answerUnit).equal;
44917
- } else {
44918
- // Are any of the accepted units the same as what the user entered?
44919
- kasCorrect = ___default["default"](rubric.acceptingUnits).any(unit => {
44920
- const thisAnswerUnit = primUnits(KAS__namespace.unitParse(unit).unit.simplify());
44921
- return KAS__namespace.compare(thisAnswerUnit, guessUnit
44922
- // TODO(joel) - make this work as intended.
44923
- // { form: true }
44924
- ).equal;
44925
- });
44926
- }
44927
- if (!kasCorrect) {
44928
- message = strings.checkUnits;
44929
- }
44930
- const correct = kasCorrect && numericallyCorrect && sigfigsCorrect;
44931
- return {
44932
- type: "points",
44933
- earned: correct ? 1 : 0,
44934
- total: 1,
44935
- message
44936
- };
44937
- }
44938
- componentDidUpdate() {
44939
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
44940
- // eslint-disable-next-line no-restricted-syntax
44941
- // @ts-expect-error - TS2769 - No overload matches this call.
44942
- clearTimeout(this._errorTimeout);
44943
- if (KAS__namespace.unitParse(this.props.value).parsed) {
44944
- this._hideError();
44945
- } else {
44946
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
44947
- // eslint-disable-next-line no-restricted-syntax
44948
- // @ts-expect-error - TS2322 - Type 'Timeout' is not assignable to type 'number'.
44949
- this._errorTimeout = setTimeout(this._showError, 2000);
44950
- }
44951
- }
44952
- componentWillUnmount() {
44953
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
44954
- // eslint-disable-next-line no-restricted-syntax
44955
- // @ts-expect-error - TS2769 - No overload matches this call.
44956
- clearTimeout(this._errorTimeout);
44957
- }
44958
- _showError = () => {
44959
- if (this.props.value === "") {
44960
- return;
44961
- }
44962
-
44963
- // eslint-disable-next-line react/no-string-refs
44964
- // @ts-expect-error - TS2769 - No overload matches this call.
44965
- const $error = $__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.error));
44966
- // @ts-expect-error - TS2339 - Property 'is' does not exist on type 'JQueryStatic'.
44967
- if (!$error.is(":visible")) {
44968
- $error
44969
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
44970
- .css({
44971
- top: 50,
44972
- opacity: 0.1
44973
- }).show().animate({
44974
- top: 0,
44975
- opacity: 1.0
44976
- }, 300);
44977
- }
44978
- };
44979
- _hideError = () => {
44980
- // eslint-disable-next-line react/no-string-refs
44981
- // @ts-expect-error - TS2769 - No overload matches this call.
44982
- const $error = $__default["default"](ReactDOM__default["default"].findDOMNode(this.refs.error));
44983
- // @ts-expect-error - TS2339 - Property 'is' does not exist on type 'JQueryStatic'.
44984
- if ($error.is(":visible")) {
44985
- // @ts-expect-error - TS2339 - Property 'animate' does not exist on type 'JQueryStatic'.
44986
- $error.animate({
44987
- top: 50,
44988
- opacity: 0.1
44989
- }, 300, function () {
44990
- // eslint-disable-next-line @babel/no-invalid-this
44991
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
44992
- // eslint-disable-next-line @babel/no-invalid-this
44993
- $__default["default"](this).hide();
44994
- });
44995
- }
44996
- };
44997
- change = (() => {
44998
- var _this = this;
44999
- return function () {
45000
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
45001
- args[_key] = arguments[_key];
45002
- }
45003
- // @ts-expect-error - TS2345 - Argument of type 'readonly any[]' is not assignable to parameter of type 'any[]'.
45004
- return change.apply(_this, args);
45005
- };
45006
- })();
45007
- handleBlur = () => {
45008
- this.props.onBlur([]);
45009
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
45010
- // eslint-disable-next-line no-restricted-syntax
45011
- // @ts-expect-error - TS2769 - No overload matches this call.
45012
- clearTimeout(this._errorTimeout);
45013
- if (!KAS__namespace.unitParse(this.props.value).parsed) {
45014
- this._showError();
45015
- }
45016
- };
45017
- handleChange = event => {
45018
- this._hideError();
45019
- this.props.onChange({
45020
- value: event.target.value
45021
- });
45022
- };
45023
- simpleValidate = (rubric, onInputError) => {
45024
- return OldUnitInput.validate(this.getUserInput(), rubric, this.context.strings);
45025
- };
45026
- getUserInput = () => {
45027
- return this.props.value;
45028
- };
45029
-
45030
- // begin mobile stuff
45031
-
45032
- getInputPaths = () => {
45033
- // The widget itself is an input, so we return a single empty list to
45034
- // indicate this.
45035
- return [[]];
45036
- };
45037
- focusInputPath = inputPath => {
45038
- // eslint-disable-next-line react/no-string-refs
45039
- const input = ReactDOM__default["default"].findDOMNode(this.refs.input);
45040
- if (input) {
45041
- /**
45042
- * TODO(somewhatabstract, JIRA-XXXX):
45043
- * Change to using a ref callback so that focus() can be
45044
- * accessed.
45045
- */
45046
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element | Text'.
45047
- input.focus();
45048
- }
45049
- };
45050
- handleFocus = () => {
45051
- this.props.onFocus([]);
45052
- };
45053
- blurInputPath = inputPath => {
45054
- // eslint-disable-next-line react/no-string-refs
45055
- const input = ReactDOM__default["default"].findDOMNode(this.refs.input);
45056
- if (input) {
45057
- /**
45058
- * TODO(somewhatabstract, JIRA-XXXX):
45059
- * Change to using a ref callback so that focus() can be
45060
- * accessed.
45061
- */
45062
- // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'Element | Text'.
45063
- input.blur();
45064
- }
45065
- };
45066
- setInputValue = (path, newValue, cb) => {
45067
- this.props.onChange({
45068
- value: newValue
45069
- }, cb);
45070
- };
45071
- getDOMNodeForPath = () => {
45072
- // eslint-disable-next-line react/no-string-refs
45073
- return ReactDOM__default["default"].findDOMNode(this.refs.input);
45074
- };
45075
- getGrammarTypeForPath = inputPath => {
45076
- return "unit";
45077
- };
45078
-
45079
- // end mobile stuff
45080
-
45081
- // TODO(joel) think about showing the error buddy
45082
- render() {
45083
- return /*#__PURE__*/React__namespace.createElement("div", {
45084
- className: "old-unit-input"
45085
- }, /*#__PURE__*/React__namespace.createElement("input", {
45086
- onChange: this.handleChange,
45087
- ref: "input" // eslint-disable-line react/no-string-refs
45088
- ,
45089
- className: ClassNames.INTERACTIVE,
45090
- value: this.props.value,
45091
- onFocus: this.handleFocus,
45092
- onBlur: this.handleBlur
45093
- }), /*#__PURE__*/React__namespace.createElement("div", {
45094
- ref: "error",
45095
- className: "error",
45096
- style: {
45097
- display: "none"
45098
- }
45099
- }, this.context.strings.dontUnderstand));
45100
- }
45101
- }
45102
-
45103
- // Extract the primitive units from a unit expression. This first simplifies
45104
- // `expr` to a `Mul` like "5 kg m / s^2" then removes the first term.
45105
- const primUnits = function (expr) {
45106
- return expr.simplify().asMul().partition()[1].flatten().simplify();
45107
- };
45108
- var Unit = {
45109
- name: "unit-input",
45110
- displayName: "Unit input",
45111
- defaultAlignment: "inline-block",
45112
- widget: OldUnitInput,
45113
- transform: x => lens(x).del(["value"]).freeze(),
45114
- version: {
45115
- major: 0,
45116
- minor: 1
45117
- },
45118
- hidden: true
45119
- };
45120
-
45121
44234
  /**
45122
44235
  * This is a component that adds a link to the transcript for embedded videos in articles.
45123
44236
  */
@@ -45292,7 +44405,7 @@ var Video$1 = {
45292
44405
  };
45293
44406
 
45294
44407
  /* eslint-disable import/no-named-as-default */
45295
- 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];
44408
+ 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];
45296
44409
 
45297
44410
  /**
45298
44411
  * This should be called by all clients, specifying whether extra widgets are
@@ -45332,7 +44445,7 @@ const init = function (options) {
45332
44445
 
45333
44446
  // This file is processed by a Rollup plugin (replace) to inject the production
45334
44447
  const libName = "@khanacademy/perseus";
45335
- const libVersion = "26.0.0";
44448
+ const libVersion = "27.0.0";
45336
44449
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45337
44450
 
45338
44451
  const apiVersion = {
@@ -45580,6 +44693,7 @@ class HintRenderer extends React__namespace.Component {
45580
44693
  // eslint-disable-next-line react/no-string-refs
45581
44694
  , {
45582
44695
  ref: "renderer",
44696
+ hintMode: true,
45583
44697
  widgets: hint.widgets,
45584
44698
  content: hint.content || "",
45585
44699
  images: hint.images,
@@ -46890,6 +46004,223 @@ function treeToItem(node) {
46890
46004
  };
46891
46005
  }
46892
46006
 
46007
+ var isObject = function (obj) {
46008
+ return obj === Object(obj);
46009
+ };
46010
+ var merge = function () {
46011
+ var obj = {};
46012
+ for (var i = 0; i < arguments.length; i++) {
46013
+ var source = arguments[i];
46014
+ if (source) {
46015
+ for (var prop in source) {
46016
+ obj[prop] = source[prop];
46017
+ }
46018
+ }
46019
+ }
46020
+ return obj;
46021
+ };
46022
+ var clone = function (obj) {
46023
+ if (!isObject(obj)) {
46024
+ return obj;
46025
+ }
46026
+ return Array.isArray(obj) ? obj.slice() : merge(obj);
46027
+ };
46028
+
46029
+ /* TODO batch *all* mutations
46030
+ * idea: freeze / thaw implementations for all types
46031
+ * lens constructor thaws, freeze delegates to type's freeze
46032
+ */
46033
+
46034
+ // equivalents, without requiring it
46035
+ // find the implementation to use for a given object
46036
+ var dispatch = function (x) {
46037
+ if (Array.isArray(x)) {
46038
+ return arr;
46039
+ } else if (isObject(x)) {
46040
+ return obj;
46041
+ } else if (typeof x === "string") {
46042
+ return str;
46043
+ }
46044
+ };
46045
+
46046
+ // This is underscore with a different name
46047
+ var lens = function (obj) {
46048
+ if (obj instanceof lens) {
46049
+ return obj;
46050
+ }
46051
+ if (!(this instanceof lens)) {
46052
+ return new lens(obj);
46053
+ }
46054
+ var ops = dispatch(obj);
46055
+ this._wrapped = ops.thaw ? ops.thaw(obj) : obj;
46056
+ };
46057
+ lens.prototype.freeze = function () {
46058
+ var obj = this._wrapped;
46059
+ var ops = dispatch(obj);
46060
+ return ops.freeze ? ops.freeze(obj) : obj;
46061
+ };
46062
+ lens.prototype.zoom = function (lensArr) {
46063
+ if (this._zoomStack === undefined) {
46064
+ this._zoomStack = [];
46065
+ }
46066
+ this._zoomStack.push({
46067
+ zoom: lensArr,
46068
+ wrapped: this._wrapped
46069
+ });
46070
+ this._wrapped = lens(this._wrapped).get(lensArr);
46071
+ return this;
46072
+ };
46073
+ lens.prototype.deZoom = function () {
46074
+ var frame = this._zoomStack.pop();
46075
+ this._wrapped = lens(frame.wrapped).set(frame.zoom, this._wrapped).freeze();
46076
+ return this;
46077
+ };
46078
+ lens.prototype.get = function (lensArr) {
46079
+ var obj = this._wrapped;
46080
+ for (var i = 0; i < lensArr.length; i++) {
46081
+ obj = dispatch(obj).get(obj, lensArr[i]);
46082
+ }
46083
+ return obj;
46084
+ };
46085
+ lens.prototype.mod = function (lensArr, f) {
46086
+ var obj = this._wrapped;
46087
+ var newObj = clone(obj);
46088
+ var ops = dispatch(obj);
46089
+ if (lensArr.length === 0) {
46090
+ this._wrapped = f(this._wrapped);
46091
+ } else if (lensArr.length === 1) {
46092
+ this._wrapped = ops.mod(newObj, lensArr[0], f);
46093
+ } else {
46094
+ var monocle = lensArr[0];
46095
+ var shortLens = lensArr.slice(1);
46096
+
46097
+ // newObj = ops.mod(obj[monocle], shortLens, f);
46098
+
46099
+ newObj[monocle] = lens(obj[monocle]).mod(shortLens, f).freeze();
46100
+ this._wrapped = newObj;
46101
+ }
46102
+ return this;
46103
+ };
46104
+
46105
+ // TODO - move to individual files
46106
+ lens.prototype.merge = function (lensArr, props) {
46107
+ this._wrapped = lens(this._wrapped).mod(lensArr, function (oldProps) {
46108
+ return merge(oldProps, props);
46109
+ }).freeze();
46110
+ return this;
46111
+ };
46112
+
46113
+ // Lens must have length >= 1 or there would be nothing to return
46114
+ lens.prototype.del = function (lensArr) {
46115
+ var obj = this._wrapped;
46116
+ var ops = dispatch(obj);
46117
+ if (lensArr.length === 1) {
46118
+ this._wrapped = ops.del(obj, lensArr[0]);
46119
+ } else {
46120
+ var monocle = lensArr[0];
46121
+ var shortLens = lensArr.slice(1);
46122
+ var newObj = clone(obj);
46123
+ newObj[monocle] = lens(obj[monocle]).del(shortLens).freeze();
46124
+ this._wrapped = newObj;
46125
+ }
46126
+ return this;
46127
+ };
46128
+ lens.prototype.set = function (lensArr, set) {
46129
+ return this.mod(lensArr, function () {
46130
+ return set;
46131
+ });
46132
+ };
46133
+
46134
+ // ARRAY FUNCTIONS
46135
+
46136
+ const arr = {
46137
+ get: function (arr, monocle) {
46138
+ return arr[monocle];
46139
+ },
46140
+ set: function (arr, monocle, val) {
46141
+ var newArr = arr.splice();
46142
+ newArr[monocle] = val;
46143
+ return newArr;
46144
+ },
46145
+ mod: function (arr, monocle, f) {
46146
+ var newArr = arr.slice();
46147
+ newArr[monocle] = f(arr[monocle]);
46148
+ return newArr;
46149
+ },
46150
+ del: function (arr, monocle) {
46151
+ var newArr = arr.slice();
46152
+ newArr.splice(monocle, 1);
46153
+ return newArr;
46154
+ }
46155
+ };
46156
+
46157
+ /*
46158
+ // Lens must point to a member of an array. We'll insert into that array.
46159
+ lens.prototype.insertAt = function(lensArr, toInsert) {
46160
+ var obj = this._wrapped;
46161
+
46162
+ var arrLens = lensArr.slice(0, -1);
46163
+ var arr = lens(obj).get(arrLens).slice(); // slice to copy
46164
+
46165
+ var arrIdx = lensArr[lensArr.length-1];
46166
+ arr.splice(arrIdx, 0, toInsert);
46167
+ return lens(obj).set(arrLens, arr);
46168
+ };
46169
+
46170
+ lens.prototype.insertBefore = lens.prototype.insertAt;
46171
+ lens.prototype.insertAfter = function(lensArr, toInsert) {
46172
+ var newLens = lensArr.slice();
46173
+ newLens[newLens.length-1] += 1;
46174
+ return lens(this._wrapped).insertAt(newLens, toInsert);
46175
+ };
46176
+ */
46177
+
46178
+ // OBJECT FUNCTIONS
46179
+
46180
+ const obj = {
46181
+ get: function (obj, monocle) {
46182
+ return obj[monocle];
46183
+ },
46184
+ set: function (obj, monocle, val) {
46185
+ var newObj = clone(obj);
46186
+ newObj[monocle] = val;
46187
+ return newObj;
46188
+ },
46189
+ mod: function (obj, monocle, f) {
46190
+ var newObj = clone(obj);
46191
+ newObj[monocle] = f(obj[monocle]);
46192
+ return newObj;
46193
+ },
46194
+ del: function (obj, monocle) {
46195
+ var newObj = clone(obj);
46196
+ delete newObj[monocle];
46197
+ return newObj;
46198
+ }
46199
+ };
46200
+
46201
+ // STRING FUNCTIONS
46202
+
46203
+ const str = {
46204
+ get: function (arr, monocle) {
46205
+ return arr[monocle];
46206
+ },
46207
+ set: function (arr, monocle, val) {
46208
+ var newArr = arr.splice();
46209
+ newArr[monocle] = val;
46210
+ return newArr;
46211
+ },
46212
+ mod: function (arr, monocle, f) {
46213
+ var newArr = arr.splice();
46214
+ newArr[monocle] = f(arr[monocle]);
46215
+ return newArr;
46216
+ },
46217
+ del: function (arr, monocle) {
46218
+ var newArr = arr.slice();
46219
+ newArr.splice(monocle);
46220
+ return newArr;
46221
+ }
46222
+ };
46223
+
46893
46224
  // TODO(mdr)
46894
46225
  // TODO(mdr)
46895
46226
  // TODO(mdr)
@@ -47493,6 +46824,372 @@ function violatingWidgets(itemData) {
47493
46824
  return ___default["default"].uniq(widgets);
47494
46825
  }
47495
46826
 
46827
+ /* eslint-disable eqeqeq, @typescript-eslint/no-unused-vars */
46828
+
46829
+ /**
46830
+ * Format a floating point for display using the specified
46831
+ * number of significant figures and least significant decimal.
46832
+ * Scientific notation may used by this method if this
46833
+ * object is very small, very large, has many significant
46834
+ * figures, or the number of significant figures would be
46835
+ * ambiguous in the output if scientific notation were not
46836
+ * used.
46837
+ *
46838
+ * @param f A floating point number that should be displayed
46839
+ * @param sigFigs desired number of significant figures (integer).
46840
+ * @param sigDecs the least significant decimal place (integer).
46841
+ * @param scientific true iff scientific notation should always be used.
46842
+ * @return a string of this object formatted correctly.
46843
+ */
46844
+ function displaySigFigs(f, sigFigs, sigDecs, scientific) {
46845
+ const s = "" + f;
46846
+ let order = parseOrder(s);
46847
+ let mantissa = parseMantissa(s);
46848
+ let positive = parseSign(s);
46849
+ let add;
46850
+ let decAdd;
46851
+ let sigAdd;
46852
+ let zeroScientific = false;
46853
+ if (f == 0 || mantissa == "" || mantissa == "0") {
46854
+ mantissa = "";
46855
+ for (let i = 0; i < sigFigs; i++) {
46856
+ mantissa += "0";
46857
+ }
46858
+ order = sigFigs + sigDecs;
46859
+ if (sigDecs < 0 && -sigDecs >= sigFigs) {
46860
+ zeroScientific = true;
46861
+ }
46862
+ } else {
46863
+ decAdd = order - mantissa.length - sigDecs;
46864
+ sigAdd = sigFigs - mantissa.length;
46865
+ add = Math.min(sigAdd, decAdd);
46866
+ if (add < 0) {
46867
+ let rounded = round(mantissa, -add);
46868
+ if (rounded.length > mantissa.length + add) {
46869
+ order++;
46870
+ if (decAdd > sigAdd) {
46871
+ rounded = round(rounded, 1);
46872
+ }
46873
+ }
46874
+ mantissa = rounded;
46875
+ } else if (add > 0) {
46876
+ for (let i = 0; i < add; i++) {
46877
+ mantissa += "0";
46878
+ }
46879
+ }
46880
+ if (mantissa == "" || mantissa == "0") {
46881
+ mantissa = "0";
46882
+ positive = true;
46883
+ order = 1 + sigDecs;
46884
+ if (order != 0) {
46885
+ zeroScientific = true;
46886
+ }
46887
+ }
46888
+ }
46889
+ const useScientific = scientific || mantissa.length > 20 || order > 4 || order < -2 || order - mantissa.length > 0 && trailingZeros(mantissa) > 0 || zeroScientific;
46890
+ let returnVal = "";
46891
+ if (!positive) {
46892
+ returnVal += "-";
46893
+ }
46894
+ if (useScientific) {
46895
+ returnVal += mantissa.charAt(0);
46896
+ if (mantissa.length > 1) {
46897
+ returnVal += "." + mantissa.substring(1, mantissa.length);
46898
+ }
46899
+ if (order - 1 != 0) {
46900
+ returnVal += " x 10^" + (order - 1);
46901
+ }
46902
+ } else {
46903
+ let wholePart = "";
46904
+ let fractPart = "";
46905
+ let needDot = true;
46906
+ if (order > 0) {
46907
+ if (mantissa.length > order) {
46908
+ wholePart = mantissa.substring(0, order);
46909
+ fractPart = mantissa.substring(order, mantissa.length);
46910
+ } else {
46911
+ wholePart = mantissa;
46912
+ needDot = trailingZeros(mantissa) != 0;
46913
+ for (let i = 0; i < order - mantissa.length; i++) {
46914
+ wholePart += "0";
46915
+ }
46916
+ }
46917
+ } else {
46918
+ for (let i = 0; i < -order; i++) {
46919
+ fractPart += "0";
46920
+ }
46921
+ fractPart += mantissa;
46922
+ }
46923
+ returnVal += (wholePart == "" ? "0" : wholePart) + (needDot ? "." : "") + fractPart;
46924
+ }
46925
+ return returnVal;
46926
+ }
46927
+
46928
+ /**
46929
+ * Count the significant trailing zeros on this object.
46930
+ *
46931
+ * @return the number of trailing zeros
46932
+ */
46933
+ function trailingZeros(mantissa) {
46934
+ let zeros = 0;
46935
+ for (let i = mantissa.length - 1; i >= 0; i--) {
46936
+ const c = mantissa.charAt(i);
46937
+ if (c == "0") {
46938
+ zeros++;
46939
+ } else {
46940
+ return zeros;
46941
+ }
46942
+ }
46943
+ return zeros;
46944
+ }
46945
+
46946
+ /**
46947
+ * Parse a string representation of a floating point
46948
+ * and pull out the sign.
46949
+ *
46950
+ * @param s the string representation of a floating point.
46951
+ * @return true iff this is a positive number
46952
+ */
46953
+ function parseSign(s) {
46954
+ let beginning = true;
46955
+ let seenDot = false;
46956
+ let seenSomething = false;
46957
+ let zeros = "";
46958
+ let all = "";
46959
+ let pos = true;
46960
+ for (let i = 0; i < s.length; i++) {
46961
+ const c = s.charAt(i);
46962
+ if (c >= "1" && c <= "9") {
46963
+ all += zeros + c;
46964
+ zeros = "";
46965
+ seenSomething = true;
46966
+ beginning = false;
46967
+ } else if (c == "0") {
46968
+ if (seenDot) {
46969
+ if (seenSomething) {
46970
+ all += zeros + c;
46971
+ zeros = "";
46972
+ }
46973
+ } else {
46974
+ if (seenSomething) {
46975
+ zeros += c;
46976
+ }
46977
+ }
46978
+ beginning = false;
46979
+ } else if (!seenDot && c == ".") {
46980
+ all += zeros;
46981
+ zeros = "";
46982
+ seenDot = true;
46983
+ beginning = false;
46984
+ } else if (c == "e" || c == "E" && i + 1 < s.length) {
46985
+ parseInt(s.substring(i + 1, s.length));
46986
+ i = s.length;
46987
+ } else if (beginning && (c == "+" || c == "-")) {
46988
+ if (c == "-") {
46989
+ pos = !pos;
46990
+ }
46991
+ }
46992
+ }
46993
+ if (all == "") {
46994
+ return true;
46995
+ }
46996
+ return pos;
46997
+ }
46998
+
46999
+ /**
47000
+ * Parse a string representation of a floating point
47001
+ * and pull out the mantissa.
47002
+ *
47003
+ * @param s the string representation of a floating point.
47004
+ * @return the mantissa of this number.
47005
+ */
47006
+ function parseMantissa(s) {
47007
+ let seenDot = false;
47008
+ let seenSomething = false;
47009
+ let zeros = "";
47010
+ let leadZeros = "";
47011
+ let all = "";
47012
+ for (let i = 0; i < s.length; i++) {
47013
+ const c = s.charAt(i);
47014
+ if (c >= "1" && c <= "9") {
47015
+ all += zeros + c;
47016
+ zeros = "";
47017
+ seenSomething = true;
47018
+ } else if (c == "0") {
47019
+ if (seenDot) {
47020
+ if (seenSomething) {
47021
+ all += zeros + c;
47022
+ zeros = "";
47023
+ } else {
47024
+ leadZeros += c;
47025
+ }
47026
+ } else {
47027
+ if (seenSomething) {
47028
+ leadZeros += c;
47029
+ zeros += c;
47030
+ } else {
47031
+ leadZeros += c;
47032
+ }
47033
+ }
47034
+ } else if (!seenDot && c == ".") {
47035
+ all += zeros;
47036
+ zeros = "";
47037
+ seenDot = true;
47038
+ } else if (c == "e" || c == "E" && i + 1 < s.length) {
47039
+ parseInt(s.substring(i + 1, s.length));
47040
+ i = s.length;
47041
+ } else ;
47042
+ }
47043
+ if (all == "") {
47044
+ return leadZeros;
47045
+ }
47046
+ return all;
47047
+ }
47048
+
47049
+ /**
47050
+ * Parse a string representation of a floating point
47051
+ * and pull out the exponent.
47052
+ *
47053
+ * @param s the string representation of a floating point.
47054
+ * @return (integer) the number after the e.
47055
+ */
47056
+ function parseOrder(s) {
47057
+ let seenDot = false;
47058
+ let seenSomething = false;
47059
+ let zeros = "";
47060
+ let all = "";
47061
+ let decPlaces = 0;
47062
+ let totalDecs = 0;
47063
+ for (let i = 0; i < s.length; i++) {
47064
+ const c = s.charAt(i);
47065
+ if (c >= "1" && c <= "9") {
47066
+ all += zeros + c;
47067
+ zeros = "";
47068
+ seenSomething = true;
47069
+ if (!seenDot) {
47070
+ totalDecs++;
47071
+ decPlaces++;
47072
+ }
47073
+ } else if (c == "0") {
47074
+ if (seenDot) {
47075
+ if (seenSomething) {
47076
+ all += zeros + c;
47077
+ zeros = "";
47078
+ } else {
47079
+ decPlaces--;
47080
+ }
47081
+ } else {
47082
+ totalDecs++;
47083
+ if (seenSomething) {
47084
+ decPlaces++;
47085
+ zeros += c;
47086
+ }
47087
+ }
47088
+ } else if (!seenDot && c == ".") {
47089
+ all += zeros;
47090
+ zeros = "";
47091
+ seenDot = true;
47092
+ } else if (c == "e" || c == "E" && i + 1 < s.length) {
47093
+ const raised = parseInt(s.substring(i + 1, s.length));
47094
+ decPlaces += raised;
47095
+ totalDecs += raised;
47096
+ i = s.length;
47097
+ } else ;
47098
+ }
47099
+ if (all == "") {
47100
+ return totalDecs;
47101
+ }
47102
+ return decPlaces;
47103
+ }
47104
+
47105
+ /**
47106
+ * Remove the specified number of digits from string by
47107
+ * rounding. Proper rounding rules for scientific purposes
47108
+ * are followed.
47109
+ * This method may cause an extra significant figure
47110
+ * to be added to the number. For example, if 999999
47111
+ * were rounded, A one would carry over and become
47112
+ * a significant figure. Those who call this method
47113
+ * should check for this and call round again if needed.
47114
+ *
47115
+ * @param mantissa A string representing an whole number of arbitrary length.
47116
+ * @param digits A number of digits to remove
47117
+ * @return A string represted the rounded version of mantissa
47118
+ */
47119
+ function round(mantissa, digits) {
47120
+ const last = mantissa.length - digits - 1;
47121
+ if (last < 0) {
47122
+ return "";
47123
+ }
47124
+ if (last >= mantissa.length - 1) {
47125
+ return mantissa;
47126
+ }
47127
+ const nextToLast = mantissa.charAt(last + 1);
47128
+ const lastChar = mantissa.charAt(last);
47129
+ let roundUp = false;
47130
+ if (nextToLast > "5") {
47131
+ roundUp = true;
47132
+ } else if (nextToLast == "5") {
47133
+ for (let j = last + 2; j < mantissa.length; j++) {
47134
+ if (mantissa.charAt(j) != "0") {
47135
+ roundUp = true;
47136
+ }
47137
+ }
47138
+ // @ts-expect-error - TS2362 - The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
47139
+ if (lastChar % 2 == 1) {
47140
+ roundUp = true;
47141
+ }
47142
+ }
47143
+ let result = "";
47144
+ for (let i = last; i >= 0; i--) {
47145
+ const c = mantissa.charAt(i);
47146
+ if (roundUp) {
47147
+ let nextChar;
47148
+ if (c == "9") {
47149
+ nextChar = "0";
47150
+ } else {
47151
+ switch (c) {
47152
+ case "0":
47153
+ nextChar = "1";
47154
+ break;
47155
+ case "1":
47156
+ nextChar = "2";
47157
+ break;
47158
+ case "2":
47159
+ nextChar = "3";
47160
+ break;
47161
+ case "3":
47162
+ nextChar = "4";
47163
+ break;
47164
+ case "4":
47165
+ nextChar = "5";
47166
+ break;
47167
+ case "5":
47168
+ nextChar = "6";
47169
+ break;
47170
+ case "6":
47171
+ nextChar = "7";
47172
+ break;
47173
+ case "7":
47174
+ nextChar = "8";
47175
+ break;
47176
+ case "8":
47177
+ nextChar = "9";
47178
+ break;
47179
+ }
47180
+ roundUp = false;
47181
+ }
47182
+ result = nextChar + result;
47183
+ } else {
47184
+ result = c + result;
47185
+ }
47186
+ }
47187
+ if (roundUp) {
47188
+ result = "1" + result;
47189
+ }
47190
+ return result;
47191
+ }
47192
+
47496
47193
  /**
47497
47194
  * Some tests require some or all of the widgets and editors to be registered
47498
47195
  * in order for them to work. Requiring this file will register all of the
@@ -48214,9 +47911,6 @@ function generateTestInteractiveGraphWidget() {
48214
47911
  markings: "graph",
48215
47912
  labels: ["x", "y"],
48216
47913
  showProtractor: false,
48217
- showRuler: false,
48218
- rulerLabel: "mm",
48219
- rulerTicks: 2,
48220
47914
  range: [[-10, 10], [-10, 10]],
48221
47915
  graph: {
48222
47916
  type: "angle"
@@ -48328,7 +48022,6 @@ const widgetNameToEnum = {
48328
48022
  simulator: "SIMULATOR",
48329
48023
  sorter: "SORTER",
48330
48024
  table: "TABLE",
48331
- "unit-input": "UNIT",
48332
48025
  video: "VIDEO"
48333
48026
  };
48334
48027
  function convertWidgetNameToEnum(name) {
@@ -48410,6 +48103,8 @@ exports.getAnswerFromUserInput = getAnswerFromUserInput;
48410
48103
  exports.getAnswersFromWidgets = getAnswersFromWidgets;
48411
48104
  exports.getCorrectAnswerForWidgetId = getCorrectAnswerForWidgetId;
48412
48105
  exports.getLineCoords = getLineCoords;
48106
+ exports.getLinearSystemCoords = getLinearSystemCoords;
48107
+ exports.getSegmentCoords = getSegmentCoords;
48413
48108
  exports.getValidWidgetIds = getValidWidgetIds;
48414
48109
  exports.getWidgetFromWidgetMap = getWidgetFromWidgetMap;
48415
48110
  exports.getWidgetIdsFromContent = getWidgetIdsFromContent;