@khanacademy/perseus-core 8.0.0 → 10.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.
Files changed (102) hide show
  1. package/dist/data-schema.d.ts +82 -84
  2. package/dist/es/index.js +67 -53
  3. package/dist/es/index.js.map +1 -1
  4. package/dist/index.d.ts +9 -2
  5. package/dist/index.js +71 -52
  6. package/dist/index.js.map +1 -1
  7. package/dist/keypad.d.ts +1 -1
  8. package/dist/parse-perseus-json/general-purpose-parsers/defaulted.d.ts +1 -1
  9. package/dist/parse-perseus-json/general-purpose-parsers/index.d.ts +1 -1
  10. package/dist/parse-perseus-json/general-purpose-parsers/is-plain-object.d.ts +8 -0
  11. package/dist/parse-perseus-json/general-purpose-parsers/test-helpers.d.ts +5 -0
  12. package/dist/parse-perseus-json/perseus-parsers/categorizer-widget.d.ts +13 -3
  13. package/dist/parse-perseus-json/perseus-parsers/categorizer-widget.typetest.d.ts +1 -0
  14. package/dist/parse-perseus-json/perseus-parsers/cs-program-widget.d.ts +12 -3
  15. package/dist/parse-perseus-json/perseus-parsers/cs-program-widget.typetest.d.ts +1 -0
  16. package/dist/parse-perseus-json/perseus-parsers/definition-widget.d.ts +5 -3
  17. package/dist/parse-perseus-json/perseus-parsers/definition-widget.typetest.d.ts +1 -0
  18. package/dist/parse-perseus-json/perseus-parsers/dropdown-widget.d.ts +10 -3
  19. package/dist/parse-perseus-json/perseus-parsers/dropdown-widget.typetest.d.ts +1 -0
  20. package/dist/parse-perseus-json/perseus-parsers/explanation-widget.d.ts +7 -3
  21. package/dist/parse-perseus-json/perseus-parsers/explanation-widget.typetest.d.ts +1 -0
  22. package/dist/parse-perseus-json/perseus-parsers/expression-widget.d.ts +16 -3
  23. package/dist/parse-perseus-json/perseus-parsers/expression-widget.typetest.d.ts +1 -0
  24. package/dist/parse-perseus-json/perseus-parsers/graded-group-set-widget.d.ts +15 -3
  25. package/dist/parse-perseus-json/perseus-parsers/graded-group-set-widget.typetest.d.ts +1 -0
  26. package/dist/parse-perseus-json/perseus-parsers/graded-group-widget.d.ts +14 -4
  27. package/dist/parse-perseus-json/perseus-parsers/graded-group-widget.typetest.d.ts +1 -0
  28. package/dist/parse-perseus-json/perseus-parsers/grapher-widget.d.ts +51 -3
  29. package/dist/parse-perseus-json/perseus-parsers/grapher-widget.typetest.d.ts +1 -0
  30. package/dist/parse-perseus-json/perseus-parsers/group-widget.d.ts +1 -3
  31. package/dist/parse-perseus-json/perseus-parsers/group-widget.typetest.d.ts +1 -0
  32. package/dist/parse-perseus-json/perseus-parsers/hint.d.ts +9 -3
  33. package/dist/parse-perseus-json/perseus-parsers/hint.typetest.d.ts +1 -0
  34. package/dist/parse-perseus-json/perseus-parsers/iframe-widget.d.ts +12 -3
  35. package/dist/parse-perseus-json/perseus-parsers/iframe-widget.typetest.d.ts +1 -0
  36. package/dist/parse-perseus-json/perseus-parsers/image-widget.d.ts +22 -3
  37. package/dist/parse-perseus-json/perseus-parsers/image-widget.typetest.d.ts +1 -0
  38. package/dist/parse-perseus-json/perseus-parsers/input-number-widget.d.ts +10 -2
  39. package/dist/parse-perseus-json/perseus-parsers/input-number-widget.typetest.d.ts +1 -0
  40. package/dist/parse-perseus-json/perseus-parsers/interaction-widget.d.ts +125 -3
  41. package/dist/parse-perseus-json/perseus-parsers/interaction-widget.typetest.d.ts +1 -0
  42. package/dist/parse-perseus-json/perseus-parsers/interactive-graph-user-input.d.ts +60 -1
  43. package/dist/parse-perseus-json/perseus-parsers/interactive-graph-widget.d.ts +326 -5
  44. package/dist/parse-perseus-json/perseus-parsers/interactive-graph-widget.typetest.d.ts +1 -0
  45. package/dist/parse-perseus-json/perseus-parsers/label-image-widget.d.ts +16 -3
  46. package/dist/parse-perseus-json/perseus-parsers/label-image-widget.typetest.d.ts +1 -0
  47. package/dist/parse-perseus-json/perseus-parsers/legacy-button-sets.d.ts +1 -2
  48. package/dist/parse-perseus-json/perseus-parsers/matcher-widget.d.ts +7 -3
  49. package/dist/parse-perseus-json/perseus-parsers/matcher-widget.typetest.d.ts +1 -0
  50. package/dist/parse-perseus-json/perseus-parsers/matrix-widget.d.ts +8 -3
  51. package/dist/parse-perseus-json/perseus-parsers/matrix-widget.typetest.d.ts +1 -0
  52. package/dist/parse-perseus-json/perseus-parsers/measurer-widget.d.ts +18 -3
  53. package/dist/parse-perseus-json/perseus-parsers/measurer-widget.typetest.d.ts +1 -0
  54. package/dist/parse-perseus-json/perseus-parsers/molecule-renderer-widget.d.ts +5 -3
  55. package/dist/parse-perseus-json/perseus-parsers/molecule-renderer-widget.typetest.d.ts +1 -0
  56. package/dist/parse-perseus-json/perseus-parsers/number-line-widget.d.ts +17 -3
  57. package/dist/parse-perseus-json/perseus-parsers/number-line-widget.typetest.d.ts +1 -0
  58. package/dist/parse-perseus-json/perseus-parsers/numeric-input-widget.d.ts +21 -4
  59. package/dist/parse-perseus-json/perseus-parsers/numeric-input-widget.typetest.d.ts +1 -0
  60. package/dist/parse-perseus-json/perseus-parsers/orderer-widget.d.ts +7 -3
  61. package/dist/parse-perseus-json/perseus-parsers/orderer-widget.typetest.d.ts +1 -0
  62. package/dist/parse-perseus-json/perseus-parsers/passage-ref-widget.d.ts +5 -3
  63. package/dist/parse-perseus-json/perseus-parsers/passage-ref-widget.typetest.d.ts +1 -0
  64. package/dist/parse-perseus-json/perseus-parsers/passage-widget.d.ts +7 -3
  65. package/dist/parse-perseus-json/perseus-parsers/passage-widget.typetest.d.ts +1 -0
  66. package/dist/parse-perseus-json/perseus-parsers/perseus-answer-area.d.ts +11 -2
  67. package/dist/parse-perseus-json/perseus-parsers/perseus-answer-area.typetest.d.ts +1 -0
  68. package/dist/parse-perseus-json/perseus-parsers/perseus-image-background.d.ts +9 -3
  69. package/dist/parse-perseus-json/perseus-parsers/perseus-image-background.typetest.d.ts +1 -0
  70. package/dist/parse-perseus-json/perseus-parsers/perseus-image-detail.d.ts +4 -0
  71. package/dist/parse-perseus-json/perseus-parsers/perseus-image-detail.typetest.d.ts +1 -0
  72. package/dist/parse-perseus-json/perseus-parsers/phet-simulation-widget.d.ts +4 -3
  73. package/dist/parse-perseus-json/perseus-parsers/phet-simulation-widget.typetest.d.ts +1 -0
  74. package/dist/parse-perseus-json/perseus-parsers/plotter-widget.d.ts +15 -3
  75. package/dist/parse-perseus-json/perseus-parsers/plotter-widget.typetest.d.ts +1 -0
  76. package/dist/parse-perseus-json/perseus-parsers/python-program-widget.d.ts +4 -3
  77. package/dist/parse-perseus-json/perseus-parsers/python-program-widget.typetest.d.ts +1 -0
  78. package/dist/parse-perseus-json/perseus-parsers/sorter-widget.d.ts +5 -3
  79. package/dist/parse-perseus-json/perseus-parsers/sorter-widget.typetest.d.ts +1 -0
  80. package/dist/parse-perseus-json/perseus-parsers/table-widget.d.ts +6 -3
  81. package/dist/parse-perseus-json/perseus-parsers/table-widget.typetest.d.ts +1 -0
  82. package/dist/parse-perseus-json/perseus-parsers/video-widget.d.ts +4 -3
  83. package/dist/parse-perseus-json/perseus-parsers/video-widget.typetest.d.ts +1 -0
  84. package/dist/types.d.ts +6 -3
  85. package/dist/utils/deep-clone.d.ts +1 -2
  86. package/dist/utils/item-has-hints.d.ts +5 -0
  87. package/dist/utils/item-has-rationales.d.ts +5 -0
  88. package/dist/utils/random-util.d.ts +1 -1
  89. package/dist/utils/split-perseus-item.d.ts +3 -3
  90. package/dist/utils/split-perseus-renderer.d.ts +7 -0
  91. package/dist/utils/test-utils.d.ts +2 -1
  92. package/dist/validation.types.d.ts +27 -27
  93. package/dist/widgets/core-widget-registry.d.ts +8 -0
  94. package/dist/widgets/group/group-util.d.ts +12 -0
  95. package/dist/widgets/input-number/input-number-util.d.ts +12 -0
  96. package/dist/widgets/logic-export.types.d.ts +3 -1
  97. package/dist/widgets/matrix/matrix-util.d.ts +1 -2
  98. package/dist/widgets/number-line/number-line-util.d.ts +1 -2
  99. package/dist/widgets/orderer/orderer-util.d.ts +2 -6
  100. package/dist/widgets/plotter/plotter-util.d.ts +1 -1
  101. package/package.json +4 -3
  102. package/dist/parse-perseus-json/general-purpose-parsers/is-object.d.ts +0 -1
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type { PerseusAnalyticsEvent, AnalyticsEventHandlerFn } from "./analytics";
2
- export type { KEScore, RendererInterface, MarkerType, InteractiveMarkerType, Relationship, Alignment, } from "./types";
2
+ export type { KEScore, RendererInterface, MarkerType, InteractiveMarkerType, Relationship, Alignment, RecursiveReadonly, } from "./types";
3
3
  export type { KeypadKey, KeypadConfiguration, KeypadContextRendererInterface, } from "./keypad";
4
4
  export type { ErrorKind } from "./error/errors";
5
5
  export type { FunctionTypeMappingKeys } from "./utils/grapher-util";
@@ -11,7 +11,9 @@ export { approximateEqual, approximateDeepEqual } from "./utils/equality";
11
11
  export { addWidget, getWidgetIdsFromContent, getWidgetIdsFromContentByType, } from "./utils/widget-id-utils";
12
12
  export { default as deepClone } from "./utils/deep-clone";
13
13
  export * as GrapherUtil from "./utils/grapher-util";
14
- export { generateTestPerseusItem } from "./utils/test-utils";
14
+ export { generateTestPerseusItem, generateTestPerseusRenderer, } from "./utils/test-utils";
15
+ export { itemHasRationales } from "./utils/item-has-rationales";
16
+ export { itemHasHints } from "./utils/item-has-hints";
15
17
  export { parsePerseusItem, parseAndMigratePerseusItem, parseAndMigratePerseusArticle, type ParseFailureDetail, } from "./parse-perseus-json";
16
18
  export { isSuccess, isFailure, type Result, type Success, type Failure, } from "./parse-perseus-json/result";
17
19
  export { libVersion } from "./version";
@@ -96,6 +98,7 @@ export { default as getCSProgramPublicWidgetOptions } from "./widgets/cs-program
96
98
  export { default as getExpressionPublicWidgetOptions } from "./widgets/expression/expression-util";
97
99
  export type { ExpressionPublicWidgetOptions } from "./widgets/expression/expression-util";
98
100
  export { default as getGrapherPublicWidgetOptions } from "./widgets/grapher/grapher-util";
101
+ export { default as getGroupPublicWidgetOptions } from "./widgets/group/group-util";
99
102
  export { default as getInteractiveGraphPublicWidgetOptions, type InteractiveGraphPublicWidgetOptions, } from "./widgets/interactive-graph/interactive-graph-util";
100
103
  export { default as getLabelImagePublicWidgetOptions } from "./widgets/label-image/label-image-util";
101
104
  export { default as getSorterPublicWidgetOptions } from "./widgets/sorter/sorter-util";
@@ -103,12 +106,16 @@ export { default as getDropdownPublicWidgetOptions } from "./widgets/dropdown/dr
103
106
  export type { DropdownPublicWidgetOptions } from "./widgets/dropdown/dropdown-util";
104
107
  export { default as getNumericInputPublicWidgetOptions } from "./widgets/numeric-input/numeric-input-util";
105
108
  export { default as getNumberLinePublicWidgetOptions } from "./widgets/number-line/number-line-util";
109
+ export type { NumberLinePublicWidgetOptions } from "./widgets/number-line/number-line-util";
106
110
  export { default as getRadioPublicWidgetOptions } from "./widgets/radio/radio-util";
107
111
  export { deriveNumCorrect } from "./widgets/radio/radio-upgrade";
108
112
  export { default as getTablePublicWidgetOptions } from "./widgets/table/table-util";
109
113
  export { default as getIFramePublicWidgetOptions } from "./widgets/iframe/iframe-util";
110
114
  export { default as getMatrixPublicWidgetOptions } from "./widgets/matrix/matrix-util";
115
+ export type { MatrixPublicWidgetOptions } from "./widgets/matrix/matrix-util";
111
116
  export { default as getPlotterPublicWidgetOptions } from "./widgets/plotter/plotter-util";
117
+ export type { PlotterPublicWidgetOptions } from "./widgets/plotter/plotter-util";
112
118
  export { default as getMatcherPublicWidgetOptions, shuffleMatcher, } from "./widgets/matcher/matcher-util";
113
119
  export { shuffle, seededRNG, random } from "./utils/random-util";
114
120
  export { default as PerseusFeatureFlags } from "./feature-flags";
121
+ export { registerCoreWidgets } from "./widgets/core-widget-registry";
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var _ = require('underscore');
6
+ require('tiny-invariant');
6
7
  var KAS = require('@khanacademy/kas');
7
8
  var perseusUtils = require('@khanacademy/perseus-utils');
8
9
 
