@khanacademy/perseus-core 28.3.0 → 30.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -173,8 +173,6 @@ export { default as videoLogic } from "./widgets/video";
173
173
  /** @hidden */
174
174
  export type { VideoDefaultWidgetOptions } from "./widgets/video";
175
175
  /** @hidden */
176
- export { convertInputNumberOptionsToNumericInput } from "./widgets/input-number/to-numeric-input";
177
- /** @hidden */
178
176
  export { convertGrapherOptionsToInteractiveGraph, convertGrapherUserInputToInteractiveGraph, convertInteractiveGraphUserInputToGrapher, } from "./widgets/grapher/to-interactive-graph";
179
177
  /** @hidden */
180
178
  export { applyDefaultsToWidgets, applyDefaultsToWidget, } from "./widgets/apply-defaults";
@@ -284,7 +282,7 @@ export { generateGradedGroupSetWidget } from "./utils/generators/graded-group-se
284
282
  export { generateGroupOptions, generateGroupWidget, } from "./utils/generators/group-widget-generator";
285
283
  /** @hidden */
286
284
  export { generateImageOptions, generateImageWidget, } from "./utils/generators/image-widget-generator";
287
- export { generateInputNumberOptions, generateInputNumberWidget, } from "./utils/generators/input-number-widget-generator";
285
+ export { generateInputNumberOptions, generateInputNumberWidget, generateInputNumberAnswer, } from "./utils/generators/input-number-widget-generator";
288
286
  /** @hidden */
289
287
  export { generateLabelImageOptions, generateLabelImageWidget, } from "./utils/generators/label-image-widget-generator";
290
288
  /** @hidden */
@@ -98,7 +98,7 @@ const parseLegacyButtonSet=enumeration("basic","basic+div","trig","prealgebra","
98
98
 
99
99
  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}))});
100
100
 
