@khanacademy/perseus-core 23.1.0 → 23.3.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 (41) hide show
  1. package/dist/data-schema.d.ts +1 -0
  2. package/dist/es/index.item-splitting.js +6 -6
  3. package/dist/es/index.item-splitting.js.map +1 -1
  4. package/dist/es/index.js +10 -10
  5. package/dist/es/index.js.map +1 -1
  6. package/dist/feature-flags.d.ts +1 -1
  7. package/dist/index.d.ts +19 -19
  8. package/dist/index.item-splitting.js +6 -6
  9. package/dist/index.item-splitting.js.map +1 -1
  10. package/dist/index.js +10 -10
  11. package/dist/index.js.map +1 -1
  12. package/dist/parse-perseus-json/general-purpose-parsers/object.d.ts +35 -0
  13. package/dist/parse-perseus-json/perseus-parsers/categorizer-widget.d.ts +0 -5
  14. package/dist/parse-perseus-json/perseus-parsers/image-widget.d.ts +1 -0
  15. package/dist/parse-perseus-json/perseus-parsers/interactive-graph-widget.d.ts +0 -1
  16. package/dist/parse-perseus-json/perseus-parsers/perseus-answer-area.d.ts +2 -2
  17. package/dist/parse-perseus-json/perseus-parsers/radio-widget.d.ts +1 -0
  18. package/dist/parse-perseus-json/perseus-parsers/widget-id-components.d.ts +0 -15
  19. package/dist/widgets/categorizer/categorizer-util.d.ts +1 -2
  20. package/dist/widgets/cs-program/cs-program-util.d.ts +1 -1
  21. package/dist/widgets/dropdown/dropdown-util.d.ts +1 -2
  22. package/dist/widgets/expression/expression-util.d.ts +1 -2
  23. package/dist/widgets/free-response/free-response-util.d.ts +2 -2
  24. package/dist/widgets/grapher/grapher-util.d.ts +1 -1
  25. package/dist/widgets/group/group-util.d.ts +2 -2
  26. package/dist/widgets/iframe/iframe-util.d.ts +1 -1
  27. package/dist/widgets/image/index.d.ts +1 -1
  28. package/dist/widgets/input-number/input-number-util.d.ts +2 -2
  29. package/dist/widgets/interactive-graph/interactive-graph-util.d.ts +1 -1
  30. package/dist/widgets/label-image/label-image-util.d.ts +1 -1
  31. package/dist/widgets/logic-export.types.d.ts +20 -20
  32. package/dist/widgets/matcher/matcher-util.d.ts +1 -2
  33. package/dist/widgets/matrix/matrix-util.d.ts +1 -1
  34. package/dist/widgets/number-line/number-line-util.d.ts +1 -1
  35. package/dist/widgets/numeric-input/numeric-input-util.d.ts +2 -2
  36. package/dist/widgets/orderer/orderer-util.d.ts +1 -2
  37. package/dist/widgets/plotter/plotter-util.d.ts +1 -2
  38. package/dist/widgets/radio/radio-util.d.ts +2 -2
  39. package/dist/widgets/sorter/sorter-util.d.ts +1 -2
  40. package/dist/widgets/table/table-util.d.ts +1 -1
  41. package/package.json +2 -2
package/dist/es/index.js CHANGED
@@ -9,7 +9,7 @@ const APPROXIMATED_PI_ERROR="APPROXIMATED_PI_ERROR";const CHOOSE_CORRECT_NUM_ERR
9
9
 
10
10
  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}
11
11
 
12
- 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]??"."}};
12
+ const getDecimalSeparator=locale=>{switch(locale){case "ka":return ",";default:const parts=new Intl.NumberFormat(locale).formatToParts(1.1);const decimalPart=parts.find(part=>part.type==="decimal");return decimalPart?.value??"."}};
13
13
 
14
14
  const localesThatUseColon=["az","bg","cs","da","de","hu","hy","id","it","ky","lt","lv","nb","nl","pl","pt-pt","ro","sv","uk","vi","ro","ru"];const getDivideSymbol=locale=>{if(localesThatUseColon.includes(locale)){return ":"}return "/"};const getDivideSymbolForTex=locale=>{const divideSymbol=getDivideSymbol(locale);return divideSymbol==="/"?"\\div":divideSymbol};