@@ -29,7 +30,7 @@ function _interopNamespaceCompat(e) {
29
30
  var ___default = /*#__PURE__*/_interopDefaultCompat(_);
30
31
  var KAS__namespace = /*#__PURE__*/_interopNamespaceCompat(KAS);
31
32
 
32
- function getMatrixSize(matrix){const matrixSize=[1,1];___default.default(matrix).each((matrixRow,row)=>{let rowWidth=0;___default.default(matrixRow).each((matrixCol,col)=>{if(matrixCol!=null&&matrixCol.toString().length){rowWidth=col+1;}});matrixSize[1]=Math.max(matrixSize[1],rowWidth);if(rowWidth>0){matrixSize[0]=Math.max(matrixSize[0],row+1);}});return matrixSize}
33
+ function getMatrixSize(matrix){const matrixSize=[1,1];matrix.forEach((matrixRow,row)=>{let rowWidth=0;matrixRow.forEach((matrixCol,col)=>{if(matrixCol!=null&&matrixCol.toString().length){rowWidth=col+1;}});matrixSize[1]=Math.max(matrixSize[1],rowWidth);if(rowWidth>0){matrixSize[0]=Math.max(matrixSize[0],row+1);}});return matrixSize}
33
34
 
34
35
  const getDecimalSeparator=locale=>{switch(locale){case "ka":return ",";default:const numberWithDecimalSeparator=1.1;const match=new Intl.NumberFormat(locale).format(numberWithDecimalSeparator).match(/[^\d\u0661\u06F1\u0967\u09e7]/);return match?.[0]??"."}};
35
36
 
@@ -37,7 +38,7 @@ function approximateEqual(x,y){if(typeof x==="number"&&typeof y==="number"){retu
37
38
 
38
39
  function addWidget(id){return `[[☃ ${id}]]`}function getWidgetRegex(){return /\[\[☃ ([A-Za-z0-9- ]+)\]\]/g}function getWidgetIdsFromContent(content){const widgets=[];const localWidgetRegex=getWidgetRegex();let match=localWidgetRegex.exec(content);while(match!==null){widgets.push(match[1]);match=localWidgetRegex.exec(content);}return widgets}function getWidgetIdsFromContentByType(type,content,widgetMap){const rv=[];const widgetIdsInContent=getWidgetIdsFromContent(content);widgetIdsInContent.forEach(widgetId=>{const widget=widgetMap[widgetId];if(widget?.type===type){rv.push(widgetId);}});return rv}
39
40
 
40
- function deepClone(obj){if(Array.isArray(obj)){return obj.map(deepClone)}return obj}
41
+ function deepClone(obj){return JSON.parse(JSON.stringify(obj))}
41
42
 
42
43
  const MOVABLES={PLOT:"PLOT",PARABOLA:"PARABOLA",SINUSOID:"SINUSOID"};function canonicalSineCoefficients(coeffs){let amplitude=coeffs[0];let angularFrequency=coeffs[1];let phase=coeffs[2];const verticalOffset=coeffs[3];if(amplitude<0){amplitude*=-1;angularFrequency*=-1;phase*=-1;}const period=2*Math.PI;if(angularFrequency<0){angularFrequency*=-1;phase*=-1;phase+=period/2;}while(phase>0){phase-=period;}while(phase<0){phase+=period;}return [amplitude,angularFrequency,phase,verticalOffset]}function canonicalTangentCoefficients(coeffs){let amplitude=coeffs[0];let angularFrequency=coeffs[1];let phase=coeffs[2];const verticalOffset=coeffs[3];if(amplitude<0){amplitude*=-1;angularFrequency*=-1;phase*=-1;}const period=Math.PI;if(angularFrequency<0){angularFrequency*=-1;phase*=-1;phase+=period/2;}while(phase>0){phase-=period;}while(phase<0){phase+=period;}return [amplitude,angularFrequency,phase,verticalOffset]}const PlotDefaults={areEqual:function(coeffs1,coeffs2){return approximateDeepEqual(coeffs1,coeffs2)},movable:MOVABLES.PLOT,getPropsForCoeffs:function(coeffs){return {fn:___default.default.partial(this.getFunctionForCoeffs,coeffs)}}};const Linear=___default.default.extend({},PlotDefaults,{url:"https://ka-perseus-graphie.s3.amazonaws.com/67aaf581e6d9ef9038c10558a1f70ac21c11c9f8.png",defaultCoords:[[.25,.75],[.75,.75]],getCoefficients:function(coords){const p1=coords[0];const p2=coords[1];const denom=p2[0]-p1[0];const num=p2[1]-p1[1];if(denom===0){return}const m=num/denom;const b=p2[1]-m*p2[0];return [m,b]},getFunctionForCoeffs:function(coeffs,x){const m=coeffs[0];const b=coeffs[1];return m*x+b},getEquationString:function(coords){const coeffs=this.getCoefficients(coords);const m=coeffs[0];const b=coeffs[1];return "y = "+m.toFixed(3)+"x + "+b.toFixed(3)}});const Quadratic=___default.default.extend({},PlotDefaults,{url:"https://ka-perseus-graphie.s3.amazonaws.com/e23d36e6fc29ee37174e92c9daba2a66677128ab.png",defaultCoords:[[.5,.5],[.75,.75]],movable:MOVABLES.PARABOLA,getCoefficients:function(coords){const p1=coords[0];const p2=coords[1];const h=p1[0];const k=p1[1];const a=(p2[1]-k)/((p2[0]-h)*(p2[0]-h));const b=-2*h*a;const c=a*h*h+k;return [a,b,c]},getFunctionForCoeffs:function(coeffs,x){const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];return (a*x+b)*x+c},getPropsForCoeffs:function(coeffs){return {a:coeffs[0],b:coeffs[1],c:coeffs[2]}},getEquationString:function(coords){const coeffs=this.getCoefficients(coords);const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];return "y = "+a.toFixed(3)+"x^2 + "+b.toFixed(3)+"x + "+c.toFixed(3)}});const Sinusoid=___default.default.extend({},PlotDefaults,{url:"https://ka-perseus-graphie.s3.amazonaws.com/3d68e7718498475f53b206c2ab285626baf8857e.png",defaultCoords:[[.5,.5],[.6,.6]],movable:MOVABLES.SINUSOID,getCoefficients:function(coords){const p1=coords[0];const p2=coords[1];const a=p2[1]-p1[1];const b=Math.PI/(2*(p2[0]-p1[0]));const c=p1[0]*b;const d=p1[1];return [a,b,c,d]},getFunctionForCoeffs:function(coeffs,x){const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];const d=coeffs[3];return a*Math.sin(b*x-c)+d},getPropsForCoeffs:function(coeffs){return {a:coeffs[0],b:coeffs[1],c:coeffs[2],d:coeffs[3]}},getEquationString:function(coords){const coeffs=this.getCoefficients(coords);const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];const d=coeffs[3];return "y = "+a.toFixed(3)+" sin("+b.toFixed(3)+"x - "+c.toFixed(3)+") + "+d.toFixed(3)},areEqual:function(coeffs1,coeffs2){return approximateDeepEqual(canonicalSineCoefficients(coeffs1),canonicalSineCoefficients(coeffs2))}});const Tangent=___default.default.extend({},PlotDefaults,{url:"https://ka-perseus-graphie.s3.amazonaws.com/7db80d23c35214f98659fe1cf0765811c1bbfbba.png",defaultCoords:[[.5,.5],[.75,.75]],getCoefficients:function(coords){const p1=coords[0];const p2=coords[1];const a=p2[1]-p1[1];const b=Math.PI/(4*(p2[0]-p1[0]));const c=p1[0]*b;const d=p1[1];return [a,b,c,d]},getFunctionForCoeffs:function(coeffs,x){const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];const d=coeffs[3];return a*Math.tan(b*x-c)+d},getEquationString:function(coords){const coeffs=this.getCoefficients(coords);const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];const d=coeffs[3];return "y = "+a.toFixed(3)+" sin("+b.toFixed(3)+"x - "+c.toFixed(3)+") + "+d.toFixed(3)},areEqual:function(coeffs1,coeffs2){return approximateDeepEqual(canonicalTangentCoefficients(coeffs1),canonicalTangentCoefficients(coeffs2))}});const Exponential=___default.default.extend({},PlotDefaults,{url:"https://ka-perseus-graphie.s3.amazonaws.com/9cbfad55525e3ce755a31a631b074670a5dad611.png",defaultCoords:[[.5,.55],[.75,.75]],defaultAsymptote:[[0,.5],[1,.5]],extraCoordConstraint:function(newCoord,oldCoord,coords,asymptote,graph){const y=asymptote[0][1];return ___default.default.all(coords,coord=>coord[1]!==y)},extraAsymptoteConstraint:function(newCoord,oldCoord,coords,asymptote,graph){const y=newCoord[1];const isValid=___default.default.all(coords,coord=>coord[1]>y)||___default.default.all(coords,coord=>coord[1]<y);if(isValid){return [oldCoord[0],y]}const oldY=oldCoord[1];const wasBelow=___default.default.all(coords,coord=>coord[1]>oldY);if(wasBelow){const bottomMost=___default.default.min(___default.default.map(coords,coord=>coord[1]));return [oldCoord[0],bottomMost-graph.snapStep[1]]}const topMost=___default.default.max(___default.default.map(coords,coord=>coord[1]));return [oldCoord[0],topMost+graph.snapStep[1]]},allowReflectOverAsymptote:true,getCoefficients:function(coords,asymptote){const p1=coords[0];const p2=coords[1];const c=asymptote[0][1];const b=Math.log((p1[1]-c)/(p2[1]-c))/(p1[0]-p2[0]);const a=(p1[1]-c)/Math.exp(b*p1[0]);return [a,b,c]},getFunctionForCoeffs:function(coeffs,x){const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];return a*Math.exp(b*x)+c},getEquationString:function(coords,asymptote){if(!asymptote){return null}const coeffs=this.getCoefficients(coords,asymptote);const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];return "y = "+a.toFixed(3)+"e^("+b.toFixed(3)+"x) + "+c.toFixed(3)}});const Logarithm=___default.default.extend({},PlotDefaults,{url:"https://ka-perseus-graphie.s3.amazonaws.com/f6491e99d34af34d924bfe0231728ad912068dc3.png",defaultCoords:[[.55,.5],[.75,.75]],defaultAsymptote:[[.5,0],[.5,1]],extraCoordConstraint:function(newCoord,oldCoord,coords,asymptote,graph){const x=asymptote[0][0];return ___default.default.all(coords,coord=>coord[0]!==x)&&coords[0][1]!==coords[1][1]},extraAsymptoteConstraint:function(newCoord,oldCoord,coords,asymptote,graph){const x=newCoord[0];const isValid=___default.default.all(coords,coord=>coord[0]>x)||___default.default.all(coords,coord=>coord[0]<x);if(isValid){return [x,oldCoord[1]]}const oldX=oldCoord[0];const wasLeft=___default.default.all(coords,coord=>coord[0]>oldX);if(wasLeft){const leftMost=___default.default.min(___default.default.map(coords,coord=>coord[0]));return [leftMost-graph.snapStep[0],oldCoord[1]]}const rightMost=___default.default.max(___default.default.map(coords,coord=>coord[0]));return [rightMost+graph.snapStep[0],oldCoord[1]]},allowReflectOverAsymptote:true,getCoefficients:function(coords,asymptote){const flip=coord=>[coord[1],coord[0]];const inverseCoeffs=Exponential.getCoefficients(___default.default.map(coords,flip),___default.default.map(asymptote,flip));if(inverseCoeffs){const c=-inverseCoeffs[2]/inverseCoeffs[0];const b=1/inverseCoeffs[0];const a=1/inverseCoeffs[1];return [a,b,c]}},getFunctionForCoeffs:function(coeffs,x,asymptote){const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];return a*Math.log(b*x+c)},getEquationString:function(coords,asymptote){if(!asymptote){return null}const coeffs=this.getCoefficients(coords,asymptote);const a=coeffs[0];const b=coeffs[1];const c=coeffs[2];return "y = ln("+a.toFixed(3)+"x + "+b.toFixed(3)+") + "+c.toFixed(3)}});const AbsoluteValue=___default.default.extend({},PlotDefaults,{url:"https://ka-perseus-graphie.s3.amazonaws.com/8256a630175a0cb1d11de223d6de0266daf98721.png",defaultCoords:[[.5,.5],[.75,.75]],getCoefficients:function(coords){const p1=coords[0];const p2=coords[1];const denom=p2[0]-p1[0];const num=p2[1]-p1[1];if(denom===0){return}let m=Math.abs(num/denom);if(p2[1]<p1[1]){m*=-1;}const horizontalOffset=p1[0];const verticalOffset=p1[1];return [m,horizontalOffset,verticalOffset]},getFunctionForCoeffs:function(coeffs,x){const m=coeffs[0];const horizontalOffset=coeffs[1];const verticalOffset=coeffs[2];return m*Math.abs(x-horizontalOffset)+verticalOffset},getEquationString:function(coords){const coeffs=this.getCoefficients(coords);const m=coeffs[0];const horizontalOffset=coeffs[1];const verticalOffset=coeffs[2];return "y = "+m.toFixed(3)+"| x - "+horizontalOffset.toFixed(3)+"| + "+verticalOffset.toFixed(3)}});const functionTypeMapping={linear:Linear,quadratic:Quadratic,sinusoid:Sinusoid,tangent:Tangent,exponential:Exponential,logarithm:Logarithm,absolute_value:AbsoluteValue};const allTypes=___default.default.keys(functionTypeMapping);function functionForType(type){return functionTypeMapping[type]}
43
44
 
@@ -48,7 +49,11 @@ var grapherUtil = /*#__PURE__*/Object.freeze({
48
49
  functionForType: functionForType
49
50
  });
50
51
 
51
- const genericPerseusItemData={question:{content:"",images:{},widgets:{}},answerArea:{calculator:false,chi2Table:false,periodicTable:false,tTable:false,zTable:false,financialCalculatorMonthlyPayment:false,financialCalculatorTotalAmount:false,financialCalculatorTimeToPayOff:false,periodicTableWithKey:false},hints:[]};function generateTestPerseusItem(customFields={}){return {...genericPerseusItemData,...customFields}}
52
+ const blankPerseusRenderer={content:"",images:{},widgets:{}};function generateTestPerseusRenderer(customFields={}){return deepClone({...blankPerseusRenderer,...customFields})}const blankPerseusItemData={question:generateTestPerseusRenderer(),answerArea:{calculator:false,chi2Table:false,periodicTable:false,tTable:false,zTable:false,financialCalculatorMonthlyPayment:false,financialCalculatorTotalAmount:false,financialCalculatorTimeToPayOff:false,periodicTableWithKey:false},hints:[]};function generateTestPerseusItem(customFields={}){return deepClone({...blankPerseusItemData,...customFields})}
53
+
54
+ const itemHasRationales=item=>widgetsHaveRationales(item.question.widgets);const widgetsHaveRationales=widgets=>Object.values(widgets).some(widgetHasRationales);const widgetHasRationales=widget=>{switch(widget.type){case "radio":return radioWidgetHasRationales(widget);case "label-image":return labelImageWidgetHasRationales(widget);case "graded-group":case "group":return widgetsHaveRationales(widget.options.widgets);default:return false}};const radioWidgetHasRationales=widget=>{return widget.options.choices.some(choice=>!!choice.clue)};const labelImageWidgetHasRationales=widget=>{return widget.options.markers.some(marker=>marker.answers.length>0)};
55
+
56
+ const itemHasHints=item=>{return item.hints.length>0};
52
57
 