101
- 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 parseAnswerForms=pipeParsers(defaulted(array(parsePossiblyInvalidAnswerForm),()=>[])).then(convert(removeInvalidAnswerForms)).parser;const version2$1=object({major:constant(2),minor:number});const parseExpressionWidgetV2=parseWidgetWithVersion(version2$1,constant("expression"),object({answerForms:parseAnswerForms,functions:array(string),times:boolean,visibleLabel:optional(string),ariaLabel:optional(string),buttonSets:parseLegacyButtonSets,buttonsVisible:optional(enumeration("always","never","focused")),extraKeys:optional(array(keypadKeys))}));const version1$1=object({major:constant(1),minor:number});const parseExpressionWidgetV1=parseWidgetWithVersion(version1$1,constant("expression"),object({answerForms:parseAnswerForms,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$2(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$2).parser;
101
+ 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 parseAnswerForms=pipeParsers(defaulted(array(parsePossiblyInvalidAnswerForm),()=>[])).then(convert(removeInvalidAnswerForms)).parser;const version2$1=object({major:constant(2),minor:number});const parseExpressionWidgetV2=parseWidgetWithVersion(version2$1,constant("expression"),object({answerForms:parseAnswerForms,functions:array(string),times:boolean,visibleLabel:optional(string),ariaLabel:optional(string),buttonSets:parseLegacyButtonSets,buttonsVisible:optional(enumeration("always","never","focused")),extraKeys:optional(array(keypadKeys))}));const version1$2=object({major:constant(1),minor:number});const parseExpressionWidgetV1=parseWidgetWithVersion(version1$2,constant("expression"),object({answerForms:parseAnswerForms,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$2=optional(object({major:constant(0),minor:number}));const parseExpressionWidgetV0=parseWidgetWithVersion(version0$2,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$4(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$4).parser;
102
102
 
103
103
  const parseFreeResponseWidget=parseWidget(constant("free-response"),object({allowUnlimitedCharacters:boolean,characterLimit:number,placeholder:string,question:string,scoringCriteria:array(object({text:string}))}));
104
104
 
@@ -120,7 +120,7 @@ function emptyToZero(x){return x===""?0:x}const imageDimensionToNumber=pipeParse
120
120
 
121
121
  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)}));
122
122
 
123
- 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)}));
123
+ const booleanToZero=(rawValue,ctx)=>{if(typeof rawValue==="boolean"){return ctx.success(0)}return ctx.failure("boolean",rawValue)};const parseMathFormat$1=enumeration("integer","mixed","improper","proper","decimal","percent","pi");const parseInputNumberWidgetV1=parseWidgetWithVersion(object({major:constant(1),minor:number}),constant("input-number"),object({size:string,coefficient:boolean,labelText:optional(string),textAlign:enumeration("left","center","right"),answers:array(object({value:optional(nullable(number)),status:string,message:string,answerForms:optional(array(parseMathFormat$1)),strict:boolean,maxError:optional(nullable(number)),simplify:enumeration("required","enforced","optional")}))}));function migrateV0ToV1$3(v0){const v1Options=convertInputNumberOptionsToNumericInput(v0.options);return {...v0,version:{major:1,minor:0},options:v1Options}}const parseInputNumberWidgetV0=parseWidgetWithVersion(optional(object({major:constant(0),minor:number})),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(booleanToZero).parser,()=>0)}));function convertInputNumberOptionsToNumericInput(inputNumberOptions){return {coefficient:false,textAlign:inputNumberOptions.rightAlign?"right":"left",size:inputNumberOptions.size,answers:[{status:"correct",value:Number(inputNumberOptions.value),simplify:inputNumberOptions.simplify,message:"",maxError:getMaxError(inputNumberOptions),strict:true,answerForms:getAnswerForms(inputNumberOptions)}]}}function getMaxError(inputNumberOptions){if(!inputNumberOptions.inexact){return 0}if(inputNumberOptions.maxError==null){return undefined}return Number(inputNumberOptions.maxError)}const mathFormatsForAnswerType={number:[],decimal:["decimal"],integer:["integer"],rational:["integer","proper","improper","mixed"],improper:["integer","proper","improper"],mixed:["integer","proper","mixed"],percent:["integer","decimal","proper","improper","mixed","percent"],pi:["pi"]};function getAnswerForms(options){const value=Number(options.value);const{inexact}=options;const precision=1e10;const rounded=Math.round(value*precision)/precision;const answerType=options.answerType??"number";if(answerType==="number"&&!inexact&&!equalFloats(rounded,value)){return ["proper","improper","mixed"]}return mathFormatsForAnswerType[answerType]}function equalFloats(a,b){return Math.abs(a-b)<Math.pow(2,-42)}const parseInputNumberWidget=versionedWidgetOptions(1,parseInputNumberWidgetV1).withMigrationFrom(0,parseInputNumberWidgetV0,migrateV0ToV1$3).parser;
124
124
 
125
125
  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)}));
126
126
 
@@ -132,13 +132,13 @@ const matcherOptions=object({labels:array(string),left:array(string),right:array
132
132
 
133
133
  const numberOrStringOrNaN=union(number).or(string).or(constant(NaN)).parser;const numeric=pipeParsers(defaulted(numberOrStringOrNaN,()=>NaN)).then(stringToNumber).parser;const parseMatrixWidget=parseWidget(defaulted(constant("matrix"),()=>"matrix"),object({prefix:optional(string),suffix:optional(string),answers:defaulted(array(array(numeric)),()=>[]),cursorPosition:optional(array(number)),matrixBoardSize:array(number),static:optional(boolean)}));
134
134
 
135
- const parseMeasurerWidgetV1=parseWidgetWithVersion(object({major:constant(1),minor:number}),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)}));const parseMeasurerWidgetV0=parseWidget(constant("measurer"),object({imageTop:number,imageLeft:number,imageUrl:optional(nullable(string)),showProtractor:boolean,showRuler:boolean,rulerLabel:string,rulerTicks:number,rulerPixels:number,rulerLength:number,box:pair(number,number)}));function migrateV0ToV1$1(v0){const{imageTop,imageLeft,imageUrl,...v1Options}=v0.options;return {...v0,version:{major:1,minor:0},options:{image:{top:imageTop,left:imageLeft,url:imageUrl},...v1Options}}}const parseMeasurerWidget=versionedWidgetOptions(1,parseMeasurerWidgetV1).withMigrationFrom(0,parseMeasurerWidgetV0,migrateV0ToV1$1).parser;
135
+ const parseMeasurerWidgetV1=parseWidgetWithVersion(object({major:constant(1),minor:number}),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)}));const parseMeasurerWidgetV0=parseWidget(constant("measurer"),object({imageTop:number,imageLeft:number,imageUrl:optional(nullable(string)),showProtractor:boolean,showRuler:boolean,rulerLabel:string,rulerTicks:number,rulerPixels:number,rulerLength:number,box:pair(number,number)}));function migrateV0ToV1$2(v0){const{imageTop,imageLeft,imageUrl,...v1Options}=v0.options;return {...v0,version:{major:1,minor:0},options:{image:{top:imageTop,left:imageLeft,url:imageUrl},...v1Options}}}const parseMeasurerWidget=versionedWidgetOptions(1,parseMeasurerWidgetV1).withMigrationFrom(0,parseMeasurerWidgetV0,migrateV0ToV1$2).parser;
136
136
 