15
15
 
@@ -64,7 +64,7 @@ function nullable(parseValue){return (rawValue,ctx)=>{if(rawValue===null){return
64
64
 
65
65
  const number=(rawValue,ctx)=>{if(typeof rawValue==="number"){return ctx.success(rawValue)}return ctx.failure("number",rawValue)};
66
66
 
67
- 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)}}
67
+ function objectWithAllPropertiesRequired(schema){return object(schema)}function object(schema){return objectParserWithInitializer(()=>({}),schema)}function looseObject(schema){return objectParserWithInitializer(rawValue=>({...rawValue}),schema)}function objectParserWithInitializer(initializeParsedValue,schema){return (rawValue,ctx)=>{if(!isPlainObject(rawValue)){return ctx.failure("object",rawValue)}const parsed=initializeParsedValue(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){parsed[prop]=result.value;}}else {mismatches.push(...result.detail);}}if(mismatches.length>0){return failure(mismatches)}return ctx.success(parsed)}}
68
68
 
69
69
  function optional(parseValue){return (rawValue,ctx)=>{if(rawValue===undefined){return ctx.success(rawValue)}return parseValue(rawValue,ctx)}}
70
70
 
@@ -86,7 +86,7 @@ const parseImages=defaulted(record(string,parsePerseusImageDetail),()=>({}));
86
86
 
87
87
  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})}
88
88
 
89
- const parseCategorizerWidget=parseWidget(constant("categorizer"),object({items:array(string),categories:array(string),randomizeItems:defaulted(boolean,()=>false),static:defaulted(boolean,()=>false),values:defaulted(array(defaulted(number,()=>0)),()=>[]),highlightLint:optional(boolean),linterContext:optional(object({contentType:string,paths:array(string),stack:array(string)}))}));
89
+ const parseCategorizerWidget=parseWidget(constant("categorizer"),object({items:array(string),categories:array(string),randomizeItems:defaulted(boolean,()=>false),static:defaulted(boolean,()=>false),values:defaulted(array(defaulted(number,()=>0)),()=>[]),highlightLint:optional(boolean)}));
90
90
 
91
91
  const parseCSProgramWidget=parseWidget(constant("cs-program"),object({programID:string,programType:any,settings:array(object({name:string,value:string})),showEditor:boolean,showButtons:boolean,height:number,static:defaulted(boolean,()=>false)}));
92
92
 
@@ -126,7 +126,7 @@ const stringToNumber=(rawValue,ctx)=>{if(typeof rawValue==="number"){return ctx.
126
126
 
127
127
  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});
128
128
 
129
- const pairOfNumbers$2=pair(number,number);const parseImageWidget=parseWidget(constant("image"),object({title:optional(string),caption:optional(string),alt:optional(string),longDescription:optional(string),decorative:optional(boolean),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)}));
129
+ const pairOfNumbers$2=pair(number,number);const parseImageWidget=parseWidget(constant("image"),object({title:optional(string),caption:optional(string),alt:optional(string),longDescription:optional(string),decorative:optional(boolean),backgroundImage:parsePerseusImageBackground,scale:optional(number),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)}));
130
130
 
131
131
  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:defaulted(union(number).or(string).or(booleanToString).parser,()=>0),customKeypad:optional(boolean)}));
132
132
 