53
58
  function isRealJSONParse(jsonParse){const randomPhrase=buildRandomPhrase();const randomHintPhrase=buildRandomPhrase();const randomString=buildRandomString();const testingObject=JSON.stringify({answerArea:{calculator:false,chi2Table:false,financialCalculatorMonthlyPayment:false,financialCalculatorTimeToPayOff:false,financialCalculatorTotalAmount:false,periodicTable:false,periodicTableWithKey:false,tTable:false,zTable:false},hints:[randomHintPhrase,`=${Math.floor(Math.random()*50)+1}`],question:{content:`${randomPhrase}`,images:{},widgets:{expression1:{alignment:"default",graded:false,options:{answerForms:[{considered:"wrong",form:false,key:0,simplify:false,value:`${randomString}`}],ariaLabel:"Answer",buttonSets:["basic"],functions:["f","g","h"],static:true,times:false,visibleLabel:"Answer"},static:true,type:"expression",version:{major:1,minor:0}}}}});const testJSON=buildTestData(testingObject.replace(/"/g,'\\"'));const parsedTestJSON=jsonParse(testJSON);const parsedTestItemData=parsedTestJSON.data.assessmentItem.item.itemData;return approximateDeepEqual(parsedTestItemData,testingObject)}function buildRandomString(capitalize=false){let randomString="";const randomLength=Math.floor(Math.random()*8)+3;for(let i=0;i<randomLength;i++){const randomLetter=String.fromCharCode(97+Math.floor(Math.random()*26));randomString+=capitalize&&i===0?randomLetter.toUpperCase():randomLetter;}return randomString}function buildRandomPhrase(){const phrases=[];const randomLength=Math.floor(Math.random()*10)+5;for(let i=0;i<randomLength;i++){phrases.push(buildRandomString(i===0));}const modifierStart=["**","$"];const modifierEnd=["**","$"];const modifierIndex=Math.floor(Math.random()*modifierStart.length);return `${modifierStart[modifierIndex]}${phrases.join(" ")}${modifierEnd[modifierIndex]}`}function buildTestData(testObject){return `{"data":{"assessmentItem":{"__typename":"AssessmentItemOrError","error":null,"item":{"__typename":"AssessmentItem","id":"x890b3c70f3e8f4a6","itemData":"${testObject}","problemType":"Type 1","sha":"c7284a3ad65214b4e62bccce236d92f7f5d35941"}}}}`}
54
59
 
@@ -72,13 +77,13 @@ function constant(acceptedValue){return (rawValue,ctx)=>{if(rawValue!==acceptedV
72
77
 
73
78
  function enumeration(...acceptedValues){return (rawValue,ctx)=>{if(typeof rawValue==="string"){const index=acceptedValues.indexOf(rawValue);if(index>-1){return ctx.success(acceptedValues[index])}}const expected=acceptedValues.map(v=>JSON.stringify(v));return ctx.failure(expected,rawValue)}}
74
79
 
75
- function isObject(x){return x!=null&&Object.getPrototypeOf(x)===Object.prototype}
80
+ const objectConstructorString=Object.prototype.constructor.toString();const functionToString=Function.prototype.toString;function isPlainObject(x){if(x==null){return false}const prototype=Object.getPrototypeOf(x);if(prototype==null){return true}return typeof prototype.constructor==="function"&&functionToString.call(prototype.constructor)===objectConstructorString}
76
81
 
77
82
  function nullable(parseValue){return (rawValue,ctx)=>{if(rawValue===null){return ctx.success(rawValue)}return parseValue(rawValue,ctx)}}
78
83
 
79
84
  const number=(rawValue,ctx)=>{if(typeof rawValue==="number"){return ctx.success(rawValue)}return ctx.failure("number",rawValue)};
80
85
 
81
- function objectWithAllPropertiesRequired(schema){return object(schema)}function object(schema){return (rawValue,ctx)=>{if(!isObject(rawValue)){return ctx.failure("object",rawValue)}const ret={...rawValue};const mismatches=[];for(const[prop,propParser]of Object.entries(schema)){const result=propParser(rawValue[prop],ctx.forSubtree(prop));if(isSuccess(result)){if(result.value!==undefined||prop in rawValue){ret[prop]=result.value;}}else {mismatches.push(...result.detail);}}if(mismatches.length>0){return failure(mismatches)}return ctx.success(ret)}}
86
+ function objectWithAllPropertiesRequired(schema){return object(schema)}function object(schema){return (rawValue,ctx)=>{if(!isPlainObject(rawValue)){return ctx.failure("object",rawValue)}const ret={...rawValue};const mismatches=[];for(const[prop,propParser]of Object.entries(schema)){const result=propParser(rawValue[prop],ctx.forSubtree(prop));if(isSuccess(result)){if(result.value!==undefined||prop in rawValue){ret[prop]=result.value;}}else {mismatches.push(...result.detail);}}if(mismatches.length>0){return failure(mismatches)}return ctx.success(ret)}}
82
87
 
83
88
  function optional(parseValue){return (rawValue,ctx)=>{if(rawValue===undefined){return ctx.success(rawValue)}return parseValue(rawValue,ctx)}}
84
89
 
@@ -86,7 +91,7 @@ function pair(parseA,parseB){return (rawValue,ctx)=>{if(!Array.isArray(rawValue)
86
91
 
87
92
  function pipeParsers(p){return new ParserPipeline(p)}class ParserPipeline{then(nextParser){return new ParserPipeline(composeParsers(this.parser,nextParser))}constructor(parser){this.parser=parser;}}function composeParsers(parserA,parserB){return (rawValue,ctx)=>{const partialResult=parserA(rawValue,ctx);if(isFailure(partialResult)){return partialResult}return parserB(partialResult.value,ctx)}}
88
93
 
89
- function record(parseKey,parseValue){return (rawValue,ctx)=>{if(!isObject(rawValue)){return ctx.failure("object",rawValue)}const result={};const mismatches=[];for(const[key,value]of Object.entries(rawValue)){const entryCtx=ctx.forSubtree(key);const keyResult=parseKey(key,entryCtx);if(isFailure(keyResult)){mismatches.push(...keyResult.detail);}const valueResult=parseValue(value,entryCtx);if(isFailure(valueResult)){mismatches.push(...valueResult.detail);}if(isSuccess(keyResult)&&isSuccess(valueResult)){result[keyResult.value]=valueResult.value;}}if(mismatches.length>0){return failure(mismatches)}return ctx.success(result)}}
94
+ function record(parseKey,parseValue){return (rawValue,ctx)=>{if(!isPlainObject(rawValue)){return ctx.failure("object",rawValue)}const result={};const mismatches=[];for(const[key,value]of Object.entries(rawValue)){const entryCtx=ctx.forSubtree(key);const keyResult=parseKey(key,entryCtx);if(isFailure(keyResult)){mismatches.push(...keyResult.detail);}const valueResult=parseValue(value,entryCtx);if(isFailure(valueResult)){mismatches.push(...valueResult.detail);}if(isSuccess(keyResult)&&isSuccess(valueResult)){result[keyResult.value]=valueResult.value;}}if(mismatches.length>0){return failure(mismatches)}return ctx.success(result)}}
90
95
 
91
96
  const string=(rawValue,ctx)=>{if(typeof rawValue==="string"){return ctx.success(rawValue)}return ctx.failure("string",rawValue)};
92
97
 
@@ -96,7 +101,9 @@ function union(parseBranch){return new UnionBuilder(parseBranch)}class UnionBuil
96
101
 
97
102
  function defaulted(parser,fallback){return (rawValue,ctx)=>{if(rawValue==null){return success(fallback(rawValue))}return parser(rawValue,ctx)}}
98
103
 
99
- const parseImages=defaulted(record(string,object({width:number,height:number})),()=>({}));
104
+ const parsePerseusImageDetail=object({width:number,height:number});
105
+
106
+ const parseImages=defaulted(record(string,parsePerseusImageDetail),()=>({}));
100
107
 
101
108
  function parseWidget(parseType,parseOptions){return objectWithAllPropertiesRequired({type:parseType,static:optional(boolean),graded:optional(boolean),alignment:optional(string),options:parseOptions,key:optional(nullable(number)),version:optional(object({major:number,minor:number}))})}function parseWidgetWithVersion(parseVersion,parseType,parseOptions){return objectWithAllPropertiesRequired({type:parseType,static:optional(boolean),graded:optional(boolean),alignment:optional(string),options:parseOptions,key:optional(nullable(number)),version:parseVersion})}
102
109
 
@@ -118,17 +125,17 @@ function convert(f){return (rawValue,ctx)=>ctx.success(f(rawValue))}
118
125
 
119
126
  const parseLegacyButtonSet=enumeration("basic","basic+div","trig","prealgebra","logarithms","basic relations","advanced relations","scientific");const parseLegacyButtonSets=defaulted(array(parseLegacyButtonSet),()=>["basic","trig","prealgebra","logarithms"]);
120
127
 
121
- function versionedWidgetOptions(latestMajorVersion,parseLatest){return new VersionedWidgetOptionsParserBuilder(latestMajorVersion,parseLatest,latest=>latest,(raw,ctx)=>ctx.failure("widget options with a known version number",raw))}class VersionedWidgetOptionsParserBuilder{withMigrationFrom(majorVersion,parseOldVersion,migrateToNextVersion){const parseOtherVersions=this.parser;const migrateToLatest=old=>this.migrateToLatest(migrateToNextVersion(old));return new VersionedWidgetOptionsParserBuilder(majorVersion,parseOldVersion,migrateToLatest,parseOtherVersions)}constructor(majorVersion,parseThisVersion,migrateToLatest,parseOtherVersions){this.migrateToLatest=migrateToLatest;this.parseOtherVersions=parseOtherVersions;const parseThisVersionAndMigrateToLatest=pipeParsers(parseThisVersion).then(convert(this.migrateToLatest)).parser;this.parser=(raw,ctx)=>{if(!isObject(raw)){return ctx.failure("object",raw)}const versionParseResult=parseVersionedObject(raw,ctx);if(isFailure(versionParseResult)){return versionParseResult}if(versionParseResult.value.version.major!==majorVersion){return this.parseOtherVersions(raw,ctx)}return parseThisVersionAndMigrateToLatest(raw,ctx)};}}const parseVersionedObject=object({version:defaulted(object({major:number,minor:number}),()=>({major:0,minor:0}))});
128
+ function versionedWidgetOptions(latestMajorVersion,parseLatest){return new VersionedWidgetOptionsParserBuilder(latestMajorVersion,parseLatest,latest=>latest,(raw,ctx)=>ctx.failure("widget options with a known version number",raw))}class VersionedWidgetOptionsParserBuilder{withMigrationFrom(majorVersion,parseOldVersion,migrateToNextVersion){const parseOtherVersions=this.parser;const migrateToLatest=old=>this.migrateToLatest(migrateToNextVersion(old));return new VersionedWidgetOptionsParserBuilder(majorVersion,parseOldVersion,migrateToLatest,parseOtherVersions)}constructor(majorVersion,parseThisVersion,migrateToLatest,parseOtherVersions){this.migrateToLatest=migrateToLatest;this.parseOtherVersions=parseOtherVersions;const parseThisVersionAndMigrateToLatest=pipeParsers(parseThisVersion).then(convert(this.migrateToLatest)).parser;this.parser=(raw,ctx)=>{if(!isPlainObject(raw)){return ctx.failure("object",raw)}const versionParseResult=parseVersionedObject(raw,ctx);if(isFailure(versionParseResult)){return versionParseResult}if(versionParseResult.value.version.major!==majorVersion){return this.parseOtherVersions(raw,ctx)}return parseThisVersionAndMigrateToLatest(raw,ctx)};}}const parseVersionedObject=object({version:defaulted(object({major:number,minor:number}),()=>({major:0,minor:0}))});
122
129
 
123
- const stringOrNumberOrNullOrUndefined=union(string).or(number).or(constant(null)).or(constant(undefined)).parser;const parsePossiblyInvalidAnswerForm=object({value:optional(string),form:defaulted(boolean,()=>false),simplify:defaulted(boolean,()=>false),considered:enumeration("correct","wrong","ungraded"),key:pipeParsers(stringOrNumberOrNullOrUndefined).then(convert(String)).parser});function removeInvalidAnswerForms(possiblyInvalid){const valid=[];for(const answerForm of possiblyInvalid){const{value}=answerForm;if(value!=null){valid.push({...answerForm,value});}}return valid}const version2$1=object({major:constant(2),minor:number});const parseExpressionWidgetV2=parseWidgetWithVersion(version2$1,constant("expression"),object({answerForms:pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,functions:array(string),times:boolean,visibleLabel:optional(string),ariaLabel:optional(string),buttonSets:parseLegacyButtonSets,buttonsVisible:optional(enumeration("always","never","focused")),extraKeys:array(enumeration(...KeypadKeys))}));const version1$1=object({major:constant(1),minor:number});const parseExpressionWidgetV1=parseWidgetWithVersion(version1$1,constant("expression"),object({answerForms:pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,functions:array(string),times:boolean,visibleLabel:optional(string),ariaLabel:optional(string),buttonSets:parseLegacyButtonSets,buttonsVisible:optional(enumeration("always","never","focused"))}));function migrateV1ToV2$1(widget){const{options}=widget;return {...widget,version:{major:2,minor:0},options:{times:options.times,buttonSets:options.buttonSets,functions:options.functions,buttonsVisible:options.buttonsVisible,visibleLabel:options.visibleLabel,ariaLabel:options.ariaLabel,answerForms:options.answerForms,extraKeys:deriveExtraKeys(options)}}}const version0$1=optional(object({major:constant(0),minor:number}));const parseExpressionWidgetV0=parseWidgetWithVersion(version0$1,constant("expression"),object({functions:array(string),times:boolean,visibleLabel:optional(string),ariaLabel:optional(string),form:boolean,simplify:boolean,value:string,buttonSets:parseLegacyButtonSets,buttonsVisible:optional(enumeration("always","never","focused"))}));function migrateV0ToV1$1(widget){const{options}=widget;return {...widget,version:{major:1,minor:0},options:{times:options.times,buttonSets:options.buttonSets,functions:options.functions,buttonsVisible:options.buttonsVisible,visibleLabel:options.visibleLabel,ariaLabel:options.ariaLabel,answerForms:[{considered:"correct",form:options.form,simplify:options.simplify,value:options.value}]}}}const parseExpressionWidget=versionedWidgetOptions(2,parseExpressionWidgetV2).withMigrationFrom(1,parseExpressionWidgetV1,migrateV1ToV2$1).withMigrationFrom(0,parseExpressionWidgetV0,migrateV0ToV1$1).parser;
130
+ const stringOrNumberOrNullOrUndefined=union(string).or(number).or(constant(null)).or(constant(undefined)).parser;function numberOrNullToString(v){return typeof v==="number"||v===null?String(v):v}const parsePossiblyInvalidAnswerForm=object({value:optional(string),form:defaulted(boolean,()=>false),simplify:defaulted(boolean,()=>false),considered:enumeration("correct","wrong","ungraded"),key:pipeParsers(stringOrNumberOrNullOrUndefined).then(convert(numberOrNullToString)).parser});function removeInvalidAnswerForms(possiblyInvalid){return possiblyInvalid.flatMap(answerForm=>{const{value}=answerForm;if(value!=null){return [{...answerForm,value}]}return []})}const version2$1=object({major:constant(2),minor:number});const parseExpressionWidgetV2=parseWidgetWithVersion(version2$1,constant("expression"),object({answerForms:pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,functions:array(string),times:boolean,visibleLabel:optional(string),ariaLabel:optional(string),buttonSets:parseLegacyButtonSets,buttonsVisible:optional(enumeration("always","never","focused")),extraKeys:optional(array(enumeration(...KeypadKeys)))}));const version1$1=object({major:constant(1),minor:number});const parseExpressionWidgetV1=parseWidgetWithVersion(version1$1,constant("expression"),object({answerForms:pipeParsers(array(parsePossiblyInvalidAnswerForm)).then(convert(removeInvalidAnswerForms)).parser,functions:array(string),times:boolean,visibleLabel:optional(string),ariaLabel:optional(string),buttonSets:parseLegacyButtonSets,buttonsVisible:optional(enumeration("always","never","focused"))}));function migrateV1ToV2$1(widget){const{options}=widget;return {...widget,version:{major:2,minor:0},options:{times:options.times,buttonSets:options.buttonSets,functions:options.functions,buttonsVisible:options.buttonsVisible,visibleLabel:options.visibleLabel,ariaLabel:options.ariaLabel,answerForms:options.answerForms,extraKeys:deriveExtraKeys(options)??[]}}}const version0$1=optional(object({major:constant(0),minor:number}));const parseExpressionWidgetV0=parseWidgetWithVersion(version0$1,constant("expression"),object({functions:array(string),times:boolean,visibleLabel:optional(string),ariaLabel:optional(string),form:boolean,simplify:boolean,value:string,buttonSets:parseLegacyButtonSets,buttonsVisible:optional(enumeration("always","never","focused"))}));function migrateV0ToV1$1(widget){const{options}=widget;return {...widget,version:{major:1,minor:0},options:{times:options.times,buttonSets:options.buttonSets,functions:options.functions,buttonsVisible:options.buttonsVisible,visibleLabel:options.visibleLabel,ariaLabel:options.ariaLabel,answerForms:[{considered:"correct",form:options.form,simplify:options.simplify,value:options.value}]}}}const parseExpressionWidget=versionedWidgetOptions(2,parseExpressionWidgetV2).withMigrationFrom(1,parseExpressionWidgetV1,migrateV1ToV2$1).withMigrationFrom(0,parseExpressionWidgetV0,migrateV0ToV1$1).parser;
124
131
 
125
132
  const falseToNull=pipeParsers(constant(false)).then(convert(()=>null)).parser;const parseGradedGroupWidgetOptions=object({title:defaulted(string,()=>""),hasHint:optional(nullable(boolean)),hint:union(falseToNull).or(constant(null)).or(constant(undefined)).or((rawVal,ctx)=>parsePerseusRenderer(rawVal,ctx)).parser,content:string,widgets:(rawVal,ctx)=>parseWidgetsMap(rawVal,ctx),widgetEnabled:optional(nullable(boolean)),immutableWidgets:optional(nullable(boolean)),images:record(string,object({width:number,height:number}))});const parseGradedGroupWidget=parseWidget(constant("graded-group"),parseGradedGroupWidgetOptions);
126
133
 
127
134
  const parseGradedGroupSetWidget=parseWidget(constant("graded-group-set"),object({gradedGroups:array(parseGradedGroupWidgetOptions)}));
128
135
 
129
- function discriminatedUnionOn(discriminantKey){const noMoreBranches=(raw,ctx)=>{if(!isObject(raw)){return ctx.failure("object",raw)}return ctx.forSubtree(discriminantKey).failure("a valid value",raw[discriminantKey])};return new DiscriminatedUnionBuilder(discriminantKey,noMoreBranches)}class DiscriminatedUnionBuilder{withBranch(discriminantValue,parseNewVariant){const parseNewBranch=discriminatedUnionBranch(this.discriminantKey,discriminantValue,parseNewVariant,this.parser);return new DiscriminatedUnionBuilder(this.discriminantKey,parseNewBranch)}constructor(discriminantKey,parser){this.discriminantKey=discriminantKey;this.parser=parser;}}function discriminatedUnionBranch(discriminantKey,discriminantValue,parseVariant,parseOtherBranches){return (raw,ctx)=>{if(!isObject(raw)){return ctx.failure("object",raw)}if(raw[discriminantKey]===discriminantValue){return parseVariant(raw,ctx)}return parseOtherBranches(raw,ctx)}}
136
+ function discriminatedUnionOn(discriminantKey){const noMoreBranches=(raw,ctx)=>{if(!isPlainObject(raw)){return ctx.failure("object",raw)}return ctx.forSubtree(discriminantKey).failure("a valid value",raw[discriminantKey])};return new DiscriminatedUnionBuilder(discriminantKey,noMoreBranches)}class DiscriminatedUnionBuilder{withBranch(discriminantValue,parseNewVariant){const parseNewBranch=discriminatedUnionBranch(this.discriminantKey,discriminantValue,parseNewVariant,this.parser);return new DiscriminatedUnionBuilder(this.discriminantKey,parseNewBranch)}constructor(discriminantKey,parser){this.discriminantKey=discriminantKey;this.parser=parser;}}function discriminatedUnionBranch(discriminantKey,discriminantValue,parseVariant,parseOtherBranches){return (raw,ctx)=>{if(!isPlainObject(raw)){return ctx.failure("object",raw)}if(raw[discriminantKey]===discriminantValue){return parseVariant(raw,ctx)}return parseOtherBranches(raw,ctx)}}
130
137
 
131
- const pairOfNumbers$3=pair(number,number);const pairOfPoints=pair(pairOfNumbers$3,pairOfNumbers$3);const parseGrapherWidget=parseWidget(constant("grapher"),object({availableTypes:array(enumeration("absolute_value","exponential","linear","logarithm","quadratic","sinusoid","tangent")),correct:discriminatedUnionOn("type").withBranch("absolute_value",object({type:constant("absolute_value"),coords:nullable(pairOfPoints)})).withBranch("exponential",object({type:constant("exponential"),asymptote:pairOfPoints,coords:nullable(pairOfPoints)})).withBranch("linear",object({type:constant("linear"),coords:nullable(pairOfPoints)})).withBranch("logarithm",object({type:constant("logarithm"),asymptote:pairOfPoints,coords:nullable(pairOfPoints)})).withBranch("quadratic",object({type:constant("quadratic"),coords:nullable(pairOfPoints)})).withBranch("sinusoid",object({type:constant("sinusoid"),coords:nullable(pairOfPoints)})).withBranch("tangent",object({type:constant("tangent"),coords:nullable(pairOfPoints)})).parser,graph:object({backgroundImage:object({bottom:optional(number),height:optional(number),left:optional(number),scale:optional(number),url:optional(nullable(string)),width:optional(number)}),box:optional(pairOfNumbers$3),editableSettings:optional(array(enumeration("graph","snap","image","measure"))),gridStep:optional(pairOfNumbers$3),labels:pair(string,string),markings:enumeration("graph","none","grid"),range:pair(pairOfNumbers$3,pairOfNumbers$3),rulerLabel:constant(""),rulerTicks:number,showProtractor:optional(boolean),showRuler:optional(boolean),showTooltips:optional(boolean),snapStep:optional(pairOfNumbers$3),step:pairOfNumbers$3,valid:optional(union(boolean).or(string).parser)})}));
138
+ const pairOfNumbers$3=pair(number,number);const pairOfPoints=pair(pairOfNumbers$3,pairOfNumbers$3);const parseGrapherWidget=parseWidget(constant("grapher"),object({availableTypes:array(enumeration("absolute_value","exponential","linear","logarithm","quadratic","sinusoid","tangent")),correct:discriminatedUnionOn("type").withBranch("absolute_value",object({type:constant("absolute_value"),coords:nullable(pairOfPoints)})).withBranch("exponential",object({type:constant("exponential"),asymptote:pairOfPoints,coords:nullable(pairOfPoints)})).withBranch("linear",object({type:constant("linear"),coords:nullable(pairOfPoints)})).withBranch("logarithm",object({type:constant("logarithm"),asymptote:pairOfPoints,coords:nullable(pairOfPoints)})).withBranch("quadratic",object({type:constant("quadratic"),coords:nullable(pairOfPoints)})).withBranch("sinusoid",object({type:constant("sinusoid"),coords:nullable(pairOfPoints)})).withBranch("tangent",object({type:constant("tangent"),coords:nullable(pairOfPoints)})).parser,graph:object({backgroundImage:object({bottom:optional(number),height:optional(number),left:optional(number),scale:optional(number),url:optional(nullable(string)),width:optional(number)}),box:optional(pairOfNumbers$3),editableSettings:optional(array(enumeration("graph","snap","image","measure"))),gridStep:optional(pairOfNumbers$3),labels:pair(string,string),markings:enumeration("graph","none","grid","axes"),range:pair(pairOfNumbers$3,pairOfNumbers$3),rulerLabel:constant(""),rulerTicks:number,showProtractor:optional(boolean),showRuler:optional(boolean),showTooltips:optional(boolean),snapStep:optional(pairOfNumbers$3),step:pairOfNumbers$3,valid:optional(union(boolean).or(string).parser)})}));
132
139
 
133
140
  const parseGroupWidget=parseWidget(constant("group"),(rawVal,ctx)=>parsePerseusRenderer(rawVal,ctx));
134
141
 
@@ -136,17 +143,17 @@ const parseIframeWidget=parseWidget(constant("iframe"),object({url:string,settin
136
143
 
137
144
  const stringToNumber=(rawValue,ctx)=>{if(typeof rawValue==="number"){return ctx.success(rawValue)}const parsedNumber=+rawValue;if(rawValue===""||isNaN(parsedNumber)){return ctx.failure("a number or numeric string",rawValue)}return ctx.success(parsedNumber)};
138
145
 
139
- function emptyToZero(x){return x===""?0:x}const imageDimensionToNumber=pipeParsers(union(number).or(string).parser).then(convert(emptyToZero)).then(stringToNumber).parser;const dimensionOrUndefined=defaulted(imageDimensionToNumber,()=>undefined);const parsePerseusImageBackground=object({url:optional(nullable(string)),width:dimensionOrUndefined,height:dimensionOrUndefined,top:dimensionOrUndefined,left:dimensionOrUndefined,bottom:dimensionOrUndefined,scale:dimensionOrUndefined});
146
+ function emptyToZero(x){return x===""?0:x}const imageDimensionToNumber=pipeParsers(union(number).or(string).parser).then(convert(emptyToZero)).then(stringToNumber).parser;const dimensionOrUndefined=defaulted(optional(imageDimensionToNumber),()=>undefined);const parsePerseusImageBackground=object({url:optional(nullable(string)),width:dimensionOrUndefined,height:dimensionOrUndefined,top:dimensionOrUndefined,left:dimensionOrUndefined,bottom:dimensionOrUndefined,scale:dimensionOrUndefined});
140
147
 
141
148
  const pairOfNumbers$2=pair(number,number);const parseImageWidget=parseWidget(constant("image"),object({title:optional(string),caption:optional(string),alt:optional(string),backgroundImage:parsePerseusImageBackground,static:optional(boolean),labels:optional(array(object({content:string,alignment:string,coordinates:array(number)}))),range:optional(pair(pairOfNumbers$2,pairOfNumbers$2)),box:optional(pairOfNumbers$2)}));
142
149
 
143
150
  const booleanToString=(rawValue,ctx)=>{if(typeof rawValue==="boolean"){return ctx.success(String(rawValue))}return ctx.failure("boolean",rawValue)};const parseInputNumberWidget=parseWidget(constant("input-number"),object({answerType:optional(enumeration("number","decimal","integer","rational","improper","mixed","percent","pi")),inexact:optional(boolean),maxError:optional(union(number).or(string).parser),rightAlign:optional(boolean),simplify:enumeration("required","optional","enforced"),size:enumeration("normal","small"),value:union(number).or(string).or(booleanToString).parser,customKeypad:optional(boolean)}));
144
151
 
145
- const pairOfNumbers$1=pair(number,number);const stringOrEmpty=defaulted(string,()=>"");const parseKey=pipeParsers(optional(string)).then(convert(String)).parser;const parseFunctionElement=object({type:constant("function"),key:parseKey,options:object({value:string,funcName:string,rangeMin:string,rangeMax:string,color:string,strokeDasharray:string,strokeWidth:number})});const parseLabelElement=object({type:constant("label"),key:parseKey,options:object({label:string,color:string,coordX:string,coordY:string})});const parseLineElement=object({type:constant("line"),key:parseKey,options:object({color:string,startX:string,startY:string,endX:string,endY:string,strokeDasharray:string,strokeWidth:number,arrows:string})});const parseMovableLineElement=object({type:constant("movable-line"),key:parseKey,options:object({startX:string,startY:string,startSubscript:number,endX:string,endY:string,endSubscript:number,constraint:string,snap:number,constraintFn:string,constraintXMin:string,constraintXMax:string,constraintYMin:string,constraintYMax:string})});const parseMovablePointElement=object({type:constant("movable-point"),key:parseKey,options:object({startX:string,startY:string,varSubscript:number,constraint:string,snap:number,constraintFn:string,constraintXMin:stringOrEmpty,constraintXMax:stringOrEmpty,constraintYMin:stringOrEmpty,constraintYMax:stringOrEmpty})});const parseParametricElement=object({type:constant("parametric"),key:parseKey,options:object({x:string,y:string,rangeMin:string,rangeMax:string,color:string,strokeDasharray:string,strokeWidth:number})});const parsePointElement=object({type:constant("point"),key:parseKey,options:object({color:string,coordX:string,coordY:string})});const parseRectangleElement=object({type:constant("rectangle"),key:parseKey,options:object({color:string,coordX:string,coordY:string,width:string,height:string})});const parseInteractionWidget=parseWidget(constant("interaction"),object({static:defaulted(boolean,()=>false),graph:object({editableSettings:optional(array(enumeration("canvas","graph"))),box:pairOfNumbers$1,labels:array(string),range:pair(pairOfNumbers$1,pairOfNumbers$1),gridStep:pairOfNumbers$1,markings:enumeration("graph","grid","none"),snapStep:optional(pairOfNumbers$1),valid:optional(union(boolean).or(string).parser),backgroundImage:optional(parsePerseusImageBackground),showProtractor:optional(boolean),showRuler:optional(boolean),rulerLabel:optional(string),rulerTicks:optional(number),tickStep:pairOfNumbers$1}),elements:array(discriminatedUnionOn("type").withBranch("function",parseFunctionElement).withBranch("label",parseLabelElement).withBranch("line",parseLineElement).withBranch("movable-line",parseMovableLineElement).withBranch("movable-point",parseMovablePointElement).withBranch("parametric",parseParametricElement).withBranch("point",parsePointElement).withBranch("rectangle",parseRectangleElement).parser)}));
152
+ const pairOfNumbers$1=pair(number,number);const stringOrEmpty=defaulted(string,()=>"");const parseKey=pipeParsers(optional(string)).then(convert(String)).parser;const parseFunctionElement=object({type:constant("function"),key:parseKey,options:object({value:string,funcName:string,rangeMin:string,rangeMax:string,color:string,strokeDasharray:string,strokeWidth:number})});const parseLabelElement=object({type:constant("label"),key:parseKey,options:object({label:string,color:string,coordX:string,coordY:string})});const parseLineElement=object({type:constant("line"),key:parseKey,options:object({color:string,startX:string,startY:string,endX:string,endY:string,strokeDasharray:string,strokeWidth:number,arrows:string})});const parseMovableLineElement=object({type:constant("movable-line"),key:parseKey,options:object({startX:string,startY:string,startSubscript:number,endX:string,endY:string,endSubscript:number,constraint:string,snap:number,constraintFn:string,constraintXMin:string,constraintXMax:string,constraintYMin:string,constraintYMax:string})});const parseMovablePointElement=object({type:constant("movable-point"),key:parseKey,options:object({startX:string,startY:string,varSubscript:number,constraint:string,snap:number,constraintFn:string,constraintXMin:stringOrEmpty,constraintXMax:stringOrEmpty,constraintYMin:stringOrEmpty,constraintYMax:stringOrEmpty})});const parseParametricElement=object({type:constant("parametric"),key:parseKey,options:object({x:string,y:string,rangeMin:string,rangeMax:string,color:string,strokeDasharray:string,strokeWidth:number})});const parsePointElement=object({type:constant("point"),key:parseKey,options:object({color:string,coordX:string,coordY:string})});const parseRectangleElement=object({type:constant("rectangle"),key:parseKey,options:object({color:string,coordX:string,coordY:string,width:string,height:string})});const parseInteractionWidget=parseWidget(constant("interaction"),object({static:defaulted(boolean,()=>false),graph:object({editableSettings:optional(array(enumeration("canvas","graph"))),box:pairOfNumbers$1,labels:array(string),range:pair(pairOfNumbers$1,pairOfNumbers$1),gridStep:pairOfNumbers$1,markings:enumeration("graph","grid","none","axes"),snapStep:optional(pairOfNumbers$1),valid:optional(union(boolean).or(string).parser),backgroundImage:optional(parsePerseusImageBackground),showProtractor:optional(boolean),showRuler:optional(boolean),rulerLabel:optional(string),rulerTicks:optional(number),tickStep:pairOfNumbers$1}),elements:array(discriminatedUnionOn("type").withBranch("function",parseFunctionElement).withBranch("label",parseLabelElement).withBranch("line",parseLineElement).withBranch("movable-line",parseMovableLineElement).withBranch("movable-point",parseMovablePointElement).withBranch("parametric",parseParametricElement).withBranch("point",parsePointElement).withBranch("rectangle",parseRectangleElement).parser)}));
146
153
 
147
154
  const ItemExtras=["calculator","chi2Table","financialCalculatorMonthlyPayment","financialCalculatorTotalAmount","financialCalculatorTimeToPayOff","periodicTable","periodicTableWithKey","tTable","zTable"];const PerseusExpressionAnswerFormConsidered=["correct","wrong","ungraded"];const lockedFigureColorNames=["blue","green","grayH","purple","pink","orange","red"];const lockedFigureColors={blue:"#3D7586",green:"#447A53",grayH:"#3B3D45",purple:"#594094",pink:"#B25071",red:"#D92916",orange:"#946700"};const lockedFigureFillStyles={none:0,white:1,translucent:.4,solid:1};const plotterPlotTypes=["bar","line","pic","histogram","dotplot"];
148
155
 
149
- const pairOfNumbers=pair(number,number);const parsePerseusGraphTypeAngle=object({type:constant("angle"),showAngles:optional(boolean),allowReflexAngles:optional(boolean),angleOffsetDeg:optional(number),snapDegrees:optional(number),match:optional(constant("congruent")),coords:optional(trio(pairOfNumbers,pairOfNumbers,pairOfNumbers)),startCoords:optional(trio(pairOfNumbers,pairOfNumbers,pairOfNumbers))});const parsePerseusGraphTypeCircle=object({type:constant("circle"),center:optional(pairOfNumbers),radius:optional(number),startCoords:optional(object({center:pairOfNumbers,radius:number})),coord:optional(pairOfNumbers)});const parsePerseusGraphTypeLinear=object({type:constant("linear"),coords:optional(nullable(pair(pairOfNumbers,pairOfNumbers))),startCoords:optional(pair(pairOfNumbers,pairOfNumbers)),coord:optional(pairOfNumbers)});const parsePerseusGraphTypeLinearSystem=object({type:constant("linear-system"),coords:optional(nullable(array(pair(pairOfNumbers,pairOfNumbers)))),startCoords:optional(array(pair(pairOfNumbers,pairOfNumbers))),coord:optional(pairOfNumbers)});const parsePerseusGraphTypeNone=object({type:constant("none")});const parsePerseusGraphTypePoint=object({type:constant("point"),numPoints:optional(union(number).or(constant("unlimited")).parser),coords:optional(nullable(array(pairOfNumbers))),startCoords:optional(array(pairOfNumbers)),coord:optional(pairOfNumbers)});const parsePerseusGraphTypePolygon=object({type:constant("polygon"),numSides:optional(union(number).or(constant("unlimited")).parser),showAngles:optional(boolean),showSides:optional(boolean),snapTo:optional(enumeration("grid","angles","sides")),match:optional(enumeration("similar","congruent","approx","exact")),startCoords:optional(array(pairOfNumbers)),coord:optional(pairOfNumbers)});const parsePerseusGraphTypeQuadratic=object({type:constant("quadratic"),coords:optional(nullable(trio(pairOfNumbers,pairOfNumbers,pairOfNumbers))),startCoords:optional(trio(pairOfNumbers,pairOfNumbers,pairOfNumbers)),coord:optional(pairOfNumbers)});const parsePerseusGraphTypeRay=object({type:constant("ray"),coords:optional(nullable(pair(pairOfNumbers,pairOfNumbers))),startCoords:optional(pair(pairOfNumbers,pairOfNumbers)),coord:optional(pairOfNumbers)});const parsePerseusGraphTypeSegment=object({type:constant("segment"),numSegments:optional(number),coords:optional(nullable(array(pair(pairOfNumbers,pairOfNumbers)))),startCoords:optional(array(pair(pairOfNumbers,pairOfNumbers))),coord:optional(pairOfNumbers)});const parsePerseusGraphTypeSinusoid=object({type:constant("sinusoid"),coords:optional(nullable(array(pairOfNumbers))),startCoords:optional(array(pairOfNumbers)),coord:optional(pairOfNumbers)});const parsePerseusGraphType=discriminatedUnionOn("type").withBranch("angle",parsePerseusGraphTypeAngle).withBranch("circle",parsePerseusGraphTypeCircle).withBranch("linear",parsePerseusGraphTypeLinear).withBranch("linear-system",parsePerseusGraphTypeLinearSystem).withBranch("none",parsePerseusGraphTypeNone).withBranch("point",parsePerseusGraphTypePoint).withBranch("polygon",parsePerseusGraphTypePolygon).withBranch("quadratic",parsePerseusGraphTypeQuadratic).withBranch("ray",parsePerseusGraphTypeRay).withBranch("segment",parsePerseusGraphTypeSegment).withBranch("sinusoid",parsePerseusGraphTypeSinusoid).parser;const parseLockedFigureColor=enumeration(...lockedFigureColorNames);const parseLockedFigureFillType=enumeration("none","white","translucent","solid");const parseLockedLineStyle=enumeration("solid","dashed");const parseLockedLabelType=object({type:constant("label"),coord:pairOfNumbers,text:string,color:parseLockedFigureColor,size:enumeration("small","medium","large")});const parseLockedPointType=object({type:constant("point"),coord:pairOfNumbers,color:parseLockedFigureColor,filled:boolean,labels:optional(array(parseLockedLabelType)),ariaLabel:optional(string)});const parseLockedLineType=object({type:constant("line"),kind:enumeration("line","ray","segment"),points:pair(parseLockedPointType,parseLockedPointType),color:parseLockedFigureColor,lineStyle:parseLockedLineStyle,showPoint1:defaulted(boolean,()=>false),showPoint2:defaulted(boolean,()=>false),labels:optional(array(parseLockedLabelType)),ariaLabel:optional(string)});const parseLockedVectorType=object({type:constant("vector"),points:pair(pairOfNumbers,pairOfNumbers),color:parseLockedFigureColor,labels:optional(array(parseLockedLabelType)),ariaLabel:optional(string)});const parseLockedEllipseType=object({type:constant("ellipse"),center:pairOfNumbers,radius:pairOfNumbers,angle:number,color:parseLockedFigureColor,fillStyle:parseLockedFigureFillType,strokeStyle:parseLockedLineStyle,labels:optional(array(parseLockedLabelType)),ariaLabel:optional(string)});const parseLockedPolygonType=object({type:constant("polygon"),points:array(pairOfNumbers),color:parseLockedFigureColor,showVertices:boolean,fillStyle:parseLockedFigureFillType,strokeStyle:parseLockedLineStyle,labels:optional(array(parseLockedLabelType)),ariaLabel:optional(string)});const parseLockedFunctionDomain=defaulted(pair(defaulted(number,()=>-Infinity),defaulted(number,()=>Infinity)),()=>[-Infinity,Infinity]);const parseLockedFunctionType=object({type:constant("function"),color:parseLockedFigureColor,strokeStyle:parseLockedLineStyle,equation:string,directionalAxis:enumeration("x","y"),domain:parseLockedFunctionDomain,labels:optional(array(parseLockedLabelType)),ariaLabel:optional(string)});const parseLockedFigure=discriminatedUnionOn("type").withBranch("point",parseLockedPointType).withBranch("line",parseLockedLineType).withBranch("vector",parseLockedVectorType).withBranch("ellipse",parseLockedEllipseType).withBranch("polygon",parseLockedPolygonType).withBranch("function",parseLockedFunctionType).withBranch("label",parseLockedLabelType).parser;const parseInteractiveGraphWidget=parseWidget(constant("interactive-graph"),object({step:pairOfNumbers,gridStep:optional(pairOfNumbers),snapStep:optional(pairOfNumbers),backgroundImage:optional(parsePerseusImageBackground),markings:enumeration("graph","grid","none"),labels:optional(array(string)),labelLocation:optional(enumeration("onAxis","alongEdge")),showProtractor:boolean,showRuler:optional(boolean),showTooltips:optional(boolean),rulerLabel:optional(string),rulerTicks:optional(number),range:pair(pairOfNumbers,pairOfNumbers),graph:defaulted(parsePerseusGraphType,()=>({type:"linear"})),correct:parsePerseusGraphType,lockedFigures:optional(array(parseLockedFigure)),fullGraphLabel:optional(string),fullGraphAriaDescription:optional(string)}));
156
+ const pairOfNumbers=pair(number,number);const parsePerseusGraphTypeAngle=object({type:constant("angle"),showAngles:optional(boolean),allowReflexAngles:optional(boolean),angleOffsetDeg:optional(number),snapDegrees:optional(number),match:optional(constant("congruent")),coords:optional(trio(pairOfNumbers,pairOfNumbers,pairOfNumbers)),startCoords:optional(trio(pairOfNumbers,pairOfNumbers,pairOfNumbers))});const parsePerseusGraphTypeCircle=object({type:constant("circle"),center:optional(pairOfNumbers),radius:optional(number),startCoords:optional(object({center:pairOfNumbers,radius:number}))});const parsePerseusGraphTypeLinear=object({type:constant("linear"),coords:optional(nullable(pair(pairOfNumbers,pairOfNumbers))),startCoords:optional(pair(pairOfNumbers,pairOfNumbers))});const parsePerseusGraphTypeLinearSystem=object({type:constant("linear-system"),coords:optional(nullable(array(pair(pairOfNumbers,pairOfNumbers)))),startCoords:optional(array(pair(pairOfNumbers,pairOfNumbers)))});const parsePerseusGraphTypeNone=object({type:constant("none")});const parsePerseusGraphTypePoint=object({type:constant("point"),numPoints:optional(union(number).or(constant("unlimited")).parser),coords:optional(nullable(array(pairOfNumbers))),startCoords:optional(array(pairOfNumbers)),coord:optional(pairOfNumbers)});const parsePerseusGraphTypePolygon=object({type:constant("polygon"),numSides:optional(union(number).or(constant("unlimited")).parser),showAngles:optional(boolean),showSides:optional(boolean),snapTo:optional(enumeration("grid","angles","sides")),match:optional(enumeration("similar","congruent","approx","exact")),startCoords:optional(array(pairOfNumbers)),coords:optional(nullable(array(pairOfNumbers)))});const parsePerseusGraphTypeQuadratic=object({type:constant("quadratic"),coords:optional(nullable(trio(pairOfNumbers,pairOfNumbers,pairOfNumbers))),startCoords:optional(trio(pairOfNumbers,pairOfNumbers,pairOfNumbers))});const parsePerseusGraphTypeRay=object({type:constant("ray"),coords:optional(nullable(pair(pairOfNumbers,pairOfNumbers))),startCoords:optional(pair(pairOfNumbers,pairOfNumbers))});const parsePerseusGraphTypeSegment=object({type:constant("segment"),numSegments:optional(number),coords:optional(nullable(array(pair(pairOfNumbers,pairOfNumbers)))),startCoords:optional(array(pair(pairOfNumbers,pairOfNumbers)))});const parsePerseusGraphTypeSinusoid=object({type:constant("sinusoid"),coords:optional(nullable(array(pairOfNumbers))),startCoords:optional(array(pairOfNumbers))});const parsePerseusGraphType=discriminatedUnionOn("type").withBranch("angle",parsePerseusGraphTypeAngle).withBranch("circle",parsePerseusGraphTypeCircle).withBranch("linear",parsePerseusGraphTypeLinear).withBranch("linear-system",parsePerseusGraphTypeLinearSystem).withBranch("none",parsePerseusGraphTypeNone).withBranch("point",parsePerseusGraphTypePoint).withBranch("polygon",parsePerseusGraphTypePolygon).withBranch("quadratic",parsePerseusGraphTypeQuadratic).withBranch("ray",parsePerseusGraphTypeRay).withBranch("segment",parsePerseusGraphTypeSegment).withBranch("sinusoid",parsePerseusGraphTypeSinusoid).parser;const parseLockedFigureColor=enumeration(...lockedFigureColorNames);const parseLockedFigureFillType=enumeration("none","white","translucent","solid");const parseLockedLineStyle=enumeration("solid","dashed");const parseLockedLabelType=object({type:constant("label"),coord:pairOfNumbers,text:string,color:parseLockedFigureColor,size:enumeration("small","medium","large")});const parseLockedPointType=object({type:constant("point"),coord:pairOfNumbers,color:parseLockedFigureColor,filled:boolean,labels:defaulted(array(parseLockedLabelType),()=>[]),ariaLabel:optional(string)});const parseLockedLineType=object({type:constant("line"),kind:enumeration("line","ray","segment"),points:pair(parseLockedPointType,parseLockedPointType),color:parseLockedFigureColor,lineStyle:parseLockedLineStyle,showPoint1:defaulted(boolean,()=>false),showPoint2:defaulted(boolean,()=>false),labels:defaulted(array(parseLockedLabelType),()=>[]),ariaLabel:optional(string)});const parseLockedVectorType=object({type:constant("vector"),points:pair(pairOfNumbers,pairOfNumbers),color:parseLockedFigureColor,labels:defaulted(array(parseLockedLabelType),()=>[]),ariaLabel:optional(string)});const parseLockedEllipseType=object({type:constant("ellipse"),center:pairOfNumbers,radius:pairOfNumbers,angle:number,color:parseLockedFigureColor,fillStyle:parseLockedFigureFillType,strokeStyle:parseLockedLineStyle,labels:defaulted(array(parseLockedLabelType),()=>[]),ariaLabel:optional(string)});const parseLockedPolygonType=object({type:constant("polygon"),points:array(pairOfNumbers),color:parseLockedFigureColor,showVertices:boolean,fillStyle:parseLockedFigureFillType,strokeStyle:parseLockedLineStyle,labels:defaulted(array(parseLockedLabelType),()=>[]),ariaLabel:optional(string)});const parseLockedFunctionDomain=defaulted(pair(defaulted(number,()=>-Infinity),defaulted(number,()=>Infinity)),()=>[-Infinity,Infinity]);const parseLockedFunctionType=object({type:constant("function"),color:parseLockedFigureColor,strokeStyle:parseLockedLineStyle,equation:string,directionalAxis:enumeration("x","y"),domain:parseLockedFunctionDomain,labels:defaulted(array(parseLockedLabelType),()=>[]),ariaLabel:optional(string)});const parseLockedFigure=discriminatedUnionOn("type").withBranch("point",parseLockedPointType).withBranch("line",parseLockedLineType).withBranch("vector",parseLockedVectorType).withBranch("ellipse",parseLockedEllipseType).withBranch("polygon",parseLockedPolygonType).withBranch("function",parseLockedFunctionType).withBranch("label",parseLockedLabelType).parser;const parseInteractiveGraphWidget=parseWidget(constant("interactive-graph"),object({step:pairOfNumbers,gridStep:optional(pairOfNumbers),snapStep:optional(pairOfNumbers),backgroundImage:optional(parsePerseusImageBackground),markings:enumeration("graph","grid","none","axes"),labels:optional(array(string)),labelLocation:optional(enumeration("onAxis","alongEdge")),showProtractor:boolean,showRuler:optional(boolean),showTooltips:optional(boolean),rulerLabel:optional(string),rulerTicks:optional(number),range:pair(pairOfNumbers,pairOfNumbers),graph:defaulted(parsePerseusGraphType,()=>({type:"linear"})),correct:parsePerseusGraphType,lockedFigures:defaulted(array(parseLockedFigure),()=>[]),fullGraphAriaLabel:optional(string),fullGraphAriaDescription:optional(string)}));
150
157
 
151
158
  const parseLabelImageWidget=parseWidget(constant("label-image"),object({choices:array(string),imageUrl:string,imageAlt:string,imageHeight:number,imageWidth:number,markers:array(object({answers:array(string),label:string,x:number,y:number})),hideChoicesFromInstructions:boolean,multipleAnswers:boolean,static:defaulted(boolean,()=>false)}));
152
159
 
@@ -154,13 +161,13 @@ const parseMatcherWidget=parseWidget(constant("matcher"),object({labels:array(st
154
161
 
155
162
  const numberOrString=union(number).or(string).parser;const numeric=pipeParsers(defaulted(numberOrString,()=>NaN)).then(stringToNumber).parser;const parseMatrixWidget=parseWidget(defaulted(constant("matrix"),()=>"matrix"),object({prefix:optional(string),suffix:optional(string),answers:array(array(numeric)),cursorPosition:optional(array(number)),matrixBoardSize:array(number),static:optional(boolean)}));
156
163
 
157
- const parseMeasurerWidget=parseWidget(constant("measurer"),object({image:defaulted(parsePerseusImageBackground,()=>({url:null,top:0,left:0})),showProtractor:boolean,showRuler:boolean,rulerLabel:string,rulerTicks:number,rulerPixels:number,rulerLength:number,box:pair(number,number),static:defaulted(boolean,()=>false)}));
164
+ const parseMeasurerWidget=parseWidget(constant("measurer"),object({image:defaulted(parsePerseusImageBackground,()=>({url:null,top:0,left:0})),showProtractor:boolean,showRuler:boolean,rulerLabel:string,rulerTicks:number,rulerPixels:number,rulerLength:number,box:pair(number,number)}));
158
165
 
159
166
  const parseMoleculeRendererWidget=parseWidget(constant("molecule-renderer"),object({widgetId:string,rotationAngle:optional(number),smiles:optional(string)}));
160
167
 
161
- const emptyStringToNull=pipeParsers(constant("")).then(convert(()=>null)).parser;const parseNumberLineWidget=parseWidget(constant("number-line"),object({range:array(number),labelRange:array(nullable(union(number).or(emptyStringToNull).parser)),labelStyle:string,labelTicks:boolean,isTickCtrl:optional(nullable(boolean)),divisionRange:array(number),numDivisions:optional(nullable(number)),snapDivisions:defaulted(number,()=>2),tickStep:optional(nullable(number)),correctRel:optional(nullable(string)),correctX:nullable(number),initialX:optional(nullable(number)),showTooltips:optional(boolean),static:defaulted(boolean,()=>false)}));
168
+ const emptyStringToNull=pipeParsers(constant("")).then(convert(()=>null)).parser;const parseNumberLineWidget=parseWidget(constant("number-line"),object({range:array(number),labelRange:array(nullable(union(number).or(emptyStringToNull).parser)),labelStyle:string,labelTicks:boolean,isTickCtrl:optional(nullable(boolean)),isInequality:defaulted(boolean,()=>false),divisionRange:array(number),numDivisions:optional(nullable(number)),snapDivisions:defaulted(number,()=>2),tickStep:optional(nullable(number)),correctRel:defaulted(optional(enumeration("eq","lt","gt","le","ge")),()=>undefined),correctX:nullable(number),initialX:optional(nullable(number)),showTooltips:optional(boolean),static:defaulted(boolean,()=>false)}));
162
169
 
163
- const parseMathFormat=enumeration("integer","mixed","improper","proper","decimal","percent","pi");const parseSimplify=pipeParsers(union(constant(null)).or(constant(undefined)).or(boolean).or(constant("required")).or(constant("correct")).or(constant("enforced")).or(constant("optional")).or(constant("accepted")).parser).then(convert(deprecatedSimplifyValuesToRequired)).parser;function deprecatedSimplifyValuesToRequired(simplify){switch(simplify){case "enforced":case "required":case "optional":return simplify;default:return "required"}}const parseNumericInputWidget=parseWidget(constant("numeric-input"),object({answers:array(object({message:string,value:optional(nullable(number)),status:string,answerForms:defaulted(array(parseMathFormat),()=>undefined),strict:boolean,maxError:optional(nullable(number)),simplify:parseSimplify})),labelText:optional(string),size:string,coefficient:defaulted(boolean,()=>false),rightAlign:optional(boolean),static:defaulted(boolean,()=>false),answerForms:optional(array(object({name:parseMathFormat,simplify:parseSimplify})))}));
170
+ const parseMathFormat=enumeration("integer","mixed","improper","proper","decimal","percent","pi");const parseSimplify=pipeParsers(union(constant(null)).or(constant(undefined)).or(boolean).or(constant("required")).or(constant("correct")).or(constant("enforced")).or(constant("optional")).or(constant("accepted")).parser).then(convert(deprecatedSimplifyValuesToRequired)).parser;function deprecatedSimplifyValuesToRequired(simplify){switch(simplify){case "enforced":case "required":case "optional":return simplify;default:return "required"}}const parseNumericInputWidget=parseWidget(constant("numeric-input"),object({answers:array(object({message:string,value:optional(nullable(number)),status:string,answerForms:defaulted(optional(array(parseMathFormat)),()=>undefined),strict:boolean,maxError:optional(nullable(number)),simplify:parseSimplify})),labelText:optional(string),size:string,coefficient:defaulted(boolean,()=>false),rightAlign:optional(boolean),static:defaulted(boolean,()=>false),answerForms:optional(array(object({name:parseMathFormat,simplify:parseSimplify})))}));
164
171
 
165
172
  function parseRenderer(rawValue,ctx){return parsePerseusRenderer(rawValue,ctx)}const largeToAuto=(height,ctx)=>{if(height==="large"){return ctx.success("auto")}return ctx.success(height)};const parseOrdererWidget=parseWidget(constant("orderer"),object({options:defaulted(array(parseRenderer),()=>[]),correctOptions:array(parseRenderer),otherOptions:array(parseRenderer),height:pipeParsers(enumeration("normal","auto","large")).then(largeToAuto).parser,layout:defaulted(enumeration("horizontal","vertical"),()=>"horizontal")}));
166
173
 
@@ -176,7 +183,7 @@ const parsePythonProgramWidget=parseWidget(constant("python-program"),object({pr
176
183
 
177
184
  const currentVersion$3={major:2,minor:0};function deriveNumCorrect(options){const{choices,numCorrect}=options;return numCorrect??choices.filter(c=>c.correct).length}const widgetOptionsUpgrades$2={"2":v1props=>{const upgraded={...v1props,numCorrect:deriveNumCorrect(v1props)};return upgraded},"1":v0props=>{const{noneOfTheAbove,...rest}=v0props;if(noneOfTheAbove){throw new Error("radio widget v0 no longer supports auto noneOfTheAbove")}return {...rest,hasNoneOfTheAbove:false}}};const defaultWidgetOptions$v={choices:[{},{},{},{}],displayCount:null,randomize:false,hasNoneOfTheAbove:false,multipleSelect:false,countChoices:false,deselectEnabled:false};
178
185
 
179
- const parseWidgetsMapOrUndefined=defaulted((rawVal,ctx)=>parseWidgetsMap(rawVal,ctx),()=>undefined);const version2=optional(object({major:constant(2),minor:number}));const parseRadioWidgetV2=parseWidgetWithVersion(version2,constant("radio"),object({numCorrect:optional(number),choices:array(object({content:defaulted(string,()=>""),clue:optional(string),correct:optional(boolean),isNoneOfTheAbove:optional(boolean),widgets:parseWidgetsMapOrUndefined})),hasNoneOfTheAbove:optional(boolean),countChoices:optional(boolean),randomize:optional(boolean),multipleSelect:optional(boolean),deselectEnabled:optional(boolean),onePerLine:optional(boolean),displayCount:optional(any),noneOfTheAbove:optional(constant(false))}));const version1=optional(object({major:constant(1),minor:number}));const parseRadioWidgetV1=parseWidgetWithVersion(version1,constant("radio"),object({choices:array(object({content:defaulted(string,()=>""),clue:optional(string),correct:optional(boolean),isNoneOfTheAbove:optional(boolean),widgets:parseWidgetsMapOrUndefined})),hasNoneOfTheAbove:optional(boolean),countChoices:optional(boolean),randomize:optional(boolean),multipleSelect:optional(boolean),deselectEnabled:optional(boolean),onePerLine:optional(boolean),displayCount:optional(any),noneOfTheAbove:optional(constant(false))}));function migrateV1ToV2(widget){const{options}=widget;return {...widget,version:{major:2,minor:0},options:{...options,numCorrect:deriveNumCorrect(options)}}}const version0=optional(object({major:constant(0),minor:number}));const parseRadioWidgetV0=parseWidgetWithVersion(version0,constant("radio"),object({choices:array(object({content:defaulted(string,()=>""),clue:optional(string),correct:optional(boolean),isNoneOfTheAbove:optional(boolean),widgets:parseWidgetsMapOrUndefined})),hasNoneOfTheAbove:optional(boolean),countChoices:optional(boolean),randomize:optional(boolean),multipleSelect:optional(boolean),deselectEnabled:optional(boolean),onePerLine:optional(boolean),displayCount:optional(any),noneOfTheAbove:optional(constant(false))}));function migrateV0ToV1(widget){const{options}=widget;const{noneOfTheAbove:_,...rest}=options;return {...widget,version:{major:1,minor:0},options:{...rest,hasNoneOfTheAbove:false,noneOfTheAbove:undefined}}}const parseRadioWidget=versionedWidgetOptions(2,parseRadioWidgetV2).withMigrationFrom(1,parseRadioWidgetV1,migrateV1ToV2).withMigrationFrom(0,parseRadioWidgetV0,migrateV0ToV1).parser;
186
+ const parseWidgetsMapOrUndefined=defaulted(optional((rawVal,ctx)=>parseWidgetsMap(rawVal,ctx)),()=>undefined);const version2=optional(object({major:constant(2),minor:number}));const parseRadioWidgetV2=parseWidgetWithVersion(version2,constant("radio"),object({numCorrect:optional(number),choices:array(object({content:defaulted(string,()=>""),clue:optional(string),correct:optional(boolean),isNoneOfTheAbove:optional(boolean),widgets:parseWidgetsMapOrUndefined})),hasNoneOfTheAbove:optional(boolean),countChoices:optional(boolean),randomize:optional(boolean),multipleSelect:optional(boolean),deselectEnabled:optional(boolean),onePerLine:optional(boolean),displayCount:optional(any),noneOfTheAbove:optional(constant(false))}));const version1=optional(object({major:constant(1),minor:number}));const parseRadioWidgetV1=parseWidgetWithVersion(version1,constant("radio"),object({choices:array(object({content:defaulted(string,()=>""),clue:optional(string),correct:optional(boolean),isNoneOfTheAbove:optional(boolean),widgets:parseWidgetsMapOrUndefined})),hasNoneOfTheAbove:optional(boolean),countChoices:optional(boolean),randomize:optional(boolean),multipleSelect:optional(boolean),deselectEnabled:optional(boolean),onePerLine:optional(boolean),displayCount:optional(any),noneOfTheAbove:optional(constant(false))}));function migrateV1ToV2(widget){const{options}=widget;return {...widget,version:{major:2,minor:0},options:{...options,numCorrect:deriveNumCorrect(options)}}}const version0=optional(object({major:constant(0),minor:number}));const parseRadioWidgetV0=parseWidgetWithVersion(version0,constant("radio"),object({choices:array(object({content:defaulted(string,()=>""),clue:optional(string),correct:optional(boolean),isNoneOfTheAbove:optional(boolean),widgets:parseWidgetsMapOrUndefined})),hasNoneOfTheAbove:optional(boolean),countChoices:optional(boolean),randomize:optional(boolean),multipleSelect:optional(boolean),deselectEnabled:optional(boolean),onePerLine:optional(boolean),displayCount:optional(any),noneOfTheAbove:optional(constant(false))}));function migrateV0ToV1(widget){const{options}=widget;const{noneOfTheAbove:_,...rest}=options;return {...widget,version:{major:1,minor:0},options:{...rest,hasNoneOfTheAbove:false,noneOfTheAbove:undefined}}}const parseRadioWidget=versionedWidgetOptions(2,parseRadioWidgetV2).withMigrationFrom(1,parseRadioWidgetV1,migrateV1ToV2).withMigrationFrom(0,parseRadioWidgetV0,migrateV0ToV1).parser;
180
187
 
181
188
  const parseSorterWidget=parseWidget(constant("sorter"),object({correct:array(string),padding:boolean,layout:enumeration("horizontal","vertical")}));
182
189
 
@@ -184,13 +191,13 @@ const parseTableWidget=parseWidget(constant("table"),object({headers:array(strin
184
191
 
185
192
  const parseVideoWidget=parseWidget(constant("video"),object({location:string,static:optional(boolean)}));
186
193
 
187
- const parseWidgetsMap=(rawValue,ctx)=>{if(!isObject(rawValue)){return ctx.failure("PerseusWidgetsMap",rawValue)}const widgetsMap={};for(const key of Object.keys(rawValue)){const entryResult=parseWidgetsMapEntry([key,rawValue[key]],widgetsMap,ctx.forSubtree(key));if(isFailure(entryResult)){return entryResult}}return ctx.success(widgetsMap)};const parseWidgetsMapEntry=([id,widget],widgetMap,ctx)=>{const idComponentsResult=parseWidgetIdComponents(id.split(" "),ctx.forSubtree("(widget ID)"));if(isFailure(idComponentsResult)){return idComponentsResult}const[type,n]=idComponentsResult.value;function parseAndAssign(key,parse){const widgetResult=parse(widget,ctx);if(isFailure(widgetResult)){return widgetResult}widgetMap[key]=widgetResult.value;return ctx.success(undefined)}switch(type){case "categorizer":return parseAndAssign(`categorizer ${n}`,parseCategorizerWidget);case "cs-program":return parseAndAssign(`cs-program ${n}`,parseCSProgramWidget);case "definition":return parseAndAssign(`definition ${n}`,parseDefinitionWidget);case "dropdown":return parseAndAssign(`dropdown ${n}`,parseDropdownWidget);case "explanation":return parseAndAssign(`explanation ${n}`,parseExplanationWidget);case "expression":return parseAndAssign(`expression ${n}`,parseExpressionWidget);case "grapher":return parseAndAssign(`grapher ${n}`,parseGrapherWidget);case "group":return parseAndAssign(`group ${n}`,parseGroupWidget);case "graded-group":return parseAndAssign(`graded-group ${n}`,parseGradedGroupWidget);case "graded-group-set":return parseAndAssign(`graded-group-set ${n}`,parseGradedGroupSetWidget);case "iframe":return parseAndAssign(`iframe ${n}`,parseIframeWidget);case "image":return parseAndAssign(`image ${n}`,parseImageWidget);case "input-number":return parseAndAssign(`input-number ${n}`,parseInputNumberWidget);case "interaction":return parseAndAssign(`interaction ${n}`,parseInteractionWidget);case "interactive-graph":return parseAndAssign(`interactive-graph ${n}`,parseInteractiveGraphWidget);case "label-image":return parseAndAssign(`label-image ${n}`,parseLabelImageWidget);case "matcher":return parseAndAssign(`matcher ${n}`,parseMatcherWidget);case "matrix":return parseAndAssign(`matrix ${n}`,parseMatrixWidget);case "measurer":return parseAndAssign(`measurer ${n}`,parseMeasurerWidget);case "molecule-renderer":return parseAndAssign(`molecule-renderer ${n}`,parseMoleculeRendererWidget);case "number-line":return parseAndAssign(`number-line ${n}`,parseNumberLineWidget);case "numeric-input":return parseAndAssign(`numeric-input ${n}`,parseNumericInputWidget);case "orderer":return parseAndAssign(`orderer ${n}`,parseOrdererWidget);case "passage":return parseAndAssign(`passage ${n}`,parsePassageWidget);case "passage-ref":return parseAndAssign(`passage-ref ${n}`,parsePassageRefWidget);case "passage-ref-target":return parseAndAssign(`passage-ref-target ${n}`,any);case "phet-simulation":return parseAndAssign(`phet-simulation ${n}`,parsePhetSimulationWidget);case "plotter":return parseAndAssign(`plotter ${n}`,parsePlotterWidget);case "python-program":return parseAndAssign(`python-program ${n}`,parsePythonProgramWidget);case "radio":return parseAndAssign(`radio ${n}`,parseRadioWidget);case "sorter":return parseAndAssign(`sorter ${n}`,parseSorterWidget);case "table":return parseAndAssign(`table ${n}`,parseTableWidget);case "video":return parseAndAssign(`video ${n}`,parseVideoWidget);case "sequence":return parseAndAssign(`sequence ${n}`,parseDeprecatedWidget);case "lights-puzzle":return parseAndAssign(`lights-puzzle ${n}`,parseDeprecatedWidget);case "simulator":return parseAndAssign(`simulator ${n}`,parseDeprecatedWidget);case "transformer":return parseAndAssign(`transformer ${n}`,parseDeprecatedWidget);default:return parseAndAssign(`${type} ${n}`,parseWidget(constant(type),any))}};const parseDeprecatedWidget=parseWidget((_,ctx)=>ctx.success("deprecated-standin"),object({}));const parseStringToNonNegativeInt=(rawValue,ctx)=>{if(typeof rawValue!=="string"||!/^(0|[1-9][0-9]*)$/.test(rawValue)){return ctx.failure("a string representing a non-negative integer",rawValue)}return ctx.success(+rawValue)};const parseWidgetIdComponents=pair(string,parseStringToNonNegativeInt);
194
+ const parseWidgetsMap=(rawValue,ctx)=>{if(!isPlainObject(rawValue)){return ctx.failure("PerseusWidgetsMap",rawValue)}const widgetsMap={};for(const key of Object.keys(rawValue)){const entryResult=parseWidgetsMapEntry([key,rawValue[key]],widgetsMap,ctx.forSubtree(key));if(isFailure(entryResult)){return entryResult}}return ctx.success(widgetsMap)};const parseWidgetsMapEntry=([id,widget],widgetMap,ctx)=>{const idComponentsResult=parseWidgetIdComponents(id.split(" "),ctx.forSubtree("(widget ID)"));if(isFailure(idComponentsResult)){return idComponentsResult}const[type,n]=idComponentsResult.value;function parseAndAssign(key,parse){const widgetResult=parse(widget,ctx);if(isFailure(widgetResult)){return widgetResult}widgetMap[key]=widgetResult.value;return ctx.success(undefined)}switch(type){case "categorizer":return parseAndAssign(`categorizer ${n}`,parseCategorizerWidget);case "cs-program":return parseAndAssign(`cs-program ${n}`,parseCSProgramWidget);case "definition":return parseAndAssign(`definition ${n}`,parseDefinitionWidget);case "dropdown":return parseAndAssign(`dropdown ${n}`,parseDropdownWidget);case "explanation":return parseAndAssign(`explanation ${n}`,parseExplanationWidget);case "expression":return parseAndAssign(`expression ${n}`,parseExpressionWidget);case "grapher":return parseAndAssign(`grapher ${n}`,parseGrapherWidget);case "group":return parseAndAssign(`group ${n}`,parseGroupWidget);case "graded-group":return parseAndAssign(`graded-group ${n}`,parseGradedGroupWidget);case "graded-group-set":return parseAndAssign(`graded-group-set ${n}`,parseGradedGroupSetWidget);case "iframe":return parseAndAssign(`iframe ${n}`,parseIframeWidget);case "image":return parseAndAssign(`image ${n}`,parseImageWidget);case "input-number":return parseAndAssign(`input-number ${n}`,parseInputNumberWidget);case "interaction":return parseAndAssign(`interaction ${n}`,parseInteractionWidget);case "interactive-graph":return parseAndAssign(`interactive-graph ${n}`,parseInteractiveGraphWidget);case "label-image":return parseAndAssign(`label-image ${n}`,parseLabelImageWidget);case "matcher":return parseAndAssign(`matcher ${n}`,parseMatcherWidget);case "matrix":return parseAndAssign(`matrix ${n}`,parseMatrixWidget);case "measurer":return parseAndAssign(`measurer ${n}`,parseMeasurerWidget);case "molecule-renderer":return parseAndAssign(`molecule-renderer ${n}`,parseMoleculeRendererWidget);case "number-line":return parseAndAssign(`number-line ${n}`,parseNumberLineWidget);case "numeric-input":return parseAndAssign(`numeric-input ${n}`,parseNumericInputWidget);case "orderer":return parseAndAssign(`orderer ${n}`,parseOrdererWidget);case "passage":return parseAndAssign(`passage ${n}`,parsePassageWidget);case "passage-ref":return parseAndAssign(`passage-ref ${n}`,parsePassageRefWidget);case "passage-ref-target":return parseAndAssign(`passage-ref-target ${n}`,any);case "phet-simulation":return parseAndAssign(`phet-simulation ${n}`,parsePhetSimulationWidget);case "plotter":return parseAndAssign(`plotter ${n}`,parsePlotterWidget);case "python-program":return parseAndAssign(`python-program ${n}`,parsePythonProgramWidget);case "radio":return parseAndAssign(`radio ${n}`,parseRadioWidget);case "sorter":return parseAndAssign(`sorter ${n}`,parseSorterWidget);case "table":return parseAndAssign(`table ${n}`,parseTableWidget);case "video":return parseAndAssign(`video ${n}`,parseVideoWidget);case "sequence":return parseAndAssign(`sequence ${n}`,parseDeprecatedWidget);case "lights-puzzle":return parseAndAssign(`lights-puzzle ${n}`,parseDeprecatedWidget);case "simulator":return parseAndAssign(`simulator ${n}`,parseDeprecatedWidget);case "transformer":return parseAndAssign(`transformer ${n}`,parseDeprecatedWidget);default:return parseAndAssign(`${type} ${n}`,parseWidget(constant(type),any))}};const parseDeprecatedWidget=parseWidget((_,ctx)=>ctx.success("deprecated-standin"),object({}));const parseStringToNonNegativeInt=(rawValue,ctx)=>{if(typeof rawValue!=="string"||!/^(0|[1-9][0-9]*)$/.test(rawValue)){return ctx.failure("a string representing a non-negative integer",rawValue)}return ctx.success(+rawValue)};const parseWidgetIdComponents=pair(string,parseStringToNonNegativeInt);
188
195
 
189
196
  const parsePerseusRenderer=defaulted(object({content:defaulted(string,()=>""),widgets:defaulted((rawVal,ctx)=>parseWidgetsMap(rawVal,ctx),()=>({})),images:parseImages,metadata:any}),()=>({content:"",widgets:{},images:{}}));
190
197
 
191
198
  const parsePerseusArticle=union(parsePerseusRenderer).or(array(parsePerseusRenderer)).parser;
192
199
 
193
- const parseHint=object({replace:defaulted(boolean,()=>undefined),content:string,widgets:defaulted(parseWidgetsMap,()=>({})),images:parseImages,metadata:any});
200
+ const parseHint=object({replace:defaulted(optional(boolean),()=>undefined),content:string,widgets:defaulted(parseWidgetsMap,()=>({})),images:parseImages,metadata:any});
194
201
 
195
202
  const parsePerseusAnswerArea=pipeParsers(defaulted(object({}),()=>({}))).then(convert(toAnswerArea)).parser;function toAnswerArea(raw){return {zTable:!!raw.zTable,calculator:!!raw.calculator,chi2Table:!!raw.chi2Table,financialCalculatorMonthlyPayment:!!raw.financialCalculatorMonthlyPayment,financialCalculatorTotalAmount:!!raw.financialCalculatorTotalAmount,financialCalculatorTimeToPayOff:!!raw.financialCalculatorTimeToPayOff,periodicTable:!!raw.periodicTable,periodicTableWithKey:!!raw.periodicTableWithKey,tTable:!!raw.tTable}}
196
203
 
@@ -198,7 +205,7 @@ const parsePerseusItem$1=object({question:parsePerseusRenderer,hints:defaulted(a
198
205
 
199
206
  function parsePerseusItem(json){if(isRealJSONParse(JSON.parse)){return JSON.parse(json)}throw new Error("Something went wrong.")}function parseAndMigratePerseusItem(json){throwErrorIfCheatingDetected();const object=JSON.parse(json);const result=parse(object,parsePerseusItem$1);if(isFailure(result)){return failure({message:result.detail,invalidObject:object})}return result}function parseAndMigratePerseusArticle(json){throwErrorIfCheatingDetected();const object=JSON.parse(json);const result=parse(object,parsePerseusArticle);if(isFailure(result)){return failure({message:result.detail,invalidObject:object})}return result}function throwErrorIfCheatingDetected(){if(!isRealJSONParse(JSON.parse)){throw new Error("Something went wrong.")}}
200
207
 
201
- const libName="@khanacademy/perseus-core";const libVersion="8.0.0";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
208
+ const libName="@khanacademy/perseus-core";const libVersion="10.0.0";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
202
209
 
203
210
  const Errors=Object.freeze({Unknown:"Unknown",Internal:"Internal",InvalidInput:"InvalidInput",NotAllowed:"NotAllowed",TransientService:"TransientService",Service:"Service"});
204
211
 
@@ -218,7 +225,7 @@ const defaultWidgetOptions$s={togglePrompt:"",definition:""};const definitionWid
218
225
 
219
226
  function getDropdownPublicWidgetOptions(options){return {choices:options.choices.map(choice=>({content:choice.content})),placeholder:options.placeholder,static:options.static,visibleLabel:options.visibleLabel,ariaLabel:options.ariaLabel}}
220
227
 
221
- const defaultWidgetOptions$r={placeholder:"",choices:[{content:"",correct:false}]};const dropdownWidgetLogic={name:"definition",defaultWidgetOptions: defaultWidgetOptions$r,defaultAlignment:"inline-block",getPublicWidgetOptions:getDropdownPublicWidgetOptions};
228
+ const defaultWidgetOptions$r={placeholder:"",choices:[{content:"",correct:false}]};const dropdownWidgetLogic={name:"dropdown",defaultWidgetOptions: defaultWidgetOptions$r,defaultAlignment:"inline-block",getPublicWidgetOptions:getDropdownPublicWidgetOptions};
222
229
 
223
230
  const defaultWidgetOptions$q={showPrompt:"Explain",hidePrompt:"Hide explanation",explanation:"explanation goes here\n\nmore explanation",widgets:{}};const explanationWidgetLogic={name:"explanation",defaultWidgetOptions: defaultWidgetOptions$q,defaultAlignment:"inline"};
224
231
 
@@ -236,67 +243,67 @@ function getGrapherPublicWidgetOptions(options){const{correct:_,...publicOptions
236
243
 
237
244
  const defaultWidgetOptions$m={graph:{labels:["x","y"],range:[[-10,10],[-10,10]],step:[1,1],backgroundImage:{url:null},markings:"graph",rulerLabel:"",rulerTicks:10,valid:true,showTooltips:false},correct:{type:"linear",coords:null},availableTypes:["linear"]};const grapherWidgetLogic={name:"grapher",defaultWidgetOptions: defaultWidgetOptions$m,getPublicWidgetOptions:getGrapherPublicWidgetOptions};
238
245
 
239
- const defaultWidgetOptions$l={content:"",widgets:{},images:{}};const groupWidgetLogic={name:"group",defaultWidgetOptions: defaultWidgetOptions$l};
240
-
241
246
  function getIFramePublicWidgetOptions(options){return options}
242
247
 
243
- const defaultWidgetOptions$k={url:"",settings:[{name:"",value:""}],width:"400",height:"400",allowFullScreen:false,allowTopNavigation:false};const iframeWidgetLogic={name:"iframe",defaultWidgetOptions: defaultWidgetOptions$k,getPublicWidgetOptions:getIFramePublicWidgetOptions};
248
+ const defaultWidgetOptions$l={url:"",settings:[{name:"",value:""}],width:"400",height:"400",allowFullScreen:false,allowTopNavigation:false};const iframeWidgetLogic={name:"iframe",defaultWidgetOptions: defaultWidgetOptions$l,getPublicWidgetOptions:getIFramePublicWidgetOptions};
244
249
 
245
- const defaultWidgetOptions$j={title:"",range:[[0,10],[0,10]],box:[400,400],backgroundImage:{url:null,width:0,height:0},labels:[],alt:"",caption:""};const imageWidgetLogic={name:"image",defaultWidgetOptions: defaultWidgetOptions$j,supportedAlignments:["block","full-width"],defaultAlignment:"block"};
250
+ const defaultWidgetOptions$k={title:"",range:[[0,10],[0,10]],box:[400,400],backgroundImage:{url:null,width:0,height:0},labels:[],alt:"",caption:""};const imageWidgetLogic={name:"image",defaultWidgetOptions: defaultWidgetOptions$k,supportedAlignments:["block","full-width"],defaultAlignment:"block"};
246
251
 
247
- const defaultWidgetOptions$i={value:0,simplify:"required",size:"normal",inexact:false,maxError:.1,answerType:"number",rightAlign:false};const inputNumberWidgetLogic={name:"input-number",defaultWidgetOptions: defaultWidgetOptions$i,defaultAlignment:"inline-block"};
252
+ function getInputNumberPublicWidgetOptions(options){const{value:_,...publicWidgetOptions}=options;return publicWidgetOptions}
248
253
 
249
- const defaultWidgetOptions$h={graph:{box:[400,400],labels:["x","y"],range:[[-10,10],[-10,10]],tickStep:[1,1],gridStep:[1,1],markings:"graph"},elements:[]};const interactionWidgetLogic={name:"interaction",defaultWidgetOptions: defaultWidgetOptions$h};
254
+ const defaultWidgetOptions$j={value:0,simplify:"required",size:"normal",inexact:false,maxError:.1,answerType:"number",rightAlign:false};const inputNumberWidgetLogic={name:"input-number",defaultWidgetOptions: defaultWidgetOptions$j,defaultAlignment:"inline-block",getPublicWidgetOptions:getInputNumberPublicWidgetOptions};
255
+
256
+ const defaultWidgetOptions$i={graph:{box:[400,400],labels:["x","y"],range:[[-10,10],[-10,10]],tickStep:[1,1],gridStep:[1,1],markings:"graph"},elements:[]};const interactionWidgetLogic={name:"interaction",defaultWidgetOptions: defaultWidgetOptions$i};
250
257
 
251
258
  function getInteractiveGraphPublicWidgetOptions(options){const{correct:_,...publicOptions}=options;return publicOptions}
252
259
 
253
- const defaultWidgetOptions$g={labels:["x","y"],range:[[-10,10],[-10,10]],step:[1,1],backgroundImage:{url:null},markings:"graph",showTooltips:false,showProtractor:false,graph:{type:"linear"},correct:{type:"linear",coords:null}};const interactiveGraphWidgetLogic={name:"interactive-graph",defaultWidgetOptions: defaultWidgetOptions$g,getPublicWidgetOptions:getInteractiveGraphPublicWidgetOptions};
260
+ const defaultWidgetOptions$h={labels:["x","y"],range:[[-10,10],[-10,10]],step:[1,1],backgroundImage:{url:null},markings:"graph",showTooltips:false,showProtractor:false,graph:{type:"linear"},correct:{type:"linear",coords:null}};const interactiveGraphWidgetLogic={name:"interactive-graph",defaultWidgetOptions: defaultWidgetOptions$h,getPublicWidgetOptions:getInteractiveGraphPublicWidgetOptions};
254
261
 
255
262
  function getLabelImagePublicWidgetOptions(options){return {...options,markers:options.markers.map(getLabelImageMarkerPublicData)}}function getLabelImageMarkerPublicData(marker){const{answers:_,...publicData}=marker;return publicData}
256
263
 
257
- const defaultWidgetOptions$f={choices:[],imageAlt:"",imageUrl:"",imageWidth:0,imageHeight:0,markers:[],multipleAnswers:false,hideChoicesFromInstructions:false};const labelImageWidgetLogic={name:"label-image",defaultWidgetOptions: defaultWidgetOptions$f,getPublicWidgetOptions:getLabelImagePublicWidgetOptions};
264
+ const defaultWidgetOptions$g={choices:[],imageAlt:"",imageUrl:"",imageWidth:0,imageHeight:0,markers:[],multipleAnswers:false,hideChoicesFromInstructions:false};const labelImageWidgetLogic={name:"label-image",defaultWidgetOptions: defaultWidgetOptions$g,getPublicWidgetOptions:getLabelImagePublicWidgetOptions};
258
265
 
259
- const seededRNG=function(seed){let randomSeed=seed;return function(){let seed=randomSeed;seed=seed+0x7ed55d16+(seed<<12)&0xffffffff;seed=(seed^0xc761c23c^seed>>>19)&0xffffffff;seed=seed+0x165667b1+(seed<<5)&0xffffffff;seed=(seed+0xd3a2646c^seed<<9)&0xffffffff;seed=seed+0xfd7046c5+(seed<<3)&0xffffffff;seed=(seed^0xb55a4f09^seed>>>16)&0xffffffff;return (randomSeed=seed&0xfffffff)/0x10000000}};function shuffle(array,randomSeed,ensurePermuted=false){const shuffled=___default.default.clone(array);if(!shuffled.length||___default.default.all(shuffled,function(value){return ___default.default.isEqual(value,shuffled[0])})){return shuffled}let random;if(typeof randomSeed==="function"){random=randomSeed;}else {random=seededRNG(randomSeed);}do{for(let top=shuffled.length;top>0;top--){const newEnd=Math.floor(random()*top);const temp=shuffled[newEnd];shuffled[newEnd]=shuffled[top-1];shuffled[top-1]=temp;}}while(ensurePermuted&&___default.default.isEqual(array,shuffled))return shuffled}const random=seededRNG(new Date().getTime()&0xffffffff);
266
+ const seededRNG=function(seed){let randomSeed=seed;return function(){let seed=randomSeed;seed=seed+0x7ed55d16+(seed<<12)&0xffffffff;seed=(seed^0xc761c23c^seed>>>19)&0xffffffff;seed=seed+0x165667b1+(seed<<5)&0xffffffff;seed=(seed+0xd3a2646c^seed<<9)&0xffffffff;seed=seed+0xfd7046c5+(seed<<3)&0xffffffff;seed=(seed^0xb55a4f09^seed>>>16)&0xffffffff;return (randomSeed=seed&0xfffffff)/0x10000000}};function shuffle(array,randomSeed,ensurePermuted=false){const shuffled=[...array];if(!shuffled.length||___default.default.all(shuffled,function(value){return ___default.default.isEqual(value,shuffled[0])})){return shuffled}let random;if(typeof randomSeed==="function"){random=randomSeed;}else {random=seededRNG(randomSeed);}do{for(let top=shuffled.length;top>0;top--){const newEnd=Math.floor(random()*top);const temp=shuffled[newEnd];shuffled[newEnd]=shuffled[top-1];shuffled[top-1]=temp;}}while(ensurePermuted&&___default.default.isEqual(array,shuffled))return shuffled}const random=seededRNG(new Date().getTime()&0xffffffff);
260
267
 
261
268
  const shuffleMatcher=props=>{const rng=seededRNG(props.problemNum);let left;if(!props.orderMatters){left=props.left;}else {left=shuffle(props.left,rng,true);}const right=shuffle(props.right,rng,true);return {left,right}};function shuffleMatcherWithRandom(data){let left;if(!data.orderMatters){left=data.left;}else {left=shuffle(data.left,Math.random,true);}const right=shuffle(data.right,Math.random,true);return {left,right}}function getMatcherPublicWidgetOptions(options){const{left,right}=shuffleMatcherWithRandom(options);return {...options,left:left,right:right}}
262
269
 
263
- const defaultWidgetOptions$e={left:["$x$","$y$","$z$"],right:["$1$","$2$","$3$"],labels:["test","label"],orderMatters:false,padding:true};const matcherWidgetLogic={name:"matcher",defaultWidgetOptions: defaultWidgetOptions$e,getPublicWidgetOptions:getMatcherPublicWidgetOptions};
270
+ const defaultWidgetOptions$f={left:["$x$","$y$","$z$"],right:["$1$","$2$","$3$"],labels:["test","label"],orderMatters:false,padding:true};const matcherWidgetLogic={name:"matcher",defaultWidgetOptions: defaultWidgetOptions$f,getPublicWidgetOptions:getMatcherPublicWidgetOptions};
264
271
 
265
272
  function getMatrixPublicWidgetOptions(options){const{answers:_,...publicOptions}=options;return publicOptions}
266
273
 
267
- const defaultWidgetOptions$d={matrixBoardSize:[3,3],answers:[[]],prefix:"",suffix:"",cursorPosition:[0,0]};const matrixWidgetLogic={name:"matrix",defaultWidgetOptions: defaultWidgetOptions$d,getPublicWidgetOptions:getMatrixPublicWidgetOptions};
274
+ const defaultWidgetOptions$e={matrixBoardSize:[3,3],answers:[[]],prefix:"",suffix:"",cursorPosition:[0,0]};const matrixWidgetLogic={name:"matrix",defaultWidgetOptions: defaultWidgetOptions$e,getPublicWidgetOptions:getMatrixPublicWidgetOptions};
268
275
 
269
- const currentVersion$1={major:1,minor:0};const widgetOptionsUpgrades={"1":v0options=>{const{imageUrl,imageTop,imageLeft,...rest}=v0options;return {...rest,image:{url:imageUrl,top:imageTop,left:imageLeft}}}};const defaultWidgetOptions$c={box:[480,480],image:{},showProtractor:true,showRuler:false,rulerLabel:"",rulerTicks:10,rulerPixels:40,rulerLength:10};
276
+ const currentVersion$1={major:1,minor:0};const widgetOptionsUpgrades={"1":v0options=>{const{imageUrl,imageTop,imageLeft,...rest}=v0options;return {...rest,image:{url:imageUrl,top:imageTop,left:imageLeft}}}};const defaultWidgetOptions$d={box:[480,480],image:{},showProtractor:true,showRuler:false,rulerLabel:"",rulerTicks:10,rulerPixels:40,rulerLength:10};
270
277
 
271
- const measurerWidgetLogic={name:"measurer",version:currentVersion$1,widgetOptionsUpgrades:widgetOptionsUpgrades,defaultWidgetOptions:defaultWidgetOptions$c};
278
+ const measurerWidgetLogic={name:"measurer",version:currentVersion$1,widgetOptionsUpgrades:widgetOptionsUpgrades,defaultWidgetOptions:defaultWidgetOptions$d};
272
279
 
273
280
  function getNumberLinePublicWidgetOptions(options){const{correctX:_,correctRel:__,...publicOptions}=options;return publicOptions}
274
281
 
275
- const defaultWidgetOptions$b={range:[0,10],labelRange:[null,null],labelStyle:"decimal",labelTicks:true,divisionRange:[1,12],numDivisions:5,snapDivisions:2,tickStep:null,correctRel:"eq",correctX:null,initialX:null,showTooltips:false};const numberLineWidgetLogic={name:"number-line",defaultWidgetOptions: defaultWidgetOptions$b,getPublicWidgetOptions:getNumberLinePublicWidgetOptions};
282
+ const defaultWidgetOptions$c={range:[0,10],labelRange:[null,null],labelStyle:"decimal",labelTicks:true,divisionRange:[1,12],numDivisions:5,snapDivisions:2,tickStep:null,correctRel:"eq",correctX:null,initialX:null,showTooltips:false};const numberLineWidgetLogic={name:"number-line",defaultWidgetOptions: defaultWidgetOptions$c,getPublicWidgetOptions:getNumberLinePublicWidgetOptions};
276
283
 
277
284
  function getNumericInputAnswerPublicData(answer){const{answerForms,simplify,status}=answer;return {answerForms,simplify,status}}function getNumericInputPublicWidgetOptions(options){const{answers,...publicWidgetOptions}=options;return {...publicWidgetOptions,answers:answers.map(getNumericInputAnswerPublicData)}}
278
285
 
279
- const defaultWidgetOptions$a={answers:[{value:null,status:"correct",message:"",simplify:"required",answerForms:[],strict:false,maxError:null}],size:"normal",coefficient:false,labelText:"",rightAlign:false};const numericInputWidgetLogic={name:"numeric-input",defaultWidgetOptions: defaultWidgetOptions$a,defaultAlignment:"inline-block",getPublicWidgetOptions:getNumericInputPublicWidgetOptions};
286
+ const defaultWidgetOptions$b={answers:[{value:null,status:"correct",message:"",simplify:"required",answerForms:[],strict:false,maxError:null}],size:"normal",coefficient:false,labelText:"",rightAlign:false};const numericInputWidgetLogic={name:"numeric-input",defaultWidgetOptions: defaultWidgetOptions$b,defaultAlignment:"inline-block",getPublicWidgetOptions:getNumericInputPublicWidgetOptions};
280
287
 
281
- function getOrdererPublicWidgetOptions(options){return {options:options.options,height:options.height,layout:options.layout}}
288
+ function getOrdererPublicWidgetOptions(fullOptions){const{options,height,layout}=fullOptions;return {options,height,layout}}
282
289
 
283
- const defaultWidgetOptions$9={correctOptions:[{content:"$x$"}],otherOptions:[{content:"$y$"}],height:"normal",layout:"horizontal"};const ordererWidgetLogic={name:"orderer",defaultWidgetOptions: defaultWidgetOptions$9,getPublicWidgetOptions:getOrdererPublicWidgetOptions};
290
+ const defaultWidgetOptions$a={correctOptions:[{content:"$x$"}],otherOptions:[{content:"$y$"}],height:"normal",layout:"horizontal"};const ordererWidgetLogic={name:"orderer",defaultWidgetOptions: defaultWidgetOptions$a,getPublicWidgetOptions:getOrdererPublicWidgetOptions};
284
291
 
285
- const defaultWidgetOptions$8={passageTitle:"",passageText:"",footnotes:"",showLineNumbers:true};const passageWidgetLogic={name:"passage",defaultWidgetOptions: defaultWidgetOptions$8};
292
+ const defaultWidgetOptions$9={passageTitle:"",passageText:"",footnotes:"",showLineNumbers:true};const passageWidgetLogic={name:"passage",defaultWidgetOptions: defaultWidgetOptions$9};
286
293
 
287
- const currentVersion={major:0,minor:1};const defaultWidgetOptions$7={passageNumber:1,referenceNumber:1,summaryText:""};
294
+ const currentVersion={major:0,minor:1};const defaultWidgetOptions$8={passageNumber:1,referenceNumber:1,summaryText:""};
288
295
 
289
- const passageRefWidgetLogic={name:"passageRef",version:currentVersion,defaultWidgetOptions:defaultWidgetOptions$7,defaultAlignment:"inline"};
296
+ const passageRefWidgetLogic={name:"passage-ref",version:currentVersion,defaultWidgetOptions:defaultWidgetOptions$8,defaultAlignment:"inline"};
290
297
 
291
- const defaultWidgetOptions$6={content:""};const passageRefTargetWidgetLogic={name:"passageRefTarget",defaultWidgetOptions: defaultWidgetOptions$6,defaultAlignment:"inline"};
298
+ const defaultWidgetOptions$7={content:""};const passageRefTargetWidgetLogic={name:"passage-ref-target",defaultWidgetOptions: defaultWidgetOptions$7,defaultAlignment:"inline"};
292
299
 
293
- const defaultWidgetOptions$5={url:"",description:""};const phetSimulationWidgetLogic={name:"phet-simulation",defaultWidgetOptions: defaultWidgetOptions$5};
300
+ const defaultWidgetOptions$6={url:"",description:""};const phetSimulationWidgetLogic={name:"phet-simulation",defaultWidgetOptions: defaultWidgetOptions$6};
294
301
 
295
302
  function getPlotterPublicWidgetOptions(options){const{correct:_,...publicOptions}=options;return publicOptions}
296
303
 
297
- const defaultWidgetOptions$4={scaleY:1,maxY:10,snapsPerLine:2,correct:[1],starting:[1],type:"bar",labels:["",""],categories:[""],picSize:30,picBoxHeight:36,plotDimensions:[275,200],labelInterval:1,picUrl:null};const plotterWidgetLogic={name:"plotter",defaultWidgetOptions: defaultWidgetOptions$4,getPublicWidgetOptions:getPlotterPublicWidgetOptions};
304
+ const defaultWidgetOptions$5={scaleY:1,maxY:10,snapsPerLine:2,correct:[1],starting:[1],type:"bar",labels:["",""],categories:[""],picSize:30,picBoxHeight:36,plotDimensions:[275,200],labelInterval:1,picUrl:null};const plotterWidgetLogic={name:"plotter",defaultWidgetOptions: defaultWidgetOptions$5,getPublicWidgetOptions:getPlotterPublicWidgetOptions};
298
305
 
299
- const defaultWidgetOptions$3={programID:"",height:400};const pythonProgramWidgetLogic={name:"python-program",defaultWidgetOptions: defaultWidgetOptions$3};
306
+ const defaultWidgetOptions$4={programID:"",height:400};const pythonProgramWidgetLogic={name:"python-program",defaultWidgetOptions: defaultWidgetOptions$4};
300
307
 
301
308
  function getRadioChoicePublicData(choice){const{content,isNoneOfTheAbove,widgets}=choice;return {content,isNoneOfTheAbove,widgets}}function usesNumCorrect(multipleSelect,countChoices,numCorrect){return multipleSelect&&countChoices&&numCorrect}function getRadioPublicWidgetOptions(options){const{numCorrect,choices,multipleSelect,countChoices}=options;return {...options,numCorrect:usesNumCorrect(multipleSelect,countChoices,numCorrect)?numCorrect:undefined,choices:choices.map(getRadioChoicePublicData)}}
302
309
 
@@ -304,15 +311,15 @@ const radioWidgetLogic={name:"radio",version:currentVersion$3,widgetOptionsUpgra
304
311
 
305
312
  function getSorterPublicWidgetOptions(options){const shuffledCorrect=shuffle(options.correct,Math.random,true);return {...options,correct:shuffledCorrect,isCorrectShuffled:true}}
306
313
 
307
- const defaultWidgetOptions$2={correct:["$x$","$y$","$z$"],layout:"horizontal",padding:true};const sorterWidgetLogic={name:"sorter",defaultWidgetOptions: defaultWidgetOptions$2,getPublicWidgetOptions:getSorterPublicWidgetOptions};
314
+ const defaultWidgetOptions$3={correct:["$x$","$y$","$z$"],layout:"horizontal",padding:true};const sorterWidgetLogic={name:"sorter",defaultWidgetOptions: defaultWidgetOptions$3,getPublicWidgetOptions:getSorterPublicWidgetOptions};
308
315
 
309
316
  function getTablePublicWidgetOptions(options){const{answers:_,...publicOptions}=options;return publicOptions}
310
317
 
311
- const defaultRows=4;const defaultColumns=1;const answers=new Array(defaultRows).fill(0).map(()=>new Array(defaultColumns).fill(""));const defaultWidgetOptions$1={headers:[""],rows:defaultRows,columns:defaultColumns,answers:answers};const tableWidgetLogic={name:"table",defaultWidgetOptions: defaultWidgetOptions$1,getPublicWidgetOptions:getTablePublicWidgetOptions};
318
+ const defaultRows=4;const defaultColumns=1;const answers=new Array(defaultRows).fill(0).map(()=>new Array(defaultColumns).fill(""));const defaultWidgetOptions$2={headers:[""],rows:defaultRows,columns:defaultColumns,answers:answers};const tableWidgetLogic={name:"table",defaultWidgetOptions: defaultWidgetOptions$2,getPublicWidgetOptions:getTablePublicWidgetOptions};
312
319
 
313
- const defaultWidgetOptions={location:""};const videoWidgetLogic={name:"video",defaultWidgetOptions,supportedAlignments:["block","float-left","float-right","full-width"],defaultAlignment:"block"};
320
+ const defaultWidgetOptions$1={location:""};const videoWidgetLogic={name:"video",defaultWidgetOptions: defaultWidgetOptions$1,supportedAlignments:["block","float-left","float-right","full-width"],defaultAlignment:"block"};
314
321
 
315
- const widgets={};function registerWidget(type,logic){widgets[type]=logic;}function isWidgetRegistered(type){const widgetLogic=widgets[type];return !!widgetLogic}function getCurrentVersion(type){const widgetLogic=widgets[type];return widgetLogic?.version||{major:0,minor:0}}const getPublicWidgetOptionsFunction=name=>{return widgets[name]?.getPublicWidgetOptions??(i=>i)};function getWidgetOptionsUpgrades(type){const widgetLogic=widgets[type];return widgetLogic?.widgetOptionsUpgrades||{}}function getDefaultWidgetOptions(type){const widgetLogic=widgets[type];return widgetLogic?.defaultWidgetOptions||{}}const getSupportedAlignments=type=>{const widgetLogic=widgets[type];if(!widgetLogic?.supportedAlignments?.[0]){return ["default"]}return widgetLogic?.supportedAlignments};const getDefaultAlignment=type=>{const widgetLogic=widgets[type];if(!widgetLogic?.defaultAlignment){return "block"}return widgetLogic.defaultAlignment};registerWidget("categorizer",categorizerWidgetLogic);registerWidget("cs-program",csProgramWidgetLogic);registerWidget("definition",definitionWidgetLogic);registerWidget("dropdown",dropdownWidgetLogic);registerWidget("explanation",explanationWidgetLogic);registerWidget("expression",expressionWidgetLogic);registerWidget("graded-group",gradedGroupWidgetLogic);registerWidget("graded-group-set",gradedGroupSetWidgetLogic);registerWidget("grapher",grapherWidgetLogic);registerWidget("group",groupWidgetLogic);registerWidget("iframe",iframeWidgetLogic);registerWidget("image",imageWidgetLogic);registerWidget("input-number",inputNumberWidgetLogic);registerWidget("interaction",interactionWidgetLogic);registerWidget("interactive-graph",interactiveGraphWidgetLogic);registerWidget("label-image",labelImageWidgetLogic);registerWidget("matcher",matcherWidgetLogic);registerWidget("matrix",matrixWidgetLogic);registerWidget("measurer",measurerWidgetLogic);registerWidget("number-line",numberLineWidgetLogic);registerWidget("numeric-input",numericInputWidgetLogic);registerWidget("orderer",ordererWidgetLogic);registerWidget("passage",passageWidgetLogic);registerWidget("passage-ref",passageRefWidgetLogic);registerWidget("passage-ref-target",passageRefTargetWidgetLogic);registerWidget("phet-simulation",phetSimulationWidgetLogic);registerWidget("plotter",plotterWidgetLogic);registerWidget("python-program",pythonProgramWidgetLogic);registerWidget("radio",radioWidgetLogic);registerWidget("sorter",sorterWidgetLogic);registerWidget("table",tableWidgetLogic);registerWidget("video",videoWidgetLogic);
322
+ const widgets={};function registerWidget(type,logic){widgets[type]=logic;}function isWidgetRegistered(type){const widgetLogic=widgets[type];return !!widgetLogic}function getCurrentVersion(type){const widgetLogic=widgets[type];return widgetLogic?.version||{major:0,minor:0}}const getPublicWidgetOptionsFunction=name=>{return widgets[name]?.getPublicWidgetOptions??(i=>i)};function getWidgetOptionsUpgrades(type){const widgetLogic=widgets[type];return widgetLogic?.widgetOptionsUpgrades||{}}function getDefaultWidgetOptions(type){const widgetLogic=widgets[type];return widgetLogic?.defaultWidgetOptions||{}}const getSupportedAlignments=type=>{const widgetLogic=widgets[type];if(!widgetLogic?.supportedAlignments?.[0]){return ["default"]}return widgetLogic?.supportedAlignments};const getDefaultAlignment=type=>{const widgetLogic=widgets[type];if(!widgetLogic?.defaultAlignment){return "block"}return widgetLogic.defaultAlignment};function registerCoreWidgets(){const widgets=[categorizerWidgetLogic,csProgramWidgetLogic,definitionWidgetLogic,dropdownWidgetLogic,explanationWidgetLogic,expressionWidgetLogic,gradedGroupWidgetLogic,gradedGroupSetWidgetLogic,grapherWidgetLogic,groupWidgetLogic,iframeWidgetLogic,imageWidgetLogic,inputNumberWidgetLogic,interactionWidgetLogic,interactiveGraphWidgetLogic,labelImageWidgetLogic,matcherWidgetLogic,matrixWidgetLogic,measurerWidgetLogic,numberLineWidgetLogic,numericInputWidgetLogic,ordererWidgetLogic,passageWidgetLogic,passageRefWidgetLogic,passageRefTargetWidgetLogic,phetSimulationWidgetLogic,plotterWidgetLogic,pythonProgramWidgetLogic,radioWidgetLogic,sorterWidgetLogic,tableWidgetLogic,videoWidgetLogic];widgets.forEach(w=>{registerWidget(w.name,w);});}
316
323
 
317
324
  var coreWidgetRegistry = /*#__PURE__*/Object.freeze({
318
325
  __proto__: null,
@@ -322,12 +329,19 @@ var coreWidgetRegistry = /*#__PURE__*/Object.freeze({
322
329
  getPublicWidgetOptionsFunction: getPublicWidgetOptionsFunction,
323
330
  getSupportedAlignments: getSupportedAlignments,
324
331
  getWidgetOptionsUpgrades: getWidgetOptionsUpgrades,
325
- isWidgetRegistered: isWidgetRegistered
332
+ isWidgetRegistered: isWidgetRegistered,
333
+ registerCoreWidgets: registerCoreWidgets
326
334
  });
327
335
 
328
336
  const DEFAULT_STATIC=false;const upgradeWidgetInfoToLatestVersion=oldWidgetInfo=>{const type=oldWidgetInfo.type;if(!___default.default.isString(type)){throw new PerseusError("widget type must be a string, but was: "+type,Errors.Internal)}if(!isWidgetRegistered(type)){return oldWidgetInfo}const initialVersion=oldWidgetInfo.version||{major:0,minor:0};const latestVersion=getCurrentVersion(type);if(initialVersion.major>latestVersion.major||initialVersion.major===latestVersion.major&&initialVersion.minor>latestVersion.minor){return oldWidgetInfo}let newEditorOptions=___default.default.clone(oldWidgetInfo.options)||{};const upgradePropsMap=getWidgetOptionsUpgrades(type);if(___default.default.keys(newEditorOptions).length!==0){for(let nextVersion=initialVersion.major+1;nextVersion<=latestVersion.major;nextVersion++){if(upgradePropsMap[String(nextVersion)]){newEditorOptions=upgradePropsMap[String(nextVersion)](newEditorOptions);}else {throw new PerseusError("No upgrade found for widget. Cannot render.",Errors.Internal,{metadata:{type,fromMajorVersion:nextVersion-1,toMajorVersion:nextVersion,oldWidgetInfo:JSON.stringify(oldWidgetInfo)}})}}}const defaultOptions=getDefaultWidgetOptions(type);newEditorOptions={...defaultOptions,...newEditorOptions};let alignment=oldWidgetInfo.alignment;if(alignment==null||alignment==="default"){alignment=getSupportedAlignments(type)?.[0];if(!alignment){throw new PerseusError("No default alignment found when upgrading widget",Errors.Internal,{metadata:{widgetType:type}})}}let widgetStatic=oldWidgetInfo.static;if(widgetStatic==null){widgetStatic=DEFAULT_STATIC;}return {...oldWidgetInfo,version:latestVersion,graded:oldWidgetInfo.graded!=null?oldWidgetInfo.graded:true,alignment:alignment,static:widgetStatic,options:newEditorOptions}};function getUpgradedWidgetOptions(oldWidgetOptions){return mapObject(oldWidgetOptions,(widgetInfo,widgetId)=>{if(!widgetInfo.type||!widgetInfo.alignment){const newValues={};if(!widgetInfo.type){newValues.type=widgetId.split(" ")[0];}if(!widgetInfo.alignment){newValues.alignment="default";}widgetInfo={...widgetInfo,...newValues};}return upgradeWidgetInfoToLatestVersion(widgetInfo)})}
329
337
 
330
- function splitPerseusItem(originalItem){const item=___default.default.clone(originalItem);const originalWidgets=item.question.widgets??{};const upgradedWidgets=getUpgradedWidgetOptions(originalWidgets);const splitWidgets={};for(const[id,widget]of Object.entries(upgradedWidgets)){const publicWidgetOptionsFun=getPublicWidgetOptionsFunction(widget.type);splitWidgets[id]={...widget,options:publicWidgetOptionsFun(widget.options)};}return {...item,question:{...item.question,widgets:splitWidgets},hints:[]}}
338
+ function splitPerseusRenderer(original){const clone=deepClone(original);const originalWidgets=clone.widgets??{};const upgradedWidgets=getUpgradedWidgetOptions(originalWidgets);const splitWidgets={};for(const[id,widget]of Object.entries(upgradedWidgets)){const publicWidgetOptionsFun=getPublicWidgetOptionsFunction(widget.type);splitWidgets[id]={...widget,options:publicWidgetOptionsFun(widget.options)};}return {...original,widgets:splitWidgets}}
339
+
340
+ function getGroupPublicWidgetOptions(options){return splitPerseusRenderer(options)}
341
+
342
+ const defaultWidgetOptions={content:"",widgets:{},images:{}};const groupWidgetLogic={name:"group",defaultWidgetOptions,getPublicWidgetOptions:getGroupPublicWidgetOptions};
343
+
344
+ function splitPerseusItem(original){const item=deepClone(original);return {...item,question:splitPerseusRenderer(item.question),hints:[]}}
331
345
 
332
346
  const PerseusFeatureFlags=["new-radio-widget"];
333
347
 
@@ -351,12 +365,14 @@ exports.dropdownLogic = dropdownWidgetLogic;
351
365
  exports.explanationLogic = explanationWidgetLogic;
352
366
  exports.expressionLogic = expressionWidgetLogic;
353
367
  exports.generateTestPerseusItem = generateTestPerseusItem;
368
+ exports.generateTestPerseusRenderer = generateTestPerseusRenderer;
354
369
  exports.getCSProgramPublicWidgetOptions = getCSProgramPublicWidgetOptions;
355
370
  exports.getCategorizerPublicWidgetOptions = getCategorizerPublicWidgetOptions;
356
371
  exports.getDecimalSeparator = getDecimalSeparator;
357
372
  exports.getDropdownPublicWidgetOptions = getDropdownPublicWidgetOptions;
358
373
  exports.getExpressionPublicWidgetOptions = getExpressionPublicWidgetOptions;
359
374
  exports.getGrapherPublicWidgetOptions = getGrapherPublicWidgetOptions;
375
+ exports.getGroupPublicWidgetOptions = getGroupPublicWidgetOptions;
360
376
  exports.getIFramePublicWidgetOptions = getIFramePublicWidgetOptions;
361
377
  exports.getInteractiveGraphPublicWidgetOptions = getInteractiveGraphPublicWidgetOptions;
362
378
  exports.getLabelImagePublicWidgetOptions = getLabelImagePublicWidgetOptions;
@@ -384,6 +400,8 @@ exports.interactionLogic = interactionWidgetLogic;
384
400
  exports.interactiveGraphLogic = interactiveGraphWidgetLogic;
385
401
  exports.isFailure = isFailure;
386
402
  exports.isSuccess = isSuccess;
403
+ exports.itemHasHints = itemHasHints;
404
+ exports.itemHasRationales = itemHasRationales;
387
405
  exports.labelImageLogic = labelImageWidgetLogic;
388
406
  exports.libVersion = libVersion;
389
407
  exports.lockedFigureColorNames = lockedFigureColorNames;
@@ -409,6 +427,7 @@ exports.pluck = pluck;
409
427
  exports.pythonProgramLogic = pythonProgramWidgetLogic;
410
428
  exports.radioLogic = radioWidgetLogic;
411
429
  exports.random = random;
430
+ exports.registerCoreWidgets = registerCoreWidgets;
412
431
  exports.seededRNG = seededRNG;
413
432
  exports.shuffle = shuffle;
414
433
  exports.shuffleMatcher = shuffleMatcher;