@khanacademy/perseus 44.0.0 → 45.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -14609,9 +14609,9 @@ const pluck = function pluck(table, subKey) {
14609
14609
  * });
14610
14610
  * {a: 2, b: 3}
14611
14611
  */
14612
- const mapObject$1 = function mapObject(obj, lambda) {
14612
+ const mapObject$2 = function mapObject(obj, lambda) {
14613
14613
  const result = {};
14614
- _.each(_.keys(obj), function (key) {
14614
+ Object.keys(obj).forEach(key => {
14615
14615
  // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
14616
14616
  result[key] = lambda(obj[key], key);
14617
14617
  });
@@ -14628,14 +14628,14 @@ const mapObject$1 = function mapObject(obj, lambda) {
14628
14628
  */
14629
14629
  const mapObjectFromArray = function mapObjectFromArray(arr, lambda) {
14630
14630
  const result = {};
14631
- _.each(arr, function (elem) {
14631
+ arr.forEach(elem => {
14632
14632
  result[elem] = lambda(elem);
14633
14633
  });
14634
14634
  return result;
14635
14635
  };
14636
14636
  var objective_ = {
14637
14637
  pluck: pluck,
14638
- mapObject: mapObject$1,
14638
+ mapObject: mapObject$2,
14639
14639
  mapObjectFromArray: mapObjectFromArray
14640
14640
  };
14641
14641
 
@@ -18719,6 +18719,140 @@ class QuestionParagraph extends React.Component {
18719
18719
  }
18720
18720
  }
18721
18721
 
18722
+ // These are all the current widgets that require user-input and are supported for automatic scoring
18723
+ const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
18724
+
18725
+ /**
18726
+ * Regex for widget placeholders in a string.
18727
+ *
18728
+ * First capture group is the widget ID (ex. 'radio 1')
18729
+ * Second capture group is the widget type (ex. "radio)
18730
+ * exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
18731
+ */
18732
+ function getWidgetRegex() {
18733
+ return /\[\[☃ ([A-Za-z0-9- ]+)\]\]/g;
18734
+ }
18735
+
18736
+ /**
18737
+ * Add a widget placeholder using the widget ID.
18738
+ * ex. addWidget("radio 1") => "[[☃ radio 1]]"
18739
+ *
18740
+ * @param {string} id
18741
+ * @returns {string}
18742
+ */
18743
+ function addWidget(id) {
18744
+ return `[[☃ ${id}]]`;
18745
+ }
18746
+
18747
+ /**
18748
+ * Get a widget type by a widget's ID
18749
+ * (please don't derive type from ID)
18750
+ *
18751
+ * @param {string} widgetId the ID of the widget
18752
+ * @param {PerseusWidgetsMap} widgetMap widget ID to widget map
18753
+ * @returns {string} the widget type (ie "radio")
18754
+ */
18755
+ function getWidgetTypeByWidgetId(WidgetId, widgetMap) {
18756
+ var _widget$type;
18757
+ const widget = widgetMap[WidgetId];
18758
+ return (_widget$type = widget == null ? void 0 : widget.type) != null ? _widget$type : null;
18759
+ }
18760
+
18761
+ /**
18762
+ * Does the content have a specific type of widget?
18763
+ *
18764
+ * @param {string} type the type of the widget in question (ie "radio")
18765
+ * @param {string} content the string to search through
18766
+ * @param {PerseusWidgetsMap} widgetMap widget ID to widget map
18767
+ * @returns {boolean} if the content includes the widget type
18768
+ */
18769
+ function contentHasWidgetType(type, content, widgetMap) {
18770
+ return getWidgetIdsFromContentByType(type, content, widgetMap).length > 0;
18771
+ }
18772
+
18773
+ /**
18774
+ * Extract all widget IDs, which includes the widget type and instance number.
18775
+ * example output: ['radio 1', 'categorizer 1', 'categorizor 2']
18776
+ *
18777
+ * Content should contain Perseus widget placeholders,
18778
+ * which look like: '[[☃ radio 1]]'.
18779
+ *
18780
+ * @param {string} content
18781
+ * @returns {ReadonlyArray<string>} widgetIds
18782
+ */
18783
+ function getWidgetIdsFromContent(content) {
18784
+ const widgets = [];
18785
+ const localWidgetRegex = getWidgetRegex();
18786
+ let match = localWidgetRegex.exec(content);
18787
+ while (match !== null) {
18788
+ widgets.push(match[1]);
18789
+ match = localWidgetRegex.exec(content);
18790
+ }
18791
+ return widgets;
18792
+ }
18793
+
18794
+ /**
18795
+ * Get a list of widget IDs from content,
18796
+ * but only for specific widget types
18797
+ *
18798
+ * @param {string} type the type of widget (ie "radio")
18799
+ * @param {string} content the string to parse
18800
+ * @param {PerseusWidgetsMap} widgetMap widget ID to widget map
18801
+ * @returns {ReadonlyArray<string>} the widget type (ie "radio")
18802
+ */
18803
+ function getWidgetIdsFromContentByType(type, content, widgetMap) {
18804
+ const rv = [];
18805
+ const widgetIdsInContent = getWidgetIdsFromContent(content);
18806
+ widgetIdsInContent.forEach(widgetId => {
18807
+ const widget = widgetMap[widgetId];
18808
+ if ((widget == null ? void 0 : widget.type) === type) {
18809
+ rv.push(widgetId);
18810
+ }
18811
+ });
18812
+ return rv;
18813
+ }
18814
+
18815
+ /**
18816
+ * Pull the widget map out of ItemData
18817
+ *
18818
+ * @param {PerseusItem} PerseusItem containing a widgetMap
18819
+ * @returns {WidgetMap} the widget map in the PerseusItem
18820
+ */
18821
+ function getWidgetsMapFromItemData(itemData) {
18822
+ return itemData.question.widgets;
18823
+ }
18824
+
18825
+ /**
18826
+ * Get the widget information from a WidgetMap
18827
+ *
18828
+ * @param {string} widgetId the ID of the widget
18829
+ * @returns {PerseusWidget | null} the widget data if it exists, otherwise null
18830
+ */
18831
+ function getWidgetFromWidgetMap(widgetId, widgetMap) {
18832
+ var _widgetMap$widgetId;
18833
+ return (_widgetMap$widgetId = widgetMap[widgetId]) != null ? _widgetMap$widgetId : null;
18834
+ }
18835
+
18836
+ /**
18837
+ * Get select widgets from a widget map.
18838
+ * Useful for multi-items that needs to split
18839
+ * one PerseusItem into several
18840
+ *
18841
+ * @param {ReadonlyArray<string>} widgetIds to extract
18842
+ * @param {PerseusWidgetsMap} widgetMap to extract from
18843
+ * @return {PerseusWidgetsMap} a new widget map with requested widgets
18844
+ */
18845
+ function getWidgetsFromWidgetMap(widgetIds, widgetMap) {
18846
+ const widgets = {};
18847
+ widgetIds.forEach(widgetId => {
18848
+ const widget = getWidgetFromWidgetMap(widgetId, widgetMap);
18849
+ if (widget) {
18850
+ widgets[widgetId] = widget;
18851
+ }
18852
+ });
18853
+ return widgets;
18854
+ }
18855
+
18722
18856
  const DEFAULT_ALIGNMENT$1 = "block";
18723
18857
  // NOTE(kevinb): "default" is not one in `validAlignments`.
18724
18858
  const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
@@ -19150,12 +19284,35 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
19150
19284
  isLintable: isLintable
19151
19285
  });
19152
19286
 
19287
+ const {
19288
+ mapObject: mapObject$1
19289
+ } = objective_;
19290
+ function getUpgradedWidgetOptions(oldWidgetOptions) {
19291
+ // @ts-expect-error - TS2322 - Type '(props: Props) => Partial<Record<string, CategorizerWidget | CSProgramWidget | DefinitionWidget | DropdownWidget | ... 35 more ... | VideoWidget>>' is not assignable to type '(props: Props) => { [key: string]: PerseusWidget; }'.
19292
+ return mapObject$1(oldWidgetOptions, (widgetInfo, widgetId) => {
19293
+ if (!widgetInfo.type || !widgetInfo.alignment) {
19294
+ const newValues = {};
19295
+ if (!widgetInfo.type) {
19296
+ // TODO: why does widget have no type?
19297
+ // We don't want to derive type from widget ID
19298
+ // see: LEMS-1845
19299
+ newValues.type = widgetId.split(" ")[0];
19300
+ }
19301
+ if (!widgetInfo.alignment) {
19302
+ newValues.alignment = "default";
19303
+ }
19304
+ widgetInfo = _extends({}, widgetInfo, newValues);
19305
+ }
19306
+ return upgradeWidgetInfoToLatestVersion(widgetInfo);
19307
+ });
19308
+ }
19153
19309
  function emptyWidgetsFunctional(widgets,
19154
19310
  // This is a port of old code, I'm not sure why
19155
19311
  // we need widgetIds vs the keys of the widgets object
19156
19312
  widgetIds, userInputMap, strings, locale) {
19313
+ const upgradedWidgets = getUpgradedWidgetOptions(widgets);
19157
19314
  return widgetIds.filter(id => {
19158
- const widget = widgets[id];
19315
+ const widget = upgradedWidgets[id];
19159
19316
  if (!widget || widget.static) {
19160
19317
  // Static widgets shouldn't count as empty
19161
19318
  return false;
@@ -19174,12 +19331,27 @@ widgetIds, userInputMap, strings, locale) {
19174
19331
  }
19175
19332
  });
19176
19333
  }
19334
+
19335
+ // TODO: combine scorePerseusItem with scoreWidgetsFunctional
19336
+ // once scorePerseusItem is the only one calling scoreWidgetsFunctional
19337
+ function scorePerseusItem(perseusRenderData, userInputMap,
19338
+ // TODO(LEMS-2461,LEMS-2391): these probably
19339
+ // need to be removed before we move this to the server
19340
+ strings, locale) {
19341
+ // There seems to be a chance that PerseusRenderer.widgets might include
19342
+ // widget data for widgets that are not in PerseusRenderer.content,
19343
+ // so this checks that the widgets are being used before scoring them
19344
+ const usedWidgetIds = getWidgetIdsFromContent(perseusRenderData.content);
19345
+ const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
19346
+ return Util.flattenScores(scores);
19347
+ }
19177
19348
  function scoreWidgetsFunctional(widgets,
19178
19349
  // This is a port of old code, I'm not sure why
19179
19350
  // we need widgetIds vs the keys of the widgets object
19180
19351
  widgetIds, userInputMap, strings, locale) {
19352
+ const upgradedWidgets = getUpgradedWidgetOptions(widgets);
19181
19353
  const gradedWidgetIds = widgetIds.filter(id => {
19182
- const props = widgets[id];
19354
+ const props = upgradedWidgets[id];
19183
19355
  const widgetIsGraded = (props == null ? void 0 : props.graded) == null || props.graded;
19184
19356
  const widgetIsStatic = !!(props != null && props.static);
19185
19357
  // Ungraded widgets or widgets set to static shouldn't be graded.
@@ -19187,14 +19359,14 @@ widgetIds, userInputMap, strings, locale) {
19187
19359
  });
19188
19360
  const widgetScores = {};
19189
19361
  gradedWidgetIds.forEach(id => {
19190
- const widget = widgets[id];
19362
+ const widget = upgradedWidgets[id];
19191
19363
  if (!widget) {
19192
19364
  return;
19193
19365
  }
19194
19366
  const userInput = userInputMap[id];
19195
19367
  const scorer = getWidgetScorer(widget.type);
19196
19368
  if (widget.type === "group") {
19197
- const scores = scoreWidgetsFunctional(widget.options.widgets, Object.keys(widget.options.widgets), userInputMap[id], strings, locale);
19369
+ const scores = scoreWidgetsFunctional(widget.options.widgets, getWidgetIdsFromContent(widget.options.content), userInputMap[id], strings, locale);
19198
19370
  widgetScores[id] = Util.flattenScores(scores);
19199
19371
  } else if (scorer) {
19200
19372
  widgetScores[id] = scorer(userInput, widget.options, strings, locale);
@@ -19556,31 +19728,12 @@ class Renderer extends React.Component {
19556
19728
  return _extends({}, ApiOptions.defaults, this.props.apiOptions);
19557
19729
  };
19558
19730
  this._getInitialWidgetState = props => {
19559
- const allWidgetInfo = this._getAllWidgetsInfo(props);
19731
+ const allWidgetInfo = getUpgradedWidgetOptions(props.widgets);
19560
19732
  return {
19561
19733
  widgetInfo: allWidgetInfo,
19562
19734
  widgetProps: this._getAllWidgetsStartProps(allWidgetInfo, props)
19563
19735
  };
19564
19736
  };
19565
- // @ts-expect-error - TS2322 - Type '(props: Props) => Partial<Record<string, CategorizerWidget | CSProgramWidget | DefinitionWidget | DropdownWidget | ... 35 more ... | VideoWidget>>' is not assignable to type '(props: Props) => { [key: string]: PerseusWidget; }'.
19566
- this._getAllWidgetsInfo = props => {
19567
- return mapObject(props.widgets, (widgetInfo, widgetId) => {
19568
- if (!widgetInfo.type || !widgetInfo.alignment) {
19569
- const newValues = {};
19570
- if (!widgetInfo.type) {
19571
- // TODO: why does widget have no type?
19572
- // We don't want to derive type from widget ID
19573
- // see: LEMS-1845
19574
- newValues.type = widgetId.split(" ")[0];
19575
- }
19576
- if (!widgetInfo.alignment) {
19577
- newValues.alignment = "default";
19578
- }
19579
- widgetInfo = _.extend({}, widgetInfo, newValues);
19580
- }
19581
- return upgradeWidgetInfoToLatestVersion(widgetInfo);
19582
- });
19583
- };
19584
19737
  this._getAllWidgetsStartProps = (allWidgetInfo, props) => {
19585
19738
  const {
19586
19739
  apiOptions,
@@ -20026,7 +20179,7 @@ class Renderer extends React.Component {
20026
20179
  // Instead, we center them with css magic in styles.less
20027
20180
  // /cry(aria)
20028
20181
  this._foundTextNodes = true;
20029
- if (_.contains(this.widgetIds, node.id)) {
20182
+ if (this.widgetIds.includes(node.id)) {
20030
20183
  // We don't want to render a duplicate widget key/ref,
20031
20184
  // as this causes problems with react (for obvious
20032
20185
  // reasons). Instead we just notify the
@@ -20330,7 +20483,7 @@ class Renderer extends React.Component {
20330
20483
  };
20331
20484
  this.getInputPaths = () => {
20332
20485
  const inputPaths = [];
20333
- _.each(this.widgetIds, widgetId => {
20486
+ this.widgetIds.forEach(widgetId => {
20334
20487
  const widget = this.getWidgetInstance(widgetId);
20335
20488
  if (widget && widget.getInputPaths) {
20336
20489
  // Grab all input paths and add widgetID to the front
@@ -20338,7 +20491,7 @@ class Renderer extends React.Component {
20338
20491
  // Prefix paths with their widgetID and add to collective
20339
20492
  // list of paths.
20340
20493
  // @ts-expect-error - TS2345 - Argument of type '(inputPath: string) => void' is not assignable to parameter of type 'CollectionIterator<FocusPath, void, readonly FocusPath[]>'.
20341
- _.each(widgetInputPaths, inputPath => {
20494
+ widgetInputPaths.forEach(inputPath => {
20342
20495
  const relativeInputPath = [widgetId].concat(inputPath);
20343
20496
  inputPaths.push(relativeInputPath);
20344
20497
  });
@@ -20467,12 +20620,20 @@ class Renderer extends React.Component {
20467
20620
  this.getWidgetIds = () => {
20468
20621
  return this.widgetIds;
20469
20622
  };
20623
+ /**
20624
+ * @deprecated use scorePerseusItem
20625
+ */
20626
+ this.guessAndScore = () => {
20627
+ const totalGuess = this.getUserInput();
20628
+ const totalScore = this.score();
20629
+ return [totalGuess, totalScore];
20630
+ };
20470
20631
  this.examples = () => {
20471
20632
  const widgetIds = this.widgetIds;
20472
- const examples = _.compact(_.map(widgetIds, widgetId => {
20633
+ const examples = widgetIds.map(widgetId => {
20473
20634
  const widget = this.getWidgetInstance(widgetId);
20474
20635
  return widget != null && widget.examples ? widget.examples() : null;
20475
- }));
20636
+ }).filter(Boolean);
20476
20637
 
20477
20638
  // no widgets with examples
20478
20639
  if (!examples.length) {
@@ -20727,27 +20888,15 @@ class Renderer extends React.Component {
20727
20888
  }
20728
20889
 
20729
20890
  /**
20730
- * Returns an object mapping from widget ID to perseus-style score.
20731
- * The keys of this object are the values of the array returned
20732
- * from `getWidgetIds`.
20733
- */
20734
- scoreWidgets() {
20735
- return scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
20736
- }
20737
-
20738
- /**
20739
- * Grades the content.
20891
+ * Scores the content.
20892
+ *
20893
+ * @deprecated use scorePerseusItem
20740
20894
  */
20741
20895
  score() {
20742
- const scores = this.scoreWidgets();
20896
+ const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
20743
20897
  const combinedScore = Util.flattenScores(scores);
20744
20898
  return combinedScore;
20745
20899
  }
20746
- guessAndScore() {
20747
- const totalGuess = this.getUserInput();
20748
- const totalScore = this.score();
20749
- return [totalGuess, totalScore];
20750
- }
20751
20900
  render() {
20752
20901
  const apiOptions = this.getApiOptions();
20753
20902
  if (this.reuseMarkdown) {
@@ -29327,6 +29476,9 @@ class Group extends React.Component {
29327
29476
  this.change("widgets", this.rendererRef.props.widgets);
29328
29477
  }
29329
29478
  };
29479
+
29480
+ // TODO(LEMS-2391): replace this when there's a separate check
29481
+ // for valid/invalid state
29330
29482
  const score = (_this$rendererRef12 = this.rendererRef) == null ? void 0 : _this$rendererRef12.score();
29331
29483
  const isValid = score && score.type !== "invalid";
29332
29484
  const isInvalid = score && score.type === "invalid";
@@ -44861,7 +45013,7 @@ const init = function init() {
44861
45013
 
44862
45014
  // This file is processed by a Rollup plugin (replace) to inject the production
44863
45015
  const libName = "@khanacademy/perseus";
44864
- const libVersion = "44.0.0";
45016
+ const libVersion = "45.0.0";
44865
45017
  addLibraryVersionToPerseusDebug(libName, libVersion);
44866
45018
 
44867
45019
  const apiVersion = {
@@ -45485,6 +45637,13 @@ class ServerItemRenderer extends React.Component {
45485
45637
  this.props.onRendered(true);
45486
45638
  }
45487
45639
  }
45640
+ if (this.props.score && this.props.score !== prevProps.score) {
45641
+ const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
45642
+ this.setState({
45643
+ questionCompleted: this.props.score.correct,
45644
+ questionHighlightedWidgets: emptyQuestionAreaWidgets
45645
+ });
45646
+ }
45488
45647
  }
45489
45648
  componentWillUnmount() {
45490
45649
  if (this.blurTimeoutID != null) {
@@ -45602,18 +45761,37 @@ class ServerItemRenderer extends React.Component {
45602
45761
  return this.questionRenderer.getPromptJSON();
45603
45762
  }
45604
45763
 
45764
+ /**
45765
+ * Returns an array of the widget `.getUserInput()` results
45766
+ *
45767
+ * TODO: can we remove this? Seems to be just for backwards
45768
+ * compatibility with old Perseus Chrome logging
45769
+ * @deprecated use getUserInput
45770
+ */
45771
+ getUserInputLegacy() {
45772
+ return this.questionRenderer.getUserInput();
45773
+ }
45774
+
45775
+ /**
45776
+ * Returns an object of the widget `.getUserInput()` results
45777
+ */
45778
+ getUserInput() {
45779
+ return this.questionRenderer.getUserInputMap();
45780
+ }
45781
+
45605
45782
  /**
45606
45783
  * Grades the item.
45784
+ *
45785
+ * @deprecated use scorePerseusItem
45607
45786
  */
45608
45787
  scoreInput() {
45609
- const guessAndScore = this.questionRenderer.guessAndScore();
45610
- const guess = guessAndScore[0];
45611
- const score = guessAndScore[1];
45788
+ const guess = this.getUserInput();
45789
+ const score = scorePerseusItem(this.props.item.question, guess, this.context.strings, this.context.locale);
45612
45790
 
45613
45791
  // Continue to include an empty guess for the now defunct answer area.
45614
45792
  // TODO(alex): Check whether we rely on the format here for
45615
45793
  // analyzing ProblemLogs. If not, remove this layer.
45616
- const maxCompatGuess = [guess, []];
45794
+ const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
45617
45795
  const keScore = Util.keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
45618
45796
  const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
45619
45797
  this.setState({
@@ -47461,140 +47639,6 @@ const registerAllWidgetsForTesting = () => {
47461
47639
  replaceDeprecatedWidgets();
47462
47640
  };
47463
47641
 
47464
- // These are all the current widgets that require user-input and are supported for automatic scoring
47465
- const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
47466
-
47467
- /**
47468
- * Regex for widget placeholders in a string.
47469
- *
47470
- * First capture group is the widget ID (ex. 'radio 1')
47471
- * Second capture group is the widget type (ex. "radio)
47472
- * exec return will look like: ['[[☃ radio 1]]', 'radio 1', 'radio']
47473
- */
47474
- function getWidgetRegex() {
47475
- return /\[\[☃ ([A-Za-z0-9- ]+)\]\]/g;
47476
- }
47477
-
47478
- /**
47479
- * Add a widget placeholder using the widget ID.
47480
- * ex. addWidget("radio 1") => "[[☃ radio 1]]"
47481
- *
47482
- * @param {string} id
47483
- * @returns {string}
47484
- */
47485
- function addWidget(id) {
47486
- return `[[☃ ${id}]]`;
47487
- }
47488
-
47489
- /**
47490
- * Get a widget type by a widget's ID
47491
- * (please don't derive type from ID)
47492
- *
47493
- * @param {string} widgetId the ID of the widget
47494
- * @param {PerseusWidgetsMap} widgetMap widget ID to widget map
47495
- * @returns {string} the widget type (ie "radio")
47496
- */
47497
- function getWidgetTypeByWidgetId(WidgetId, widgetMap) {
47498
- var _widget$type;
47499
- const widget = widgetMap[WidgetId];
47500
- return (_widget$type = widget == null ? void 0 : widget.type) != null ? _widget$type : null;
47501
- }
47502
-
47503
- /**
47504
- * Does the content have a specific type of widget?
47505
- *
47506
- * @param {string} type the type of the widget in question (ie "radio")
47507
- * @param {string} content the string to search through
47508
- * @param {PerseusWidgetsMap} widgetMap widget ID to widget map
47509
- * @returns {boolean} if the content includes the widget type
47510
- */
47511
- function contentHasWidgetType(type, content, widgetMap) {
47512
- return getWidgetIdsFromContentByType(type, content, widgetMap).length > 0;
47513
- }
47514
-
47515
- /**
47516
- * Extract all widget IDs, which includes the widget type and instance number.
47517
- * example output: ['radio 1', 'categorizer 1', 'categorizor 2']
47518
- *
47519
- * Content should contain Perseus widget placeholders,
47520
- * which look like: '[[☃ radio 1]]'.
47521
- *
47522
- * @param {string} content
47523
- * @returns {ReadonlyArray<string>} widgetIds
47524
- */
47525
- function getWidgetIdsFromContent(content) {
47526
- const widgets = [];
47527
- const localWidgetRegex = getWidgetRegex();
47528
- let match = localWidgetRegex.exec(content);
47529
- while (match !== null) {
47530
- widgets.push(match[1]);
47531
- match = localWidgetRegex.exec(content);
47532
- }
47533
- return widgets;
47534
- }
47535
-
47536
- /**
47537
- * Get a list of widget IDs from content,
47538
- * but only for specific widget types
47539
- *
47540
- * @param {string} type the type of widget (ie "radio")
47541
- * @param {string} content the string to parse
47542
- * @param {PerseusWidgetsMap} widgetMap widget ID to widget map
47543
- * @returns {ReadonlyArray<string>} the widget type (ie "radio")
47544
- */
47545
- function getWidgetIdsFromContentByType(type, content, widgetMap) {
47546
- const rv = [];
47547
- const widgetIdsInContent = getWidgetIdsFromContent(content);
47548
- widgetIdsInContent.forEach(widgetId => {
47549
- const widget = widgetMap[widgetId];
47550
- if ((widget == null ? void 0 : widget.type) === type) {
47551
- rv.push(widgetId);
47552
- }
47553
- });
47554
- return rv;
47555
- }
47556
-
47557
- /**
47558
- * Pull the widget map out of ItemData
47559
- *
47560
- * @param {PerseusItem} PerseusItem containing a widgetMap
47561
- * @returns {WidgetMap} the widget map in the PerseusItem
47562
- */
47563
- function getWidgetsMapFromItemData(itemData) {
47564
- return itemData.question.widgets;
47565
- }
47566
-
47567
- /**
47568
- * Get the widget information from a WidgetMap
47569
- *
47570
- * @param {string} widgetId the ID of the widget
47571
- * @returns {PerseusWidget | null} the widget data if it exists, otherwise null
47572
- */
47573
- function getWidgetFromWidgetMap(widgetId, widgetMap) {
47574
- var _widgetMap$widgetId;
47575
- return (_widgetMap$widgetId = widgetMap[widgetId]) != null ? _widgetMap$widgetId : null;
47576
- }
47577
-
47578
- /**
47579
- * Get select widgets from a widget map.
47580
- * Useful for multi-items that needs to split
47581
- * one PerseusItem into several
47582
- *
47583
- * @param {ReadonlyArray<string>} widgetIds to extract
47584
- * @param {PerseusWidgetsMap} widgetMap to extract from
47585
- * @return {PerseusWidgetsMap} a new widget map with requested widgets
47586
- */
47587
- function getWidgetsFromWidgetMap(widgetIds, widgetMap) {
47588
- const widgets = {};
47589
- widgetIds.forEach(widgetId => {
47590
- const widget = getWidgetFromWidgetMap(widgetId, widgetMap);
47591
- if (widget) {
47592
- widgets[widgetId] = widget;
47593
- }
47594
- });
47595
- return widgets;
47596
- }
47597
-
47598
47642
  /**
47599
47643
  * This function extracts the answers from the widgets.
47600
47644
  *
@@ -49651,5 +49695,5 @@ const GrapherUtil = {
49651
49695
  typeToButton
49652
49696
  };
49653
49697
 
49654
- export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, buildEmptyItemTreeForShape, buildMapper, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mathOnlyParser, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
49698
+ export { ApiOptions, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, addWidget, apiVersion, bodyXsmallBold, buildEmptyItemTreeForShape, buildMapper, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getClockwiseAngle, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, makeSafeUrl, mathOnlyParser, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, scorePerseusItem, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
49655
49699
  //# sourceMappingURL=index.js.map