@@ -160,7 +160,7 @@ const parsePythonProgramWidget=parseWidget(constant("python-program"),object({pr
160
160
 
161
161
  function deriveNumCorrect(choices){return choices.filter(c=>c.correct).length}
162
162
 
163
- const parseWidgetsMapOrUndefined=defaulted(optional((rawVal,ctx)=>parseWidgetsMap(rawVal,ctx)),()=>undefined);function getDefaultOptions(){return {choices:[{content:"",id:generateChoiceId(0)},{content:"",id:generateChoiceId(1)},{content:"",id:generateChoiceId(2)},{content:"",id:generateChoiceId(3)}]}}const parseOnePerLine=defaulted(optional(boolean),()=>undefined);const parseNoneOfTheAbove=defaulted(optional(constant(false)),()=>undefined);function generateChoiceId(index){return `radio-choice-${index}`}const version3=optional(object({major:constant(3),minor:number}));const parseRadioWidgetV3=parseWidgetWithVersion(version3,constant("radio"),object({numCorrect:optional(number),choices:arrayWithIndex(index=>object({content:defaulted(string,()=>""),rationale:optional(string),correct:optional(boolean),isNoneOfTheAbove:optional(boolean),id:defaultedNonEmptyString(()=>generateChoiceId(index))})),hasNoneOfTheAbove:optional(boolean),countChoices:optional(boolean),randomize:optional(boolean),multipleSelect:optional(boolean),deselectEnabled:optional(boolean)}));const version2=optional(object({major:constant(2),minor:number}));const parseRadioWidgetV2=parseWidgetWithVersion(version2,constant("radio"),defaulted(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:parseOnePerLine,displayCount:optional(any),noneOfTheAbove:parseNoneOfTheAbove}),getDefaultOptions));const version1=optional(object({major:constant(1),minor:number}));const parseRadioWidgetV1=parseWidgetWithVersion(version1,constant("radio"),defaulted(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:parseOnePerLine,displayCount:optional(any),noneOfTheAbove:parseNoneOfTheAbove}),getDefaultOptions));const version0=optional(object({major:constant(0),minor:number}));const parseRadioWidgetV0=parseWidgetWithVersion(version0,constant("radio"),defaulted(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:parseOnePerLine,displayCount:optional(any),noneOfTheAbove:parseNoneOfTheAbove}),getDefaultOptions));function migrateV2toV3(widget){const{options}=widget;return {...widget,version:{major:3,minor:0},options:{numCorrect:options.numCorrect,hasNoneOfTheAbove:options.hasNoneOfTheAbove,countChoices:options.countChoices,randomize:options.randomize,multipleSelect:options.multipleSelect,deselectEnabled:options.deselectEnabled,choices:options.choices.map((choice,index)=>({content:choice.content,rationale:choice["rationale"]||choice.clue,correct:choice.correct,isNoneOfTheAbove:choice.isNoneOfTheAbove,id:generateChoiceId(index)}))}}}function migrateV1ToV2(widget){const{options}=widget;return {...widget,version:{major:2,minor:0},options:{...options,numCorrect:deriveNumCorrect(options.choices)}}}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(3,parseRadioWidgetV3).withMigrationFrom(2,parseRadioWidgetV2,migrateV2toV3).withMigrationFrom(1,parseRadioWidgetV1,migrateV1ToV2).withMigrationFrom(0,parseRadioWidgetV0,migrateV0ToV1).parser;
163
+ const parseWidgetsMapOrUndefined=defaulted(optional((rawVal,ctx)=>parseWidgetsMap(rawVal,ctx)),()=>undefined);function getDefaultOptions(){return {choices:[{content:"",id:generateChoiceId(0)},{content:"",id:generateChoiceId(1)},{content:"",id:generateChoiceId(2)},{content:"",id:generateChoiceId(3)}]}}const parseOnePerLine=defaulted(optional(boolean),()=>undefined);const parseNoneOfTheAbove=defaulted(optional(constant(false)),()=>undefined);function generateChoiceId(index){return `radio-choice-${index}`}const version3=optional(object({major:constant(3),minor:number}));const parseRadioWidgetV3=parseWidgetWithVersion(version3,constant("radio"),object({numCorrect:optional(number),choices:arrayWithIndex(index=>object({content:defaulted(string,()=>""),rationale:optional(string),correct:optional(boolean),isNoneOfTheAbove:optional(boolean),id:defaultedNonEmptyString(()=>generateChoiceId(index))})),hasNoneOfTheAbove:optional(boolean),countChoices:optional(boolean),randomize:optional(boolean),multipleSelect:optional(boolean),deselectEnabled:optional(boolean)}));const version2=optional(object({major:constant(2),minor:number}));const parseRadioWidgetV2=parseWidgetWithVersion(version2,constant("radio"),defaulted(object({numCorrect:optional(number),choices:array(object({content:defaulted(string,()=>""),clue:optional(string),correct:optional(boolean),isNoneOfTheAbove:optional(boolean),rationale:optional(string),widgets:parseWidgetsMapOrUndefined})),hasNoneOfTheAbove:optional(boolean),countChoices:optional(boolean),randomize:optional(boolean),multipleSelect:optional(boolean),deselectEnabled:optional(boolean),onePerLine:parseOnePerLine,displayCount:optional(any),noneOfTheAbove:parseNoneOfTheAbove}),getDefaultOptions));const version1=optional(object({major:constant(1),minor:number}));const parseRadioWidgetV1=parseWidgetWithVersion(version1,constant("radio"),defaulted(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:parseOnePerLine,displayCount:optional(any),noneOfTheAbove:parseNoneOfTheAbove}),getDefaultOptions));const version0=optional(object({major:constant(0),minor:number}));const parseRadioWidgetV0=parseWidgetWithVersion(version0,constant("radio"),defaulted(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:parseOnePerLine,displayCount:optional(any),noneOfTheAbove:parseNoneOfTheAbove}),getDefaultOptions));function migrateV2toV3(widget){const{options}=widget;return {...widget,version:{major:3,minor:0},options:{numCorrect:options.numCorrect,hasNoneOfTheAbove:options.hasNoneOfTheAbove,countChoices:options.countChoices,randomize:options.randomize,multipleSelect:options.multipleSelect,deselectEnabled:options.deselectEnabled,choices:options.choices.map((choice,index)=>({content:choice.content,rationale:choice["rationale"]||choice.clue,correct:choice.correct,isNoneOfTheAbove:choice.isNoneOfTheAbove,id:generateChoiceId(index)}))}}}function migrateV1ToV2(widget){const{options}=widget;return {...widget,version:{major:2,minor:0},options:{...options,numCorrect:deriveNumCorrect(options.choices)}}}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(3,parseRadioWidgetV3).withMigrationFrom(2,parseRadioWidgetV2,migrateV2toV3).withMigrationFrom(1,parseRadioWidgetV1,migrateV1ToV2).withMigrationFrom(0,parseRadioWidgetV0,migrateV0ToV1).parser;
164
164
 
