@khanacademy/perseus 19.1.8 → 19.2.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
@@ -244,7 +244,7 @@ class MathInput extends React.Component {
244
244
  className = className + " " + this.props.className;
245
245
  }
246
246
  return /*#__PURE__*/React.createElement(View, {
247
- style: [styles$B.outerWrapper, this.state.focused && styles$B.wrapperFocused, this.props.hasError && styles$B.wrapperError]
247
+ style: [styles$C.outerWrapper, this.state.focused && styles$C.wrapperFocused, this.props.hasError && styles$C.wrapperError]
248
248
  }, /*#__PURE__*/React.createElement("div", {
249
249
  style: {
250
250
  display: "flex",
@@ -277,7 +277,7 @@ class MathInput extends React.Component {
277
277
  var _this$props$keypadBut, _this$props;
278
278
  return /*#__PURE__*/React.createElement(PopoverContentCore, {
279
279
  closeButtonVisible: true,
280
- style: styles$B.popoverContent
280
+ style: styles$C.popoverContent
281
281
  }, /*#__PURE__*/React.createElement(DesktopKeypad, _extends({
282
282
  onAnalyticsEvent: this.props.analytics.onAnalyticsEvent,
283
283
  extraKeys: this.props.extraKeys,
@@ -321,9 +321,9 @@ const MathInputIcon = ({
321
321
  color = Color.offBlack;
322
322
  break;
323
323
  }
324
- const dynamicClass = active || focused ? styles$B.iconActive : styles$B.iconInactive;
324
+ const dynamicClass = active || focused ? styles$C.iconActive : styles$C.iconInactive;
325
325
  return /*#__PURE__*/React.createElement(View, {
326
- style: [styles$B.iconContainer, dynamicClass]
326
+ style: [styles$C.iconContainer, dynamicClass]
327
327
  }, /*#__PURE__*/React.createElement("svg", {
328
328
  xmlns: "http://www.w3.org/2000/svg",
329
329
  width: "16",
@@ -368,7 +368,7 @@ const inputFocused = {
368
368
  borderColor: Color.blue,
369
369
  margin: -1
370
370
  };
371
- const styles$B = StyleSheet.create({
371
+ const styles$C = StyleSheet.create({
372
372
  iconContainer: {
373
373
  display: "flex",
374
374
  justifyContent: "center",
@@ -8685,7 +8685,12 @@ class Graphie$1 {
8685
8685
  writable: true,
8686
8686
  value: void 0
8687
8687
  });
8688
+ // The primary drawing layer
8688
8689
  this.raphael = void 0;
8690
+ // The layer that all visuals with mouse handlers are drawn into
8691
+ this.mouselayer = void 0;
8692
+ this._mouselayerWrapper = void 0;
8693
+ this._visiblelayerWrapper = void 0;
8689
8694
  this.isMobile = false;
8690
8695
  // Set up some reasonable defaults
8691
8696
  this.currentStyle = {
@@ -9526,13 +9531,126 @@ class Graphie$1 {
9526
9531
  return processed;
9527
9532
  }
9528
9533
 
9529
- // This is a stub that's overridden in interactive.ts
9530
- getMouseCoord(event) {
9531
- throw new Error("getMouseCoord is a stub, and is not implemented");
9534
+ // Graphie puts text spans on top of the SVG, which looks good, but gets
9535
+ // in the way of mouse events. This adds another SVG element on top
9536
+ // of everything else where we can add invisible shapes with mouse
9537
+ // handlers wherever we want.
9538
+ addMouseLayer(options) {
9539
+ const localOptions = _extends({
9540
+ allowScratchpad: false,
9541
+ setDrawingAreaAvailable: function () {}
9542
+ }, options);
9543
+ const mouselayerZIndex = 2;
9544
+ this.mouselayer = Raphael$1(this.el, this.xpixels, this.ypixels);
9545
+ $(this.mouselayer.canvas).css("z-index", mouselayerZIndex);
9546
+ if (localOptions.onClick || localOptions.onMouseDown || localOptions.onMouseMove || localOptions.onMouseOver || localOptions.onMouseOut) {
9547
+ const canvasClickTarget = this.mouselayer.rect(0, 0, this.xpixels, this.ypixels).attr({
9548
+ fill: "#000",
9549
+ opacity: 0
9550
+ });
9551
+ let isClickingCanvas = false;
9552
+ $(this.mouselayer.canvas).on("vmousedown", e => {
9553
+ if (e.target === canvasClickTarget[0]) {
9554
+ if (localOptions.onMouseDown) {
9555
+ localOptions.onMouseDown(this.getMouseCoord(e));
9556
+ }
9557
+ isClickingCanvas = true;
9558
+ if (localOptions.onMouseMove) {
9559
+ const handler = localOptions.onMouseMove;
9560
+ $(document).bind("vmousemove.mouseLayer", e => {
9561
+ if (isClickingCanvas) {
9562
+ e.preventDefault();
9563
+ handler(this.getMouseCoord(e));
9564
+ }
9565
+ });
9566
+ }
9567
+ $(document).bind("vmouseup.mouseLayer", e => {
9568
+ $(document).unbind(".mouseLayer");
9569
+
9570
+ // Only register clicks that started on the canvas,
9571
+ // and not on another mouseLayer target
9572
+ if (isClickingCanvas && localOptions.onClick) {
9573
+ localOptions.onClick(this.getMouseCoord(e));
9574
+ }
9575
+ isClickingCanvas = false;
9576
+ });
9577
+ }
9578
+ });
9579
+ if (localOptions.onMouseOver) {
9580
+ const handler = localOptions.onMouseOver;
9581
+ $(this.mouselayer.canvas).on("vmouseover", e => {
9582
+ handler(this.getMouseCoord(e));
9583
+ });
9584
+ }
9585
+ if (localOptions.onMouseOut) {
9586
+ const handler = localOptions.onMouseOut;
9587
+ $(this.mouselayer.canvas).on("vmouseout", e => {
9588
+ handler(this.getMouseCoord(e));
9589
+ });
9590
+ }
9591
+ }
9592
+ if (!localOptions.allowScratchpad) {
9593
+ localOptions.setDrawingAreaAvailable == null || localOptions.setDrawingAreaAvailable(false);
9594
+ }
9595
+
9596
+ // Add mouse and visible wrapper layers for DOM-node-wrapped movables
9597
+ this._mouselayerWrapper = document.createElement("div");
9598
+ $(this._mouselayerWrapper).css({
9599
+ position: "absolute",
9600
+ left: 0,
9601
+ top: 0,
9602
+ zIndex: mouselayerZIndex
9603
+ });
9604
+ this._visiblelayerWrapper = document.createElement("div");
9605
+ $(this._visiblelayerWrapper).css({
9606
+ position: "absolute",
9607
+ left: 0,
9608
+ top: 0
9609
+ });
9610
+ const el = this.el;
9611
+ el.appendChild(this._visiblelayerWrapper);
9612
+ el.appendChild(this._mouselayerWrapper);
9613
+
9614
+ // Add functions for adding to wrappers
9615
+ this.addToMouseLayerWrapper = el => {
9616
+ var _this$_mouselayerWrap;
9617
+ (_this$_mouselayerWrap = this._mouselayerWrapper) == null || _this$_mouselayerWrap.appendChild(el);
9618
+ };
9619
+ this.addToVisibleLayerWrapper = el => {
9620
+ var _this$_visiblelayerWr;
9621
+ (_this$_visiblelayerWr = this._visiblelayerWrapper) == null || _this$_visiblelayerWr.appendChild(el);
9622
+ };
9623
+ }
9624
+ addToMouseLayerWrapper(el) {
9625
+ throw new Error("addToMouseLayerWrapper is not ready. Call addMouseLayer() first.");
9626
+ }
9627
+ addToVisibleLayerWrapper(el) {
9628
+ throw new Error("addToVisibleLayerWrapper is not ready. Call addMouseLayer() first.");
9629
+ }
9630
+ /**
9631
+ * Get mouse coordinates in pixels
9632
+ */
9633
+ getMousePx(event) {
9634
+ const offset = $(this.el).offset();
9635
+ const mouseX =
9636
+ // @ts-expect-error - TS18048 - 'event.pageX' is possibly 'undefined'.
9637
+ event.pageX -
9638
+ // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
9639
+ offset.left;
9640
+ const mouseY =
9641
+ // @ts-expect-error - TS18048 - 'event.pageY' is possibly 'undefined'.
9642
+ event.pageY -
9643
+ // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
9644
+ offset.top;
9645
+ return [mouseX, mouseY];
9532
9646
  }
9533
9647
 
9534
- // This is a stub that's overridden in interactive.ts
9535
- addMouseLayer(options) {}
9648
+ /**
9649
+ * Get mouse coordinates in graph coordinates
9650
+ */
9651
+ getMouseCoord(event) {
9652
+ return this.unscalePoint(this.getMousePx(event));
9653
+ }
9536
9654
  }
9537
9655
  const labelDirections = {
9538
9656
  center: [-0.5, -0.5],
@@ -10084,89 +10202,49 @@ const GraphUtils = {
10084
10202
  }
10085
10203
  })($, window, document);
10086
10204
 
10087
- /**
10088
- * A work-in-progress of _ methods for objects.
10089
- * That is, they take an object as a parameter,
10090
- * and return an object instead of an array.
10091
- *
10092
- * TODO(aria): Move this out of interactive2
10093
- */
10094
-
10095
- /**
10096
- * Does a pluck on keys inside objects in an object
10097
- *
10098
- * Ex:
10099
- * tools = {
10100
- * translation: {
10101
- * enabled: true
10102
- * },
10103
- * rotation: {
10104
- * enabled: false
10105
- * }
10106
- * };
10107
- * pluckObject(tools, "enabled") returns {
10108
- * translation: true
10109
- * rotation: false
10110
- * }
10111
- */
10112
- const pluck = function pluck(table, subKey) {
10113
- return _.object(_.map(table, function (value, key) {
10114
- return [key, value[subKey]];
10115
- }));
10116
- };
10117
-
10118
- /**
10119
- * Maps an object to an object
10120
- *
10121
- * > mapObject({a: '1', b: '2'}, (value, key) => {
10122
- * return value + 1;
10123
- * });
10124
- * {a: 2, b: 3}
10125
- */
10126
- const mapObject$2 = function mapObject(obj, lambda) {
10127
- const result = {};
10128
- _.each(_.keys(obj), function (key) {
10129
- // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
10130
- result[key] = lambda(obj[key], key);
10131
- });
10132
- return result;
10133
- };
10205
+ // Given `coord` and `angle`, find the point where a line extended
10206
+ // from `coord` in the direction of `angle` would be clipped by the
10207
+ // edge of the graphie canvas.
10208
+ const getClipPoint = function getClipPoint(graph, coord, angle) {
10209
+ // Actually put the arrowheads 4px from the edge so they have
10210
+ // a bit of room
10211
+ const xExtent = graph.range[0][1] - graph.range[0][0];
10212
+ const yExtent = graph.range[1][1] - graph.range[1][0];
10134
10213
 
10135
- /**
10136
- * Maps an array to an object
10137
- *
10138
- * > mapObjectFromArray(['a', 'b'], function(elem) {
10139
- * return elem + elem;
10140
- * });
10141
- * {a: 'aa', b: 'bb'}
10142
- */
10143
- const mapObjectFromArray$1 = function mapObjectFromArray(arr, lambda) {
10144
- const result = {};
10145
- _.each(arr, function (elem) {
10146
- result[elem] = lambda(elem);
10147
- });
10148
- return result;
10149
- };
10150
- var objective_ = {
10151
- pluck: pluck,
10152
- mapObject: mapObject$2,
10153
- mapObjectFromArray: mapObjectFromArray$1
10214
+ // shoot a point off into the distance ...
10215
+ const distance = xExtent + yExtent;
10216
+ // we need to scale the point according to the scale of the axes
10217
+ const angleVec = graph.unscaleVector(vector$1.cartFromPolarDeg(1, angle));
10218
+ const distVec = vector$1.scale(vector$1.normalize(angleVec), distance);
10219
+ const farCoord = vector$1.add(coord, distVec);
10220
+ const scaledAngle = vector$1.polarDegFromCart(angleVec)[1];
10221
+ // ... and then bring it back
10222
+ const clipPoint = graph.constrainToBoundsOnAngle(farCoord, 4, scaledAngle * Math.PI / 180);
10223
+ return clipPoint;
10154
10224
  };
10155
10225
 
10156
10226
  /* eslint-disable @babel/no-invalid-this */
10157
10227
 
10158
10228
  /*
10159
- * These functions, when called on the wrapped object, simply pass the
10229
+ * attr() and animate() simply pass the
10160
10230
  * arguments to the underlying Raphael object.
10161
10231
  */
10162
- const PASS_TO_RAPHAEL = ["attr", "animate"];
10163
- const WrappedDefaults = _.extend({
10164
- transform: function (transformation) {
10232
+
10233
+ class WrappedDrawing {
10234
+ constructor(graphie, initialPoint) {
10235
+ this.graphie = void 0;
10236
+ this.wrapper = void 0;
10237
+ this.visibleShape = void 0;
10238
+ this.initialPoint = void 0;
10239
+ this.graphie = graphie;
10240
+ this.initialPoint = initialPoint;
10241
+ }
10242
+ transform(transformation) {
10165
10243
  const prefixedTransform = InteractiveUtil.getPrefixedTransform();
10166
10244
  // @ts-expect-error - TS2538 - Type 'null' cannot be used as an index type. | TS2538 - Type 'undefined' cannot be used as an index type.
10167
10245
  this.wrapper.style[prefixedTransform] = transformation;
10168
- },
10169
- toFront: function () {
10246
+ }
10247
+ toFront() {
10170
10248
  const parentNode = this.wrapper.parentNode;
10171
10249
  // TODO(emily): Sometimes, we call `.remove()` but then hold a
10172
10250
  // reference to this object, and sometimes call `.toFront` on it.
@@ -10175,152 +10253,214 @@ const WrappedDefaults = _.extend({
10175
10253
  if (parentNode) {
10176
10254
  parentNode.appendChild(this.wrapper);
10177
10255
  }
10178
- },
10179
- toBack: function () {
10256
+ }
10257
+ toBack() {
10180
10258
  const parentNode = this.wrapper.parentNode;
10181
- if (parentNode.firstChild !== this.wrapper) {
10259
+ if (parentNode != null && parentNode.firstChild !== this.wrapper) {
10182
10260
  parentNode.insertBefore(this.wrapper, parentNode.firstChild);
10183
10261
  }
10184
- },
10185
- remove: function () {
10262
+ }
10263
+ remove() {
10186
10264
  this.visibleShape.remove();
10187
10265
  $(this.wrapper).remove();
10188
- },
10189
- getMouseTarget: function () {
10266
+ }
10267
+ getMouseTarget() {
10190
10268
  return this.visibleShape[0];
10191
- },
10192
- moveTo: function (point) {
10269
+ }
10270
+ moveTo(point) {
10193
10271
  const delta = vector$1.subtract(this.graphie.scalePoint(point), this.graphie.scalePoint(this.initialPoint));
10194
10272
  const do3dTransform = InteractiveUtil.getCanUse3dTransform();
10195
10273
  const transformation = "translateX(" + delta[0] + "px) " + "translateY(" + delta[1] + "px)" + (do3dTransform ? " translateZ(0)" : "");
10196
10274
  this.transform(transformation);
10197
- },
10198
- hide: function () {
10275
+ }
10276
+ hide() {
10199
10277
  this.visibleShape.hide();
10200
- },
10201
- show: function () {
10278
+ }
10279
+ show() {
10202
10280
  this.visibleShape.show();
10203
10281
  }
10204
- }, objective_.mapObjectFromArray(PASS_TO_RAPHAEL, function (attribute) {
10205
- return function (...args) {
10206
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10207
- this.visibleShape[attribute](...args);
10208
- };
10209
- }));
10282
+ attr(...args) {
10283
+ this.visibleShape.attr(...args);
10284
+ }
10285
+ animate(...args) {
10286
+ this.visibleShape.animate(...args);
10287
+ }
10288
+ }
10210
10289
 
10211
10290
  const DEFAULT_OPTIONS$2 = {
10212
- maxScale: 1,
10213
- mouselayer: false,
10214
- shadow: false,
10215
- disableMouseEventsOnWrapper: false
10291
+ center: null,
10292
+ // gets ignored in `graphie.fixedPath` if `null`
10293
+ createPath: null,
10294
+ // gets defaulted in `graphie.fixedPath` if `null`
10295
+ mouselayer: false
10216
10296
  };
10217
- const WrappedEllipse = function WrappedEllipse(graphie, center, radii, options) {
10218
- options = _.extend({}, DEFAULT_OPTIONS$2, options);
10219
-
10220
- // Add `wrapper`, `visibleShape`, and remaining properties.
10221
- const fixedEllipse = graphie.fixedEllipse(center, radii, options.maxScale, options.padding);
10222
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10223
- _.extend(this, fixedEllipse, {
10224
- graphie: graphie,
10225
- initialPoint: center
10226
- });
10227
- if (options.interactiveKindForTesting) {
10228
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10229
- this.wrapper.setAttribute("data-interactive-kind-for-testing", options.interactiveKindForTesting);
10297
+ class WrappedPath extends WrappedDrawing {
10298
+ constructor(graphie, points, options) {
10299
+ const initialPoint = graphie.scalePoint(_.head(points));
10300
+ super(graphie, initialPoint);
10301
+ this.wrapper = void 0;
10302
+ this.visibleShape = void 0;
10303
+ options = _.extend({}, DEFAULT_OPTIONS$2, options);
10304
+ const fixedPath = graphie.fixedPath(points, options.center, options.createPath);
10305
+ this.wrapper = fixedPath.wrapper;
10306
+ this.visibleShape = fixedPath.visibleShape;
10307
+
10308
+ // Add to appropriate graphie layer
10309
+ if (options.mouselayer) {
10310
+ // Disable browser handling of all panning and zooming gestures on the
10311
+ // movable wrapper so that when moved the browser does not scroll page
10312
+ this.wrapper.style.touchAction = "none";
10313
+ this.graphie.addToMouseLayerWrapper(this.wrapper);
10314
+ } else {
10315
+ this.graphie.addToVisibleLayerWrapper(this.wrapper);
10316
+ }
10230
10317
  }
10318
+ }
10231
10319
 
10232
- // Add to appropriate graphie layer
10233
- if (options.mouselayer) {
10234
- // Disable browser handling of all panning and zooming gestures on the
10235
- // movable wrapper so that when moved the browser does not scroll page
10236
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10237
- this.wrapper.style.touchAction = "none";
10238
-
10239
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10240
- this.graphie.addToMouseLayerWrapper(this.wrapper);
10241
- } else {
10242
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10243
- this.graphie.addToVisibleLayerWrapper(this.wrapper);
10320
+ class Arrowhead extends WrappedPath {
10321
+ constructor(graphie, style) {
10322
+ // Points that define the arrowhead
10323
+ const center = [0.75, 0];
10324
+ const points = [[-3, 4], [-2.75, 2.5], [0, 0.25], center, [0, -0.25], [-2.75, -2.5], [-3, -4]].map(point => {
10325
+ // Scale points by `Arrowhead.scale` around `center`
10326
+ const pv = vector$1.subtract(point, center);
10327
+ const pvScaled = vector$1.scale(pv, Arrowhead.scale);
10328
+ return vector$1.add(center, pvScaled);
10329
+ });
10330
+ const unscaledPoints = points.map(graphie.unscalePoint);
10331
+ super(graphie, unscaledPoints, {
10332
+ center: graphie.unscalePoint(center),
10333
+ createPath: createCubicPath
10334
+ });
10335
+ this.center = void 0;
10336
+ this.center = center;
10337
+ this.attr(_.extend({
10338
+ "stroke-linejoin": "round",
10339
+ "stroke-linecap": "round",
10340
+ "stroke-dasharray": ""
10341
+ }, style));
10244
10342
  }
10245
- if (options.shadow) {
10246
- const filter = "drop-shadow(0px 0px 5px rgba(0, 0, 0, 0.5))";
10247
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10248
- const wrapper = this.wrapper;
10249
- wrapper.style.filter = filter;
10250
-
10251
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10252
- this.moveTo = function (point) {
10253
- const delta = vector$1.subtract(this.graphie.scalePoint(point), this.graphie.scalePoint(this.initialPoint));
10254
- const do3dTransform = InteractiveUtil.getCanUse3dTransform();
10255
- const transform = "translateX(" + Math.round(delta[0]) + "px) " + "translateY(" + Math.round(delta[1]) + "px)" + (do3dTransform ? " translateZ(0)" : "");
10256
- this.transform(transform);
10257
- };
10343
+ toCoordAtAngle(coord, angle) {
10344
+ const clipPoint = this.graphie.scalePoint(getClipPoint(this.graphie, coord, angle));
10345
+ this.transform("translateX(" + (clipPoint[0] + Arrowhead.scale * this.center[0]) + "px) " + "translateY(" + (clipPoint[1] + Arrowhead.scale * this.center[1]) + "px) " + "translateZ(0) " + "rotate(" + (360 - KhanMath.bound(angle)) + "deg)");
10258
10346
  }
10259
- if (options.disableMouseEventsOnWrapper) {
10260
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10261
- this.wrapper.style.pointerEvents = "none";
10262
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10263
- this.visibleShape.node.style.pointerEvents = "auto";
10347
+ }
10348
+
10349
+ // We can't just pass in a path to `graph.fixedPath` as we need to modify
10350
+ // the points in some way, so instead we provide a function for creating
10351
+ // the path once the points have been transformed
10352
+ Arrowhead.scale = 1.4;
10353
+ const createCubicPath = function createCubicPath(points) {
10354
+ let path = "M" + points[0][0] + " " + points[0][1];
10355
+ for (let i = 1; i < points.length; i += 3) {
10356
+ path += "C" + points[i][0] + " " + points[i][1] + " " + points[i + 1][0] + " " + points[i + 1][1] + " " + points[i + 2][0] + " " + points[i + 2][1];
10264
10357
  }
10358
+ return path;
10265
10359
  };
10266
- _.extend(WrappedEllipse.prototype, WrappedDefaults);
10267
10360
 
10268
10361
  const DEFAULT_OPTIONS$1 = {
10269
- thickness: 2,
10270
- mouselayer: false
10362
+ maxScale: 1,
10363
+ mouselayer: false,
10364
+ shadow: false,
10365
+ disableMouseEventsOnWrapper: false
10271
10366
  };
10272
- const WrappedLine = function WrappedLine(graphie, start, end, options) {
10273
- options = _.extend({}, DEFAULT_OPTIONS$1, options);
10274
-
10275
- // Always make the line as large as possible and horizontal; this
10276
- // simplifies a lot of the transforms, e.g., we can rotate by exactly the
10277
- // angle of the argument points in `moveTo`.
10278
- const initialStart = [graphie.range[0][0], 0];
10279
- const initialEnd = [graphie.range[0][1], 0];
10367
+ class WrappedEllipse extends WrappedDrawing {
10368
+ constructor(graphie, center, radii, options) {
10369
+ super(graphie, center);
10370
+ this.initialPoint = void 0;
10371
+ this.wrapper = void 0;
10372
+ this.visibleShape = void 0;
10373
+ options = _.extend({}, DEFAULT_OPTIONS$1, options);
10374
+
10375
+ // Add `wrapper`, `visibleShape`, and remaining properties.
10376
+ const fixedEllipse = graphie.fixedEllipse(center, radii, options.maxScale, options.padding);
10377
+ this.visibleShape = fixedEllipse.visibleShape;
10378
+ this.wrapper = fixedEllipse.wrapper;
10379
+ this.graphie = graphie;
10380
+ this.initialPoint = center;
10381
+ if (options.interactiveKindForTesting) {
10382
+ this.wrapper.setAttribute("data-interactive-kind-for-testing", options.interactiveKindForTesting);
10383
+ }
10280
10384
 
10281
- // Add `wrapper` and `visibleShape`
10282
- _.extend(
10283
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10284
- this, graphie.fixedLine(initialStart, initialEnd, options.thickness));
10285
- if (options.interactiveKindForTesting) {
10286
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10287
- this.wrapper.setAttribute("data-interactive-kind-for-testing", options.interactiveKindForTesting);
10385
+ // Add to appropriate graphie layer
10386
+ if (options.mouselayer) {
10387
+ // Disable browser handling of all panning and zooming gestures on the
10388
+ // movable wrapper so that when moved the browser does not scroll page
10389
+ this.wrapper.style.touchAction = "none";
10390
+ this.graphie.addToMouseLayerWrapper(this.wrapper);
10391
+ } else {
10392
+ this.graphie.addToVisibleLayerWrapper(this.wrapper);
10393
+ }
10394
+ if (options.shadow) {
10395
+ const filter = "drop-shadow(0px 0px 5px rgba(0, 0, 0, 0.5))";
10396
+ const wrapper = this.wrapper;
10397
+ wrapper.style.filter = filter;
10398
+ this.moveTo = function (point) {
10399
+ const delta = vector$1.subtract(this.graphie.scalePoint(point), this.graphie.scalePoint(this.initialPoint));
10400
+ const do3dTransform = InteractiveUtil.getCanUse3dTransform();
10401
+ const transform = "translateX(" + Math.round(delta[0]) + "px) " + "translateY(" + Math.round(delta[1]) + "px)" + (do3dTransform ? " translateZ(0)" : "");
10402
+ this.transform(transform);
10403
+ };
10404
+ }
10405
+ if (options.disableMouseEventsOnWrapper) {
10406
+ this.wrapper.style.pointerEvents = "none";
10407
+ // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10408
+ this.visibleShape.node.style.pointerEvents = "auto";
10409
+ }
10288
10410
  }
10411
+ }
10289
10412
 
10290
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10291
- this.visibleShape.attr(options.normalStyle);
10292
-
10293
- // Save properties for computing transformations
10294
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10295
- _.extend(this, {
10296
- graphie: graphie,
10297
- initialPoint: graphie.scalePoint(initialStart),
10298
- initialLength: point.distanceToPoint(graphie.scalePoint(initialStart), graphie.scalePoint(initialEnd))
10299
- });
10300
-
10301
- // Add to appropriate graphie layer
10302
- if (options.mouselayer) {
10303
- // Disable browser handling of all panning and zooming gestures on the
10304
- // movable wrapper so that when moved the browser does not scroll page
10305
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10306
- this.wrapper.style.touchAction = "none";
10413
+ const DEFAULT_OPTIONS = {
10414
+ thickness: 2,
10415
+ mouselayer: false
10416
+ };
10417
+ class WrappedLine extends WrappedDrawing {
10418
+ constructor(graphie, start, end, options) {
10419
+ // Always make the line as large as possible and horizontal; this
10420
+ // simplifies a lot of the transforms, e.g., we can rotate by exactly the
10421
+ // angle of the argument points in `moveTo`.
10422
+ const initialStart = [graphie.range[0][0], 0];
10423
+ const initialEnd = [graphie.range[0][1], 0];
10424
+ const initialPoint = graphie.scalePoint(initialStart);
10425
+ super(graphie, initialPoint);
10426
+ this.initialLength = void 0;
10427
+ this.wrapper = void 0;
10428
+ this.visibleShape = void 0;
10429
+ options = _.extend({}, DEFAULT_OPTIONS, options);
10430
+ const fixedLine = graphie.fixedLine(initialStart, initialEnd, options.thickness);
10431
+ this.wrapper = fixedLine.wrapper;
10432
+ this.visibleShape = fixedLine.visibleShape;
10433
+ if (options.interactiveKindForTesting) {
10434
+ this.wrapper.setAttribute("data-interactive-kind-for-testing", options.interactiveKindForTesting);
10435
+ }
10436
+ this.visibleShape.attr(options.normalStyle);
10437
+
10438
+ // Save properties for computing transformations
10439
+ this.initialPoint = initialPoint;
10440
+ this.initialLength = point.distanceToPoint(graphie.scalePoint(initialStart), graphie.scalePoint(initialEnd));
10441
+
10442
+ // Add to appropriate graphie layer
10443
+ if (options.mouselayer) {
10444
+ // Disable browser handling of all panning and zooming gestures on the
10445
+ // movable wrapper so that when moved the browser does not scroll page
10446
+ this.wrapper.style.touchAction = "none";
10447
+ this.graphie.addToMouseLayerWrapper(this.wrapper);
10448
+ } else {
10449
+ this.graphie.addToVisibleLayerWrapper(this.wrapper);
10450
+ }
10307
10451
 
10308
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10309
- this.graphie.addToMouseLayerWrapper(this.wrapper);
10310
- } else {
10311
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10312
- this.graphie.addToVisibleLayerWrapper(this.wrapper);
10452
+ // Move line to initial points
10453
+ this.moveTo(start, end);
10313
10454
  }
10314
-
10315
- // Move to argument points
10316
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10317
- this.moveTo(start, end);
10318
- };
10319
- _.extend(WrappedLine.prototype, WrappedDefaults, {
10320
- getMouseTarget: function () {
10455
+ getMouseTarget() {
10321
10456
  return this.wrapper;
10322
- },
10323
- moveTo: function (start, end) {
10457
+ }
10458
+
10459
+ // TODO(benchristel): we need to suppress a type error here because moveTo
10460
+ // takes 2 parameters here, but only 1 parameter in the superclass's
10461
+ // definition. Rename this method after confirming it's safe to do so
10462
+ // @ts-expect-error - TS2416: Property 'moveTo' in type 'WrappedLine' is not assignable to the same property in base type 'WrappedDefaults'.
10463
+ moveTo(start, end) {
10324
10464
  const scaledStart = this.graphie.scalePoint(start);
10325
10465
  const scaledEnd = this.graphie.scalePoint(end);
10326
10466
 
@@ -10336,45 +10476,7 @@ _.extend(WrappedLine.prototype, WrappedDefaults, {
10336
10476
  const transformation = "translateX(" + delta[0] + "px) " + "translateY(" + delta[1] + "px) " + (do3dTransform ? " translateZ(0)" : "") + "rotate(" + angle + "deg) " + "scaleX(" + scale + ") scaleY(1)";
10337
10477
  this.transform(transformation);
10338
10478
  }
10339
- });
10340
-
10341
- const DEFAULT_OPTIONS = {
10342
- center: null,
10343
- // gets ignored in `graphie.fixedPath` if `null`
10344
- createPath: null,
10345
- // gets defaulted in `graphie.fixedPath` if `null`
10346
- mouselayer: false
10347
- };
10348
- const WrappedPath = function WrappedPath(graphie, points, options) {
10349
- options = _.extend({}, DEFAULT_OPTIONS, options);
10350
-
10351
- // Add `wrapper` and `visibleShape`
10352
- _.extend(
10353
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10354
- this, graphie.fixedPath(points, options.center, options.createPath));
10355
-
10356
- // Add remaining properties
10357
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10358
- _.extend(this, {
10359
- graphie: graphie,
10360
- initialPoint: graphie.scalePoint(_.head(points))
10361
- });
10362
-
10363
- // Add to appropriate graphie layer
10364
- if (options.mouselayer) {
10365
- // Disable browser handling of all panning and zooming gestures on the
10366
- // movable wrapper so that when moved the browser does not scroll page
10367
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10368
- this.wrapper.style.touchAction = "none";
10369
-
10370
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10371
- this.graphie.addToMouseLayerWrapper(this.wrapper);
10372
- } else {
10373
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
10374
- this.graphie.addToVisibleLayerWrapper(this.wrapper);
10375
- }
10376
- };
10377
- _.extend(WrappedPath.prototype, WrappedDefaults);
10479
+ }
10378
10480
 
10379
10481
  /**
10380
10482
  * A collection of geomtry-related utility functions
@@ -10599,9 +10701,6 @@ firstPoints, secondPoints) {
10599
10701
  }
10600
10702
 
10601
10703
  /* eslint-disable @babel/no-invalid-this */
10602
- const {
10603
- getCanUse3dTransform
10604
- } = InteractiveUtil;
10605
10704
  function scaledDistanceFromAngle(angle) {
10606
10705
  const a = 3.51470560176242 * 20;
10607
10706
  const b = 0.5687298702748785 * 20;
@@ -10632,114 +10731,6 @@ const InteractiveUtils = {
10632
10731
  }
10633
10732
  };
10634
10733
  _.extend(GraphUtils.Graphie.prototype, {
10635
- // graphie puts text spans on top of the SVG, which looks good, but gets
10636
- // in the way of mouse events. This adds another SVG element on top
10637
- // of everything else where we can add invisible shapes with mouse
10638
- // handlers wherever we want.
10639
- addMouseLayer: function (options) {
10640
- const graph = this;
10641
- options = _.extend({
10642
- allowScratchpad: false,
10643
- setDrawingAreaAvailable: function () {}
10644
- }, options);
10645
- const mouselayerZIndex = 2;
10646
- graph.mouselayer = Raphael$1(graph.raphael.canvas.parentNode, graph.xpixels, graph.ypixels);
10647
- $(graph.mouselayer.canvas).css("z-index", mouselayerZIndex);
10648
- if (options.onClick || options.onMouseDown || options.onMouseMove || options.onMouseOver || options.onMouseOut) {
10649
- const canvasClickTarget = graph.mouselayer.rect(0, 0, graph.xpixels, graph.ypixels).attr({
10650
- fill: "#000",
10651
- opacity: 0
10652
- });
10653
- let isClickingCanvas = false;
10654
- $(graph.mouselayer.canvas).on("vmousedown", function (e) {
10655
- if (e.target === canvasClickTarget[0]) {
10656
- if (options.onMouseDown) {
10657
- options.onMouseDown(graph.getMouseCoord(e));
10658
- }
10659
- isClickingCanvas = true;
10660
- if (options.onMouseMove) {
10661
- const handler = options.onMouseMove;
10662
- $(document).bind("vmousemove.mouseLayer", function (e) {
10663
- if (isClickingCanvas) {
10664
- e.preventDefault();
10665
- handler(graph.getMouseCoord(e));
10666
- }
10667
- });
10668
- }
10669
- $(document).bind("vmouseup.mouseLayer", function (e) {
10670
- $(document).unbind(".mouseLayer");
10671
-
10672
- // Only register clicks that started on the canvas,
10673
- // and not on another mouseLayer target
10674
- if (isClickingCanvas && options.onClick) {
10675
- options.onClick(graph.getMouseCoord(e));
10676
- }
10677
- isClickingCanvas = false;
10678
- });
10679
- }
10680
- });
10681
- if (options.onMouseOver) {
10682
- const handler = options.onMouseOver;
10683
- $(graph.mouselayer.canvas).on("vmouseover", function (e) {
10684
- handler(graph.getMouseCoord(e));
10685
- });
10686
- }
10687
- if (options.onMouseOut) {
10688
- const handler = options.onMouseOut;
10689
- $(graph.mouselayer.canvas).on("vmouseout", function (e) {
10690
- handler(graph.getMouseCoord(e));
10691
- });
10692
- }
10693
- }
10694
- if (!options.allowScratchpad) {
10695
- options.setDrawingAreaAvailable == null || options.setDrawingAreaAvailable(false);
10696
- }
10697
-
10698
- // Add mouse and visible wrapper layers for DOM-node-wrapped movables
10699
- graph._mouselayerWrapper = document.createElement("div");
10700
- $(graph._mouselayerWrapper).css({
10701
- position: "absolute",
10702
- left: 0,
10703
- top: 0,
10704
- zIndex: mouselayerZIndex
10705
- });
10706
- graph._visiblelayerWrapper = document.createElement("div");
10707
- $(graph._visiblelayerWrapper).css({
10708
- position: "absolute",
10709
- left: 0,
10710
- top: 0
10711
- });
10712
- const el = graph.raphael.canvas.parentNode;
10713
- el.appendChild(graph._visiblelayerWrapper);
10714
- el.appendChild(graph._mouselayerWrapper);
10715
-
10716
- // Add functions for adding to wrappers
10717
- graph.addToMouseLayerWrapper = function (el) {
10718
- this._mouselayerWrapper.appendChild(el);
10719
- };
10720
- graph.addToVisibleLayerWrapper = function (el) {
10721
- this._visiblelayerWrapper.appendChild(el);
10722
- };
10723
- },
10724
- /**
10725
- * Get mouse coordinates in pixels
10726
- */
10727
- getMousePx: function (event) {
10728
- const graphie = this;
10729
- const mouseX =
10730
- // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
10731
- event.pageX - $(graphie.raphael.canvas.parentNode).offset().left;
10732
- const mouseY =
10733
- // @ts-expect-error - TS2532 - Object is possibly 'undefined'.
10734
- event.pageY - $(graphie.raphael.canvas.parentNode).offset().top;
10735
- return [mouseX, mouseY];
10736
- },
10737
- /**
10738
- * Get mouse coordinates in graph coordinates
10739
- */
10740
- getMouseCoord: function (event) {
10741
- return this.unscalePoint(this.getMousePx(event));
10742
- },
10743
10734
  /**
10744
10735
  * Unlike all other Graphie-related code, the following three functions use
10745
10736
  * a lot of scaled coordinates (so that labels appear the same size
@@ -11593,52 +11584,15 @@ _.extend(GraphUtils.Graphie.prototype, {
11593
11584
  _.each(elements, function (element) {
11594
11585
  element.moveTo(start, end);
11595
11586
  });
11596
- const createArrow = function createArrow(graph, style) {
11597
- const center = [0.75, 0];
11598
- let points = [[-3, 4], [-2.75, 2.5], [0, 0.25], center, [0, -0.25], [-2.75, -2.5], [-3, -4]];
11599
- const scale = 1.4;
11600
- points = _.map(points, function (point) {
11601
- const pv = vector$1.subtract(point, center);
11602
- const pvScaled = vector$1.scale(pv, scale);
11603
- return vector$1.add(center, pvScaled);
11604
- });
11605
- const createCubicPath = function createCubicPath(points) {
11606
- let path = "M" + points[0][0] + " " + points[0][1];
11607
- for (let i = 1; i < points.length; i += 3) {
11608
- path += "C" + points[i][0] + " " + points[i][1] + " " + points[i + 1][0] + " " + points[i + 1][1] + " " + points[i + 2][0] + " " + points[i + 2][1];
11609
- }
11610
- return path;
11611
- };
11612
- const unscaledPoints = _.map(points, graph.unscalePoint);
11613
- const options = {
11614
- center: graph.unscalePoint(center),
11615
- createPath: createCubicPath
11616
- };
11617
- const arrowHead = new WrappedPath(graph, unscaledPoints, options);
11618
- arrowHead.attr(_.extend({
11619
- "stroke-linejoin": "round",
11620
- "stroke-linecap": "round",
11621
- "stroke-dasharray": ""
11622
- }, style));
11623
-
11624
- // Add custom function for transforming arrowheads that
11625
- // accounts for center, scaling, etc.
11626
- arrowHead.toCoordAtAngle = function (coord, angle) {
11627
- const clipPoint = graph.scalePoint(getClipPoint(graph, coord, angle));
11628
- const do3dTransform = getCanUse3dTransform();
11629
- arrowHead.transform("translateX(" + (clipPoint[0] + scale * center[0]) + "px) " + "translateY(" + (clipPoint[1] + scale * center[1]) + "px) " + (do3dTransform ? "translateZ(0) " : "") + "rotate(" + (360 - KhanMath.bound(angle)) + "deg)");
11630
- };
11631
- return arrowHead;
11632
- };
11633
11587
 
11634
11588
  // Add arrows
11635
11589
  if (this._arrows == null) {
11636
11590
  this._arrows = [];
11637
11591
  if (this.extendLine) {
11638
- this._arrows.push(createArrow(graph, this.normalStyle));
11639
- this._arrows.push(createArrow(graph, this.normalStyle));
11592
+ this._arrows.push(new Arrowhead(graph, this.normalStyle));
11593
+ this._arrows.push(new Arrowhead(graph, this.normalStyle));
11640
11594
  } else if (this.extendRay) {
11641
- this._arrows.push(createArrow(graph, this.normalStyle));
11595
+ this._arrows.push(new Arrowhead(graph, this.normalStyle));
11642
11596
  }
11643
11597
  }
11644
11598
  const coordForArrow = [this.coordA, this.coordZ];
@@ -14057,78 +14011,6 @@ const getScaledAngle = function getScaledAngle(line) {
14057
14011
  return vector$1.polarDegFromCart(vector$1.subtract(scaledZ, scaledA))[1];
14058
14012
  };
14059
14013
 
14060
- // Given `coord` and `angle`, find the point where a line extended
14061
- // from `coord` in the direction of `angle` would be clipped by the
14062
- // edge of the graphie canvas. Then draw an arrowhead at that point
14063
- // pointing in the direction of `angle`.
14064
- const getClipPoint = function getClipPoint(graph, coord, angle) {
14065
- // Actually put the arrowheads 4px from the edge so they have
14066
- // a bit of room
14067
- const xExtent = graph.range[0][1] - graph.range[0][0];
14068
- const yExtent = graph.range[1][1] - graph.range[1][0];
14069
-
14070
- // shoot a point off into the distance ...
14071
- const distance = xExtent + yExtent;
14072
- // we need to scale the point according to the scale of the axes
14073
- const angleVec = graph.unscaleVector(vector$1.cartFromPolarDeg(1, angle));
14074
- const distVec = vector$1.scale(vector$1.normalize(angleVec), distance);
14075
- const farCoord = vector$1.add(coord, distVec);
14076
- const scaledAngle = vector$1.polarDegFromCart(angleVec)[1];
14077
- // ... and then bring it back
14078
- const clipPoint = graph.constrainToBoundsOnAngle(farCoord, 4, scaledAngle * Math.PI / 180);
14079
- return clipPoint;
14080
- };
14081
-
14082
- // Given `coord` and `angle`, find the point where a line extended
14083
- // from `coord` in the direction of `angle` would be clipped by the
14084
- // edge of the graphie canvas. Then draw an arrowhead at that point
14085
- // pointing in the direction of `angle`.
14086
- const createArrow = function createArrow(graph, style) {
14087
- // Points that define the arrowhead
14088
- const center = [0.75, 0];
14089
- let points = [[-3, 4], [-2.75, 2.5], [0, 0.25], center, [0, -0.25], [-2.75, -2.5], [-3, -4]];
14090
-
14091
- // Scale points by 1.4 around (0.75, 0)
14092
- const scale = 1.4;
14093
- points = _.map(points, function (point) {
14094
- const pv = vector$1.subtract(point, center);
14095
- const pvScaled = vector$1.scale(pv, scale);
14096
- return vector$1.add(center, pvScaled);
14097
- });
14098
-
14099
- // We can't just pass in a path to `graph.fixedPath` as we need to modify
14100
- // the points in some way, so instead we provide a function for creating
14101
- // the path once the points have been transformed
14102
- const createCubicPath = function createCubicPath(points) {
14103
- let path = "M" + points[0][0] + " " + points[0][1];
14104
- for (let i = 1; i < points.length; i += 3) {
14105
- path += "C" + points[i][0] + " " + points[i][1] + " " + points[i + 1][0] + " " + points[i + 1][1] + " " + points[i + 2][0] + " " + points[i + 2][1];
14106
- }
14107
- return path;
14108
- };
14109
-
14110
- // Create arrowhead
14111
- const unscaledPoints = _.map(points, graph.unscalePoint);
14112
- const options = {
14113
- center: graph.unscalePoint(center),
14114
- createPath: createCubicPath
14115
- };
14116
- const arrowHead = new WrappedPath(graph, unscaledPoints, options);
14117
- arrowHead.attr(_.extend({
14118
- "stroke-linejoin": "round",
14119
- "stroke-linecap": "round",
14120
- "stroke-dasharray": ""
14121
- }, style));
14122
-
14123
- // Add custom function for transforming arrowheads that accounts for
14124
- // center, scaling, etc.
14125
- arrowHead.toCoordAtAngle = function (coord, angle) {
14126
- const clipPoint = graph.scalePoint(getClipPoint(graph, coord, angle));
14127
- arrowHead.transform("translateX(" + (clipPoint[0] + scale * center[0]) + "px) " + "translateY(" + (clipPoint[1] + scale * center[1]) + "px) " + "translateZ(0) " + "rotate(" + (360 - KhanMath.bound(angle)) + "deg)");
14128
- };
14129
- return arrowHead;
14130
- };
14131
-
14132
14014
  /**
14133
14015
  * MovableLine option functions
14134
14016
  */
@@ -14215,16 +14097,16 @@ const draw$3 = {
14215
14097
  // @ts-expect-error - TS2551 - Property '_arrows' does not exist on type '{ readonly basic: (state: any) => void; readonly arrows: (state: any) => void; readonly highlight: (state: any, prevState: any) => void; }'. Did you mean 'arrows'?
14216
14098
  this._arrows.push(
14217
14099
  // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type '{ readonly basic: (state: any) => void; readonly arrows: (state: any) => void; readonly highlight: (state: any, prevState: any) => void; }'. | TS2339 - Property 'normalStyle' does not exist on type '{ readonly basic: (state: any) => void; readonly arrows: (state: any) => void; readonly highlight: (state: any, prevState: any) => void; }'.
14218
- createArrow(this.graphie, this.normalStyle()));
14100
+ new Arrowhead(this.graphie, this.normalStyle()));
14219
14101
  // @ts-expect-error - TS2551 - Property '_arrows' does not exist on type '{ readonly basic: (state: any) => void; readonly arrows: (state: any) => void; readonly highlight: (state: any, prevState: any) => void; }'. Did you mean 'arrows'?
14220
14102
  this._arrows.push(
14221
14103
  // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type '{ readonly basic: (state: any) => void; readonly arrows: (state: any) => void; readonly highlight: (state: any, prevState: any) => void; }'. | TS2339 - Property 'normalStyle' does not exist on type '{ readonly basic: (state: any) => void; readonly arrows: (state: any) => void; readonly highlight: (state: any, prevState: any) => void; }'.
14222
- createArrow(this.graphie, this.normalStyle()));
14104
+ new Arrowhead(this.graphie, this.normalStyle()));
14223
14105
  } else if (state.extendRay) {
14224
14106
  // @ts-expect-error - TS2551 - Property '_arrows' does not exist on type '{ readonly basic: (state: any) => void; readonly arrows: (state: any) => void; readonly highlight: (state: any, prevState: any) => void; }'. Did you mean 'arrows'?
14225
14107
  this._arrows.push(
14226
14108
  // @ts-expect-error - TS2339 - Property 'graphie' does not exist on type '{ readonly basic: (state: any) => void; readonly arrows: (state: any) => void; readonly highlight: (state: any, prevState: any) => void; }'. | TS2339 - Property 'normalStyle' does not exist on type '{ readonly basic: (state: any) => void; readonly arrows: (state: any) => void; readonly highlight: (state: any, prevState: any) => void; }'.
14227
- createArrow(this.graphie, this.normalStyle()));
14109
+ new Arrowhead(this.graphie, this.normalStyle()));
14228
14110
  }
14229
14111
  }
14230
14112
 
@@ -14370,6 +14252,75 @@ var MovableLineOptions = {
14370
14252
  }
14371
14253
  };
14372
14254
 
14255
+ /**
14256
+ * A work-in-progress of _ methods for objects.
14257
+ * That is, they take an object as a parameter,
14258
+ * and return an object instead of an array.
14259
+ *
14260
+ * TODO(aria): Move this out of interactive2
14261
+ */
14262
+
14263
+ /**
14264
+ * Does a pluck on keys inside objects in an object
14265
+ *
14266
+ * Ex:
14267
+ * tools = {
14268
+ * translation: {
14269
+ * enabled: true
14270
+ * },
14271
+ * rotation: {
14272
+ * enabled: false
14273
+ * }
14274
+ * };
14275
+ * pluckObject(tools, "enabled") returns {
14276
+ * translation: true
14277
+ * rotation: false
14278
+ * }
14279
+ */
14280
+ const pluck = function pluck(table, subKey) {
14281
+ return _.object(_.map(table, function (value, key) {
14282
+ return [key, value[subKey]];
14283
+ }));
14284
+ };
14285
+
14286
+ /**
14287
+ * Maps an object to an object
14288
+ *
14289
+ * > mapObject({a: '1', b: '2'}, (value, key) => {
14290
+ * return value + 1;
14291
+ * });
14292
+ * {a: 2, b: 3}
14293
+ */
14294
+ const mapObject$2 = function mapObject(obj, lambda) {
14295
+ const result = {};
14296
+ _.each(_.keys(obj), function (key) {
14297
+ // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
14298
+ result[key] = lambda(obj[key], key);
14299
+ });
14300
+ return result;
14301
+ };
14302
+
14303
+ /**
14304
+ * Maps an array to an object
14305
+ *
14306
+ * > mapObjectFromArray(['a', 'b'], function(elem) {
14307
+ * return elem + elem;
14308
+ * });
14309
+ * {a: 'aa', b: 'bb'}
14310
+ */
14311
+ const mapObjectFromArray$1 = function mapObjectFromArray(arr, lambda) {
14312
+ const result = {};
14313
+ _.each(arr, function (elem) {
14314
+ result[elem] = lambda(elem);
14315
+ });
14316
+ return result;
14317
+ };
14318
+ var objective_ = {
14319
+ pluck: pluck,
14320
+ mapObject: mapObject$2,
14321
+ mapObjectFromArray: mapObjectFromArray$1
14322
+ };
14323
+
14373
14324
  /**
14374
14325
  * MovableLine
14375
14326
  */
@@ -17897,33 +17848,33 @@ class Lint extends React.Component {
17897
17848
  let warningText;
17898
17849
  let warningTextStyle;
17899
17850
  if (this.props.severity === Severity.Error) {
17900
- severityStyle = styles$A.indicatorError;
17851
+ severityStyle = styles$B.indicatorError;
17901
17852
  warningText = "Error";
17902
- warningTextStyle = styles$A.publishBlockingError;
17853
+ warningTextStyle = styles$B.publishBlockingError;
17903
17854
  } else if (this.props.severity === Severity.Warning) {
17904
- severityStyle = styles$A.indicatorWarning;
17855
+ severityStyle = styles$B.indicatorWarning;
17905
17856
  warningText = "Warning";
17906
- warningTextStyle = styles$A.warning;
17857
+ warningTextStyle = styles$B.warning;
17907
17858
  } else {
17908
- severityStyle = styles$A.indicatorGuideline;
17859
+ severityStyle = styles$B.indicatorGuideline;
17909
17860
  warningText = "Recommendation";
17910
- warningTextStyle = styles$A.warning;
17861
+ warningTextStyle = styles$B.warning;
17911
17862
  }
17912
17863
  return /*#__PURE__*/React.createElement("a", {
17913
17864
  href: `https://khanacademy.org/r/linter-rules#${this.props.ruleName}`,
17914
17865
  target: "lint-help-window",
17915
17866
  className: css(style)
17916
17867
  }, /*#__PURE__*/React.createElement("span", {
17917
- className: css(styles$A.indicator, severityStyle)
17868
+ className: css(styles$B.indicator, severityStyle)
17918
17869
  }, this.props.severity === 1 && /*#__PURE__*/React.createElement(InlineIcon, exclamationIcon)), /*#__PURE__*/React.createElement("div", {
17919
- className: css(styles$A.tooltip, tooltipAbove && styles$A.tooltipAbove)
17870
+ className: css(styles$B.tooltip, tooltipAbove && styles$B.tooltipAbove)
17920
17871
  }, this.props.message.split("\n\n").map((m, i) => /*#__PURE__*/React.createElement("p", {
17921
17872
  key: i,
17922
- className: css(styles$A.tooltipParagraph)
17873
+ className: css(styles$B.tooltipParagraph)
17923
17874
  }, /*#__PURE__*/React.createElement("span", {
17924
17875
  className: css(warningTextStyle)
17925
17876
  }, warningText, ":", " "), m)), /*#__PURE__*/React.createElement("div", {
17926
- className: css(styles$A.tail, tooltipAbove && styles$A.tailAbove)
17877
+ className: css(styles$B.tail, tooltipAbove && styles$B.tailAbove)
17927
17878
  })));
17928
17879
  };
17929
17880
  }
@@ -17961,20 +17912,20 @@ class Lint extends React.Component {
17961
17912
  }
17962
17913
  if (blockHighlight) {
17963
17914
  return /*#__PURE__*/React.createElement("span", {
17964
- className: css(styles$A.lintContainer, styles$A.lintContainerBlock)
17965
- }, this.renderLink(styles$A.radioWidgetHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
17915
+ className: css(styles$B.lintContainer, styles$B.lintContainerBlock)
17916
+ }, this.renderLink(styles$B.radioWidgetHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
17966
17917
  }
17967
17918
  if (inline) {
17968
17919
  return /*#__PURE__*/React.createElement("span", {
17969
- className: css(styles$A.lintContainer)
17970
- }, this.renderLink(styles$A.inlineHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
17920
+ className: css(styles$B.lintContainer)
17921
+ }, this.renderLink(styles$B.inlineHoverTarget), /*#__PURE__*/React.createElement("span", null, children));
17971
17922
  }
17972
17923
  return /*#__PURE__*/React.createElement("div", {
17973
- className: css(styles$A.lintContainer)
17974
- }, this.renderLink(styles$A.hoverTarget), /*#__PURE__*/React.createElement("div", null, children));
17924
+ className: css(styles$B.lintContainer)
17925
+ }, this.renderLink(styles$B.hoverTarget), /*#__PURE__*/React.createElement("div", null, children));
17975
17926
  }
17976
17927
  }
17977
- const styles$A = StyleSheet.create({
17928
+ const styles$B = StyleSheet.create({
17978
17929
  // This is the class of the outermost element.
17979
17930
  // We use relative positioning so that the lint indicator can be
17980
17931
  // positioned absolutely relative to the position of the linty container.
@@ -19877,7 +19828,7 @@ class Renderer extends React.Component {
19877
19828
  paddingLeft: margin,
19878
19829
  paddingRight: margin
19879
19830
  };
19880
- const mobileInnerStyle = _extends({}, innerStyle, styles$z.mobileZoomableParentFix);
19831
+ const mobileInnerStyle = _extends({}, innerStyle, styles$A.mobileZoomableParentFix);
19881
19832
  return /*#__PURE__*/React.createElement("div", {
19882
19833
  key: state.key,
19883
19834
  className: "perseus-block-math",
@@ -19998,7 +19949,7 @@ class Renderer extends React.Component {
19998
19949
  paddingLeft: 0,
19999
19950
  paddingRight: 0
20000
19951
  };
20001
- const mobileInnerStyle = _extends({}, innerStyle, styles$z.mobileZoomableParentFix);
19952
+ const mobileInnerStyle = _extends({}, innerStyle, styles$A.mobileZoomableParentFix);
20002
19953
  const wrappedOutput = /*#__PURE__*/React.createElement("div", {
20003
19954
  style: _extends({}, mobileInnerStyle, {
20004
19955
  overflowX: "auto"
@@ -20645,7 +20596,7 @@ Renderer.defaultProps = {
20645
20596
  onSerializedStateUpdated: () => {},
20646
20597
  linterContext: PerseusLinter.linterContextDefault
20647
20598
  };
20648
- const styles$z = {
20599
+ const styles$A = {
20649
20600
  mobileZoomableParentFix: {
20650
20601
  // NOTE(abdul): There is an issue where transform animations will
20651
20602
  // cause the Zoomable component to disappear when running on the
@@ -21662,10 +21613,10 @@ class InputNumber extends React.Component {
21662
21613
  // Note: This is _very_ similar to what `numeric-input.jsx` does. If
21663
21614
  // you modify this, double-check if you also need to modify that
21664
21615
  // component.
21665
- const inputStyles = [styles$y.default, this.props.size === "small" ? styles$y.small : null, this.props.rightAlign ? styles$y.rightAlign : styles$y.leftAlign];
21616
+ const inputStyles = [styles$z.default, this.props.size === "small" ? styles$z.small : null, this.props.rightAlign ? styles$z.rightAlign : styles$z.leftAlign];
21666
21617
  // Unanswered
21667
21618
  if (rubric && !this.props.currentValue) {
21668
- inputStyles.push(styles$y.answerStateUnanswered);
21619
+ inputStyles.push(styles$z.answerStateUnanswered);
21669
21620
  }
21670
21621
  return /*#__PURE__*/React.createElement(InputWithExamples
21671
21622
  // eslint-disable-next-line react/no-string-refs
@@ -21750,7 +21701,7 @@ InputNumber.defaultProps = {
21750
21701
  apiOptions: ApiOptions.defaults,
21751
21702
  linterContext: linterContextDefault
21752
21703
  };
21753
- const styles$y = StyleSheet.create({
21704
+ const styles$z = StyleSheet.create({
21754
21705
  default: {
21755
21706
  width: 80,
21756
21707
  height: "auto"
@@ -22751,7 +22702,7 @@ const FocusRing = function FocusRing(props) {
22751
22702
  };
22752
22703
  return /*#__PURE__*/React.createElement("span", {
22753
22704
  "data-test-id": "focus-ring",
22754
- className: css(styles$x.ring),
22705
+ className: css(styles$y.ring),
22755
22706
  style: style
22756
22707
  }, children);
22757
22708
  };
@@ -22760,7 +22711,7 @@ FocusRing.defaultProps = {
22760
22711
  color: kaGreen$1,
22761
22712
  multipleSelect: false
22762
22713
  };
22763
- const styles$x = StyleSheet.create({
22714
+ const styles$y = StyleSheet.create({
22764
22715
  ring: {
22765
22716
  margin: "auto",
22766
22717
  display: "inline-block",
@@ -22797,7 +22748,7 @@ function getChoiceLetter(pos) {
22797
22748
  }
22798
22749
 
22799
22750
  const CHOICE_ICON_SIZE = 24;
22800
- const styles$w = StyleSheet.create({
22751
+ const styles$x = StyleSheet.create({
22801
22752
  iconWrapper: {
22802
22753
  display: "inline-block",
22803
22754
  position: "relative"
@@ -22820,7 +22771,7 @@ const CrossOutLine = function CrossOutLine(props) {
22820
22771
  width: CROSS_OUT_LINE_SIZE,
22821
22772
  height: CROSS_OUT_LINE_SIZE,
22822
22773
  viewBox: `0 0 ${CROSS_OUT_LINE_SIZE} ${CROSS_OUT_LINE_SIZE}`,
22823
- className: css(styles$v.crossOutLine),
22774
+ className: css(styles$w.crossOutLine),
22824
22775
  "data-test-id": "choice-icon__cross-out-line"
22825
22776
  }, /*#__PURE__*/React.createElement("line", {
22826
22777
  // The line stretches from the bottom-left to top-right.
@@ -22835,7 +22786,7 @@ const CrossOutLine = function CrossOutLine(props) {
22835
22786
  strokeLinecap: "round"
22836
22787
  }));
22837
22788
  };
22838
- const styles$v = StyleSheet.create({
22789
+ const styles$w = StyleSheet.create({
22839
22790
  crossOutLine: {
22840
22791
  // Center the icon within the container.
22841
22792
  position: "absolute",
@@ -22916,7 +22867,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
22916
22867
  } = props;
22917
22868
  const dynamicStyles = getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, correct);
22918
22869
  return /*#__PURE__*/React.createElement("div", {
22919
- className: css(styles$w.iconWrapper)
22870
+ className: css(styles$x.iconWrapper)
22920
22871
  }, /*#__PURE__*/React.createElement(FocusRing, {
22921
22872
  color: Color.blue,
22922
22873
  visible: focused || hovered,
@@ -22925,13 +22876,13 @@ const ChoiceIcon = function ChoiceIcon(props) {
22925
22876
  // @ts-expect-error - TS2322 - Type '{ backgroundColor: string | null | undefined; borderColor: string; color: string; borderRadius: number; }' is not assignable to type 'Properties<string | number, string & {}>'.
22926
22877
  style: dynamicStyles,
22927
22878
  "data-test-id": "choice-icon__library-choice-icon",
22928
- className: css(styles$u.circle, showCorrectness && correct && styles$u.circleCorrect, showCorrectness && !correct && styles$u.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$u.circleIncorrectAnswered)
22879
+ className: css(styles$v.circle, showCorrectness && correct && styles$v.circleCorrect, showCorrectness && !correct && styles$v.circleIncorrect, showCorrectness && !correct && (checked || previouslyAnswered) && styles$v.circleIncorrectAnswered)
22929
22880
  // used in BaseRadio editMode to check
22930
22881
  // if we actually clicked on the radio icon
22931
22882
  ,
22932
22883
  "data-is-radio-icon": true
22933
22884
  }, /*#__PURE__*/React.createElement("div", {
22934
- className: css(styles$u.innerWrapper)
22885
+ className: css(styles$v.innerWrapper)
22935
22886
  }, /*#__PURE__*/React.createElement(ChoiceInner, {
22936
22887
  pos: pos,
22937
22888
  showCorrectness: showCorrectness,
@@ -22940,7 +22891,7 @@ const ChoiceIcon = function ChoiceIcon(props) {
22940
22891
  color: dynamicStyles.borderColor
22941
22892
  }));
22942
22893
  };
22943
- const styles$u = StyleSheet.create({
22894
+ const styles$v = StyleSheet.create({
22944
22895
  innerWrapper: {
22945
22896
  width: CHOICE_ICON_SIZE,
22946
22897
  height: CHOICE_ICON_SIZE,
@@ -23032,19 +22983,19 @@ const OptionStatus = function OptionStatus(props) {
23032
22983
  }
23033
22984
  let textStyle;
23034
22985
  if (correct) {
23035
- textStyle = styles$t.correct;
22986
+ textStyle = styles$u.correct;
23036
22987
  } else {
23037
22988
  if (checked || previouslyAnswered) {
23038
- textStyle = styles$t.incorrectAnswered;
22989
+ textStyle = styles$u.incorrectAnswered;
23039
22990
  } else {
23040
- textStyle = styles$t.incorrect;
22991
+ textStyle = styles$u.incorrect;
23041
22992
  }
23042
22993
  }
23043
22994
  return /*#__PURE__*/React.createElement("div", {
23044
- className: css(styles$t.text, textStyle)
22995
+ className: css(styles$u.text, textStyle)
23045
22996
  }, renderText(checked, correct, crossedOut));
23046
22997
  };
23047
- const styles$t = StyleSheet.create({
22998
+ const styles$u = StyleSheet.create({
23048
22999
  text: {
23049
23000
  alignItems: "center",
23050
23001
  display: "flex",
@@ -23119,8 +23070,8 @@ const Choice = function Choice(props) {
23119
23070
  crossedOut: updatedCrossedOut
23120
23071
  });
23121
23072
  }
23122
- const descriptionClassName = classNames("description", css(styles$s.description));
23123
- const rationaleClassName = classNames("perseus-radio-rationale-content", css(styles$s.rationale));
23073
+ const descriptionClassName = classNames("description", css(styles$t.description));
23074
+ const rationaleClassName = classNames("perseus-radio-rationale-content", css(styles$t.rationale));
23124
23075
 
23125
23076
  // We want to show the choices as dimmed out when the choices are disabled.
23126
23077
  // However, we don't want to do this when we're in review mode in the
@@ -23226,14 +23177,16 @@ const Choice = function Choice(props) {
23226
23177
  content: ({
23227
23178
  close
23228
23179
  }) => /*#__PURE__*/React.createElement(PopoverContent, {
23229
- title: "Cross out",
23230
- content: "Cross out option",
23180
+ title: i18n._("Cross out"),
23181
+ content: i18n._("Cross out option"),
23231
23182
  closeButtonVisible: true,
23232
23183
  actions: /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Strut, {
23233
23184
  size: Spacing.medium_16
23234
23185
  }), /*#__PURE__*/React.createElement(Button, {
23235
23186
  kind: "primary",
23236
- "aria-label": `Cross out Choice ${getChoiceLetter(pos)}`,
23187
+ "aria-label": i18n._("Cross out Choice %(letter)s", {
23188
+ letter: getChoiceLetter(pos)
23189
+ }),
23237
23190
  disabled: apiOptions.readOnly || reviewMode,
23238
23191
  onClick: () => {
23239
23192
  if (!crossedOut) {
@@ -23252,13 +23205,15 @@ const Choice = function Choice(props) {
23252
23205
  }
23253
23206
  close();
23254
23207
  }
23255
- }, crossedOut ? "Bring back" : "Cross out"))
23208
+ }, crossedOut ? i18n._("Bring back") : i18n._("Cross out")))
23256
23209
  })
23257
23210
  }, ({
23258
23211
  open
23259
23212
  }) => /*#__PURE__*/React.createElement(Clickable, {
23260
23213
  onClick: open,
23261
- "aria-label": `Open menu for Choice ${getChoiceLetter(pos)}`,
23214
+ "aria-label": i18n._(`Open menu for Choice %(letter)s`, {
23215
+ letter: getChoiceLetter(pos)
23216
+ }),
23262
23217
  style: {
23263
23218
  alignSelf: "center",
23264
23219
  padding: "5px",
@@ -23280,7 +23235,7 @@ const Choice = function Choice(props) {
23280
23235
  "data-test-id": `perseus-radio-rationale-content-${pos}`
23281
23236
  }, rationale));
23282
23237
  };
23283
- const styles$s = StyleSheet.create({
23238
+ const styles$t = StyleSheet.create({
23284
23239
  description: {
23285
23240
  display: "inline-block",
23286
23241
  width: "100%"
@@ -23472,10 +23427,10 @@ const BaseRadio = function BaseRadio(props) {
23472
23427
  const isMobile = apiOptions.isMobile;
23473
23428
  const firstChoiceHighlighted = choices[0].highlighted;
23474
23429
  const lastChoiceHighlighted = choices[choices.length - 1].highlighted;
23475
- const className = classNames("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$r.radio, styles$r.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$r.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$r.radioContainerLastHighlighted));
23476
- const instructionsClassName = classNames("instructions", css(styles$r.instructions, isMobile && styles$r.instructionsMobile));
23430
+ const className = classNames("perseus-widget-radio", !editMode && "perseus-rendered-radio", css(styles$s.radio, styles$s.responsiveRadioContainer, firstChoiceHighlighted && isMobile && styles$s.radioContainerFirstHighlighted, lastChoiceHighlighted && isMobile && styles$s.radioContainerLastHighlighted));
23431
+ const instructionsClassName = classNames("instructions", css(styles$s.instructions, isMobile && styles$s.instructionsMobile));
23477
23432
  const instructions = getInstructionsText(multipleSelect, countChoices, numCorrect);
23478
- const responsiveClassName = css(styles$r.responsiveFieldset);
23433
+ const responsiveClassName = css(styles$s.responsiveFieldset);
23479
23434
  const fieldset = /*#__PURE__*/React.createElement("fieldset", {
23480
23435
  className: `perseus-widget-radio-fieldset ${responsiveClassName}`
23481
23436
  }, /*#__PURE__*/React.createElement("legend", {
@@ -23526,7 +23481,7 @@ const BaseRadio = function BaseRadio(props) {
23526
23481
  const aphroditeClassName = checked => {
23527
23482
  // Whether or not to show correctness borders
23528
23483
  // for this choice and the next choice.
23529
- return css(sharedStyles.aboveScratchpad, styles$r.item, styles$r.responsiveItem, checked && styles$r.selectedItem, checked && choice.highlighted && styles$r.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$r.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$r.nextHighlighted);
23484
+ return css(sharedStyles.aboveScratchpad, styles$s.item, styles$s.responsiveItem, checked && styles$s.selectedItem, checked && choice.highlighted && styles$s.aboveBackdrop, checked && choice.highlighted && apiOptions.isMobile && styles$s.aboveBackdropMobile, nextChoiceHighlighted && apiOptions.isMobile && styles$s.nextHighlighted);
23530
23485
  };
23531
23486
 
23532
23487
  // HACK(abdulrahman): Preloads the selection-state
@@ -23600,14 +23555,14 @@ const BaseRadio = function BaseRadio(props) {
23600
23555
  // Allow for horizontal scrolling if content is too wide, which may be
23601
23556
  // an issue especially on phones.
23602
23557
  return /*#__PURE__*/React.createElement("div", {
23603
- className: css(styles$r.responsiveContainer)
23558
+ className: css(styles$s.responsiveContainer)
23604
23559
  }, fieldset);
23605
23560
  };
23606
23561
  BaseRadio.defaultProps = {
23607
23562
  editMode: false,
23608
23563
  multipleSelect: false
23609
23564
  };
23610
- const styles$r = StyleSheet.create({
23565
+ const styles$s = StyleSheet.create({
23611
23566
  instructions: {
23612
23567
  display: "block",
23613
23568
  color: gray17,
@@ -24255,9 +24210,9 @@ class Categorizer extends React.Component {
24255
24210
  indexedItems = Util.shuffle(indexedItems, this.props.problemNum);
24256
24211
  }
24257
24212
  const table = /*#__PURE__*/React.createElement("table", {
24258
- className: "categorizer-table " + css(styles$q.mobileTable)
24213
+ className: "categorizer-table " + css(styles$r.mobileTable)
24259
24214
  }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", {
24260
- className: css(styles$q.emptyHeaderCell)
24215
+ className: css(styles$r.emptyHeaderCell)
24261
24216
  }), this.props.categories.map((category, i) => {
24262
24217
  // Array index is the correct key here, as that's
24263
24218
  // how category grading actually works -- no way
@@ -24265,7 +24220,7 @@ class Categorizer extends React.Component {
24265
24220
  // middle. (If we later add that, this should be
24266
24221
  // fixed.)
24267
24222
  return /*#__PURE__*/React.createElement("th", {
24268
- className: css(styles$q.header),
24223
+ className: css(styles$r.header),
24269
24224
  key: i
24270
24225
  }, /*#__PURE__*/React.createElement(Renderer, {
24271
24226
  content: category,
@@ -24285,7 +24240,7 @@ class Categorizer extends React.Component {
24285
24240
  })), self.props.categories.map((catName, catNum) => {
24286
24241
  const selected = self.props.values[itemNum] === catNum;
24287
24242
  return /*#__PURE__*/React.createElement("td", {
24288
- className: "category " + css(styles$q.cell),
24243
+ className: "category " + css(styles$r.cell),
24289
24244
  key: catNum
24290
24245
  }, /*#__PURE__*/React.createElement("div", {
24291
24246
  className: ClassNames.INTERACTIVE,
@@ -24304,7 +24259,7 @@ class Categorizer extends React.Component {
24304
24259
  onChange: this.onChange.bind(this, itemNum, catNum),
24305
24260
  onClick: e => e.stopPropagation()
24306
24261
  }), !isMobile && /*#__PURE__*/React.createElement("span", {
24307
- className: css(styles$q.radioSpan, selected && styles$q.checkedRadioSpan, this.props.static && selected && styles$q.staticCheckedRadioSpan)
24262
+ className: css(styles$r.radioSpan, selected && styles$r.checkedRadioSpan, this.props.static && selected && styles$r.staticCheckedRadioSpan)
24308
24263
  }, selected ? /*#__PURE__*/React.createElement(InlineIcon, iconCircle) : /*#__PURE__*/React.createElement(InlineIcon, iconCircleThin))));
24309
24264
  }));
24310
24265
  /* eslint-enable max-len */
@@ -24316,7 +24271,7 @@ class Categorizer extends React.Component {
24316
24271
  "categorizer-container": true,
24317
24272
  "static-mode": this.props.static
24318
24273
  });
24319
- const inlineStyles = this.props.apiOptions.isMobile ? [styles$q.fullBleedContainer] : [];
24274
+ const inlineStyles = this.props.apiOptions.isMobile ? [styles$r.fullBleedContainer] : [];
24320
24275
  return /*#__PURE__*/React.createElement("div", {
24321
24276
  className: extraClassNames + " " + css(...inlineStyles)
24322
24277
  }, table);
@@ -24361,7 +24316,7 @@ const pageMargin = 16;
24361
24316
 
24362
24317
  // Stylesheets aren't directly testable
24363
24318
  /* c8 ignore next */
24364
- const styles$q = StyleSheet.create({
24319
+ const styles$r = StyleSheet.create({
24365
24320
  mobileTable: {
24366
24321
  [mediaQueries.smOrSmaller]: {
24367
24322
  minWidth: "auto"
@@ -24468,7 +24423,7 @@ const toAbsoluteUrl = url => parseUrl(url).href;
24468
24423
  const {
24469
24424
  updateQueryString: updateQueryString$1
24470
24425
  } = Util;
24471
- function getUrlFromProgramID(programID) {
24426
+ function getUrlFromProgramID$1(programID) {
24472
24427
  const {
24473
24428
  InitialRequestUrl
24474
24429
  } = getDependencies();
@@ -24553,7 +24508,7 @@ class CSProgram extends React.Component {
24553
24508
  return /*#__PURE__*/React.createElement("div", null);
24554
24509
  }
24555
24510
  let styleContainer = false;
24556
- let url = getUrlFromProgramID(this.props.programID);
24511
+ let url = getUrlFromProgramID$1(this.props.programID);
24557
24512
  let className;
24558
24513
  const style = {
24559
24514
  height: this.props.height,
@@ -24597,7 +24552,7 @@ class CSProgram extends React.Component {
24597
24552
  // creator "went wild".
24598
24553
  // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
24599
24554
  return /*#__PURE__*/React.createElement("div", {
24600
- className: css(styles$p.widthOverride, styleContainer && styles$p.container)
24555
+ className: css(styles$q.widthOverride, styleContainer && styles$q.container)
24601
24556
  }, /*#__PURE__*/React.createElement("iframe", {
24602
24557
  sandbox: sandboxOptions,
24603
24558
  src: url,
@@ -24626,7 +24581,7 @@ CSProgram.defaultProps = {
24626
24581
  // optional message
24627
24582
  message: null
24628
24583
  };
24629
- const styles$p = StyleSheet.create({
24584
+ const styles$q = StyleSheet.create({
24630
24585
  // Override the inherited width from the perseus paragraph class
24631
24586
  // 820 is minimum width for 420px editor and 400px canvas
24632
24587
  widthOverride: {
@@ -24671,7 +24626,7 @@ class Definition extends React.Component {
24671
24626
  }) => /*#__PURE__*/React.createElement(Popover, {
24672
24627
  content: /*#__PURE__*/React.createElement(PopoverContentCore, {
24673
24628
  color: "white",
24674
- style: styles$o.tooltipBody,
24629
+ style: styles$p.tooltipBody,
24675
24630
  closeButtonVisible: true
24676
24631
  }, /*#__PURE__*/React.createElement(Renderer, {
24677
24632
  apiOptions: this.props.apiOptions,
@@ -24702,7 +24657,7 @@ Definition.defaultProps = {
24702
24657
  togglePrompt: "define me",
24703
24658
  definition: "definition goes here"
24704
24659
  };
24705
- const styles$o = {
24660
+ const styles$p = {
24706
24661
  tooltipBody: {
24707
24662
  color: Color.offBlack,
24708
24663
  fontSize: 20,
@@ -24908,15 +24863,15 @@ class Explanation extends React.Component {
24908
24863
  const onClick = this._onClick;
24909
24864
  if (isMobile) {
24910
24865
  linkContainer = /*#__PURE__*/React.createElement("div", {
24911
- className: css(styles$n.linkContainer)
24866
+ className: css(styles$o.linkContainer)
24912
24867
  }, /*#__PURE__*/React.createElement("a", {
24913
- className: css(styles$n.mobileExplanationLink),
24868
+ className: css(styles$o.mobileExplanationLink),
24914
24869
  href: href,
24915
24870
  onClick: onClick,
24916
24871
  role: "button",
24917
24872
  "aria-expanded": this.state.expanded
24918
24873
  }, linkAnchor), this.state.expanded && /*#__PURE__*/React.createElement("svg", {
24919
- className: css(styles$n.disclosureArrow)
24874
+ className: css(styles$o.disclosureArrow)
24920
24875
  }, /*#__PURE__*/React.createElement("polygon", {
24921
24876
  style: {
24922
24877
  fill: backgroundColor
@@ -24929,13 +24884,13 @@ class Explanation extends React.Component {
24929
24884
  // an anchor tag (if rendered in a modal) or a Wonder Blocks
24930
24885
  // link component in lesson page, see: `article-page.jsx`.
24931
24886
  linkContainer = /*#__PURE__*/React.createElement("div", {
24932
- className: css(styles$n.linkContainer, styles$n.articleLink)
24887
+ className: css(styles$o.linkContainer, styles$o.articleLink)
24933
24888
  }, /*#__PURE__*/React.createElement(Link, {
24934
- className: css(styles$n.explanationLink)
24889
+ className: css(styles$o.explanationLink)
24935
24890
  // HACK(michaelpolyak): WB Link doesn't passthrough
24936
24891
  // class name.
24937
24892
  ,
24938
- style: styles$n.explanationLink,
24893
+ style: styles$o.explanationLink,
24939
24894
  href: href,
24940
24895
  onClick: onClick,
24941
24896
  role: "button",
@@ -24945,9 +24900,9 @@ class Explanation extends React.Component {
24945
24900
  // NOTE: For exercises, the baseElements `Link` is an
24946
24901
  // anchor tag, see: `perseus-api.jsx`.
24947
24902
  linkContainer = /*#__PURE__*/React.createElement("div", {
24948
- className: css(styles$n.linkContainer, styles$n.exerciseLink)
24903
+ className: css(styles$o.linkContainer, styles$o.exerciseLink)
24949
24904
  }, /*#__PURE__*/React.createElement(Link, {
24950
- className: css(styles$n.explanationLink),
24905
+ className: css(styles$o.explanationLink),
24951
24906
  href: href,
24952
24907
  onClick: onClick,
24953
24908
  role: "button",
@@ -24955,11 +24910,11 @@ class Explanation extends React.Component {
24955
24910
  }, `[${linkAnchor}]`));
24956
24911
  }
24957
24912
  }
24958
- const expandedStyle = isMobile ? styles$n.contentExpandedMobile : styles$n.contentExpanded;
24913
+ const expandedStyle = isMobile ? styles$o.contentExpandedMobile : styles$o.contentExpanded;
24959
24914
  return /*#__PURE__*/React.createElement("div", {
24960
- className: css(styles$n.container)
24915
+ className: css(styles$o.container)
24961
24916
  }, linkContainer, this.state.expanded && /*#__PURE__*/React.createElement("div", {
24962
- className: css(styles$n.content, isMobile && styles$n.contentMobile, this.state.expanded && expandedStyle)
24917
+ className: css(styles$o.content, isMobile && styles$o.contentMobile, this.state.expanded && expandedStyle)
24963
24918
  // eslint-disable-next-line react/no-string-refs
24964
24919
  ,
24965
24920
  ref: "content"
@@ -24983,7 +24938,7 @@ const verticalContentPadding = 10;
24983
24938
  const arrowWidth = 30;
24984
24939
  const arrowHeight = 14;
24985
24940
  const backgroundColor = gray95;
24986
- const styles$n = StyleSheet.create({
24941
+ const styles$o = StyleSheet.create({
24987
24942
  container: {
24988
24943
  display: "inline",
24989
24944
  position: "relative"
@@ -25088,15 +25043,15 @@ class GradedGroupAnswerBar extends React.Component {
25088
25043
  onCheckAnswer,
25089
25044
  onNextQuestion
25090
25045
  } = this.props;
25091
- const answerBarStyle = _extends({}, styles$m.answerBar, {
25046
+ const answerBarStyle = _extends({}, styles$n.answerBar, {
25092
25047
  backgroundColor: answerBarState === "CORRECT" ? Color.offWhite : "white",
25093
25048
  // Center the "Correct!" message only when there's no next question
25094
25049
  justifyContent: answerBarState === "CORRECT" && !onNextQuestion ? "center" : "space-between"
25095
25050
  });
25096
25051
  const message = answerBarState === "INCORRECT" ? /*#__PURE__*/React.createElement("span", {
25097
- style: styles$m.text
25052
+ style: styles$n.text
25098
25053
  }, /*#__PURE__*/React.createElement("span", {
25099
- style: styles$m.tryAgainIcon
25054
+ style: styles$n.tryAgainIcon
25100
25055
  }, /*#__PURE__*/React.createElement(InlineIcon, iconTryAgain)), /*#__PURE__*/React.createElement("span", {
25101
25056
  style: {
25102
25057
  marginLeft: 8
@@ -25115,7 +25070,7 @@ class GradedGroupAnswerBar extends React.Component {
25115
25070
  return /*#__PURE__*/React.createElement("div", {
25116
25071
  style: answerBarStyle
25117
25072
  }, /*#__PURE__*/React.createElement("span", {
25118
- style: styles$m.text
25073
+ style: styles$n.text
25119
25074
  }, /*#__PURE__*/React.createElement("span", {
25120
25075
  style: {
25121
25076
  fontSize: 28,
@@ -25137,7 +25092,7 @@ class GradedGroupAnswerBar extends React.Component {
25137
25092
  }
25138
25093
  }
25139
25094
  const fontSize = 17;
25140
- const styles$m = {
25095
+ const styles$n = {
25141
25096
  answerBar: {
25142
25097
  display: "flex",
25143
25098
  alignItems: "center",
@@ -25323,7 +25278,7 @@ class GradedGroup extends React.Component {
25323
25278
  // @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'null'.
25324
25279
  gradeStatus = i18n._("Incorrect");
25325
25280
  }
25326
- const mobileClass = this.props.inGradedGroupSet ? css(styles$l.gradedGroupInSet) : css(styles$l.gradedGroup);
25281
+ const mobileClass = this.props.inGradedGroupSet ? css(styles$m.gradedGroupInSet) : css(styles$m.gradedGroup);
25327
25282
  const classes = classNames({
25328
25283
  [mobileClass]: apiOptions.isMobile,
25329
25284
  "perseus-graded-group": true,
@@ -25342,7 +25297,7 @@ class GradedGroup extends React.Component {
25342
25297
  return /*#__PURE__*/React.createElement("div", {
25343
25298
  className: classes
25344
25299
  }, !!this.props.title && /*#__PURE__*/React.createElement("div", {
25345
- className: css(styles$l.title)
25300
+ className: css(styles$m.title)
25346
25301
  }, this.props.title), /*#__PURE__*/React.createElement(Renderer, _extends({}, this.props, {
25347
25302
  // eslint-disable-next-line react/no-string-refs
25348
25303
  ref: "renderer",
@@ -25374,7 +25329,7 @@ class GradedGroup extends React.Component {
25374
25329
  }, i18n._("Next question")), this.props.hint && this.props.hint.content && (this.state.showHint ? /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("button", {
25375
25330
  // @ts-expect-error - TS2322 - Type 'string' is not assignable to type 'number | undefined'.
25376
25331
  tabIndex: "0",
25377
- className: css(styles$l.explanationTitle),
25332
+ className: css(styles$m.explanationTitle),
25378
25333
  onClick: () => this.setState({
25379
25334
  showHint: false
25380
25335
  }),
@@ -25406,7 +25361,7 @@ class GradedGroup extends React.Component {
25406
25361
  showHint: true
25407
25362
  });
25408
25363
  },
25409
- className: css(styles$l.showHintLink)
25364
+ className: css(styles$m.showHintLink)
25410
25365
  }, i18n._("Explain"))), apiOptions.isMobile && answerBarState !== "HIDDEN" && /*#__PURE__*/React.createElement(GradedGroupAnswerBar, {
25411
25366
  apiOptions: apiOptions,
25412
25367
  answerBarState: answerBarState,
@@ -25427,7 +25382,7 @@ GradedGroup.defaultProps = {
25427
25382
  const traverseChildWidgets$3 = function traverseChildWidgets(props, traverseRenderer) {
25428
25383
  return _.extend({}, props, traverseRenderer(props));
25429
25384
  };
25430
- const styles$l = StyleSheet.create({
25385
+ const styles$m = StyleSheet.create({
25431
25386
  gradedGroupInSet: {
25432
25387
  // Reset a few desktop-only styles that come from graded-group.less
25433
25388
  marginLeft: 0,
@@ -25500,18 +25455,18 @@ class Indicators extends React.Component {
25500
25455
  return /*#__PURE__*/React.createElement("ul", {
25501
25456
  // reduntantly add class name for use in .less files--
25502
25457
  // the styles object key gets hashed
25503
- className: classNames(css(styles$k.indicatorContainer), "indicatorContainer")
25458
+ className: classNames(css(styles$l.indicatorContainer), "indicatorContainer")
25504
25459
  }, this.props.gradedGroups.map(({
25505
25460
  title
25506
25461
  }, i) => /*#__PURE__*/React.createElement("li", {
25507
- className: css(styles$k.indicator),
25462
+ className: css(styles$l.indicator),
25508
25463
  key: title
25509
25464
  }, /*#__PURE__*/React.createElement(Clickable, {
25510
25465
  role: "button",
25511
25466
  "aria-label": i18n._("Skip to %(title)s", {
25512
25467
  title
25513
25468
  }),
25514
- style: styles$k.indicatorButton,
25469
+ style: styles$l.indicatorButton,
25515
25470
  onClick: () => this.props.onChangeCurrentGroup(i),
25516
25471
  onKeyDown: e => this.handleKeyDown(e, i)
25517
25472
  }, ({
@@ -25519,9 +25474,9 @@ class Indicators extends React.Component {
25519
25474
  focused,
25520
25475
  pressed
25521
25476
  }) => /*#__PURE__*/React.createElement(View, {
25522
- style: [styles$k.indicatorDot, (hovered || focused || pressed) && styles$k.indicatorDotFocused]
25477
+ style: [styles$l.indicatorDot, (hovered || focused || pressed) && styles$l.indicatorDotFocused]
25523
25478
  }, i === this.props.currentGroup && /*#__PURE__*/React.createElement(View, {
25524
- style: styles$k.indicatorDotActive
25479
+ style: styles$l.indicatorDotActive
25525
25480
  }, /*#__PURE__*/React.createElement("span", {
25526
25481
  className: css(a11y.srOnly)
25527
25482
  }, i18n._("Current"))))))));
@@ -25587,7 +25542,7 @@ class GradedGroupSet extends React.Component {
25587
25542
  } = getDependencies();
25588
25543
  if (JIPT.useJIPT && this.props.gradedGroups.length > 1) {
25589
25544
  return /*#__PURE__*/React.createElement("div", {
25590
- className: css(styles$k.container)
25545
+ className: css(styles$l.container)
25591
25546
  }, this.props.gradedGroups.map((gradedGroup, i) => {
25592
25547
  return (
25593
25548
  /*#__PURE__*/
@@ -25610,13 +25565,13 @@ class GradedGroupSet extends React.Component {
25610
25565
  const numGroups = this.props.gradedGroups.length;
25611
25566
  const handleNextQuestion = this.state.currentGroup < numGroups - 1 ? this.handleNextQuestion : null;
25612
25567
  return /*#__PURE__*/React.createElement("div", {
25613
- className: css(styles$k.container)
25568
+ className: css(styles$l.container)
25614
25569
  }, /*#__PURE__*/React.createElement("div", {
25615
- className: css(styles$k.top)
25570
+ className: css(styles$l.top)
25616
25571
  }, /*#__PURE__*/React.createElement("div", {
25617
- className: css(styles$k.title)
25572
+ className: css(styles$l.title)
25618
25573
  }, currentGroup.title), /*#__PURE__*/React.createElement("div", {
25619
- className: css(styles$k.spacer)
25574
+ className: css(styles$l.spacer)
25620
25575
  }), /*#__PURE__*/React.createElement(Indicators, {
25621
25576
  currentGroup: this.state.currentGroup,
25622
25577
  gradedGroups: this.props.gradedGroups,
@@ -25658,7 +25613,7 @@ var GradedGroupSet$1 = {
25658
25613
  tracking: "all",
25659
25614
  isLintable: true
25660
25615
  };
25661
- const styles$k = StyleSheet.create({
25616
+ const styles$l = StyleSheet.create({
25662
25617
  top: {
25663
25618
  display: "flex",
25664
25619
  flexDirection: "row"
@@ -25763,7 +25718,7 @@ class ButtonGroup extends React.Component {
25763
25718
  id: "" + i,
25764
25719
  ref: "button" + i,
25765
25720
  key: "" + i,
25766
- className: css(styles$j.buttonStyle, button.value === value && styles$j.selectedStyle),
25721
+ className: css(styles$k.buttonStyle, button.value === value && styles$k.selectedStyle),
25767
25722
  onClick: () => this.toggleSelect(button.value)
25768
25723
  }, button.content || "" + button.value);
25769
25724
  });
@@ -25780,7 +25735,7 @@ ButtonGroup.defaultProps = {
25780
25735
  value: null,
25781
25736
  allowEmpty: true
25782
25737
  };
25783
- const styles$j = StyleSheet.create({
25738
+ const styles$k = StyleSheet.create({
25784
25739
  buttonStyle: {
25785
25740
  backgroundColor: "white",
25786
25741
  border: "1px solid #ccc",
@@ -31064,7 +31019,7 @@ const HideAnswersToggle = props => {
31064
31019
  const switchId = idFactory.get("hide-answers-toggle");
31065
31020
  const labelId = idFactory.get("hide-answers-label");
31066
31021
  return /*#__PURE__*/React.createElement(View, {
31067
- style: styles$i.switchWrapper
31022
+ style: styles$j.switchWrapper
31068
31023
  }, /*#__PURE__*/React.createElement(Switch, {
31069
31024
  id: switchId,
31070
31025
  checked: props.areAnswersHidden,
@@ -31076,7 +31031,7 @@ const HideAnswersToggle = props => {
31076
31031
  tag: "label"
31077
31032
  }, strings.hideAnswersToggleLabel));
31078
31033
  };
31079
- const styles$i = StyleSheet.create({
31034
+ const styles$j = StyleSheet.create({
31080
31035
  switchWrapper: {
31081
31036
  display: "flex",
31082
31037
  flexDirection: "row",
@@ -31129,13 +31084,13 @@ const AnswerPill = props => {
31129
31084
  id: idFactory.get("perseus-label-image-widget-answer-pill"),
31130
31085
  onClick: correct ? undefined : onClick,
31131
31086
  ref: ref,
31132
- style: [style, popperStyle, styles$h.pill, correct && styles$h.correct, incorrect && styles$h.incorrect, (focused || hovered) && BringToFront]
31087
+ style: [style, popperStyle, styles$i.pill, correct && styles$i.correct, incorrect && styles$i.incorrect, (focused || hovered) && BringToFront]
31133
31088
  }, /*#__PURE__*/React.createElement(Renderer, {
31134
31089
  content: answerString,
31135
31090
  inline: true
31136
31091
  })));
31137
31092
  };
31138
- const styles$h = StyleSheet.create({
31093
+ const styles$i = StyleSheet.create({
31139
31094
  correct: {
31140
31095
  // WB green darkened by 18%
31141
31096
  backgroundColor: "#00880b"
@@ -31188,23 +31143,23 @@ class Marker extends React.Component {
31188
31143
  icon: iconNull
31189
31144
  };
31190
31145
  if (showCorrectness) {
31191
- iconStyles = [styles$g.markerGraded, showCorrectness === "correct" ? styles$g.markerCorrect : styles$g.markerIncorrect, isOpen && styles$g.markerSelected];
31146
+ iconStyles = [styles$h.markerGraded, showCorrectness === "correct" ? styles$h.markerCorrect : styles$h.markerIncorrect, isOpen && styles$h.markerSelected];
31192
31147
  args = _extends({}, args, {
31193
31148
  icon: showCorrectness === "correct" ? iconCheck : iconMinus
31194
31149
  });
31195
31150
  } else if (selectedAnswers && selectedAnswers.length > 0) {
31196
- iconStyles = [styles$g.markerFilled, isOpen && styles$g.markerSelected];
31151
+ iconStyles = [styles$h.markerFilled, isOpen && styles$h.markerSelected];
31197
31152
  } else if (isOpen) {
31198
- iconStyles = [styles$g.markerSelected];
31153
+ iconStyles = [styles$h.markerSelected];
31199
31154
  args = _extends({}, args, {
31200
31155
  icon: iconChevronDown,
31201
31156
  size: 8
31202
31157
  });
31203
31158
  } else if (showPulsate) {
31204
- iconStyles = [styles$g.markerPulsateBase, shouldReduceMotion() ? showPulsate && styles$g.markerUnfilledPulsateOnce : showPulsate && styles$g.markerUnfilledPulsateInfinite];
31159
+ iconStyles = [styles$h.markerPulsateBase, shouldReduceMotion() ? showPulsate && styles$h.markerUnfilledPulsateOnce : showPulsate && styles$h.markerUnfilledPulsateInfinite];
31205
31160
  }
31206
31161
  return /*#__PURE__*/React.createElement(View, {
31207
- style: [styles$g.markerIcon, iconStyles],
31162
+ style: [styles$h.markerIcon, iconStyles],
31208
31163
  ref: node => this._icon = node
31209
31164
  }, /*#__PURE__*/React.createElement(Icon, args));
31210
31165
  }
@@ -31226,7 +31181,7 @@ class Marker extends React.Component {
31226
31181
  const markerDisabled = showCorrectness === "correct";
31227
31182
  const active = hovered || focused;
31228
31183
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
31229
- style: [styles$g.marker, active && !markerDisabled && styles$g.markerActive]
31184
+ style: [styles$h.marker, active && !markerDisabled && styles$h.markerActive]
31230
31185
  // already translated in: /widgets/graded-group.tsx
31231
31186
  ,
31232
31187
  "aria-label": markerDisabled ? i18n._("Correct!") : label
@@ -31244,7 +31199,7 @@ class Marker extends React.Component {
31244
31199
  Marker.defaultProps = {
31245
31200
  selected: []
31246
31201
  };
31247
- const styles$g = StyleSheet.create({
31202
+ const styles$h = StyleSheet.create({
31248
31203
  marker: {
31249
31204
  position: "absolute",
31250
31205
  backgroundColor: Color.white,
@@ -31814,13 +31769,13 @@ class LabelImage extends React.Component {
31814
31769
  const promptString = isMobile ? multipleAnswers ? i18n._("Tap each dot on the image to select all answers that apply.") : i18n._("Tap each dot on the image to select an answer.") : multipleAnswers ? i18n._("Click each dot on the image to select all answers that apply.") : i18n._("Click each dot on the image to select an answer.");
31815
31770
  const choicesString = i18n._("Choices:");
31816
31771
  return /*#__PURE__*/React.createElement("div", {
31817
- className: classNames("perseus-label-image-widget-instructions", css(styles$f.instructions))
31772
+ className: classNames("perseus-label-image-widget-instructions", css(styles$g.instructions))
31818
31773
  }, /*#__PURE__*/React.createElement("div", {
31819
- className: css(styles$f.instructionsCaption)
31774
+ className: css(styles$g.instructionsCaption)
31820
31775
  }, promptString, " ", !hideChoices && choicesString), !hideChoices && /*#__PURE__*/React.createElement("div", {
31821
- className: css(styles$f.instructionsChoices)
31776
+ className: css(styles$g.instructionsChoices)
31822
31777
  }, choices.map((choice, index) => /*#__PURE__*/React.createElement("div", {
31823
- className: css(styles$f.instructionsChoice),
31778
+ className: css(styles$g.instructionsChoice),
31824
31779
  key: index
31825
31780
  }, /*#__PURE__*/React.createElement(Renderer, {
31826
31781
  content: choice
@@ -31837,20 +31792,20 @@ class LabelImage extends React.Component {
31837
31792
  activeMarkerIndex
31838
31793
  } = this.state;
31839
31794
  return /*#__PURE__*/React.createElement("div", null, this.renderInstructions(), /*#__PURE__*/React.createElement("div", {
31840
- className: css(styles$f.markersCanvas),
31795
+ className: css(styles$g.markersCanvas),
31841
31796
  style: {
31842
31797
  maxWidth: imageWidth,
31843
31798
  maxHeight: imageHeight
31844
31799
  }
31845
31800
  }, /*#__PURE__*/React.createElement("div", {
31846
- className: css(styles$f.imageContainer,
31801
+ className: css(styles$g.imageContainer,
31847
31802
  // Ensure image interaction is disabled while answer
31848
31803
  // choices popup is visible. This addresses specific
31849
31804
  // case where user may click on question image to
31850
31805
  // dismiss the popup. If the image is larger in size
31851
31806
  // than its rendered in the widget, this would
31852
31807
  // result in a zoom of the image.
31853
- activeMarkerIndex !== -1 && styles$f.imageInteractionDisabled)
31808
+ activeMarkerIndex !== -1 && styles$g.imageInteractionDisabled)
31854
31809
  }, /*#__PURE__*/React.createElement(context$1.Consumer, null, ({
31855
31810
  setAssetStatus
31856
31811
  }) => /*#__PURE__*/React.createElement(SvgImage, {
@@ -31874,7 +31829,7 @@ class LabelImage extends React.Component {
31874
31829
  }));
31875
31830
  }
31876
31831
  }
31877
- const styles$f = StyleSheet.create({
31832
+ const styles$g = StyleSheet.create({
31878
31833
  instructions: {
31879
31834
  paddingBottom: 16
31880
31835
  },
@@ -32231,7 +32186,7 @@ class Placeholder extends React.Component {
32231
32186
  const {
32232
32187
  layout
32233
32188
  } = this.props;
32234
- const className = css(styles$e.card, styles$e.placeholder, layout === Layout.HORIZONTAL && styles$e.horizontalCard);
32189
+ const className = css(styles$f.card, styles$f.placeholder, layout === Layout.HORIZONTAL && styles$f.horizontalCard);
32235
32190
  const style = {
32236
32191
  width: this.props.width,
32237
32192
  height: this.props.height
@@ -32414,7 +32369,7 @@ class Draggable extends React.Component {
32414
32369
  // We need to keep backwards compatbility with rules specified directly
32415
32370
  // in CSS. Hence the hacky tacking on of manual classNames.
32416
32371
  // See sortable.less for details.
32417
- let className = css(styles$e.card, styles$e.draggable, layout === Layout.HORIZONTAL && styles$e.horizontalCard, layout === Layout.VERTICAL && styles$e.verticalCard, type === ItemState.DRAGGING && styles$e.dragging, type === ItemState.DISABLED && styles$e.disabled, !includePadding && styles$e.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
32372
+ let className = css(styles$f.card, styles$f.draggable, layout === Layout.HORIZONTAL && styles$f.horizontalCard, layout === Layout.VERTICAL && styles$f.verticalCard, type === ItemState.DRAGGING && styles$f.dragging, type === ItemState.DISABLED && styles$f.disabled, !includePadding && styles$f.unpaddedCard) + " " + ClassNames.INTERACTIVE + " perseus-sortable-draggable";
32418
32373
  if (!includePadding) {
32419
32374
  className += " perseus-sortable-draggable-unpadded";
32420
32375
  }
@@ -32621,7 +32576,7 @@ class Sortable extends React.Component {
32621
32576
  } = this.props;
32622
32577
  // We need to keep backwards compatbility with rules specified directly
32623
32578
  // in CSS. See sortable.less for details.
32624
- const className = css(styles$e.sortable) + " perseus-sortable";
32579
+ const className = css(styles$f.sortable) + " perseus-sortable";
32625
32580
  const syncWidth = ((_this$props$constrain = this.props.constraints) == null ? void 0 : _this$props$constrain.width) || layout === Layout.VERTICAL;
32626
32581
  const syncHeight = ((_this$props$constrain2 = this.props.constraints) == null ? void 0 : _this$props$constrain2.height) || layout === Layout.HORIZONTAL;
32627
32582
  _.each(this.state.items, function (item, i, items) {
@@ -32863,7 +32818,7 @@ Sortable.defaultProps = {
32863
32818
  linterContext: PerseusLinter.linterContextDefault,
32864
32819
  waitForTexRendererToLoad: true
32865
32820
  };
32866
- const styles$e = StyleSheet.create({
32821
+ const styles$f = StyleSheet.create({
32867
32822
  sortable: {
32868
32823
  boxSizing: "border-box",
32869
32824
  float: "left",
@@ -33025,23 +32980,23 @@ class Matcher extends React.Component {
33025
32980
  };
33026
32981
  const cellMarginPx = this.props.apiOptions.isMobile ? 8 : 5;
33027
32982
  return /*#__PURE__*/React.createElement("table", {
33028
- className: css(styles$d.widget) + " " + HACKY_CSS_CLASSNAME
32983
+ className: css(styles$e.widget) + " " + HACKY_CSS_CLASSNAME
33029
32984
  }, /*#__PURE__*/React.createElement("tbody", null, showLabels && /*#__PURE__*/React.createElement("tr", {
33030
- className: css(styles$d.row)
32985
+ className: css(styles$e.row)
33031
32986
  }, /*#__PURE__*/React.createElement("th", {
33032
- className: css(styles$d.column, styles$d.columnLabel)
32987
+ className: css(styles$e.column, styles$e.columnLabel)
33033
32988
  }, /*#__PURE__*/React.createElement(Renderer, {
33034
32989
  content: this.props.labels[0] || "...",
33035
32990
  linterContext: this.props.linterContext
33036
32991
  })), /*#__PURE__*/React.createElement("th", {
33037
- className: css(styles$d.column, styles$d.columnRight, styles$d.columnLabel)
32992
+ className: css(styles$e.column, styles$e.columnRight, styles$e.columnLabel)
33038
32993
  }, /*#__PURE__*/React.createElement(Renderer, {
33039
32994
  content: this.props.labels[1] || "...",
33040
32995
  linterContext: this.props.linterContext
33041
32996
  }))), /*#__PURE__*/React.createElement("tr", {
33042
- className: css(styles$d.row)
32997
+ className: css(styles$e.row)
33043
32998
  }, /*#__PURE__*/React.createElement("td", {
33044
- className: css(styles$d.column)
32999
+ className: css(styles$e.column)
33045
33000
  }, /*#__PURE__*/React.createElement(Sortable, {
33046
33001
  options: left,
33047
33002
  layout: Layout.VERTICAL,
@@ -33056,7 +33011,7 @@ class Matcher extends React.Component {
33056
33011
  ,
33057
33012
  ref: "left"
33058
33013
  })), /*#__PURE__*/React.createElement("td", {
33059
- className: css(styles$d.column, styles$d.columnRight)
33014
+ className: css(styles$e.column, styles$e.columnRight)
33060
33015
  }, /*#__PURE__*/React.createElement(Sortable
33061
33016
  // @ts-expect-error - TS2322 - Type 'readonly unknown[]' is not assignable to type 'readonly string[]'.
33062
33017
  , {
@@ -33109,7 +33064,7 @@ _.extend(Matcher, {
33109
33064
  });
33110
33065
  const padding = 5;
33111
33066
  const border = "1px solid #444";
33112
- const styles$d = StyleSheet.create({
33067
+ const styles$e = StyleSheet.create({
33113
33068
  widget: {
33114
33069
  paddingTop: padding,
33115
33070
  maxWidth: "100%",
@@ -33976,9 +33931,9 @@ function drawText(ctx, item) {
33976
33931
  if (item.value === null) {
33977
33932
  return;
33978
33933
  }
33979
- ctx.fillStyle = styles$c.fgColor;
33980
- ctx.font = styles$c.font;
33981
- ctx.fillText(item.value, item.pos[0] - styles$c.fontSizePx / 2 + 1, item.pos[1] + styles$c.fontSizePx / 2);
33934
+ ctx.fillStyle = styles$d.fgColor;
33935
+ ctx.font = styles$d.font;
33936
+ ctx.fillText(item.value, item.pos[0] - styles$d.fontSizePx / 2 + 1, item.pos[1] + styles$d.fontSizePx / 2);
33982
33937
  }
33983
33938
 
33984
33939
  /**
@@ -33999,16 +33954,16 @@ function drawText(ctx, item) {
33999
33954
  function drawDoubleLine(ctx, item) {
34000
33955
  // Outer line that forms both of the bond lines.
34001
33956
  let path = new Path2D();
34002
- ctx.lineWidth = 5 * styles$c.lineWidth;
34003
- ctx.strokeStyle = styles$c.fgColor;
33957
+ ctx.lineWidth = 5 * styles$d.lineWidth;
33958
+ ctx.strokeStyle = styles$d.fgColor;
34004
33959
  path.moveTo(item.startPos[0], item.startPos[1]);
34005
33960
  path.lineTo(item.endPos[0], item.endPos[1]);
34006
33961
  ctx.stroke(path);
34007
33962
 
34008
33963
  // Inner white line that separates the two bond lines.
34009
33964
  path = new Path2D();
34010
- ctx.lineWidth = 3 * styles$c.lineWidth;
34011
- ctx.strokeStyle = styles$c.bgColor;
33965
+ ctx.lineWidth = 3 * styles$d.lineWidth;
33966
+ ctx.strokeStyle = styles$d.bgColor;
34012
33967
  path.moveTo(item.startPos[0], item.startPos[1]);
34013
33968
  path.lineTo(item.endPos[0], item.endPos[1]);
34014
33969
  ctx.stroke(path);
@@ -34035,16 +33990,16 @@ function drawTripleLine(ctx, item) {
34035
33990
 
34036
33991
  // Outer line that will form the two outer bond lines.
34037
33992
  let path = new Path2D();
34038
- ctx.lineWidth = 7 * styles$c.lineWidth;
34039
- ctx.strokeStyle = styles$c.fgColor;
33993
+ ctx.lineWidth = 7 * styles$d.lineWidth;
33994
+ ctx.strokeStyle = styles$d.fgColor;
34040
33995
  path.moveTo(item.startPos[0], item.startPos[1]);
34041
33996
  path.lineTo(item.endPos[0], item.endPos[1]);
34042
33997
  ctx.stroke(path);
34043
33998
 
34044
33999
  // Middle white line that separates the bonds
34045
34000
  path = new Path2D();
34046
- ctx.lineWidth = 5 * styles$c.lineWidth;
34047
- ctx.strokeStyle = styles$c.bgColor;
34001
+ ctx.lineWidth = 5 * styles$d.lineWidth;
34002
+ ctx.strokeStyle = styles$d.bgColor;
34048
34003
  path.moveTo(item.startPos[0], item.startPos[1]);
34049
34004
  path.lineTo(item.endPos[0], item.endPos[1]);
34050
34005
  ctx.stroke(path);
@@ -34066,8 +34021,8 @@ function drawTripleLine(ctx, item) {
34066
34021
  */
34067
34022
  function drawLine(ctx, item) {
34068
34023
  const path = new Path2D();
34069
- ctx.lineWidth = styles$c.lineWidth;
34070
- ctx.strokeStyle = styles$c.fgColor;
34024
+ ctx.lineWidth = styles$d.lineWidth;
34025
+ ctx.strokeStyle = styles$d.fgColor;
34071
34026
  path.moveTo(item.startPos[0], item.startPos[1]);
34072
34027
  path.lineTo(item.endPos[0], item.endPos[1]);
34073
34028
  ctx.stroke(path);
@@ -34119,7 +34074,7 @@ function compareElements(item0, item1) {
34119
34074
  function draw(ctx, items) {
34120
34075
  items.sort(compareElements).forEach(drawItem(ctx));
34121
34076
  }
34122
- const styles$c = {
34077
+ const styles$d = {
34123
34078
  bgColor: "rgb(255, 255, 255)",
34124
34079
  fgColor: "rgb(0, 0, 0)",
34125
34080
  fontSizePx: 12,
@@ -36681,7 +36636,7 @@ class HighlightRenderer extends React.PureComponent {
36681
36636
  const rects = this.state.cachedHighlightRects;
36682
36637
  return /*#__PURE__*/React.createElement("div", null, rects.map((rect, index) => /*#__PURE__*/React.createElement("div", {
36683
36638
  key: index,
36684
- className: css(styles$b.highlightRect),
36639
+ className: css(styles$c.highlightRect),
36685
36640
  style: {
36686
36641
  // NOTE(mdr): We apply `position: absolute` here
36687
36642
  // rather than in Aphrodite styles, because
@@ -36700,7 +36655,7 @@ class HighlightRenderer extends React.PureComponent {
36700
36655
  })));
36701
36656
  }
36702
36657
  }
36703
- const styles$b = StyleSheet.create({
36658
+ const styles$c = StyleSheet.create({
36704
36659
  highlightRect: {
36705
36660
  background: "#fffabe" // highlighter yellow :)
36706
36661
  }
@@ -36781,7 +36736,7 @@ class HighlightTooltip extends React.PureComponent {
36781
36736
  // TooltipContent wouldn't let me overwrite
36782
36737
  // user-select and onClick
36783
36738
  const content = /*#__PURE__*/React.createElement("div", {
36784
- className: css(styles$a.tooltipLabel),
36739
+ className: css(styles$b.tooltipLabel),
36785
36740
  onClick: this.props.onClick
36786
36741
  }, this.props.label);
36787
36742
  const style = {
@@ -36801,7 +36756,7 @@ class HighlightTooltip extends React.PureComponent {
36801
36756
  }, /*#__PURE__*/React.createElement("div", null)));
36802
36757
  }
36803
36758
  }
36804
- const styles$a = StyleSheet.create({
36759
+ const styles$b = StyleSheet.create({
36805
36760
  tooltipLabel: {
36806
36761
  // Unlike most tooltip contents, we don't want this label to be
36807
36762
  // selectable. Selecting it is not only unhelpful, but dangerous: if
@@ -37353,7 +37308,7 @@ class HighlightableContent extends React.PureComponent {
37353
37308
  // starting point: https://phabricator.khanacademy.org/D35623?id=170698
37354
37309
  const buildHighlight = r => this._buildHighlight(r);
37355
37310
  return /*#__PURE__*/React.createElement("div", {
37356
- className: css(styles$9.container),
37311
+ className: css(styles$a.container),
37357
37312
  ref: container => this._container = container
37358
37313
  }, /*#__PURE__*/React.createElement("div", null, this.props.enabled && this._container && this._content && /*#__PURE__*/React.createElement(HighlightingUI, {
37359
37314
  buildHighlight: buildHighlight,
@@ -37374,14 +37329,14 @@ class HighlightableContent extends React.PureComponent {
37374
37329
  onAddHighlight: this._handleAddHighlight,
37375
37330
  onRemoveHighlight: this._handleRemoveHighlight
37376
37331
  })), /*#__PURE__*/React.createElement("div", {
37377
- className: css(styles$9.content),
37332
+ className: css(styles$a.content),
37378
37333
  ref: content => this._content = content
37379
37334
  }, /*#__PURE__*/React.createElement(WordIndexer, {
37380
37335
  onWordsUpdate: this._handleWordsUpdate
37381
37336
  }, this.props.children)));
37382
37337
  }
37383
37338
  }
37384
- const styles$9 = StyleSheet.create({
37339
+ const styles$a = StyleSheet.create({
37385
37340
  // We render content with z-index 1, and highlights with z-index 0, so that
37386
37341
  // content will receive the browser's native pointer events. (Highlights
37387
37342
  // have their own magic code to manage hover detection, despite being
@@ -37781,7 +37736,7 @@ class LineHeightMeasurer extends React.Component {
37781
37736
  }
37782
37737
  render() {
37783
37738
  return /*#__PURE__*/React.createElement("div", {
37784
- className: css(styles$8.measurer),
37739
+ className: css(styles$9.measurer),
37785
37740
  "aria-hidden": "true"
37786
37741
  }, /*#__PURE__*/React.createElement("div", {
37787
37742
  ref: ref => this._line = ref,
@@ -37789,7 +37744,7 @@ class LineHeightMeasurer extends React.Component {
37789
37744
  }, "Line Height Measurement"));
37790
37745
  }
37791
37746
  }
37792
- const styles$8 = StyleSheet.create({
37747
+ const styles$9 = StyleSheet.create({
37793
37748
  measurer: {
37794
37749
  position: "absolute",
37795
37750
  top: 0,
@@ -39079,6 +39034,52 @@ var Plotter$1 = {
39079
39034
  staticTransform: staticTransform
39080
39035
  };
39081
39036
 
39037
+ /**
39038
+ * This widget is for embedding Khan Academy Python programs.
39039
+ */
39040
+ function getUrlFromProgramID(programID) {
39041
+ const path = `/python-program/${programID}/embedded`;
39042
+ return toAbsoluteUrl(path);
39043
+ }
39044
+ /* This renders the program in an iframe. */
39045
+ class PythonProgram extends React.Component {
39046
+ render() {
39047
+ const url = getUrlFromProgramID(this.props.programID);
39048
+ const iframeStyle = {
39049
+ height: this.props.height,
39050
+ width: "100%"
39051
+ };
39052
+ const sandboxOptions = ["allow-popups", "allow-same-origin", "allow-scripts", "allow-top-navigation"].join(" ");
39053
+
39054
+ // We sandbox the iframe so that we allowlist only the functionality
39055
+ // that we need. This makes it a bit safer in case some content
39056
+ // creator "went wild".
39057
+ // http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
39058
+ return /*#__PURE__*/React.createElement(View, {
39059
+ style: styles$8.container
39060
+ }, /*#__PURE__*/React.createElement("iframe", {
39061
+ sandbox: sandboxOptions,
39062
+ src: url,
39063
+ style: iframeStyle,
39064
+ allowFullScreen: true
39065
+ }));
39066
+ }
39067
+ }
39068
+ PythonProgram.defaultProps = {
39069
+ height: 400
39070
+ };
39071
+ const styles$8 = StyleSheet.create({
39072
+ container: {
39073
+ margin: "auto",
39074
+ width: "100%"
39075
+ }
39076
+ });
39077
+ var PythonProgram$1 = {
39078
+ name: "python-program",
39079
+ displayName: "Python Program",
39080
+ widget: PythonProgram
39081
+ };
39082
+
39082
39083
  class Separator extends React.Component {
39083
39084
  constructor(...args) {
39084
39085
  super(...args);
@@ -41514,7 +41515,7 @@ var Video$1 = {
41514
41515
  };
41515
41516
 
41516
41517
  /* eslint-disable import/no-named-as-default */
41517
- 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, LightsPuzzle$1, Matrix$1, Matcher$1, Measurer$1, Molecule$1, NumberLine$1, Orderer$1, Passage$1, PassageRef$1, PassageRefTarget$1, Plotter$1, ReactionDiagram, Sequence$1, Simulator$1, Sorter$1, Table$1, Unit, Video$1, DeprecatedStandin$1];
41518
+ 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, LightsPuzzle$1, Matrix$1, Matcher$1, Measurer$1, Molecule$1, NumberLine$1, Orderer$1, Passage$1, PassageRef$1, PassageRefTarget$1, Plotter$1, PythonProgram$1, ReactionDiagram, Sequence$1, Simulator$1, Sorter$1, Table$1, Unit, Video$1, DeprecatedStandin$1];
41518
41519
 
41519
41520
  /**
41520
41521
  * This should be called by all clients, specifying whether extra widgets are
@@ -41554,7 +41555,7 @@ const init = function init(options) {
41554
41555
 
41555
41556
  // This file is processed by a Rollup plugin (replace) to inject the production
41556
41557
  const libName = "@khanacademy/perseus";
41557
- const libVersion = "19.1.8";
41558
+ const libVersion = "19.2.0";
41558
41559
  addLibraryVersionToPerseusDebug(libName, libVersion);
41559
41560
 
41560
41561
  const apiVersion = {
@@ -43000,6 +43001,8 @@ const ItemExtras = [
43000
43001
 
43001
43002
  // prettier-ignore
43002
43003
 
43004
+ // prettier-ignore
43005
+
43003
43006
  //prettier-ignore
43004
43007
 
43005
43008
  // A background image applied to various widgets.