137
137
  const parseMoleculeRendererWidget=parseWidget(constant("molecule-renderer"),object({widgetId:string,rotationAngle:optional(number),smiles:optional(string)}));
138
138
 
139
139
  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:defaulted(boolean,()=>false),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:defaulted(nullable(number),()=>null),initialX:optional(nullable(number)),showTooltips:optional(boolean),static:defaulted(boolean,()=>false)}));
140
140
 
141
- const parseMathFormat=enumeration("integer","mixed","improper","proper","decimal","percent","pi");const parseSimplify=pipeParsers(union(constant(null)).or(constant(undefined)).or(boolean).or(constant("true")).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:defaulted(string,()=>""),value:optional(nullable(number)),status:string,answerForms:defaulted(optional(array(parseMathFormat)),()=>undefined),strict:defaulted(boolean,()=>false),maxError:optional(nullable(number)),simplify:parseSimplify})),labelText:optional(string),size:string,coefficient:defaulted(boolean,()=>false),rightAlign:optional(boolean)}));
141
+ const parseMathFormat=enumeration("integer","mixed","improper","proper","decimal","percent","pi");const parseSimplify=pipeParsers(union(constant(null)).or(constant(undefined)).or(boolean).or(constant("true")).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 parseNumericInputAnswers=array(object({message:defaulted(string,()=>""),value:optional(nullable(number)),status:string,answerForms:defaulted(optional(array(parseMathFormat)),()=>undefined),strict:defaulted(boolean,()=>false),maxError:optional(nullable(number)),simplify:parseSimplify}));const version1$1=object({major:constant(1),minor:number});const parseNumericInputWidgetV1=parseWidgetWithVersion(version1$1,constant("numeric-input"),object({answers:parseNumericInputAnswers,labelText:optional(string),size:string,coefficient:defaulted(boolean,()=>false),textAlign:defaulted(enumeration("left","right","center"),()=>"left")}));const version0$1=optional(object({major:constant(0),minor:number}));const parseNumericInputWidgetV0=parseWidgetWithVersion(version0$1,constant("numeric-input"),object({answers:parseNumericInputAnswers,labelText:optional(string),size:string,coefficient:defaulted(boolean,()=>false),rightAlign:optional(boolean)}));function migrateV0ToV1$1(widget){const{rightAlign,...options}=widget.options;return {...widget,version:{major:1,minor:0},options:{...options,textAlign:rightAlign?"right":"left"}}}const parseNumericInputWidget=versionedWidgetOptions(1,parseNumericInputWidgetV1).withMigrationFrom(0,parseNumericInputWidgetV0,migrateV0ToV1$1).parser;
142
142
 
143
143
  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:defaulted(array(parseRenderer),()=>[]),otherOptions:defaulted(array(parseRenderer),()=>[]),height:pipeParsers(enumeration("normal","auto","large")).then(largeToAuto).parser,layout:defaulted(enumeration("horizontal","vertical"),()=>"horizontal")}));
144
144