165
165
  const parseSorterWidget=parseWidget(constant("sorter"),object({correct:array(string),padding:boolean,layout:enumeration("horizontal","vertical")}));
166
166
 
@@ -170,7 +170,7 @@ const parseVideoWidget=parseWidget(constant("video"),object({location:string,sta
170
170
 
171
171
  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);
172
172
 
173
- 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 "free-response":return parseAndAssign(`free-response ${n}`,parseFreeResponseWidget);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 "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);case "passage":return parseAndAssign(`passage ${n}`,parseDeprecatedWidget);case "passage-ref":return parseAndAssign(`passage-ref ${n}`,parseDeprecatedWidget);case "passage-ref-target":return parseAndAssign(`passage-ref-target ${n}`,parseDeprecatedWidget);default:return parseAndAssign(`${type} ${n}`,parseWidget(constant(type),any))}};const parseDeprecatedWidget=parseWidget((_,ctx)=>ctx.success("deprecated-standin"),object({}));
173
+ 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 "free-response":return parseAndAssign(`free-response ${n}`,parseFreeResponseWidget);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 "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);case "passage":return parseAndAssign(`passage ${n}`,parseDeprecatedWidget);case "passage-ref":return parseAndAssign(`passage-ref ${n}`,parseDeprecatedWidget);case "passage-ref-target":return parseAndAssign(`passage-ref-target ${n}`,parseDeprecatedWidget);default:return parseAndAssign(`${type} ${n}`,parseWidget(constant(type),any))}};const parseDeprecatedWidget=parseWidget((_,ctx)=>ctx.success("deprecated-standin"),looseObject({}));
174
174
 
175
175
  const parsePerseusRenderer=defaulted(object({content:defaulted(string,()=>""),widgets:defaulted((rawVal,ctx)=>parseWidgetsMap(rawVal,ctx),()=>({})),images:parseImages,metadata:any}),()=>({content:"",widgets:{},images:{}}));
176
176
 
@@ -178,7 +178,7 @@ const parsePerseusArticle=union(parsePerseusRenderer).or(array(parsePerseusRende
178
178
 
179
179
  const parseHint=object({replace:defaulted(optional(boolean),()=>undefined),placeholder:defaulted(optional(boolean),()=>undefined),content:string,widgets:defaulted(parseWidgetsMap,()=>({})),images:parseImages,metadata:any});
180
180
 
181
- const parsePerseusAnswerArea=pipeParsers(defaulted(object({}),()=>({}))).then(convert(toAnswerArea)).parser;function toAnswerArea(raw){return {calculator:!!raw.calculator,financialCalculatorMonthlyPayment:!!raw.financialCalculatorMonthlyPayment,financialCalculatorTotalAmount:!!raw.financialCalculatorTotalAmount,financialCalculatorTimeToPayOff:!!raw.financialCalculatorTimeToPayOff,periodicTable:!!raw.periodicTable,periodicTableWithKey:!!raw.periodicTableWithKey}}
181
+ const booleanOrFalse=defaulted(boolean,()=>false);const parsePerseusAnswerArea=defaulted(object({calculator:booleanOrFalse,financialCalculatorMonthlyPayment:booleanOrFalse,financialCalculatorTotalAmount:booleanOrFalse,financialCalculatorTimeToPayOff:booleanOrFalse,periodicTable:booleanOrFalse,periodicTableWithKey:booleanOrFalse}),()=>({calculator:false,financialCalculatorMonthlyPayment:false,financialCalculatorTotalAmount:false,financialCalculatorTimeToPayOff:false,periodicTable:false,periodicTableWithKey:false}));
182
182
 
183
183
  const parsePerseusItem=object({question:parsePerseusRenderer,hints:defaulted(array(parseHint),()=>[]),answerArea:parsePerseusAnswerArea});
184
184
 
@@ -226,7 +226,7 @@ const parseUserInputMap=(rawValue,ctx)=>{if(!isPlainObject(rawValue)){return ctx
226
226
 
227
227
  function parseAndMigratePerseusItem(data){const object=typeof data==="string"?JSON.parse(data):data;const result=parse(object,parsePerseusItem);if(isFailure(result)){return failure({message:result.detail,invalidObject:object})}return result}function parseAndMigratePerseusArticle(data){const object=typeof data==="string"?JSON.parse(data):data;const result=parse(object,parsePerseusArticle);if(isFailure(result)){return failure({message:result.detail,invalidObject:object})}return result}function parseAndMigrateUserInputMap(data){const object=typeof data==="string"?JSON.parse(data):data;const result=parse(object,parseUserInputMap);if(isFailure(result)){return failure({message:result.detail,invalidObject:object})}return result}
228
228
 
229
- const libName="@khanacademy/perseus-core";const libVersion="23.1.0";addLibraryVersionToPerseusDebug(libName,libVersion);
229
+ const libName="@khanacademy/perseus-core";const libVersion="23.3.0";addLibraryVersionToPerseusDebug(libName,libVersion);
230
230
 
231
231
  const Errors=Object.freeze({Unknown:"Unknown",Internal:"Internal",InvalidInput:"InvalidInput",NotAllowed:"NotAllowed",TransientService:"TransientService",Service:"Service"});
232
232
 
@@ -274,7 +274,7 @@ function getIFramePublicWidgetOptions(options){return options}
274
274
 
275
275
  const defaultWidgetOptions$j={url:"",settings:[{name:"",value:""}],width:"400",height:"400",allowFullScreen:false,allowTopNavigation:false};const iframeWidgetLogic={name:"iframe",defaultWidgetOptions: defaultWidgetOptions$j,getPublicWidgetOptions:getIFramePublicWidgetOptions,accessible:false};
276
276
 
277
- const defaultWidgetOptions$i={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$i,supportedAlignments:["block","wrap-left","wrap-right","full-width"],defaultAlignment:"block",accessible:widgetOptions=>{const imageOptions=widgetOptions;const bgImage=imageOptions.backgroundImage;const hasBackgroundImage=bgImage.url!=null;const hasAltText=!!imageOptions.alt;const isDecorative=imageOptions.decorative===true;return hasBackgroundImage&&(hasAltText||isDecorative)}};
277
+ const defaultWidgetOptions$i={title:"",range:[[0,10],[0,10]],box:[400,400],backgroundImage:{url:null,width:0,height:0},scale:1,labels:[],alt:"",caption:""};const imageWidgetLogic={name:"image",defaultWidgetOptions: defaultWidgetOptions$i,supportedAlignments:["block","wrap-left","wrap-right","full-width"],defaultAlignment:"block",accessible:widgetOptions=>{const imageOptions=widgetOptions;const bgImage=imageOptions.backgroundImage;const hasBackgroundImage=bgImage.url!=null;const hasAltText=!!imageOptions.alt;const isDecorative=imageOptions.decorative===true;return hasBackgroundImage&&(hasAltText||isDecorative)}};
278
278
 
279
279
  function getInputNumberPublicWidgetOptions(options){const{value:_,...publicWidgetOptions}=options;return publicWidgetOptions}
280
280
 
@@ -369,7 +369,7 @@ const DEFAULT_COLOR="grayH";function getDefaultFigureForType(type){switch(type){
369
369
 
370
370
  function splitPerseusItem(original){const item=deepClone(original);return {...item,question:splitPerseusRenderer(item.question),hints:original.hints.map(()=>({content:"",widgets:{},images:{},placeholder:true}))}}function splitPerseusItemJSON(data){const parseResult=parseAndMigratePerseusItem(data);if(isFailure(parseResult)){throw new SyntaxError(parseResult.detail.message)}const item=parseResult.value;return JSON.stringify(splitPerseusItem(item))}
371
371
 
372
- const PerseusFeatureFlags=["new-radio-widget","image-widget-upgrade-alignment","image-widget-upgrade-gif-controls"];function isFeatureOn(props,flag){return props.apiOptions?.flags?.[flag]??false}
372
+ const PerseusFeatureFlags=["new-radio-widget","image-widget-upgrade-alignment","image-widget-upgrade-gif-controls","image-widget-upgrade-scale"];function isFeatureOn(props,flag){return props.apiOptions?.flags?.[flag]??false}
373
373
 
374
374
  const noop=function(){};const deepCallbackFor=function(contentCallback,widgetCallback,optionsCallback){const deepCallback=function(widgetInfo,widgetId){const newWidgetInfo=traverseChildWidgets$1(widgetInfo,rendererOptions=>{return traverseRenderer(rendererOptions,contentCallback,deepCallback,optionsCallback)});const userWidgetInfo=widgetCallback(newWidgetInfo,widgetId);if(userWidgetInfo!==undefined){return userWidgetInfo}return newWidgetInfo};return deepCallback};const traverseRenderer=function(rendererOptions,contentCallback,deepWidgetCallback,optionsCallback){let newContent=rendererOptions.content;if(rendererOptions.content!=null){const modifiedContent=contentCallback(rendererOptions.content);if(modifiedContent!==undefined){newContent=modifiedContent;}}const newWidgets=mapObject(rendererOptions.widgets||{},function(widgetInfo,widgetId){if(widgetInfo==null||widgetInfo.type==null){return widgetInfo}return deepWidgetCallback(widgetInfo,widgetId)});const newOptions=_.extend({},rendererOptions,{content:newContent,widgets:newWidgets});const userOptions=optionsCallback(newOptions);if(userOptions!==undefined){return userOptions}return newOptions};const traverse=function(rendererOptions,contentCallback,widgetCallback,optionsCallback){contentCallback=contentCallback||noop;widgetCallback=widgetCallback||noop;optionsCallback=optionsCallback||noop;return traverseRenderer(rendererOptions,contentCallback,deepCallbackFor(contentCallback,widgetCallback,optionsCallback),optionsCallback)};
375
375