@khanacademy/perseus 65.3.7 → 65.5.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 (59) hide show
  1. package/dist/es/index.js +62 -60
  2. package/dist/es/index.js.map +1 -1
  3. package/dist/hint-renderer.d.ts +6 -0
  4. package/dist/hints-renderer.d.ts +6 -0
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.js +61 -59
  7. package/dist/index.js.map +1 -1
  8. package/dist/mixins/changeable.d.ts +14 -0
  9. package/dist/mixins/widget-prop-denylist.d.ts +5 -0
  10. package/dist/renderer.d.ts +23 -11
  11. package/dist/server-item-renderer.d.ts +10 -0
  12. package/dist/types.d.ts +47 -13
  13. package/dist/widget-ai-utils/number-line/number-line-ai-utils.d.ts +1 -1
  14. package/dist/widget-type-utils.d.ts +1 -1
  15. package/dist/widgets/categorizer/categorizer.d.ts +28 -10
  16. package/dist/widgets/cs-program/cs-program.d.ts +9 -5
  17. package/dist/widgets/definition/definition.d.ts +0 -1
  18. package/dist/widgets/dropdown/dropdown.d.ts +22 -9
  19. package/dist/widgets/explanation/explanation.d.ts +0 -1
  20. package/dist/widgets/expression/expression.d.ts +107 -11
  21. package/dist/widgets/free-response/free-response.d.ts +9 -7
  22. package/dist/widgets/graded-group-set/graded-group-set.d.ts +1 -3
  23. package/dist/widgets/grapher/grapher.d.ts +139 -14
  24. package/dist/widgets/grapher/util.d.ts +2 -2
  25. package/dist/widgets/group/group.d.ts +6 -0
  26. package/dist/widgets/iframe/iframe.d.ts +10 -8
  27. package/dist/widgets/image/image.d.ts +1 -2
  28. package/dist/widgets/input-number/input-number.d.ts +17 -6
  29. package/dist/widgets/interaction/interaction.d.ts +0 -2
  30. package/dist/widgets/interactive-graph.d.ts +101 -4
  31. package/dist/widgets/interactive-graphs/backgrounds/axis-labels.d.ts +0 -10
  32. package/dist/widgets/interactive-graphs/backgrounds/axis-ticks.d.ts +0 -5
  33. package/dist/widgets/interactive-graphs/backgrounds/utils.d.ts +20 -0
  34. package/dist/widgets/interactive-graphs/mafs-graph.d.ts +0 -5
  35. package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +1 -1
  36. package/dist/widgets/interactive-graphs/types.d.ts +2 -2
  37. package/dist/widgets/interactive-graphs/utils.d.ts +4 -0
  38. package/dist/widgets/label-image/label-image.d.ts +28 -8
  39. package/dist/widgets/matcher/matcher.d.ts +19 -7
  40. package/dist/widgets/matrix/matrix.d.ts +30 -18
  41. package/dist/widgets/mock-widgets/mock-widget.d.ts +89 -8
  42. package/dist/widgets/number-line/number-line.d.ts +108 -13
  43. package/dist/widgets/numeric-input/numeric-input.class.d.ts +93 -4
  44. package/dist/widgets/numeric-input/numeric-input.d.ts +1 -2
  45. package/dist/widgets/orderer/orderer.d.ts +15 -8
  46. package/dist/widgets/passage-ref/passage-ref.d.ts +1 -2
  47. package/dist/widgets/passage-ref-target/passage-ref-target.d.ts +1 -3
  48. package/dist/widgets/phet-simulation/phet-simulation.d.ts +1 -2
  49. package/dist/widgets/plotter/plotter.d.ts +105 -6
  50. package/dist/widgets/radio/radio-component.d.ts +5 -2
  51. package/dist/widgets/radio/radio.class.new.d.ts +5 -2
  52. package/dist/widgets/radio/radio.d.ts +8 -1
  53. package/dist/widgets/radio/radio.ff.d.ts +93 -3
  54. package/dist/widgets/radio/radio.new.d.ts +2 -2
  55. package/dist/widgets/radio/util.d.ts +3 -0
  56. package/dist/widgets/sorter/sorter.d.ts +26 -9
  57. package/dist/widgets/table/table.d.ts +88 -5
  58. package/dist/widgets/video/video.d.ts +0 -1
  59. package/package.json +45 -45
package/dist/es/index.js CHANGED
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
  import React__default, { useContext, forwardRef, useId, useImperativeHandle, useRef, useState, useEffect, createElement, createContext, useLayoutEffect } from 'react';
3
3
  import * as KAS from '@khanacademy/kas';
4
4
  import { MathInputI18nContext, DesktopKeypad, getCursorContext, CursorContext, getKeyTranslator, createMathField, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, keypadElementPropType } from '@khanacademy/math-input';
5
- import { PerseusError, Errors, expressionLogic, getDecimalSeparator, isLabeledSVG, getDataUrl, getSvgUrl, getBaseUrl, getRealImageUrl, getImageSizeModern, approximateDeepEqual, pluck, Registry, CoreWidgetRegistry, applyDefaultsToWidgets, mapObject, getDefaultAnswerArea, splitPerseusItem, passageRefLogic, usesNumCorrect, radioLogic, shuffle, random, GrapherUtil as GrapherUtil$1, deepClone, lockedFigureColors, lockedFigureFillStyles, approximateEqual, shuffleMatcher, getMatrixSize, measurerLogic, shuffleSorter, traverse, applyDefaultsToWidget, getWidgetIdsFromContentByType } from '@khanacademy/perseus-core';
5
+ import { expressionLogic, getDecimalSeparator, PerseusError, Errors, isLabeledSVG, getDataUrl, getSvgUrl, getBaseUrl, getRealImageUrl, getImageSizeModern, approximateDeepEqual, pluck, Registry, CoreWidgetRegistry, getWidgetIdsFromContentByType, applyDefaultsToWidgets, mapObject, getDefaultAnswerArea, splitPerseusItem, passageRefLogic, usesNumCorrect, deepClone, radioLogic, shuffle, random, GrapherUtil as GrapherUtil$1, lockedFigureColors, lockedFigureFillStyles, approximateEqual, shuffleMatcher, getMatrixSize, measurerLogic, shuffleSorter, traverse, applyDefaultsToWidget } from '@khanacademy/perseus-core';
6
6
  export { isFailure, isSuccess, parseAndMigratePerseusArticle, parseAndMigratePerseusItem, parsePerseusItem } from '@khanacademy/perseus-core';
7
7
  import * as PerseusLinter from '@khanacademy/perseus-linter';
8
8
  import { linterContextDefault, Rule } from '@khanacademy/perseus-linter';
@@ -1416,16 +1416,6 @@ var dependencies = /*#__PURE__*/Object.freeze({
1416
1416
  useDependencies: useDependencies
1417
1417
  });
1418
1418
 
1419
- const denylist=["key","ref","containerSizeClass","widgetId","onChange","problemNum","apiOptions","widgetIsOpen","questionCompleted","findWidgets","onRemove","id","onBlur","onFocus","trackInteraction","keypadElement"];
1420
-
1421
- const USAGE="Usage:\n"+" this.change({propName: 5}, callback);\n"+' this.change("propName", 5, callback);\n'+' this.change("propName")';const _changeMultiple=function(component,newProps,callback){const currProps=_.omit(component.props,denylist);const nextProps=_.extend(currProps,newProps);component.props.onChange(nextProps,callback);};const _changeSingle=function(component,propName,value,callback){if(value===undefined){return _.partial(_changeSingle,component,propName)}const newProps={};newProps[propName]=value;_changeMultiple(component,newProps,callback);};const change=function(newPropsOrSinglePropName,propValue,callback){if(_.isObject(newPropsOrSinglePropName)&&callback===undefined){callback=propValue;return _changeMultiple(this,newPropsOrSinglePropName,callback)}if(typeof newPropsOrSinglePropName==="string"){return _changeSingle(this,newPropsOrSinglePropName,propValue,callback)}throw new PerseusError("Invalid types sent to this.change(): "+_.toArray(arguments).join()+"\n"+USAGE,Errors.Internal)};const propTypes={onChange:PropTypes.func.isRequired};
1422
-
1423
- var changeable = /*#__PURE__*/Object.freeze({
1424
- __proto__: null,
1425
- change: change,
1426
- propTypes: propTypes
1427
- });
1428
-
1429
1419
  const textWidthCache={};function getTextWidth(text){if(!textWidthCache[text]){const $test=$("<span>").text(text).appendTo("body");textWidthCache[text]=$test.width()+5;$test.remove();}return textWidthCache[text]}class TextListEditor extends React.Component{UNSAFE_componentWillReceiveProps(nextProps){this.setState({items:nextProps.options.concat("")});}render(){const className=["perseus-text-list-editor","perseus-clearfix","layout-"+this.props.layout].join(" ");const inputs=_.map(this.state.items,function(item,i){return jsxRuntimeExports.jsx("li",{children:jsxRuntimeExports.jsx("input",{ref:"input_"+i,type:"text",value:item,onChange:this.onChange.bind(this,i),onKeyDown:this.onKeyDown.bind(this,i),style:{width:getTextWidth(item)}})},i)},this);return jsxRuntimeExports.jsx("ul",{className:className,children:inputs})}constructor(...args){super(...args),this.state={items:this.props.options.concat("")},this.onChange=(index,event)=>{let items=_.clone(this.state.items);items[index]=event.target.value;if(index===items.length-1){items=items.concat("");}this.setState({items:items});this.props.onChange(_.compact(items));},this.onKeyDown=(index,event)=>{const which=event.nativeEvent.keyCode;if(which===8&&this.state.items[index]===""){event.preventDefault();const items=_.clone(this.state.items);const focusIndex=index===0?0:index-1;if(index===items.length-1&&(index===0||items[focusIndex]!=="")){ReactDOM__default.findDOMNode(this.refs["input_"+focusIndex]).focus();}else {items.splice(index,1);this.setState({items:items},function(){ReactDOM__default.findDOMNode(this.refs["input_"+focusIndex]).focus();});}}else if(which===8&&this.state.items[index].length===1&&index===this.state.items.length-2){event.preventDefault();const items=_.clone(this.state.items);items.splice(index,1);this.setState({items:items});this.props.onChange(_.compact(items));}else if(which===13){event.preventDefault();const items=_.clone(this.state.items);const focusIndex=index+1;if(index===items.length-2){ReactDOM__default.findDOMNode(this.refs["input_"+focusIndex]).focus();}else {items.splice(focusIndex,0,"");this.setState({items:items},function(){ReactDOM__default.findDOMNode(this.refs["input_"+focusIndex]).focus();});}}};}}TextListEditor.propTypes={options:PropTypes.array,layout:PropTypes.oneOf(["horizontal","vertical"]),onChange:PropTypes.func.isRequired};TextListEditor.defaultProps={options:[],layout:"horizontal"};
1430
1420
 
1431
1421
  const EMPTY_ARRAY=[];class StubTagEditor extends React.Component{render(){return jsxRuntimeExports.jsxs("div",{children:[this.props.showTitle&&jsxRuntimeExports.jsx("div",{style:{fontSize:14},children:"Tags:"}),jsxRuntimeExports.jsx(TextListEditor,{options:this.props.value||EMPTY_ARRAY,layout:"vertical",onChange:this.props.onChange})]})}}StubTagEditor.propTypes={value:PropTypes.arrayOf(PropTypes.string),onChange:PropTypes.func.isRequired,showTitle:PropTypes.bool.isRequired};StubTagEditor.defaultProps={value:EMPTY_ARRAY,showTitle:true};
@@ -1434,7 +1424,7 @@ const ApiOptions={propTypes:PropTypes.shape({isArticle:PropTypes.bool.isRequired
1434
1424
 
1435
1425
  const getPromptJSON$t=(renderProps,userInput)=>{return {type:"expression",label:renderProps.visibleLabel,userInput:{value:userInput}}};
1436
1426
 
1437
- const englishOperators={arctg:"arctan",cosec:"csc",cossec:"csc",cotg:"cot",ctg:"cot",sen:"sin",tg:"tan"};const anglicizeOperators=tex=>{return tex.replace(/\\operatorname{([a-z]+)}/g,(_,op)=>`\\${englishOperators[op]??op} `)};const normalizeTex=tex=>{return anglicizeOperators(tex)};class Expression extends React.Component{static getUserInputFromProps(props){return normalizeTex(props.value)}getUserInput(){return Expression.getUserInputFromProps(this.props)}getPromptJSON(){return getPromptJSON$t(this.props,this.getUserInput())}focusInputPath(inputPath){this.refs.input.focus();}blurInputPath(inputPath){if(typeof this.refs.input?.blur==="function"){this.refs.input?.blur();}}insert(keyPressed){this.refs.input.insert(keyPressed);}setInputValue(path,newValue,cb){this.props.onChange({value:newValue},cb);}render(){if(this.props.apiOptions.customKeypad){return jsxRuntimeExports.jsxs(View,{className:css(styles$I.mobileLabelInputWrapper),children:[!!this.props.visibleLabel&&jsxRuntimeExports.jsx(LabelSmall,{htmlFor:this._textareaId,tag:"label",children:this.props.visibleLabel}),jsxRuntimeExports.jsx(KeypadInput,{ref:"input",ariaLabel:this.props.ariaLabel||this.context.strings.mathInputBox,value:this.props.value,keypadElement:this.props.keypadElement,onChange:this.changeAndTrack,onFocus:()=>{this.props.keypadElement?.configure(this.props.keypadConfiguration,()=>{if(this._isMounted){this._handleFocus();}});},onBlur:this._handleBlur})]})}const className=classNames$1({"perseus-widget-expression":true,"show-error-tooltip":this.state.showErrorTooltip});const{ERROR_MESSAGE,ERROR_TITLE}=this.context.strings;return jsxRuntimeExports.jsxs(View,{className:css(styles$I.desktopLabelInputWrapper),children:[!!this.props.visibleLabel&&jsxRuntimeExports.jsx(LabelSmall,{htmlFor:this._textareaId,tag:"label",children:this.props.visibleLabel}),jsxRuntimeExports.jsxs("div",{className:className,onBlur:()=>this.state.invalid&&this.setState({showErrorTooltip:true,showErrorStyle:true}),onFocus:()=>this.setState({showErrorTooltip:false}),children:[jsxRuntimeExports.jsx(View,{style:a11y.srOnly,role:"alert",children:this.state.showErrorTooltip&&ERROR_TITLE+" "+ERROR_MESSAGE}),jsxRuntimeExports.jsx(Tooltip,{forceAnchorFocusivity:false,opened:this.state.showErrorTooltip,title:ERROR_TITLE,content:ERROR_MESSAGE,children:jsxRuntimeExports.jsx(MathInput,{ref:"input",value:this.props.value,onChange:this.changeAndTrack,convertDotToTimes:this.props.times,buttonSets:this.props.buttonSets,onFocus:this._handleFocus,onBlur:this._handleBlur,hasError:this.state.showErrorStyle,ariaLabel:this.props.ariaLabel||this.context.strings.mathInputBox,extraKeys:this.props.keypadConfiguration?.extraKeys,onAnalyticsEvent:this.props.analytics?.onAnalyticsEvent??(async()=>{})})})]})]})}constructor(...args){super(...args),this._textareaId=`expression_textarea_${Date.now()}`,this._isMounted=false,this.displayName="Expression",this.state={invalid:false,showErrorTooltip:false,showErrorStyle:false},this.componentDidMount=()=>{document.addEventListener("mousedown",this._handleMouseDown);this._isMounted=true;if(this.refs.input){const isMobile=this.props.apiOptions.customKeypad;const container=ReactDOM__default.findDOMNode(this.refs.input);const selector=isMobile?".mq-textarea > span":"textarea";const inputElement=container.querySelector(selector);inputElement?.setAttribute("id",this._textareaId);}},this.componentDidUpdate=prevProps=>{if(!_.isEqual(this.props.value,prevProps.value)||!_.isEqual(this.props.functions,prevProps.functions)){this.setState({invalid:false,showErrorTooltip:false,showErrorStyle:false});if(!this.parse(this.props.value,this.props).parsed){this.setState({invalid:true});}}},this.componentWillUnmount=()=>{this._isMounted=false;},this._handleMouseDown=()=>{if(this._isMounted&&this.state.showErrorTooltip){this.setState({showErrorTooltip:false});}},this.change=(...args)=>{return change.apply(this,args)},this.parse=(value,props)=>{const options=_.pick(props||this.props,"functions");_.extend(options,{decimal_separator:getDecimalSeparator(this.context.locale)});return KAS.parse(normalizeTex(value),options)},this.changeAndTrack=(e,cb)=>{this.change("value",e,cb);this.props.trackInteraction();},this._handleFocus=()=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:expression-focused",payload:null});this.props.onFocus([]);},this._handleBlur=()=>{this.props.onBlur([]);},this.focus=()=>{if(this.props.apiOptions.customKeypad){this.refs.input.focus();}return true},this.getInputPaths=()=>{return [[]]};}}Expression.contextType=PerseusI18nContext;Expression.defaultProps={value:"",times:false,functions:[],buttonSets:["basic","trig","prealgebra","logarithms"],onFocus:()=>{},onBlur:()=>{},apiOptions:ApiOptions.defaults,linterContext:linterContextDefault};const styles$I=StyleSheet.create({mobileLabelInputWrapper:{padding:"15px 4px 0"},desktopLabelInputWrapper:{margin:"5px 5px 0"}});const ExpressionWithDependencies=React.forwardRef((props,ref)=>{const deps=useDependencies();return jsxRuntimeExports.jsx(Expression,{ref:ref,analytics:deps.analytics,...props})});ExpressionWithDependencies.getUserInputFromProps=Expression.getUserInputFromProps;var Expression$1 = {name:"expression",displayName:"Expression / Equation",widget:ExpressionWithDependencies,transform:widgetOptions=>{const{times,functions,buttonSets,buttonsVisible,visibleLabel,ariaLabel,extraKeys}=widgetOptions;return {keypadConfiguration:{keypadType:"EXPRESSION",extraKeys,times},times,functions,buttonSets,buttonsVisible,visibleLabel,ariaLabel}},version:expressionLogic.version,isLintable:true,getOneCorrectAnswerFromRubric(rubric){const correctAnswers=(rubric.answerForms||[]).filter(answerForm=>answerForm.considered==="correct");if(correctAnswers.length===0){return}return correctAnswers[0].value}};
1427
+ const englishOperators={arctg:"arctan",cosec:"csc",cossec:"csc",cotg:"cot",ctg:"cot",sen:"sin",tg:"tan"};const anglicizeOperators=tex=>{return tex.replace(/\\operatorname{([a-z]+)}/g,(_,op)=>`\\${englishOperators[op]??op} `)};const normalizeTex=tex=>{return anglicizeOperators(tex)};class Expression extends React.Component{getUserInput(){return normalizeTex(this.props.userInput)}getPromptJSON(){return getPromptJSON$t(this.props,this.getUserInput())}focusInputPath(inputPath){this.refs.input.focus();}blurInputPath(inputPath){if(typeof this.refs.input?.blur==="function"){this.refs.input?.blur();}}insert(keyPressed){this.refs.input.insert(keyPressed);}setInputValue(path,newValue,cb){this.props.handleUserInput(newValue,cb);}getSerializedState(){const{userInput:_,...rest}=this.props;return {...rest,value:this.props.userInput}}render(){if(this.props.apiOptions.customKeypad){return jsxRuntimeExports.jsxs(View,{className:css(styles$I.mobileLabelInputWrapper),children:[!!this.props.visibleLabel&&jsxRuntimeExports.jsx(LabelSmall,{htmlFor:this._textareaId,tag:"label",children:this.props.visibleLabel}),jsxRuntimeExports.jsx(KeypadInput,{ref:"input",ariaLabel:this.props.ariaLabel||this.context.strings.mathInputBox,value:this.props.userInput,keypadElement:this.props.keypadElement,onChange:this.changeAndTrack,onFocus:()=>{this.props.keypadElement?.configure(this.props.keypadConfiguration,()=>{if(this._isMounted){this._handleFocus();}});},onBlur:this._handleBlur})]})}const className=classNames$1({"perseus-widget-expression":true,"show-error-tooltip":this.state.showErrorTooltip});const{ERROR_MESSAGE,ERROR_TITLE}=this.context.strings;return jsxRuntimeExports.jsxs(View,{className:css(styles$I.desktopLabelInputWrapper),children:[!!this.props.visibleLabel&&jsxRuntimeExports.jsx(LabelSmall,{htmlFor:this._textareaId,tag:"label",children:this.props.visibleLabel}),jsxRuntimeExports.jsxs("div",{className:className,onBlur:()=>this.state.invalid&&this.setState({showErrorTooltip:true,showErrorStyle:true}),onFocus:()=>this.setState({showErrorTooltip:false}),children:[jsxRuntimeExports.jsx(View,{style:a11y.srOnly,role:"alert",children:this.state.showErrorTooltip&&ERROR_TITLE+" "+ERROR_MESSAGE}),jsxRuntimeExports.jsx(Tooltip,{forceAnchorFocusivity:false,opened:this.state.showErrorTooltip,title:ERROR_TITLE,content:ERROR_MESSAGE,children:jsxRuntimeExports.jsx(MathInput,{ref:"input",value:this.props.userInput,onChange:this.changeAndTrack,convertDotToTimes:this.props.times,buttonSets:this.props.buttonSets,onFocus:this._handleFocus,onBlur:this._handleBlur,hasError:this.state.showErrorStyle,ariaLabel:this.props.ariaLabel||this.context.strings.mathInputBox,extraKeys:this.props.keypadConfiguration?.extraKeys,onAnalyticsEvent:this.props.analytics?.onAnalyticsEvent??(async()=>{})})})]})]})}constructor(...args){super(...args),this._textareaId=`expression_textarea_${Date.now()}`,this._isMounted=false,this.displayName="Expression",this.state={invalid:false,showErrorTooltip:false,showErrorStyle:false},this.componentDidMount=()=>{document.addEventListener("mousedown",this._handleMouseDown);this._isMounted=true;if(this.refs.input){const isMobile=this.props.apiOptions.customKeypad;const container=ReactDOM__default.findDOMNode(this.refs.input);const selector=isMobile?".mq-textarea > span":"textarea";const inputElement=container.querySelector(selector);inputElement?.setAttribute("id",this._textareaId);}},this.componentDidUpdate=prevProps=>{if(!_.isEqual(this.props.userInput,prevProps.userInput)||!_.isEqual(this.props.functions,prevProps.functions)){this.setState({invalid:false,showErrorTooltip:false,showErrorStyle:false});if(!this.parse(this.props.userInput,this.props).parsed){this.setState({invalid:true});}}},this.componentWillUnmount=()=>{this._isMounted=false;},this._handleMouseDown=()=>{if(this._isMounted&&this.state.showErrorTooltip){this.setState({showErrorTooltip:false});}},this.parse=(value,props)=>{const options=_.pick(props||this.props,"functions");_.extend(options,{decimal_separator:getDecimalSeparator(this.context.locale)});return KAS.parse(normalizeTex(value),options)},this.changeAndTrack=(userInput,cb)=>{this.props.handleUserInput(normalizeTex(userInput),cb);this.props.trackInteraction();},this._handleFocus=()=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:expression-focused",payload:null});this.props.onFocus([]);},this._handleBlur=()=>{this.props.onBlur([]);},this.focus=()=>{if(this.props.apiOptions.customKeypad){this.refs.input.focus();}return true},this.getInputPaths=()=>{return [[]]};}}Expression.contextType=PerseusI18nContext;Expression.defaultProps={times:false,functions:[],buttonSets:["basic","trig","prealgebra","logarithms"],onFocus:()=>{},onBlur:()=>{},apiOptions:ApiOptions.defaults,linterContext:linterContextDefault,userInput:""};const styles$I=StyleSheet.create({mobileLabelInputWrapper:{padding:"15px 4px 0"},desktopLabelInputWrapper:{margin:"5px 5px 0"}});const ExpressionWithDependencies=React.forwardRef((props,ref)=>{const deps=useDependencies();return jsxRuntimeExports.jsx(Expression,{ref:ref,analytics:deps.analytics,...props})});function getUserInputFromSerializedState$h(serializedState){return normalizeTex(serializedState.value)}var Expression$1 = {name:"expression",displayName:"Expression / Equation",widget:ExpressionWithDependencies,transform:widgetOptions=>{const{times,functions,buttonSets,buttonsVisible,visibleLabel,ariaLabel,extraKeys}=widgetOptions;return {keypadConfiguration:{keypadType:"EXPRESSION",extraKeys,times},times,functions,buttonSets,buttonsVisible,visibleLabel,ariaLabel}},version:expressionLogic.version,isLintable:true,getOneCorrectAnswerFromRubric(rubric){const correctAnswers=(rubric.answerForms||[]).filter(answerForm=>answerForm.considered==="correct");if(correctAnswers.length===0){return}return correctAnswers[0].value},getUserInputFromSerializedState: getUserInputFromSerializedState$h};
1438
1428
 
1439
1429
  class SimpleKeypadInput extends React.Component{componentDidMount(){this._isMounted=true;}componentWillUnmount(){this._isMounted=false;}focus(){this.inputRef.current?.focus(this.context.setKeypadActive);}blur(){this.inputRef.current?.blur();}getValue(){return this.props.value}render(){const _this=this;const{keypadElement,onFocus,value,...rest}=_this.props;return jsxRuntimeExports.jsx(KeypadInput,{ref:this.inputRef,keypadElement:keypadElement,onFocus:()=>{if(keypadElement){keypadElement.configure({keypadType:"FRACTION"},()=>{if(_this._isMounted){onFocus?.();}});}else {onFocus?.();}},value:value==null?"":""+value,...rest})}constructor(...args){super(...args),this._isMounted=false,this.inputRef=React.createRef();}}SimpleKeypadInput.contextType=KeypadContext;SimpleKeypadInput.propTypes={keypadElement:keypadElementPropType,onFocus:PropTypes.func,value:PropTypes.oneOfType([PropTypes.string,PropTypes.number])};
1440
1430
 
@@ -1630,6 +1620,8 @@ function _noop(){}class InteractionTracker{constructor(trackApi,widgetType,widge
1630
1620
 
1631
1621
  const arrayRules={fence:{match:SimpleMarkdown.defaultRules.fence.match,order:1,parse:(capture,state,parse)=>({type:"codeBlock",lang:capture[2]||undefined,content:capture[3]})},paragraph:{match:SimpleMarkdown.defaultRules.paragraph.match,order:2,parse:(capture,state,parse)=>({content:capture[1]})}};const builtArrayParser=SimpleMarkdown.parserFor(arrayRules);const parseToArray=source=>{const paragraphedSource=source.replace(/^\n\s*\n/,"")+"\n\n";return builtArrayParser(paragraphedSource,{inline:false}).map(c=>{return c["content"]})};const joinFromArray=paragraphs=>paragraphs.join("\n\n");var JiptParagraphs = {parseToArray:parseToArray,joinFromArray:joinFromArray};
1632
1622
 
1623
+ const denylist=["key","ref","containerSizeClass","widgetId","onChange","problemNum","apiOptions","widgetIsOpen","questionCompleted","findWidgets","onRemove","id","onBlur","onFocus","trackInteraction","keypadElement","linterContext","isLastUsedWidget","handleUserInput","analytics","showSolutions","reviewMode","reviewModeRubric"];function excludeDenylistKeys(obj){if(obj==null){return obj}const rv={};for(const k of Object.keys(obj)){if(!denylist.includes(k)){rv[k]=obj[k];}}return rv}
1624
+
1633
1625
  const exclamationIcon={path:"M6 11a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0-9a1 1 0 0 1 1 1v4a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1z",height:12,width:12};class Lint extends React.Component{componentDidMount(){this._positionTimeout=window.setTimeout(this.getPosition);}componentWillUnmount(){window.clearTimeout(this._positionTimeout);}render(){const{children,inline,blockHighlight,insideTable}=this.props;if(insideTable){if(inline){return jsxRuntimeExports.jsx("span",{"data-lint-inside-table":"true",children:children})}return jsxRuntimeExports.jsx("div",{"data-lint-inside-table":"true",children:children})}if(blockHighlight){return jsxRuntimeExports.jsxs("span",{className:css(styles$H.lintContainer,styles$H.lintContainerBlock),children:[this.renderLink(styles$H.radioWidgetHoverTarget),jsxRuntimeExports.jsx("span",{children:children})]})}if(inline){return jsxRuntimeExports.jsxs("span",{className:css(styles$H.lintContainer),children:[this.renderLink(styles$H.inlineHoverTarget),jsxRuntimeExports.jsx("span",{children:children})]})}return jsxRuntimeExports.jsxs("div",{className:css(styles$H.lintContainer),children:[this.renderLink(styles$H.hoverTarget),jsxRuntimeExports.jsx("div",{children:children})]})}constructor(...args){super(...args),this.state={tooltipAbove:true},this.getPosition=()=>{const rect=ReactDOM__default.findDOMNode(this).getBoundingClientRect();this.setState({tooltipAbove:rect.top>100});},this.renderLink=style=>{const tooltipAbove=this.state.tooltipAbove;let severityStyle;let warningText;let warningTextStyle;if(this.props.severity===1){severityStyle=styles$H.indicatorError;warningText="Error";warningTextStyle=styles$H.publishBlockingError;}else if(this.props.severity===2){severityStyle=styles$H.indicatorWarning;warningText="Warning";warningTextStyle=styles$H.warning;}else {severityStyle=styles$H.indicatorGuideline;warningText="Recommendation";warningTextStyle=styles$H.warning;}return jsxRuntimeExports.jsxs("a",{href:`https://khanacademy.org/r/linter-rules#${this.props.ruleName}`,target:"lint-help-window",className:css(style),children:[jsxRuntimeExports.jsx("span",{className:css(styles$H.indicator,severityStyle),children:this.props.severity===1&&jsxRuntimeExports.jsx(InlineIcon,{...exclamationIcon})}),jsxRuntimeExports.jsxs("div",{className:css(styles$H.tooltip,tooltipAbove&&styles$H.tooltipAbove),children:[this.props.message.split("\n\n").map((m,i)=>jsxRuntimeExports.jsxs("p",{className:css(styles$H.tooltipParagraph),children:[jsxRuntimeExports.jsxs("span",{className:css(warningTextStyle),children:[warningText,":"," "]}),m]},i)),jsxRuntimeExports.jsx("div",{className:css(styles$H.tail,tooltipAbove&&styles$H.tailAbove)})]})]})};}}const styles$H=StyleSheet.create({lintContainer:{position:"relative"},lintContainerBlock:{display:"block"},hoverTarget:{position:"absolute",top:0,right:-40,display:"block",width:24,height:24,":hover > span":{backgroundColor:warningColorHover},":hover div":{display:"block"},":hover ~ div":{outline:"1px solid "+warningColor},":hover ~ div div[data-lint-inside-table]":{outline:"1px solid "+warningColor},":hover ~ div span[data-lint-inside-table]":{backgroundColor:warningColor,color:white}},inlineHoverTarget:{float:"right",position:"relative",marginRight:-40,display:"block",width:24,height:24,":hover > span":{backgroundColor:warningColorHover},":hover div":{display:"block"},":hover ~ span":{backgroundColor:warningColor,color:white}},radioWidgetHoverTarget:{position:"absolute",left:-40,display:"block",width:24,height:24,minWidth:264,":hover > span":{backgroundColor:warningColorHover},":hover > div":{display:"block",padding:8,width:280},":hover > div > div":{left:8},":hover ~ span":{backgroundColor:warningColor,color:white}},indicator:{alignItems:"center",borderRadius:4,color:"white",display:"flex",fontSize:12,height:8,justifyContent:"center",margin:8,width:8},indicatorError:{backgroundColor:"#be2612",borderRadius:8,height:16,width:16},indicatorWarning:{backgroundColor:"#f86700"},indicatorGuideline:{backgroundColor:"#ffbe26"},tooltip:{position:"absolute",right:-12,display:"none",zIndex:1e3,color:white,backgroundColor:gray17,opacity:.9,fontFamily:baseFontFamily,fontSize:"12px",lineHeight:"15px",width:"320px",borderRadius:"4px"},tooltipAbove:{bottom:32},tail:{position:"absolute",top:-12,right:16,width:0,height:0,borderLeft:"8px solid transparent",borderRight:"8px solid transparent",borderBottom:"12px solid "+gray17},tailAbove:{bottom:-12,borderBottom:"none",borderTop:"12px solid "+gray17,top:"auto"},tooltipParagraph:{margin:12},warning:{color:warningColor,fontFamily:boldFontFamily},publishBlockingError:{color:publishBlockingErrorColor}});
1634
1626
 
1635
1627
  const rules$1={...pureMarkdownRules,columns:{...pureMarkdownRules.columns,react:(node,output,state)=>{return jsxRuntimeExports.jsxs("div",{className:"perseus-two-columns",children:[jsxRuntimeExports.jsx("div",{className:"perseus-column",children:jsxRuntimeExports.jsx("div",{className:"perseus-column-content",children:output(node.col1,state)})}),jsxRuntimeExports.jsx("div",{className:"perseus-column",children:jsxRuntimeExports.jsx("div",{className:"perseus-column-content",children:output(node.col2,state)})})]},state.key)}},crowdinId:{...pureMarkdownRules.crowdinId,react:(node,output,state)=>node.id},table:{...pureMarkdownRules.table,react:function(node,output,state){const table=SimpleMarkdown.defaultRules.table.react(node,output,state);if(!state.isMobile){return table}return React.cloneElement(table,{tabIndex:0})}},titledTable:{...pureMarkdownRules.titledTable,react:(node,output,state)=>{let contents;if(!node.table){contents="//invalid table//";}else if(node.table.type==="lint"){if(node.table.content?.type==="table"){contents=rules$1.lint.react({...node.table,content:{...node,table:node.table.content}},output,{...state,unwrapTitledTableContents:true});}else {contents=rules$1.lint.react(node.table,output,state);}}else {const tableOutput=SimpleMarkdown.defaultRules.table.react(node.table,output,state);const caption=jsxRuntimeExports.jsx("caption",{className:"perseus-table-title",children:output(node.title,state)},"caption");contents=React.cloneElement(tableOutput,null,[caption,...tableOutput.props.children]);}if(state.unwrapTitledTableContents){return contents}return jsxRuntimeExports.jsx("div",{className:"perseus-titled-table",children:contents},state.key)}},widget:{...pureMarkdownRules.widget,react:(node,output,state)=>{return jsxRuntimeExports.jsx("em",{children:`[Widget: ${node.id}]`},state.key)}},blockMath:{...pureMarkdownRules.blockMath,react:(node,output,state)=>{const{TeX}=getDependencies();return jsxRuntimeExports.jsx(TeX,{children:node.content},state.key)}},math:{...pureMarkdownRules.math,react:(node,output,state)=>{const{TeX}=getDependencies();return jsxRuntimeExports.jsx(TeX,{children:node.content},state.key)}},unescapedDollar:{...pureMarkdownRules.unescapedDollar,react:(node,output,state)=>{return "$"}},link:{...pureMarkdownRules.link,react:function(node,output,state){const link=SimpleMarkdown.defaultRules.link.react(node,output,state);const href=link.props.href;let rel=null;const isKAUrl=href?href.match(/https?:\/\/[^/]*khanacademy.org|^\//):false;if(!isKAUrl){rel="noopener noreferrer";}const newProps={...link.props,target:"_blank",href,rel};if(state.baseElements&&state.baseElements.Link){return state.baseElements.Link(newProps)}return React.cloneElement(link,newProps)}},codeBlock:{...pureMarkdownRules.codeBlock,react:(node,output,state)=>{if(node.lang==="alt"){return jsxRuntimeExports.jsx("div",{className:"perseus-markdown-alt perseus-sr-only",children:output(node.content,state)},state.key)}return SimpleMarkdown.defaultRules.codeBlock.react(node,output,state)}},lint:{...pureMarkdownRules.lint,react:(node,output,state)=>{return jsxRuntimeExports.jsx(Lint,{message:node.message,ruleName:node.ruleName,inline:isInline(node.content),blockHighlight:node.blockHighlight,insideTable:node.insideTable,severity:node.severity,children:output(node.content,state)})}}};function isInline(node){return !!(node&&node.type&&inlineNodeTypes.hasOwnProperty(node.type))}const inlineNodeTypes={text:true,math:true,unescapedDollar:true,link:true,img:true,strong:true,u:true,em:true,del:true,code:true};const builtParser$1=SimpleMarkdown.parserFor(rules$1);const parse$3=(source,state)=>{const paragraphedSource=source+"\n\n";return builtParser$1(paragraphedSource,{...state,inline:false})};const inlineParser=(source,state)=>{return builtParser$1(source,{...state,inline:true})};const traverseContent=(ast,cb)=>{if(_.isArray(ast)){_.each(ast,node=>traverseContent(node,cb));}else if(_.isObject(ast)){cb(ast);if(ast.type==="table"){traverseContent(ast.header,cb);traverseContent(ast.cells,cb);}else if(ast.type==="list"){traverseContent(ast.items,cb);}else if(ast.type==="titledTable"){traverseContent(ast.table,cb);}else if(ast.type==="columns"){traverseContent(ast.col1,cb);traverseContent(ast.col2,cb);}else if(_.isArray(ast.content)){traverseContent(ast.content,cb);}}};const getContent=ast=>{if(_.isArray(ast)){return _.flatten(_.map(ast,getContent))}if(ast.content&&_.isString(ast.content)){if(ast.type.toLowerCase().indexOf("code")!==-1){return ["",ast.content,""]}return [ast.content.replace(/\s+/g," ")]}const children=_.chain(ast).values().flatten().filter(object=>object!=null&&_.has(object,"type")).value();if(!children.length){return []}const nestedContent=getContent(children);if(ast.type==="paragraph"&&nestedContent.length){nestedContent[0]=nestedContent[0].replace(/^\s+/,"");const last=nestedContent.length-1;nestedContent[last]=nestedContent[last].replace(/\s+$/,"");}return nestedContent};const characterCount=source=>{const ast=parse$3(source);const content=getContent(ast).join("");return content.length};var PerseusMarkdown = {characterCount:characterCount,traverseContent:traverseContent,parse:parse$3,parseInline:inlineParser,reactFor:SimpleMarkdown.reactFor,ruleOutput:SimpleMarkdown.ruleOutput(rules$1,"react"),basicOutput:SimpleMarkdown.reactFor(SimpleMarkdown.ruleOutput(rules$1,"react")),sanitizeUrl:SimpleMarkdown.sanitizeUrl};
@@ -1642,7 +1634,7 @@ var preprocessTex = (texCode=>texCode.replace(/\{align[*]?\}/g,"{aligned}").repl
1642
1634
 
1643
1635
  const{interactiveSizes}=constants;const smMax=articleMaxWidthTableInPx;const mdMax=articleMaxWidthInPx;const containerSizeClass={SMALL:"small",MEDIUM:"medium",LARGE:"large",XLARGE:"xlarge"};const getClassFromWidth=width=>{if(!width){return containerSizeClass.MEDIUM}if(width<=smMax){return containerSizeClass.SMALL}if(width<=mdMax){return containerSizeClass.MEDIUM}return containerSizeClass.LARGE};const getInteractiveBoxFromSizeClass=sizeClass=>{if(sizeClass===containerSizeClass.SMALL){return [interactiveSizes.defaultBoxSizeSmall,interactiveSizes.defaultBoxSizeSmall]}return [interactiveSizes.defaultBoxSize,interactiveSizes.defaultBoxSize]};
1644
1636
 
1645
- const DEFAULT_TRACKING="";const DEFAULT_LINTABLE=false;const widgets=new Registry("Perseus widget registry");const editors=new Registry("Perseus widget editor registry");const identity=val=>val;const registerWidget=(type,widget)=>{widgets.set(type,widget);};const registerWidgets=widgetArr=>{widgetArr.forEach(widget=>{registerWidget(widget.name,widget);});};const replaceWidget=(type,replacementType)=>{const substituteWidget=widgets.get(replacementType);if(!substituteWidget){const errorMsg=`Failed to replace ${type} with ${replacementType}`;throw new PerseusError(errorMsg,Errors.Internal)}registerWidget(type,substituteWidget);};const replaceDeprecatedWidgets=()=>{replaceWidget("transformer","deprecated-standin");replaceWidget("lights-puzzle","deprecated-standin");replaceWidget("reaction-diagram","deprecated-standin");replaceWidget("sequence","deprecated-standin");replaceWidget("simulator","deprecated-standin");replaceWidget("unit-input","deprecated-standin");};const registerEditors=editorsToRegister=>{editorsToRegister.forEach(editor=>{if(!editor.widgetName){throw new PerseusError(`Editor ${editor.displayName} doesn't have a widgetName property`,Errors.Internal)}editors.set(editor.widgetName,editor);});};const replaceEditor=(type,replacementType)=>{const substituteEditor=editors.get(replacementType);if(!substituteEditor&&Log){const errorMsg=`Failed to replace editor ${type} with ${replacementType}`;Log.error(errorMsg,Errors.Internal);return}editors.set(type,substituteEditor);};const replaceDeprecatedEditors=()=>{replaceEditor("transformer","deprecated-standin");replaceEditor("lights-puzzle","deprecated-standin");replaceEditor("reaction-diagram","deprecated-standin");replaceEditor("sequence","deprecated-standin");replaceEditor("simulator","deprecated-standin");replaceEditor("unit-input","deprecated-standin");};const getWidget=type=>{const widget=widgets.get(type);if(widget==null){return null}if(widget.getWidget){return widget.getWidget()}return widget.widget};const getWidgetExport=type=>{return widgets.get(type)??null};const getEditor=type=>{return editors.get(type)??null};const getTransform=type=>{const widget=widgets.get(type);if(widget==null){return null}return widget.transform||identity};const getVersion=type=>{const widget=widgets.get(type);if(widget!=null){return widget.version||{major:0,minor:0}}return};const getVersionVector=()=>{const version={};widgets.keys().forEach(type=>{version[type]=getVersion(type);});return version};const getPublicWidgets=()=>{return widgets.entries().reduce((acc,[key,value])=>{if(!value.hidden){acc[key]=value;}return acc},{})};const getAllWidgetTypes=()=>{return widgets.keys()};const getRendererPropsForWidgetInfo=(widgetInfo,strings,problemNum)=>{const type=widgetInfo.type;const widgetExports=widgets.get(type);if(widgetExports==null){return widgetInfo}let transform;if(widgetInfo.static){transform=getStaticTransform(type)||identity;}else {transform=widgetExports.transform||identity;}return transform(widgetInfo.options,strings,problemNum)};const supportsStaticMode=type=>{const widgetInfo=widgets.get(type);return widgetInfo&&widgetInfo.staticTransform!=null};const getStaticTransform=type=>{const widgetInfo=widgets.get(type);return widgetInfo&&widgetInfo.staticTransform};const getTracking=type=>{const widgetExport=widgets.get(type);return widgetExport&&widgetExport.tracking||DEFAULT_TRACKING};const isLintable=type=>{const widgetExports=widgets.get(type);return widgetExports&&widgetExports.isLintable||DEFAULT_LINTABLE};
1637
+ const DEFAULT_TRACKING="";const DEFAULT_LINTABLE=false;const widgets=new Registry("Perseus widget registry");const editors=new Registry("Perseus widget editor registry");const identity=val=>val;const registerWidget=(type,widget)=>{widgets.set(type,widget);};const registerWidgets=widgetArr=>{widgetArr.forEach(widget=>{registerWidget(widget.name,widget);});};const replaceWidget=(type,replacementType)=>{const substituteWidget=widgets.get(replacementType);if(!substituteWidget){const errorMsg=`Failed to replace ${type} with ${replacementType}`;throw new PerseusError(errorMsg,Errors.Internal)}registerWidget(type,substituteWidget);};const replaceDeprecatedWidgets=()=>{replaceWidget("transformer","deprecated-standin");replaceWidget("lights-puzzle","deprecated-standin");replaceWidget("reaction-diagram","deprecated-standin");replaceWidget("sequence","deprecated-standin");replaceWidget("simulator","deprecated-standin");replaceWidget("unit-input","deprecated-standin");};const registerEditors=editorsToRegister=>{editorsToRegister.forEach(editor=>{if(!editor.widgetName){throw new PerseusError(`Editor ${editor.displayName} doesn't have a widgetName property`,Errors.Internal)}editors.set(editor.widgetName,editor);});};const replaceEditor=(type,replacementType)=>{const substituteEditor=editors.get(replacementType);if(!substituteEditor&&Log){const errorMsg=`Failed to replace editor ${type} with ${replacementType}`;Log.error(errorMsg,Errors.Internal);return}editors.set(type,substituteEditor);};const replaceDeprecatedEditors=()=>{replaceEditor("transformer","deprecated-standin");replaceEditor("lights-puzzle","deprecated-standin");replaceEditor("reaction-diagram","deprecated-standin");replaceEditor("sequence","deprecated-standin");replaceEditor("simulator","deprecated-standin");replaceEditor("unit-input","deprecated-standin");};const getWidget=type=>{const widget=widgets.get(type);if(widget==null){return null}if(widget.getWidget){return widget.getWidget()}return widget.widget};const getWidgetExport=type=>{return widgets.get(type)??null};const getEditor=type=>{return editors.get(type)??null};const getTransform=type=>{const widget=widgets.get(type);if(widget==null){return null}return widget.transform||identity};const getVersion=type=>{const widget=widgets.get(type);if(widget!=null){return widget.version||{major:0,minor:0}}return};const getVersionVector=()=>{const version={};widgets.keys().forEach(type=>{version[type]=getVersion(type);});return version};const getPublicWidgets=()=>{return widgets.entries().reduce((acc,[key,value])=>{if(process.env.STORYBOOK||!value.hidden){acc[key]=value;}return acc},{})};const getAllWidgetTypes=()=>{return widgets.keys()};const getRendererPropsForWidgetInfo=(widgetInfo,strings,problemNum)=>{const type=widgetInfo.type;const widgetExports=widgets.get(type);if(widgetExports==null){return widgetInfo}let transform;if(widgetInfo.static){transform=getStaticTransform(type)||identity;}else {transform=widgetExports.transform||identity;}return transform(widgetInfo.options,strings,problemNum)};const supportsStaticMode=type=>{const widgetInfo=widgets.get(type);return widgetInfo&&widgetInfo.staticTransform!=null};const getStaticTransform=type=>{const widgetInfo=widgets.get(type);return widgetInfo?.staticTransform};const getTracking=type=>{const widgetExport=widgets.get(type);return widgetExport&&widgetExport.tracking||DEFAULT_TRACKING};const isLintable=type=>{const widgetExports=widgets.get(type);return widgetExports&&widgetExports.isLintable||DEFAULT_LINTABLE};
1646
1638
 
1647
1639
  var widgets$1 = /*#__PURE__*/Object.freeze({
1648
1640
  __proto__: null,
@@ -1670,21 +1662,23 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
1670
1662
 
1671
1663
  class WidgetContainer extends React.Component{componentDidMount(){if(this.state.widgetProps.apiOptions.isMobile){const containerWidth=ReactDOM__default.findDOMNode(this).offsetWidth;this.setState({sizeClass:getClassFromWidth(containerWidth)});}}UNSAFE_componentWillReceiveProps(nextProps){if(this.props.type!==nextProps.type){throw new Error("WidgetContainer can't change widget type; set a different "+"key instead to recreate the container.")}}shouldComponentUpdate(nextProps,nextState){return this.props.shouldHighlight!==nextProps.shouldHighlight||this.props.type!==nextProps.type||this.state.widgetProps!==nextState.widgetProps||this.state.sizeClass!==nextState.sizeClass}render(){let className=classNames$1({"perseus-widget-container":true,"widget-highlight":this.props.shouldHighlight,"widget-nohighlight":!this.props.shouldHighlight,"perseus-widget__definition":this.props.type==="definition"});const type=this.props.type;const userAgent=navigator.userAgent;const WidgetType=getWidget(type);if(WidgetType==null){console.warn(`Widget type '${type}' not found!`);return jsxRuntimeExports.jsx("div",{className:className})}let subType="null";if(type==="interactive-graph"){const props=this.state.widgetProps;subType=props.graph?.type??"null";}let alignment=this.state.widgetProps.alignment;if(alignment==="default"){alignment=CoreWidgetRegistry.getDefaultAlignment(type);}className+=" widget-"+alignment;const apiOptions=this.state.widgetProps.apiOptions;const isStatic=this.state.widgetProps.static||apiOptions.readOnly;const staticContainerStyles={position:"relative",overflow:"visible"};const staticOverlayStyles={width:"100%",height:"100%",position:"absolute",top:0,left:0,zIndex:3};const linterContext=isLintable(type)?this.props.linterContext:{...this.props.linterContext,highlightLint:false};return jsxRuntimeExports.jsx("div",{className:className,style:isStatic?staticContainerStyles:{},children:jsxRuntimeExports.jsx(DependenciesContext.Consumer,{children:({analytics})=>jsxRuntimeExports.jsxs(ErrorBoundary,{metadata:{widget_type:type,widget_id:this.props.id},onError:error=>{analytics.onAnalyticsEvent({type:"perseus:widget-rendering-error:ti",payload:{widgetSubType:subType,widgetType:type,widgetId:this.props.id,message:error.message,userAgent:userAgent}});},children:[jsxRuntimeExports.jsx(WidgetType,{...this.state.widgetProps,linterContext:linterContext,containerSizeClass:this.state.sizeClass,ref:this.widgetRef}),isStatic&&jsxRuntimeExports.jsx("div",{style:staticOverlayStyles})]})})})}constructor(...args){super(...args),this.widgetRef=React.createRef(),this.state={sizeClass:containerSizeClass.MEDIUM,widgetProps:this.props.initialProps},this.getWidget=()=>{return this.widgetRef.current},this.replaceWidgetProps=newWidgetProps=>{this.setState({widgetProps:newWidgetProps});};}}WidgetContainer.defaultProps={linterContext:linterContextDefault};
1672
1664
 
1673
- const rContainsNonWhitespace=/\S/;const rImageURL=/(web\+graphie|https):\/\/[^\s]*/;const noopOnRender=()=>{};const makeContainerId=id=>"container:"+id;const isIdPathPrefix=function(prefixArray,wholeArray){if(prefixArray===null||wholeArray===null){return prefixArray===wholeArray}return _.every(prefixArray,(elem,i)=>{if(wholeArray!=null){return _.isEqual(elem,wholeArray[i])}})};function isDifferentQuestion(prev,next){if(prev.problemNum!==next.problemNum){return true}function answerlessStringified(props){const answerful={question:{content:props.content,widgets:props.widgets,images:{}},hints:[],answerArea:getDefaultAnswerArea()};const answerless=splitPerseusItem(answerful);return JSON.stringify(answerless)}return answerlessStringified(prev)!==answerlessStringified(next)}class Renderer extends React.Component{componentDidMount(){this._isMounted=true;this.handleRender({});this._currentFocus=null;if(this.props.serializedState){this.restoreSerializedState(this.props.serializedState);}if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}}UNSAFE_componentWillReceiveProps(nextProps){if(isDifferentQuestion(this.props,nextProps)){this.setState(this._getInitialWidgetState(nextProps));}}shouldComponentUpdate(nextProps,nextState){if(this.props.alwaysUpdate){return true}const stateChanged=!_.isEqual(this.state,nextState);const propsChanged=!_.isEqual(this.props,nextProps);return propsChanged||stateChanged}UNSAFE_componentWillUpdate(nextProps,nextState){const oldJipt=this.shouldRenderJiptPlaceholder(this.props,this.state);const newJipt=this.shouldRenderJiptPlaceholder(nextProps,nextState);const oldContent=this.getContent(this.props,this.state);const newContent=this.getContent(nextProps,nextState);const oldHighlightedWidgets=this.props.highlightedWidgets;const newHighlightedWidgets=nextProps.highlightedWidgets;this.reuseMarkdown=!oldJipt&&!newJipt&&oldContent===newContent&&_.isEqual(this.state.translationLintErrors,nextState.translationLintErrors)&&(!this.props.linterContext.highlightLint||_.isEqual(this.props.widgets,nextProps.widgets))&&this.props.linterContext.highlightLint===nextProps.linterContext.highlightLint&&oldHighlightedWidgets===newHighlightedWidgets;}componentDidUpdate(prevProps,prevState){this.handleRender(prevProps);this.widgetIds.forEach(id=>{const container=this._widgetContainers.get(makeContainerId(id));container?.replaceWidgetProps(this.getWidgetProps(id));});if(this.props.serializedState&&!_.isEqual(this.props.serializedState,this.getSerializedState())){this.restoreSerializedState(this.props.serializedState);}if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}}componentWillUnmount(){this.widgetIds=[];if(this.translationIndex!=null){getDependencies().rendererTranslationComponents.removeComponentAtIndex(this.translationIndex);}this._isMounted=false;}getWidgetProps(widgetId){const apiOptions=this.getApiOptions();const widgetProps=this.state.widgetProps[widgetId]||{};const widgetInfo=this.state.widgetInfo[widgetId];const reviewModeRubric=this.props.reviewMode&&widgetInfo?widgetInfo.options:null;if(!this._interactionTrackers){this._interactionTrackers={};}let interactionTracker=this._interactionTrackers[widgetId];if(!interactionTracker){interactionTracker=this._interactionTrackers[widgetId]=new InteractionTracker(apiOptions.trackInteraction,widgetInfo&&widgetInfo.type,widgetId,getTracking(widgetInfo&&widgetInfo.type));}return {...widgetProps,widgetId:widgetId,alignment:widgetInfo&&widgetInfo.alignment,static:widgetInfo?.static,problemNum:this.props.problemNum,apiOptions:this.getApiOptions(),keypadElement:this.props.keypadElement,questionCompleted:this.props.questionCompleted,showSolutions:this.props.showSolutions,onFocus:_.partial(this._onWidgetFocus,widgetId),onBlur:_.partial(this._onWidgetBlur,widgetId),findWidgets:this.findWidgets,reviewModeRubric:reviewModeRubric,reviewMode:this.props.reviewMode,onChange:(newProps,cb,silent=false)=>{this._setWidgetProps(widgetId,newProps,cb,silent);},trackInteraction:interactionTracker.track,isLastUsedWidget:widgetId===this.state.lastUsedWidgetId}}emptyWidgets(){return emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.getUserInputMap(),this.context.locale)}getUserInput(){return this.widgetIds.map(id=>{const widget=this.getWidgetInstance(id);if(widget&&widget.getUserInput){return widget.getUserInput()}})}getUserInputMap(){const userInputMap={};this.widgetIds.forEach(id=>{const widget=this.getWidgetInstance(id);if(widget?.getUserInputMap){userInputMap[id]=widget.getUserInputMap();}else if(widget?.getUserInput){userInputMap[id]=widget.getUserInput();}});return userInputMap}getPromptJSON(){const{content}=this.props;const widgetJSON={};this.widgetIds.forEach(id=>{const widget=this.getWidgetInstance(id);widgetJSON[id]=widget?.getPromptJSON?.()||{};});return {content,widgets:widgetJSON}}score(){const scores=scoreWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.getUserInputMap(),this.context.locale);const combinedScore=flattenScores(scores);return combinedScore}render(){const apiOptions=this.getApiOptions();if(this.reuseMarkdown){return this.lastRenderedMarkdown}const content=this.getContent(this.props,this.state);this.widgetIds=[];if(this.shouldRenderJiptPlaceholder(this.props,this.state)){if(!this.translationIndex){this.translationIndex=getDependencies().rendererTranslationComponents.addComponent(this);}if(!apiOptions.isArticle){return jsxRuntimeExports.jsx(DefinitionProvider,{children:jsxRuntimeExports.jsx("div",{"data-perseus-component-index":this.translationIndex,children:content})})}}this._isTwoColumn=false;const parsedMarkdown=this.props.inline?PerseusMarkdown.parseInline(content,{isJipt:this.translationIndex!=null}):PerseusMarkdown.parse(content,{isJipt:this.translationIndex!=null});if(this.props.linterContext.highlightLint){const fullLinterContext={content:this.props.content,widgets:this.props.widgets,...this.props.linterContext};PerseusLinter.runLinter(parsedMarkdown,fullLinterContext,true);this._translationLinter.applyLintErrors(parsedMarkdown,[...this.state.translationLintErrors,...this.props.legacyPerseusLint||[]]);}const markdownContents=this.outputMarkdown(parsedMarkdown,{baseElements:apiOptions.baseElements});const className=classNames$1({[ClassNames.RENDERER]:true,[ClassNames.RESPONSIVE_RENDERER]:true,[ClassNames.TWO_COLUMN_RENDERER]:this._isTwoColumn});this.lastRenderedMarkdown=jsxRuntimeExports.jsx(DefinitionProvider,{children:jsxRuntimeExports.jsx("div",{className:className,children:markdownContents})});return this.lastRenderedMarkdown}constructor(props){super(props),this._widgetContainers=new Map,this.getApiOptions=()=>{return {...ApiOptions.defaults,...this.props.apiOptions}},this._getInitialWidgetState=props=>{const allWidgetInfo=applyDefaultsToWidgets(props.widgets);return {widgetInfo:allWidgetInfo,widgetProps:this._getAllWidgetsStartProps(allWidgetInfo,props)}},this._getAllWidgetsStartProps=(allWidgetInfo,props)=>{const{apiOptions,problemNum}=props;const widgetsStartProps={};const{strings}=this.props;entries(allWidgetInfo).forEach(([key,widgetInfo])=>{widgetsStartProps[key]=getRendererPropsForWidgetInfo(widgetInfo,strings,problemNum);});apiOptions?.onWidgetStartProps?.(widgetsStartProps);return widgetsStartProps},this._getDefaultWidgetInfo=widgetId=>{const widgetIdParts=Util.rTypeFromWidgetId.exec(widgetId);if(widgetIdParts==null){return {}}return {type:widgetIdParts[1],graded:true,options:{}}},this._getWidgetInfo=widgetId=>{return this.state.widgetInfo[widgetId]||this._getDefaultWidgetInfo(widgetId)},this.renderWidget=(impliedType,id,state)=>{const widgetInfo=this.state.widgetInfo[id];if(widgetInfo&&widgetInfo.alignment==="full-width"){state.foundFullWidth=true;}if(widgetInfo){const type=widgetInfo&&widgetInfo.type||impliedType;const shouldHighlight=_.contains(this.props.highlightedWidgets,id);return jsxRuntimeExports.jsx(WidgetContainer,{id:id,ref:node=>{const containerId=makeContainerId(id);if(node!=null){this._widgetContainers.set(containerId,node);}else {this._widgetContainers.delete(containerId);}},type:type,initialProps:this.getWidgetProps(id),shouldHighlight:shouldHighlight,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"widget")},makeContainerId(id))}return null},this.getSerializedState=widgetProps=>{return mapObject(widgetProps||this.state.widgetProps,(props,widgetId)=>{const widget=this.getWidgetInstance(widgetId);if(widget&&widget.getSerializedState){return widget.getSerializedState()}return props})},this.restoreSerializedState=(serializedState,callback)=>{const serializedWidgetIds=_.keys(serializedState);const widgetPropIds=_.keys(this.state.widgetProps);if(serializedWidgetIds.length!==widgetPropIds.length||_.intersection(serializedWidgetIds,widgetPropIds).length!==serializedWidgetIds.length){Log.error("Refusing to restore bad serialized state:",Errors.Internal,{loggedMetadata:{serializedState:JSON.stringify(serializedState),currentProps:JSON.stringify(this.state.widgetProps)}});return}let numCallbacks=1;const fireCallback=()=>{--numCallbacks;if(callback&&numCallbacks===0){callback();}};this.setState({widgetProps:mapObject(serializedState,(props,widgetId)=>{const widget=this.getWidgetInstance(widgetId);if(widget&&widget.restoreSerializedState){++numCallbacks;const restoreResult=widget.restoreSerializedState(props,fireCallback);return _.extend({},this.state.widgetProps[widgetId],restoreResult)}return props})},()=>{setTimeout(fireCallback,0);});},this.findInternalWidgets=filterCriterion=>{let filterFunc;if(typeof filterCriterion==="string"){if(filterCriterion.indexOf(" ")!==-1){const widgetId=filterCriterion;filterFunc=(id,widgetInfo,widget)=>id===widgetId;}else {const widgetType=filterCriterion;filterFunc=(id,widgetInfo,widget)=>{return widgetInfo.type===widgetType};}}else {filterFunc=filterCriterion;}const results=this.widgetIds.filter(id=>{const widgetInfo=this._getWidgetInfo(id);const widget=this.getWidgetInstance(id);return filterFunc(id,widgetInfo,widget)}).map(this.getWidgetInstance);return results},this.findWidgets=filterCriterion=>{return [...this.findInternalWidgets(filterCriterion),...this.props.findExternalWidgets(filterCriterion)]},this.getWidgetInstance=id=>{const ref=this._widgetContainers.get(makeContainerId(id));if(!ref){return null}return ref.getWidget()},this._onWidgetFocus=(id,focusPath=[])=>{if(!_.isArray(focusPath)){throw new PerseusError("widget props.onFocus focusPath must be an Array, "+"but was"+JSON.stringify(focusPath),Errors.Internal)}this._setCurrentFocus([id].concat(focusPath));},this._onWidgetBlur=(id,blurPath)=>{const blurringFocusPath=this._currentFocus;const fullPath=[id].concat(blurPath);if(!_.isEqual(fullPath,blurringFocusPath)){return}_.defer(()=>{if(_.isEqual(this._currentFocus,blurringFocusPath)){this._setCurrentFocus(null);}});},this.getContent=(props,state)=>{return state.jiptContent||props.content},this.shouldRenderJiptPlaceholder=(props,state)=>{return getDependencies().JIPT.useJIPT&&state.jiptContent==null&&props.content.indexOf("crwdns")!==-1},this.replaceJiptContent=(content,paragraphIndex)=>{if(paragraphIndex==null){this.setState({jiptContent:content});}else {const codeFenceRegex=/^\s*(`{3,}|~{3,})\s*(\S+)?\s*\n([\s\S]+?)\s*\1\s*$/;if(codeFenceRegex.test(content));else if(/\S\n\s*\n\S/.test(content)){content="$\\large{\\red{\\text{Please translate each "+"paragraph to a single paragraph.}}}$";}else if(/^\s*$/.test(content)){content="$\\large{\\red{\\text{Translated paragraph is "+"currently empty}}}$";}const allContent=this.getContent(this.props,this.state);const paragraphs=JiptParagraphs.parseToArray(allContent);paragraphs[paragraphIndex]=content;this.setState({jiptContent:JiptParagraphs.joinFromArray(paragraphs)});}},this.outputMarkdown=(ast,state)=>{if(_.isArray(ast)){const oldKey=state.key;const result=[];let lastWasString=false;for(let i=0;i<ast.length;i++){state.key=i;state.paragraphIndex=i;const nodeOut=this.outputMarkdown(ast[i],state);const isString=typeof nodeOut==="string";if(typeof nodeOut==="string"&&lastWasString){result[result.length-1]+=nodeOut;}else {result.push(nodeOut);}lastWasString=isString;}state.key=oldKey;return result}this._foundTextNodes=false;state.foundFullWidth=false;const output=this.outputNested(ast,state);let className;if(this.translationIndex!=null){className=null;}else {className=classNames$1({"perseus-paragraph-centered":!this._foundTextNodes,"perseus-paragraph-full-width":state.foundFullWidth&&ast.content.length===1});}return jsxRuntimeExports.jsx(QuestionParagraph,{className:className,translationIndex:this.translationIndex,paragraphIndex:state.paragraphIndex,inline:this.props.inline,children:jsxRuntimeExports.jsx(ErrorBoundary,{children:output})},state.key)},this.outputNested=(ast,state)=>{if(_.isArray(ast)){const oldKey=state.key;const result=[];let lastWasString=false;for(let i=0;i<ast.length;i++){state.key=i;const nodeOut=this.outputNested(ast[i],state);const isString=typeof nodeOut==="string";if(typeof nodeOut==="string"&&lastWasString){result[result.length-1]+=nodeOut;}else {result.push(nodeOut);}lastWasString=isString;}state.key=oldKey;return result}return this.outputNode(ast,this.outputNested,state)},this.outputNode=(node,nestedOutput,state)=>{const apiOptions=this.getApiOptions();const imagePlaceholder=apiOptions.imagePlaceholder;if(node.type==="widget"){const widgetPlaceholder=apiOptions.widgetPlaceholder;if(widgetPlaceholder){return widgetPlaceholder}this._foundTextNodes=true;if(this.widgetIds.includes(node.id)){return jsxRuntimeExports.jsx("span",{className:"renderer-widget-error",children:["Widget [[","☃"," ",node.id,"]] already exists."].join("")},state.key)}this.widgetIds.push(node.id);return this.renderWidget(node.widgetType,node.id,state)}if(node.type==="blockMath"){const content=preprocessTex(node.content);const innerStyle={overflowX:"auto",overflowY:"hidden",paddingTop:10,paddingBottom:10,marginTop:-10,marginBottom:-10};if(apiOptions.isMobile){const margin=16;const outerStyle={marginLeft:-16,marginRight:-16};const horizontalPadding={paddingLeft:margin,paddingRight:margin};const mobileInnerStyle={...innerStyle,...styles$G.mobileZoomableParentFix};return jsxRuntimeExports.jsx("div",{className:"perseus-block-math",style:outerStyle,children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx("div",{className:"perseus-block-math-inner",style:{...mobileInnerStyle,...horizontalPadding},children:jsxRuntimeExports.jsx(ZoomableTeX,{children:content})})})},state.key)}return jsxRuntimeExports.jsx("div",{className:"perseus-block-math",children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx("div",{className:"perseus-block-math-inner",style:innerStyle,children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(Tex,{setAssetStatus:setAssetStatus,children:content})})})})},state.key)}if(node.type==="math"){const tex=node.content;return jsxRuntimeExports.jsx("span",{style:{whiteSpace:"nowrap"},children:jsxRuntimeExports.jsxs(ErrorBoundary,{children:[jsxRuntimeExports.jsx("span",{}),jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(Tex,{onRender:this.props.onRender,setAssetStatus:setAssetStatus,children:tex})}),jsxRuntimeExports.jsx("span",{})]})},state.key)}if(node.type==="image"){if(imagePlaceholder){return imagePlaceholder}const extraAttrs=_.has(this.props.images,node.target)?this.props.images[node.target]:null;const responsive=!state.inTable;return jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{setAssetStatus:setAssetStatus,src:PerseusMarkdown.sanitizeUrl(node.target),alt:node.alt,title:node.title,responsive:responsive,onUpdate:this.props.onRender,zoomToFullSizeOnMobile:apiOptions.isMobile&&apiOptions.isArticle,...extraAttrs})})},state.key)}if(node.type==="columns"){this._isTwoColumn=true;return jsxRuntimeExports.jsx(ErrorBoundary,{children:PerseusMarkdown.ruleOutput(node,nestedOutput,state)},state.key)}if(node.type==="text"){if(rContainsNonWhitespace.test(node.content)){this._foundTextNodes=true;}if(imagePlaceholder&&rImageURL.test(node.content)){return imagePlaceholder}return node.content}if(node.type==="table"||node.type==="titledTable"){const output=PerseusMarkdown.ruleOutput(node,nestedOutput,{...state,isMobile:apiOptions.isMobile,inTable:true});if(!apiOptions.isMobile){return output}const outerStyle={marginLeft:-16,marginRight:-16};const innerStyle={paddingLeft:0,paddingRight:0};const mobileInnerStyle={...innerStyle,...styles$G.mobileZoomableParentFix};const wrappedOutput=jsxRuntimeExports.jsx("div",{style:{...mobileInnerStyle,overflowX:"auto"},children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(Zoomable,{animateHeight:true,children:output})})});return jsxRuntimeExports.jsx("div",{style:outerStyle,children:wrappedOutput})}return jsxRuntimeExports.jsx(ErrorBoundary,{children:PerseusMarkdown.ruleOutput(node,nestedOutput,state)},state.key)},this.handleRender=prevProps=>{const onRender=this.props.onRender;const oldOnRender=prevProps.onRender;if(onRender!==noopOnRender||oldOnRender!==noopOnRender){const $images=$(ReactDOM__default.findDOMNode(this)).find("img");if(oldOnRender!==noopOnRender){$images.off("load",oldOnRender);}if(onRender!==noopOnRender){$images.on("load",onRender);}}onRender();},this._setCurrentFocus=path=>{const apiOptions=this.getApiOptions();if(!isIdPathPrefix(path,this._currentFocus)){const prevFocus=this._currentFocus;if(prevFocus){this.blurPath(prevFocus);}this._currentFocus=path;apiOptions.onFocusChange(this._currentFocus,prevFocus);}},this.focus=()=>{let id;let focusResult;for(let i=0;i<this.widgetIds.length;i++){const widgetId=this.widgetIds[i];const widget=this.getWidgetInstance(widgetId);const widgetFocusResult=widget?.focus?.();if(widgetFocusResult){id=widgetId;focusResult=widgetFocusResult;break}}if(id){let path;if(typeof focusResult==="object"){path=[id].concat(focusResult.path||[]);Log.error("Renderer received a focus result of type 'object' "+"instead of the expected type 'boolean'",Errors.Internal,{loggedMetadata:{focusResult:JSON.stringify(focusResult),currentProps:JSON.stringify(this.state.widgetProps)}});}else {path=[id];}this._setCurrentFocus(path);return true}},this.getDOMNodeForPath=path=>{const widgetId=_.first(path);const interWidgetPath=_.rest(path);const widget=this.getWidgetInstance(widgetId);if(widget?.getDOMNodeForPath){return widget.getDOMNodeForPath(interWidgetPath)}if(interWidgetPath.length===0){return ReactDOM__default.findDOMNode(widget)}},this.getInputPaths=()=>{const inputPaths=[];this.widgetIds.forEach(widgetId=>{const widget=this.getWidgetInstance(widgetId);if(widget&&widget.getInputPaths){const widgetInputPaths=widget.getInputPaths();widgetInputPaths.forEach(inputPath=>{const relativeInputPath=[widgetId].concat(inputPath);inputPaths.push(relativeInputPath);});}});return inputPaths},this.focusPath=path=>{if(_.isEqual(this._currentFocus,path)){return}if(this._currentFocus){this.blurPath(this._currentFocus);}const widgetId=_.first(path);const interWidgetPath=_.rest(path);const focusWidget=this.getWidgetInstance(widgetId);focusWidget?.focusInputPath?.(interWidgetPath);},this.blurPath=path=>{if(!_.isEqual(this._currentFocus,path)){return}const widgetId=_.first(path);const interWidgetPath=_.rest(path);const widget=this.getWidgetInstance(widgetId);if(widget){const blurWidget=this.getWidgetInstance(widgetId);blurWidget?.blurInputPath?.(interWidgetPath);}},this.blur=()=>{if(this._currentFocus){this.blurPath(this._currentFocus);}},this.serialize=()=>{const state={};_.each(this.state.widgetInfo,function(info,id){const widget=this.getWidgetInstance(id);const s=widget.serialize();if(!_.isEmpty(s)){state[id]=s;}},this);return state},this._setWidgetProps=(id,newProps,cb,silent)=>{this.setState(prevState=>{const widgetProps={...prevState.widgetProps,[id]:{...prevState.widgetProps[id],...newProps}};const lastUsedWidgetId=silent?prevState.lastUsedWidgetId:id;if(!silent){this.props.onSerializedStateUpdated(this.getSerializedState(widgetProps));}return {lastUsedWidgetId,widgetProps}},()=>{setTimeout(()=>{const cbResult=cb&&cb();if(!silent){this.props.onInteractWithWidget(id);}if(cbResult!==false){this._setCurrentFocus([id]);}},0);});},this.setInputValue=(path,newValue,cb)=>{const widgetId=_.first(path);const interWidgetPath=_.rest(path);const widget=this.getWidgetInstance(widgetId);widget?.setInputValue?.(interWidgetPath,newValue,cb);},this.getWidgetIds=()=>{return this.widgetIds},this.handletranslationLintErrors=lintErrors=>{if(!this._isMounted){return}this.setState({translationLintErrors:lintErrors});};this._translationLinter=new TranslationLinter;this.state={jiptContent:null,translationLintErrors:[],lastUsedWidgetId:null,...this._getInitialWidgetState(props)};}}Renderer.contextType=PerseusI18nContext;Renderer.defaultProps={content:"",widgets:{},images:{},highlightedWidgets:[],questionCompleted:false,showSolutions:"none",onRender:noopOnRender,onInteractWithWidget:function(){},findExternalWidgets:()=>[],alwaysUpdate:false,reviewMode:false,serializedState:null,onSerializedStateUpdated:()=>{},linterContext:PerseusLinter.linterContextDefault};const styles$G={mobileZoomableParentFix:{transform:"translate3d(0,0,0)"}};
1665
+ function getWidgetTypeByWidgetId(widgetId,widgetMap){const widget=widgetMap[widgetId];return widget?.type??null}function getWidgetSubTypeByWidgetId(widgetId,widgetMap){const widget=widgetMap[widgetId];const widgetType=widget?.type??null;switch(widgetType){case "interactive-graph":const graph=widget.options.graph;return graph?.type??null;default:return null}}function contentHasWidgetType(type,content,widgetMap){return getWidgetIdsFromContentByType(type,content,widgetMap).length>0}function getWidgetsMapFromItemData(itemData){return itemData.question.widgets}function getWidgetFromWidgetMap(widgetId,widgetMap){return widgetMap[widgetId]??null}function getWidgetsFromWidgetMap(widgetIds,widgetMap){const widgets={};widgetIds.forEach(widgetId=>{const widget=getWidgetFromWidgetMap(widgetId,widgetMap);if(widget){widgets[widgetId]=widget;}});return widgets}
1666
+
1667
+ const rContainsNonWhitespace=/\S/;const rImageURL=/(web\+graphie|https):\/\/[^\s]*/;const noopOnRender=()=>{};const makeContainerId=id=>"container:"+id;const isIdPathPrefix=function(prefixArray,wholeArray){if(prefixArray===null||wholeArray===null){return prefixArray===wholeArray}return _.every(prefixArray,(elem,i)=>{if(wholeArray!=null){return _.isEqual(elem,wholeArray[i])}})};function isDifferentQuestion(prev,next){if(prev.problemNum!==next.problemNum){return true}function answerlessStringified(props){const answerful={question:{content:props.content,widgets:props.widgets,images:{}},hints:[],answerArea:getDefaultAnswerArea()};const answerless=splitPerseusItem(answerful);return JSON.stringify(answerless)}return answerlessStringified(prev)!==answerlessStringified(next)}class Renderer extends React.Component{componentDidMount(){this._isMounted=true;this.handleRender({});this._currentFocus=null;if(this.props.serializedState){this.restoreSerializedState(this.props.serializedState);}if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}}UNSAFE_componentWillReceiveProps(nextProps){if(isDifferentQuestion(this.props,nextProps)){this.setState(this._getInitialWidgetState(nextProps));}}shouldComponentUpdate(nextProps,nextState){if(this.props.alwaysUpdate){return true}const stateChanged=!_.isEqual(this.state,nextState);const propsChanged=!_.isEqual(this.props,nextProps);return propsChanged||stateChanged}UNSAFE_componentWillUpdate(nextProps,nextState){const oldJipt=this.shouldRenderJiptPlaceholder(this.props,this.state);const newJipt=this.shouldRenderJiptPlaceholder(nextProps,nextState);const oldContent=this.getContent(this.props,this.state);const newContent=this.getContent(nextProps,nextState);const oldHighlightedWidgets=this.props.highlightedWidgets;const newHighlightedWidgets=nextProps.highlightedWidgets;this.reuseMarkdown=!oldJipt&&!newJipt&&oldContent===newContent&&_.isEqual(this.state.translationLintErrors,nextState.translationLintErrors)&&(!this.props.linterContext.highlightLint||_.isEqual(this.props.widgets,nextProps.widgets))&&this.props.linterContext.highlightLint===nextProps.linterContext.highlightLint&&oldHighlightedWidgets===newHighlightedWidgets;}componentDidUpdate(prevProps,prevState){this.handleRender(prevProps);this.widgetIds.forEach(id=>{const container=this._widgetContainers.get(makeContainerId(id));container?.replaceWidgetProps(this.getWidgetProps(id));});if(this.props.serializedState&&!_.isEqual(this.props.serializedState,this.getSerializedState())){this.restoreSerializedState(this.props.serializedState);}if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}}componentWillUnmount(){this.widgetIds=[];if(this.translationIndex!=null){getDependencies().rendererTranslationComponents.removeComponentAtIndex(this.translationIndex);}this._isMounted=false;}_getAllWidgetsStartUserInput(props){const widgetMap=props.widgets;const startUserInput={};entries(widgetMap).forEach(([id,widgetInfo])=>{const widgetExports=getWidgetExport(widgetInfo.type);if(widgetInfo.static&&widgetExports?.getCorrectUserInput){startUserInput[id]=widgetExports.getCorrectUserInput(widgetInfo.options);}else if(widgetExports?.getStartUserInput){startUserInput[id]=widgetExports.getStartUserInput(widgetInfo.options,props.problemNum??0);}});return startUserInput}getWidgetProps(widgetId){const apiOptions=this.getApiOptions();const widgetProps=this.state.widgetProps[widgetId]||{};const widgetInfo=this.state.widgetInfo[widgetId];const reviewModeRubric=this.props.reviewMode&&widgetInfo?widgetInfo.options:null;if(!this._interactionTrackers){this._interactionTrackers={};}let interactionTracker=this._interactionTrackers[widgetId];if(!interactionTracker){interactionTracker=this._interactionTrackers[widgetId]=new InteractionTracker(apiOptions.trackInteraction,widgetInfo&&widgetInfo.type,widgetId,getTracking(widgetInfo&&widgetInfo.type));}return {...widgetProps,userInput:this.state.userInput[widgetId],widgetId:widgetId,alignment:widgetInfo&&widgetInfo.alignment,static:widgetInfo?.static,problemNum:this.props.problemNum,apiOptions:this.getApiOptions(),keypadElement:this.props.keypadElement,questionCompleted:this.props.questionCompleted,showSolutions:this.props.showSolutions,onFocus:_.partial(this._onWidgetFocus,widgetId),onBlur:_.partial(this._onWidgetBlur,widgetId),findWidgets:this.findWidgets,reviewModeRubric:reviewModeRubric,reviewMode:this.props.reviewMode,onChange:(newProps,cb,silent=false)=>{this._setWidgetProps(widgetId,newProps,cb,silent);},handleUserInput:(newUserInput,cb,silent=false)=>{this._setUserInput(widgetId,newUserInput,cb,silent);},trackInteraction:interactionTracker.track,isLastUsedWidget:widgetId===this.state.lastUsedWidgetId}}emptyWidgets(){return emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.getUserInputMap(),this.context.locale)}handleStateUpdate(id,cb,silent){this.setState(prevState=>{const lastUsedWidgetId=silent?prevState.lastUsedWidgetId:id;return {lastUsedWidgetId}},()=>{setTimeout(()=>{const cbResult=cb&&cb();if(!silent){this.props.onInteractWithWidget(id);}if(cbResult!==false){this._setCurrentFocus([id]);}},0);});}_setUserInput(id,nextUserInput,cb,silent){this.setState(prevState=>{const userInput=nextUserInput!=null?{...this.state.userInput,[id]:nextUserInput}:prevState.userInput;return {userInput}},()=>{this.handleStateUpdate(id,cb,silent);});}_setWidgetProps(id,nextWidgetProps,cb,silent){this.setState(prevState=>{const widgetProps=nextWidgetProps?{...prevState.widgetProps,[id]:{...prevState.widgetProps[id],...nextWidgetProps}}:prevState.widgetProps;return {widgetProps}},()=>{this.handleStateUpdate(id,cb,silent);});}getUserInput(){return this.widgetIds.map(id=>{const widget=this.getWidgetInstance(id);if(widget&&widget.getUserInput){return widget.getUserInput()}})}getUserInputMap(){const userInputMap={};this.widgetIds.forEach(id=>{const widget=this.getWidgetInstance(id);if(this.state.userInput[id]){userInputMap[id]=this.state.userInput[id];}else if(widget?.getUserInputMap){userInputMap[id]=widget.getUserInputMap();}else if(widget?.getUserInput){userInputMap[id]=widget.getUserInput();}});return userInputMap}getPromptJSON(){const{content}=this.props;const widgetJSON={};this.widgetIds.forEach(id=>{const widget=this.getWidgetInstance(id);widgetJSON[id]=widget?.getPromptJSON?.()||{};});return {content,widgets:widgetJSON}}score(){const scores=scoreWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.getUserInputMap(),this.context.locale);const combinedScore=flattenScores(scores);return combinedScore}render(){const apiOptions=this.getApiOptions();if(this.reuseMarkdown){return this.lastRenderedMarkdown}const content=this.getContent(this.props,this.state);this.widgetIds=[];if(this.shouldRenderJiptPlaceholder(this.props,this.state)){if(!this.translationIndex){this.translationIndex=getDependencies().rendererTranslationComponents.addComponent(this);}if(!apiOptions.isArticle){return jsxRuntimeExports.jsx(DefinitionProvider,{children:jsxRuntimeExports.jsx("div",{"data-perseus-component-index":this.translationIndex,children:content})})}}this._isTwoColumn=false;const parsedMarkdown=this.props.inline?PerseusMarkdown.parseInline(content,{isJipt:this.translationIndex!=null}):PerseusMarkdown.parse(content,{isJipt:this.translationIndex!=null});if(this.props.linterContext.highlightLint){const fullLinterContext={content:this.props.content,widgets:this.props.widgets,...this.props.linterContext};PerseusLinter.runLinter(parsedMarkdown,fullLinterContext,true);this._translationLinter.applyLintErrors(parsedMarkdown,[...this.state.translationLintErrors,...this.props.legacyPerseusLint||[]]);}const markdownContents=this.outputMarkdown(parsedMarkdown,{baseElements:apiOptions.baseElements});const className=classNames$1({[ClassNames.RENDERER]:true,[ClassNames.RESPONSIVE_RENDERER]:true,[ClassNames.TWO_COLUMN_RENDERER]:this._isTwoColumn});this.lastRenderedMarkdown=jsxRuntimeExports.jsx(DefinitionProvider,{children:jsxRuntimeExports.jsx("div",{className:className,children:markdownContents})});return this.lastRenderedMarkdown}constructor(props){super(props),this._widgetContainers=new Map,this.getApiOptions=()=>{return {...ApiOptions.defaults,...this.props.apiOptions}},this._getInitialWidgetState=props=>{const allWidgetInfo=applyDefaultsToWidgets(props.widgets);return {widgetInfo:allWidgetInfo,widgetProps:this._getAllWidgetsStartProps(allWidgetInfo,props),userInput:this._getAllWidgetsStartUserInput(props)}},this._getAllWidgetsStartProps=(allWidgetInfo,props)=>{const{apiOptions,problemNum}=props;const widgetsStartProps={};const{strings}=this.props;entries(allWidgetInfo).forEach(([key,widgetInfo])=>{widgetsStartProps[key]=getRendererPropsForWidgetInfo(widgetInfo,strings,problemNum);});apiOptions?.onWidgetStartProps?.(widgetsStartProps);return widgetsStartProps},this._getDefaultWidgetInfo=widgetId=>{const widgetIdParts=Util.rTypeFromWidgetId.exec(widgetId);if(widgetIdParts==null){return {}}return {type:widgetIdParts[1],graded:true,options:{}}},this._getWidgetInfo=widgetId=>{return this.state.widgetInfo[widgetId]||this._getDefaultWidgetInfo(widgetId)},this.renderWidget=(impliedType,id,state)=>{const widgetInfo=this.state.widgetInfo[id];if(widgetInfo&&widgetInfo.alignment==="full-width"){state.foundFullWidth=true;}if(widgetInfo){const type=widgetInfo&&widgetInfo.type||impliedType;const shouldHighlight=_.contains(this.props.highlightedWidgets,id);return jsxRuntimeExports.jsx(WidgetContainer,{id:id,ref:node=>{const containerId=makeContainerId(id);if(node!=null){this._widgetContainers.set(containerId,node);}else {this._widgetContainers.delete(containerId);}},type:type,initialProps:this.getWidgetProps(id),shouldHighlight:shouldHighlight,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"widget")},makeContainerId(id))}return null},this.getSerializedState=widgetProps=>{return mapObject(widgetProps||this.state.widgetProps,(props,widgetId)=>{const widget=this.getWidgetInstance(widgetId);if(widget&&widget.getSerializedState){return excludeDenylistKeys(widget.getSerializedState())}return props})},this.restoreSerializedState=(serializedState,callback)=>{const serializedWidgetIds=_.keys(serializedState);const widgetPropIds=_.keys(this.state.widgetProps);if(serializedWidgetIds.length!==widgetPropIds.length||_.intersection(serializedWidgetIds,widgetPropIds).length!==serializedWidgetIds.length){Log.error("Refusing to restore bad serialized state:",Errors.Internal,{loggedMetadata:{serializedState:JSON.stringify(serializedState),currentProps:JSON.stringify(this.state.widgetProps)}});return}let numCallbacks=1;const fireCallback=()=>{--numCallbacks;if(callback&&numCallbacks===0){callback();}};const restoredWidgetProps={};const restoredUserInput={};Object.entries(serializedState).forEach(([widgetId,props])=>{const widget=this.getWidgetInstance(widgetId);const widgetType=getWidgetTypeByWidgetId(widgetId,this.props.widgets);const widgetExport=getWidgetExport(widgetType);if(widget?.restoreSerializedState){++numCallbacks;const restoreResult=widget.restoreSerializedState(props,fireCallback);restoredWidgetProps[widgetId]={...this.state.widgetProps[widgetId],...restoreResult};}else {restoredWidgetProps[widgetId]=props;}if(widgetExport?.getUserInputFromSerializedState){const restoreResult=widgetExport.getUserInputFromSerializedState(props);restoredUserInput[widgetId]=restoreResult;}});this.setState({widgetProps:restoredWidgetProps,userInput:restoredUserInput},()=>{setTimeout(fireCallback,0);});},this.findInternalWidgets=filterCriterion=>{let filterFunc;if(typeof filterCriterion==="string"){if(filterCriterion.indexOf(" ")!==-1){const widgetId=filterCriterion;filterFunc=(id,widgetInfo,widget)=>id===widgetId;}else {const widgetType=filterCriterion;filterFunc=(id,widgetInfo,widget)=>{return widgetInfo.type===widgetType};}}else {filterFunc=filterCriterion;}const results=this.widgetIds.filter(id=>{const widgetInfo=this._getWidgetInfo(id);const widget=this.getWidgetInstance(id);return filterFunc(id,widgetInfo,widget)}).map(this.getWidgetInstance);return results},this.findWidgets=filterCriterion=>{return [...this.findInternalWidgets(filterCriterion),...this.props.findExternalWidgets(filterCriterion)]},this.getWidgetInstance=id=>{const ref=this._widgetContainers.get(makeContainerId(id));if(!ref){return null}return ref.getWidget()},this._onWidgetFocus=(id,focusPath=[])=>{if(!_.isArray(focusPath)){throw new PerseusError("widget props.onFocus focusPath must be an Array, "+"but was"+JSON.stringify(focusPath),Errors.Internal)}this._setCurrentFocus([id].concat(focusPath));},this._onWidgetBlur=(id,blurPath)=>{const blurringFocusPath=this._currentFocus;const fullPath=[id].concat(blurPath);if(!_.isEqual(fullPath,blurringFocusPath)){return}_.defer(()=>{if(_.isEqual(this._currentFocus,blurringFocusPath)){this._setCurrentFocus(null);}});},this.getContent=(props,state)=>{return state.jiptContent||props.content},this.shouldRenderJiptPlaceholder=(props,state)=>{return getDependencies().JIPT.useJIPT&&state.jiptContent==null&&props.content.indexOf("crwdns")!==-1},this.replaceJiptContent=(content,paragraphIndex)=>{if(paragraphIndex==null){this.setState({jiptContent:content});}else {const codeFenceRegex=/^\s*(`{3,}|~{3,})\s*(\S+)?\s*\n([\s\S]+?)\s*\1\s*$/;if(codeFenceRegex.test(content));else if(/\S\n\s*\n\S/.test(content)){content="$\\large{\\red{\\text{Please translate each "+"paragraph to a single paragraph.}}}$";}else if(/^\s*$/.test(content)){content="$\\large{\\red{\\text{Translated paragraph is "+"currently empty}}}$";}const allContent=this.getContent(this.props,this.state);const paragraphs=JiptParagraphs.parseToArray(allContent);paragraphs[paragraphIndex]=content;this.setState({jiptContent:JiptParagraphs.joinFromArray(paragraphs)});}},this.outputMarkdown=(ast,state)=>{if(_.isArray(ast)){const oldKey=state.key;const result=[];let lastWasString=false;for(let i=0;i<ast.length;i++){state.key=i;state.paragraphIndex=i;const nodeOut=this.outputMarkdown(ast[i],state);const isString=typeof nodeOut==="string";if(typeof nodeOut==="string"&&lastWasString){result[result.length-1]+=nodeOut;}else {result.push(nodeOut);}lastWasString=isString;}state.key=oldKey;return result}this._foundTextNodes=false;state.foundFullWidth=false;const output=this.outputNested(ast,state);let className;if(this.translationIndex!=null){className=null;}else {className=classNames$1({"perseus-paragraph-centered":!this._foundTextNodes,"perseus-paragraph-full-width":state.foundFullWidth&&ast.content.length===1});}return jsxRuntimeExports.jsx(QuestionParagraph,{className:className,translationIndex:this.translationIndex,paragraphIndex:state.paragraphIndex,inline:this.props.inline,children:jsxRuntimeExports.jsx(ErrorBoundary,{children:output})},state.key)},this.outputNested=(ast,state)=>{if(_.isArray(ast)){const oldKey=state.key;const result=[];let lastWasString=false;for(let i=0;i<ast.length;i++){state.key=i;const nodeOut=this.outputNested(ast[i],state);const isString=typeof nodeOut==="string";if(typeof nodeOut==="string"&&lastWasString){result[result.length-1]+=nodeOut;}else {result.push(nodeOut);}lastWasString=isString;}state.key=oldKey;return result}return this.outputNode(ast,this.outputNested,state)},this.outputNode=(node,nestedOutput,state)=>{const apiOptions=this.getApiOptions();const imagePlaceholder=apiOptions.imagePlaceholder;if(node.type==="widget"){const widgetPlaceholder=apiOptions.widgetPlaceholder;if(widgetPlaceholder){return widgetPlaceholder}this._foundTextNodes=true;if(this.widgetIds.includes(node.id)){return jsxRuntimeExports.jsx("span",{className:"renderer-widget-error",children:["Widget [[","☃"," ",node.id,"]] already exists."].join("")},state.key)}this.widgetIds.push(node.id);return this.renderWidget(node.widgetType,node.id,state)}if(node.type==="blockMath"){const content=preprocessTex(node.content);const innerStyle={overflowX:"auto",overflowY:"hidden",paddingTop:10,paddingBottom:10,marginTop:-10,marginBottom:-10};if(apiOptions.isMobile){const margin=16;const outerStyle={marginLeft:-16,marginRight:-16};const horizontalPadding={paddingLeft:margin,paddingRight:margin};const mobileInnerStyle={...innerStyle,...styles$G.mobileZoomableParentFix};return jsxRuntimeExports.jsx("div",{className:"perseus-block-math",style:outerStyle,children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx("div",{className:"perseus-block-math-inner",style:{...mobileInnerStyle,...horizontalPadding},children:jsxRuntimeExports.jsx(ZoomableTeX,{children:content})})})},state.key)}return jsxRuntimeExports.jsx("div",{className:"perseus-block-math",children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx("div",{className:"perseus-block-math-inner",style:innerStyle,children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(Tex,{setAssetStatus:setAssetStatus,children:content})})})})},state.key)}if(node.type==="math"){const tex=node.content;return jsxRuntimeExports.jsx("span",{style:{whiteSpace:"nowrap"},children:jsxRuntimeExports.jsxs(ErrorBoundary,{children:[jsxRuntimeExports.jsx("span",{}),jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(Tex,{onRender:this.props.onRender,setAssetStatus:setAssetStatus,children:tex})}),jsxRuntimeExports.jsx("span",{})]})},state.key)}if(node.type==="image"){if(imagePlaceholder){return imagePlaceholder}const extraAttrs=_.has(this.props.images,node.target)?this.props.images[node.target]:null;const responsive=!state.inTable;return jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{setAssetStatus:setAssetStatus,src:PerseusMarkdown.sanitizeUrl(node.target),alt:node.alt,title:node.title,responsive:responsive,onUpdate:this.props.onRender,zoomToFullSizeOnMobile:apiOptions.isMobile&&apiOptions.isArticle,...extraAttrs})})},state.key)}if(node.type==="columns"){this._isTwoColumn=true;return jsxRuntimeExports.jsx(ErrorBoundary,{children:PerseusMarkdown.ruleOutput(node,nestedOutput,state)},state.key)}if(node.type==="text"){if(rContainsNonWhitespace.test(node.content)){this._foundTextNodes=true;}if(imagePlaceholder&&rImageURL.test(node.content)){return imagePlaceholder}return node.content}if(node.type==="table"||node.type==="titledTable"){const output=PerseusMarkdown.ruleOutput(node,nestedOutput,{...state,isMobile:apiOptions.isMobile,inTable:true});if(!apiOptions.isMobile){return output}const outerStyle={marginLeft:-16,marginRight:-16};const innerStyle={paddingLeft:0,paddingRight:0};const mobileInnerStyle={...innerStyle,...styles$G.mobileZoomableParentFix};const wrappedOutput=jsxRuntimeExports.jsx("div",{style:{...mobileInnerStyle,overflowX:"auto"},children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(Zoomable,{animateHeight:true,children:output})})});return jsxRuntimeExports.jsx("div",{style:outerStyle,children:wrappedOutput})}return jsxRuntimeExports.jsx(ErrorBoundary,{children:PerseusMarkdown.ruleOutput(node,nestedOutput,state)},state.key)},this.handleRender=prevProps=>{const onRender=this.props.onRender;const oldOnRender=prevProps.onRender;if(onRender!==noopOnRender||oldOnRender!==noopOnRender){const $images=$(ReactDOM__default.findDOMNode(this)).find("img");if(oldOnRender!==noopOnRender){$images.off("load",oldOnRender);}if(onRender!==noopOnRender){$images.on("load",onRender);}}onRender();},this._setCurrentFocus=path=>{const apiOptions=this.getApiOptions();if(!isIdPathPrefix(path,this._currentFocus)){const prevFocus=this._currentFocus;if(prevFocus){this.blurPath(prevFocus);}this._currentFocus=path;apiOptions.onFocusChange(this._currentFocus,prevFocus);}},this.focus=()=>{let id;let focusResult;for(let i=0;i<this.widgetIds.length;i++){const widgetId=this.widgetIds[i];const widget=this.getWidgetInstance(widgetId);const widgetFocusResult=widget?.focus?.();if(widgetFocusResult){id=widgetId;focusResult=widgetFocusResult;break}}if(id){let path;if(typeof focusResult==="object"){path=[id].concat(focusResult.path||[]);Log.error("Renderer received a focus result of type 'object' "+"instead of the expected type 'boolean'",Errors.Internal,{loggedMetadata:{focusResult:JSON.stringify(focusResult),currentProps:JSON.stringify(this.state.widgetProps)}});}else {path=[id];}this._setCurrentFocus(path);return true}},this.getDOMNodeForPath=path=>{const widgetId=_.first(path);const interWidgetPath=_.rest(path);const widget=this.getWidgetInstance(widgetId);if(widget?.getDOMNodeForPath){return widget.getDOMNodeForPath(interWidgetPath)}if(interWidgetPath.length===0){return ReactDOM__default.findDOMNode(widget)}},this.getInputPaths=()=>{const inputPaths=[];this.widgetIds.forEach(widgetId=>{const widget=this.getWidgetInstance(widgetId);if(widget&&widget.getInputPaths){const widgetInputPaths=widget.getInputPaths();widgetInputPaths.forEach(inputPath=>{const relativeInputPath=[widgetId].concat(inputPath);inputPaths.push(relativeInputPath);});}});return inputPaths},this.focusPath=path=>{if(_.isEqual(this._currentFocus,path)){return}if(this._currentFocus){this.blurPath(this._currentFocus);}const widgetId=_.first(path);const interWidgetPath=_.rest(path);const focusWidget=this.getWidgetInstance(widgetId);focusWidget?.focusInputPath?.(interWidgetPath);},this.blurPath=path=>{if(!_.isEqual(this._currentFocus,path)){return}const widgetId=_.first(path);const interWidgetPath=_.rest(path);const widget=this.getWidgetInstance(widgetId);if(widget){const blurWidget=this.getWidgetInstance(widgetId);blurWidget?.blurInputPath?.(interWidgetPath);}},this.blur=()=>{if(this._currentFocus){this.blurPath(this._currentFocus);}},this.serialize=()=>{const state={};_.each(this.state.widgetInfo,function(info,id){const widget=this.getWidgetInstance(id);const s=widget.serialize();if(!_.isEmpty(s)){state[id]=s;}},this);return state},this.setInputValue=(path,newValue,cb)=>{const widgetId=_.first(path);const interWidgetPath=_.rest(path);const widget=this.getWidgetInstance(widgetId);widget?.setInputValue?.(interWidgetPath,newValue,cb);},this.getWidgetIds=()=>{return this.widgetIds},this.handletranslationLintErrors=lintErrors=>{if(!this._isMounted){return}this.setState({translationLintErrors:lintErrors});};this._translationLinter=new TranslationLinter;this.state={jiptContent:null,translationLintErrors:[],lastUsedWidgetId:null,...this._getInitialWidgetState(props)};}}Renderer.contextType=PerseusI18nContext;Renderer.defaultProps={content:"",widgets:{},images:{},highlightedWidgets:[],questionCompleted:false,showSolutions:"none",onRender:noopOnRender,onInteractWithWidget:function(){},findExternalWidgets:()=>[],alwaysUpdate:false,reviewMode:false,serializedState:null,linterContext:PerseusLinter.linterContextDefault};const styles$G={mobileZoomableParentFix:{transform:"translate3d(0,0,0)"}};
1674
1668
 
1675
1669
  const{captureScratchpadTouchStart: captureScratchpadTouchStart$3}=Util;const InputWithExamples=forwardRef(({shouldShowExamples=true,onFocus=()=>{},onBlur=()=>{},disabled=false,linterContext=PerseusLinter.linterContextDefault,className="",...props},ref)=>{const context=React.useContext(PerseusI18nContext);const inputRef=React.useRef(null);const[inputFocused,setInputFocused]=React.useState(false);const id=useId();const ariaId=`aria-for-${id}`;useImperativeHandle(ref,()=>({current:inputRef.current,focus:()=>{if(inputRef.current){inputRef.current.focus();}},blur:()=>{if(inputRef.current){inputRef.current.blur();}}}));const getInputClassName=()=>{let inputClassName=ClassNames.INPUT;if(inputFocused){inputClassName+=" "+ClassNames.FOCUSED;}if(className){inputClassName+=" "+className;}return inputClassName};const handleFocus=()=>{onFocus();setInputFocused(true);};const handleBlur=()=>{onBlur();setInputFocused(false);};const renderInput=()=>{const examplesAria=shouldShowExamples?`${props.examples[0]}
1676
1670
  ${props.examples.slice(1).join(", or\n")}`.replaceAll("*","").replaceAll("$","").replaceAll("\\ \\text{pi}"," pi").replaceAll("\\ "," and "):"";const inputProps={id:id,"aria-describedby":shouldShowExamples?ariaId:undefined,ref:inputRef,className:getInputClassName(),labelText:props.labelText,value:props.value,onFocus:handleFocus,onBlur:handleBlur,disabled:disabled,style:props.style,onChange:props.onChange,onTouchStart:captureScratchpadTouchStart$3,autoCapitalize:"off",autoComplete:"off",autoCorrect:"off",spellCheck:"false"};return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(TextInput,{...inputProps}),jsxRuntimeExports.jsx("span",{id:ariaId,style:{display:"none"},children:examplesAria})]})};const renderTooltipContent=()=>{return jsxRuntimeExports.jsx(TooltipContent,{children:jsxRuntimeExports.jsx("div",{id:id,className:"input-with-examples-tooltip",children:jsxRuntimeExports.jsx(Renderer,{content:examplesContent,linterContext:PerseusLinter.pushContextStack(linterContext,"input-with-examples"),strings:context.strings})})})};const examplesContent=props.examples.length<=2?props.examples.join(" "):props.examples.map((example,index)=>{return index===0&&example.startsWith("**")?`${example}
1677
1671
  `:`- ${example}`}).join("\n");const showExamplesTooltip=shouldShowExamples&&inputFocused;return jsxRuntimeExports.jsx(Tooltip,{content:renderTooltipContent(),opened:showExamplesTooltip,placement:"bottom",children:renderInput()})});
1678
1672
 
1679
- const formExamples={integer:function(_,strings){return strings.integerExample},proper:function(options,strings){if(options.simplify==="optional"){return strings.properExample}return strings.simplifiedProperExample},improper:function(options,strings){if(options.simplify==="optional"){return strings.improperExample}return strings.simplifiedImproperExample},mixed:function(_,strings){return strings.mixedExample},decimal:function(_,strings){return strings.decimalExample},percent:function(_,strings){return strings.percentExample},pi:function(_,strings){return strings.piExample}};class InputNumber extends React.Component{static getUserInputFromProps(props){return {currentValue:props.currentValue}}getUserInput(){return InputNumber.getUserInputFromProps(this.props)}getPromptJSON(){return getPromptJSON$s(this.props,this.getUserInput())}examples(){const{strings}=this.context;const type=this.props.answerType;const forms=inputNumberAnswerTypes[type].forms.split(/\s*,\s*/);const examples=forms.map(form=>formExamples[form](this.props,strings));return [strings.yourAnswer].concat(examples)}render(){if(this.props.apiOptions.customKeypad){const input=jsxRuntimeExports.jsx(SimpleKeypadInput,{ref:"input",value:this.props.currentValue,keypadElement:this.props.keypadElement,onChange:this.handleChange,onFocus:this._handleFocus,onBlur:this._handleBlur});if(this.props.rightAlign){return jsxRuntimeExports.jsx("div",{className:"perseus-input-right-align",children:input})}return input}const inputStyles=[styles$F.default,this.props.size==="small"?styles$F.small:null,this.props.rightAlign?styles$F.rightAlign:styles$F.leftAlign];if(this.props.reviewMode&&!this.props.currentValue){inputStyles.push(styles$F.answerStateUnanswered);}return jsxRuntimeExports.jsx(InputWithExamples,{ref:"input",value:this.props.currentValue,onChange:this.handleChange,style:inputStyles,examples:this.examples(),shouldShowExamples:this.shouldShowExamples(),onFocus:this._handleFocus,onBlur:this._handleBlur,id:this.props.widgetId,disabled:this.props.apiOptions.readOnly,linterContext:this.props.linterContext})}constructor(...args){super(...args),this.shouldShowExamples=()=>{return this.props.answerType!=="number"},this.handleChange=(newValue,cb)=>{this.props.onChange({currentValue:newValue},cb);},this._handleFocus=()=>{this.props.onFocus([]);},this._handleBlur=()=>{this.props.onBlur([]);},this.focus=()=>{this.refs.input.focus();return true},this.focusInputPath=inputPath=>{this.refs.input.focus();},this.blurInputPath=inputPath=>{if(typeof this.refs.input?.blur==="function"){this.refs.input?.blur();}},this.getInputPaths=()=>{return [[]]},this.setInputValue=(path,newValue,cb)=>{this.props.onChange({currentValue:newValue},cb);};}}InputNumber.contextType=PerseusI18nContext;InputNumber.defaultProps={currentValue:"",size:"normal",answerType:"number",rightAlign:false,apiOptions:ApiOptions.defaults,linterContext:linterContextDefault};const styles$F=StyleSheet.create({default:{width:80,height:"auto"},small:{width:40},leftAlign:{paddingLeft:spacing.xxxSmall_4,paddingRight:0},rightAlign:{textAlign:"right",paddingLeft:0,paddingRight:spacing.xxxSmall_4},answerStateUnanswered:{backgroundColor:"#eee",border:"solid 1px #999"}});function transform$4(widgetOptions){const{simplify,size,answerType,rightAlign}=widgetOptions;return {simplify,size,answerType,rightAlign}}function getOneCorrectAnswerFromRubric(rubric){if(rubric.value==null){return}let answerString=String(rubric.value);if(rubric.inexact&&rubric.maxError){answerString+=" ± "+rubric.maxError;}return answerString}var InputNumber$1 = {name:"input-number",displayName:"Input number (deprecated - use numeric input instead)",hidden:true,widget:InputNumber,isLintable:true,transform: transform$4,getOneCorrectAnswerFromRubric};
1673
+ const formExamples={integer:function(_,strings){return strings.integerExample},proper:function(options,strings){if(options.simplify==="optional"){return strings.properExample}return strings.simplifiedProperExample},improper:function(options,strings){if(options.simplify==="optional"){return strings.improperExample}return strings.simplifiedImproperExample},mixed:function(_,strings){return strings.mixedExample},decimal:function(_,strings){return strings.decimalExample},percent:function(_,strings){return strings.percentExample},pi:function(_,strings){return strings.piExample}};class InputNumber extends React.Component{getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$s(this.props,this.getUserInput())}examples(){const{strings}=this.context;const type=this.props.answerType;const forms=inputNumberAnswerTypes[type].forms.split(/\s*,\s*/);const examples=forms.map(form=>formExamples[form](this.props,strings));return [strings.yourAnswer].concat(examples)}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,currentValue:userInput.currentValue}}render(){if(this.props.apiOptions.customKeypad){const input=jsxRuntimeExports.jsx(SimpleKeypadInput,{ref:"input",value:this.props.userInput.currentValue,keypadElement:this.props.keypadElement,onChange:this.handleChange,onFocus:this._handleFocus,onBlur:this._handleBlur});if(this.props.rightAlign){return jsxRuntimeExports.jsx("div",{className:"perseus-input-right-align",children:input})}return input}const inputStyles=[styles$F.default,this.props.size==="small"?styles$F.small:null,this.props.rightAlign?styles$F.rightAlign:styles$F.leftAlign];if(this.props.reviewMode&&!this.props.userInput.currentValue){inputStyles.push(styles$F.answerStateUnanswered);}return jsxRuntimeExports.jsx(InputWithExamples,{ref:"input",value:this.props.userInput.currentValue,onChange:this.handleChange,style:inputStyles,examples:this.examples(),shouldShowExamples:this.shouldShowExamples(),onFocus:this._handleFocus,onBlur:this._handleBlur,id:this.props.widgetId,disabled:this.props.apiOptions.readOnly,linterContext:this.props.linterContext})}constructor(...args){super(...args),this.shouldShowExamples=()=>{return this.props.answerType!=="number"},this.handleChange=(newValue,cb)=>{this.props.handleUserInput({currentValue:newValue},cb);},this._handleFocus=()=>{this.props.onFocus([]);},this._handleBlur=()=>{this.props.onBlur([]);},this.focus=()=>{this.refs.input.focus();return true},this.focusInputPath=inputPath=>{this.refs.input.focus();},this.blurInputPath=inputPath=>{if(typeof this.refs.input?.blur==="function"){this.refs.input?.blur();}},this.getInputPaths=()=>{return [[]]},this.setInputValue=(path,newValue,cb)=>{this.props.handleUserInput({currentValue:newValue},cb);};}}InputNumber.contextType=PerseusI18nContext;InputNumber.defaultProps={size:"normal",answerType:"number",rightAlign:false,apiOptions:ApiOptions.defaults,linterContext:linterContextDefault,userInput:{currentValue:""}};const styles$F=StyleSheet.create({default:{width:80,height:"auto"},small:{width:40},leftAlign:{paddingLeft:spacing.xxxSmall_4,paddingRight:0},rightAlign:{textAlign:"right",paddingLeft:0,paddingRight:spacing.xxxSmall_4},answerStateUnanswered:{backgroundColor:"#eee",border:"solid 1px #999"}});function transform$6(widgetOptions){const{simplify,size,answerType,rightAlign}=widgetOptions;return {simplify,size,answerType,rightAlign}}function getOneCorrectAnswerFromRubric(rubric){if(rubric.value==null){return}let answerString=String(rubric.value);if(rubric.inexact&&rubric.maxError){answerString+=" ± "+rubric.maxError;}return answerString}function getUserInputFromSerializedState$g(serializedState){return {currentValue:serializedState.currentValue}}var InputNumber$1 = {name:"input-number",displayName:"Input number (deprecated - use numeric input instead)",hidden:true,widget:InputNumber,isLintable:true,transform: transform$6,getOneCorrectAnswerFromRubric,getUserInputFromSerializedState: getUserInputFromSerializedState$g};
1680
1674
 
1681
1675
  const getPromptJSON$r=(renderProps,userInput)=>{return {type:"numeric-input",label:renderProps.labelText,userInput:{value:userInput.currentValue}}};
1682
1676
 
1683
1677
  const NumericExampleStrings={integer:(form,strings)=>strings.integerExample,proper:(form,strings)=>form.simplify==="optional"?strings.properExample:strings.simplifiedProperExample,improper:(form,strings)=>form.simplify==="optional"?strings.improperExample:strings.simplifiedImproperExample,mixed:(form,strings)=>strings.mixedExample,decimal:(form,strings)=>strings.decimalExample,pi:(form,strings)=>strings.piExample};const generateExamples=(answerForms,strings)=>{if(answerForms.length===0){return []}const uniqueForms=getUniqueAnswerForms(answerForms);const examples=uniqueForms.map(form=>{return NumericExampleStrings[form.name](form,strings)});return [strings.yourAnswer].concat(examples)};const shouldShowExamples=answerForms=>{if(answerForms.length===0){return false}const answerFormNames=getUniqueAnswerForms(answerForms).map(form=>form.name);const allFormsAccepted=answerFormNames.length>=Object.keys(NumericExampleStrings).length;return !allFormsAccepted};const getUniqueAnswerForms=function(list){const foundForms=new Set;return list.filter(form=>{if(foundForms.has(form.name)){return false}foundForms.add(form.name);return true})};const unionAnswerForms=function(answerFormsList){const allForms=answerFormsList.flat();const uniqueForms=getUniqueAnswerForms(allForms);const formExampleKeys=Object.keys(NumericExampleStrings);return uniqueForms.sort((a,b)=>{return formExampleKeys.indexOf(a.name)-formExampleKeys.indexOf(b.name)})};
1684
1678
 
1685
- const NumericInputComponent=forwardRef((props,ref)=>{const context=useContext(PerseusI18nContext);const inputRef=useRef(null);const[isFocused,setIsFocused]=useState(false);useImperativeHandle(ref,()=>({current:inputRef.current,focus:()=>{if(inputRef.current){inputRef.current?.focus();setIsFocused(true);}},blur:()=>{if(inputRef.current){inputRef.current?.blur();setIsFocused(false);}}}));const handleChange=(newValue,cb)=>{props.onChange({currentValue:newValue},cb);props.trackInteraction();};const handleFocus=()=>{props.onFocus([]);setIsFocused(true);};const handleBlur=()=>{props.onBlur([]);setIsFocused(false);};const styles=StyleSheet.create({inputWithExamples:{borderRadius:"3px",borderWidth:isFocused?"2px":"1px",display:"inline-block",fontFamily:`Symbola, "Times New Roman", serif`,fontSize:"18px",height:"32px",lineHeight:"18px",padding:isFocused?"4px":"4px 5px",textAlign:props.rightAlign?"right":"left",width:props.size==="small"?40:80}});if(props.apiOptions.customKeypad){const alignmentClass=props.rightAlign?"perseus-input-right-align":undefined;return jsxRuntimeExports.jsx("div",{className:alignmentClass,children:jsxRuntimeExports.jsx(SimpleKeypadInput,{ref:inputRef,value:props.currentValue,keypadElement:props.keypadElement,onChange:handleChange,onFocus:handleFocus,onBlur:handleBlur})})}return jsxRuntimeExports.jsx(InputWithExamples,{ref:inputRef,value:props.currentValue,onChange:handleChange,labelText:props.labelText,examples:generateExamples(props.answerForms,context.strings),shouldShowExamples:shouldShowExamples(props.answerForms),onFocus:handleFocus,onBlur:handleBlur,id:props.widgetId,disabled:props.apiOptions.readOnly,style:styles.inputWithExamples})});
1679
+ const NumericInputComponent=forwardRef((props,ref)=>{const context=useContext(PerseusI18nContext);const inputRef=useRef(null);const[isFocused,setIsFocused]=useState(false);useImperativeHandle(ref,()=>({current:inputRef.current,focus:()=>{if(inputRef.current){inputRef.current?.focus();setIsFocused(true);}},blur:()=>{if(inputRef.current){inputRef.current?.blur();setIsFocused(false);}}}));const handleChange=newValue=>{props.handleUserInput({currentValue:newValue});props.trackInteraction();};const handleFocus=()=>{props.onFocus([]);setIsFocused(true);};const handleBlur=()=>{props.onBlur([]);setIsFocused(false);};const styles=StyleSheet.create({inputWithExamples:{borderRadius:"3px",borderWidth:isFocused?"2px":"1px",display:"inline-block",fontFamily:`Symbola, "Times New Roman", serif`,fontSize:"18px",height:"32px",lineHeight:"18px",padding:isFocused?"4px":"4px 5px",textAlign:props.rightAlign?"right":"left",width:props.size==="small"?40:80}});if(props.apiOptions.customKeypad){const alignmentClass=props.rightAlign?"perseus-input-right-align":undefined;return jsxRuntimeExports.jsx("div",{className:alignmentClass,children:jsxRuntimeExports.jsx(SimpleKeypadInput,{ref:inputRef,value:props.userInput.currentValue,keypadElement:props.keypadElement,onChange:handleChange,onFocus:handleFocus,onBlur:handleBlur})})}return jsxRuntimeExports.jsx(InputWithExamples,{ref:inputRef,value:props.userInput.currentValue,onChange:handleChange,labelText:props.labelText,examples:generateExamples(props.answerForms,context.strings),shouldShowExamples:shouldShowExamples(props.answerForms),onFocus:handleFocus,onBlur:handleBlur,id:props.widgetId,disabled:props.apiOptions.readOnly,style:styles.inputWithExamples})});
1686
1680
 
1687
- class NumericInput extends React.Component{static getUserInputFromProps(props){return {currentValue:props.currentValue}}getUserInput(){return NumericInput.getUserInputFromProps(this.props)}getPromptJSON(){return getPromptJSON$r(this.props,this.getUserInput())}render(){return jsxRuntimeExports.jsx(NumericInputComponent,{...this.props,ref:this.inputRef})}constructor(...args){super(...args),this.inputRef=React.createRef(),this.focus=()=>{this.inputRef.current?.focus();return true},this.focusInputPath=()=>{this.inputRef.current?.focus();},this.blurInputPath=()=>{this.inputRef.current?.blur();},this.getInputPaths=()=>{return [[]]},this.setInputValue=(path,newValue,cb)=>{this.props.onChange({currentValue:newValue},cb);};}}NumericInput.defaultProps={currentValue:"",size:"normal",rightAlign:false,apiOptions:ApiOptions.defaults,coefficient:false,answerForms:[],labelText:"",linterContext:linterContextDefault};const propsTransform=function(widgetOptions){const{answers:_,...rendererProps}={...widgetOptions,answerForms:unionAnswerForms(widgetOptions.answers.filter(answer=>answer.status==="correct").map(answer=>{return (answer.answerForms||[]).map(form=>{return {simplify:answer.simplify,name:form}})}))};return rendererProps};var NumericInput$1 = {name:"numeric-input",displayName:"Numeric input",widget:NumericInput,transform:propsTransform,isLintable:true,getOneCorrectAnswerFromRubric(rubric){const correctAnswers=rubric.answers.filter(answer=>answer.status==="correct");const answerStrings=correctAnswers.map(answer=>{const format=answer.answerForms&&answer.answerForms[0]?answer.answerForms[0]:"decimal";let answerString=KhanMath.toNumericString(answer.value,format);if(answer.maxError){answerString+=" ± "+KhanMath.toNumericString(answer.maxError,format);}return answerString});if(answerStrings.length===0){return}return answerStrings[0]}};
1681
+ class NumericInput extends React.Component{getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$r(this.props,this.getUserInput())}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,currentValue:userInput.currentValue}}render(){return jsxRuntimeExports.jsx(NumericInputComponent,{...this.props,ref:this.inputRef})}constructor(...args){super(...args),this.inputRef=React.createRef(),this.focus=()=>{this.inputRef.current?.focus();return true},this.focusInputPath=()=>{this.inputRef.current?.focus();},this.blurInputPath=()=>{this.inputRef.current?.blur();},this.getInputPaths=()=>{return [[]]},this.setInputValue=(path,newValue,cb)=>{this.props.handleUserInput({currentValue:newValue},cb);};}}NumericInput.defaultProps={size:"normal",rightAlign:false,apiOptions:ApiOptions.defaults,coefficient:false,answerForms:[],labelText:"",linterContext:linterContextDefault,userInput:{currentValue:""}};const propsTransform=function(widgetOptions){const{answers:_,...rendererProps}={...widgetOptions,answerForms:unionAnswerForms(widgetOptions.answers.filter(answer=>answer.status==="correct").map(answer=>{return (answer.answerForms||[]).map(form=>{return {simplify:answer.simplify,name:form}})}))};return rendererProps};function getUserInputFromSerializedState$f(serializedState){return {currentValue:serializedState.currentValue}}var NumericInput$1 = {name:"numeric-input",displayName:"Numeric input",widget:NumericInput,transform:propsTransform,isLintable:true,getOneCorrectAnswerFromRubric(rubric){const correctAnswers=rubric.answers.filter(answer=>answer.status==="correct");const answerStrings=correctAnswers.map(answer=>{const format=answer.answerForms&&answer.answerForms[0]?answer.answerForms[0]:"decimal";let answerString=KhanMath.toNumericString(answer.value,format);if(answer.maxError){answerString+=" ± "+KhanMath.toNumericString(answer.maxError,format);}return answerString});if(answerStrings.length===0){return}return answerStrings[0]},getUserInputFromSerializedState: getUserInputFromSerializedState$f};
1688
1682
 
1689
1683
  const getPromptJSON$q=(renderProps,userInput)=>{const choices=renderProps.choices||[];const options=choices.map(choice=>{const option={value:choice.content};if(choice.rationale){option.rationale=choice.rationale;}return option});return {type:"radio",hasNoneOfTheAbove:!!renderProps.hasNoneOfTheAbove,options,userInput:{selectedOptions:userInput.choicesSelected.slice()}}};
1690
1684
 
@@ -1718,7 +1712,7 @@ class LineHeightMeasurer extends React.Component{measureLineHeight(){if(!this._l
1718
1712
 
1719
1713
  function isPassageWidget(widget){return widget instanceof Passage}
1720
1714
 
1721
- const EN_DASH$1="–";class PassageRef extends React.Component{componentDidMount(){this._isMounted=true;this._deferredUpdateRange();this._throttledUpdateRange=_.throttle(this._deferredUpdateRange,500);window.addEventListener("resize",this._throttledUpdateRange);}shouldComponentUpdate(nextProps,nextState){return !_.isEqual(this.props,nextProps)||!_.isEqual(this.state,nextState)}componentDidUpdate(){this._deferredUpdateRange();}componentWillUnmount(){window.removeEventListener("resize",this._throttledUpdateRange);this._isMounted=false;}getPromptJSON(){return getPromptJSON$p(this.props)}render(){const{strings}=this.context;const lineRange=this.state.lineRange;let lineRangeOutput;if(!lineRange){lineRangeOutput=strings.lineRange({lineRange:`?${EN_DASH$1}?`});}else if(lineRange[0]===lineRange[1]){lineRangeOutput=strings.lineNumber({lineNumber:String(lineRange[0])});}else {lineRangeOutput=strings.lineRange({lineRange:lineRange[0]+EN_DASH$1+lineRange[1]});}let summaryOutput;if(this.props.summaryText){const summaryTree=PerseusMarkdown.parseInline(this.props.summaryText);summaryOutput=jsxRuntimeExports.jsxs("span",{"aria-hidden":true,children:[" ","(“",PerseusMarkdown.basicOutput(summaryTree),"”)"]});}else {summaryOutput=null;}return jsxRuntimeExports.jsxs("span",{children:[lineRangeOutput,summaryOutput,lineRange&&jsxRuntimeExports.jsx("div",{className:"perseus-sr-only",children:this.state.content})]})}constructor(...args){super(...args),this.displayName="PassageRef",this.isWidget=true,this.state={lineRange:null,content:null},this.change=(...args)=>{return change.apply(this,args)},this._deferredUpdateRange=()=>{_.defer(this._updateRange);},this._updateRange=()=>{const passage=this.props.findWidgets("passage "+this.props.passageNumber).filter(isPassageWidget)[0];const refInfo=passage?.getReference(this.props.referenceNumber);if(this._isMounted){if(refInfo){this.setState({lineRange:[refInfo.startLine,refInfo.endLine],content:refInfo.content});}else {this.setState({lineRange:null,content:null});}}};}}PassageRef.contextType=PerseusI18nContext;PassageRef.defaultProps={passageNumber:1,referenceNumber:1,summaryText:""};var PassageRef$1 = {name:"passage-ref",displayName:"PassageRef (SAT only)",hidden:true,widget:PassageRef,transform:widgetOptions=>({passageNumber:widgetOptions.passageNumber,referenceNumber:widgetOptions.referenceNumber,summaryText:widgetOptions.summaryText}),version:passageRefLogic.version};
1715
+ const EN_DASH$1="–";class PassageRef extends React.Component{componentDidMount(){this._isMounted=true;this._deferredUpdateRange();this._throttledUpdateRange=_.throttle(this._deferredUpdateRange,500);window.addEventListener("resize",this._throttledUpdateRange);}shouldComponentUpdate(nextProps,nextState){return !_.isEqual(this.props,nextProps)||!_.isEqual(this.state,nextState)}componentDidUpdate(){this._deferredUpdateRange();}componentWillUnmount(){window.removeEventListener("resize",this._throttledUpdateRange);this._isMounted=false;}getPromptJSON(){return getPromptJSON$p(this.props)}render(){const{strings}=this.context;const lineRange=this.state.lineRange;let lineRangeOutput;if(!lineRange){lineRangeOutput=strings.lineRange({lineRange:`?${EN_DASH$1}?`});}else if(lineRange[0]===lineRange[1]){lineRangeOutput=strings.lineNumber({lineNumber:String(lineRange[0])});}else {lineRangeOutput=strings.lineRange({lineRange:lineRange[0]+EN_DASH$1+lineRange[1]});}let summaryOutput;if(this.props.summaryText){const summaryTree=PerseusMarkdown.parseInline(this.props.summaryText);summaryOutput=jsxRuntimeExports.jsxs("span",{"aria-hidden":true,children:[" ","(“",PerseusMarkdown.basicOutput(summaryTree),"”)"]});}else {summaryOutput=null;}return jsxRuntimeExports.jsxs("span",{children:[lineRangeOutput,summaryOutput,lineRange&&jsxRuntimeExports.jsx("div",{className:"perseus-sr-only",children:this.state.content})]})}constructor(...args){super(...args),this.displayName="PassageRef",this.isWidget=true,this.state={lineRange:null,content:null},this._deferredUpdateRange=()=>{_.defer(this._updateRange);},this._updateRange=()=>{const passage=this.props.findWidgets("passage "+this.props.passageNumber).filter(isPassageWidget)[0];const refInfo=passage?.getReference(this.props.referenceNumber);if(this._isMounted){if(refInfo){this.setState({lineRange:[refInfo.startLine,refInfo.endLine],content:refInfo.content});}else {this.setState({lineRange:null,content:null});}}};}}PassageRef.contextType=PerseusI18nContext;PassageRef.defaultProps={passageNumber:1,referenceNumber:1,summaryText:""};var PassageRef$1 = {name:"passage-ref",displayName:"PassageRef (SAT only)",hidden:true,widget:PassageRef,transform:widgetOptions=>({passageNumber:widgetOptions.passageNumber,referenceNumber:widgetOptions.referenceNumber,summaryText:widgetOptions.summaryText}),version:passageRefLogic.version};
1722
1716
 
1723
1717
  const{pureXsMax,pureSmMax,pureMdMin,pureMdMax,pureLgMin,pureLgMax,pureXlMin}=constants;var mediaQueries = {md:`@media screen and (min-width: ${pureMdMin}) and `+`(max-width: ${pureMdMax})`,xl:`@media screen and (min-width: ${pureXlMin})`,xsOrSmaller:`@media screen and (max-width: ${pureXsMax})`,smOrSmaller:`@media screen and (max-width: ${pureSmMax})`,mdOrSmaller:`@media screen and (max-width: ${pureMdMax})`,lgOrSmaller:`@media screen and (max-width: ${pureLgMax})`,lgOrLarger:`@media screen and (min-width: ${pureLgMin})`};
1724
1718
 
@@ -1728,7 +1722,7 @@ function getA11yText$1(letter,checked,correct,showCorrectness,strings){if(showCo
1728
1722
 
1729
1723
  const FocusRing=function({visible=true,color=kaGreen,multipleSelect=false,children}){const borderColor=visible?color:"transparent";const borderRadius=multipleSelect?5:"50%";const style={borderColor,borderRadius};return jsxRuntimeExports.jsx("span",{"data-testid":"focus-ring",className:css(styles$A.ring),style:style,children:children})};const styles$A=StyleSheet.create({ring:{margin:"auto",display:"inline-block",borderWidth:2,padding:2,borderStyle:"solid"}});
1730
1724
 
1731
- function getChoiceLetter$1(pos,strings){const lettersString=strings.letters;const letters=lettersString.split(" ");if(pos<letters.length){return letters[pos]}return " "}
1725
+ function getChoiceLetter$1(pos,strings){const lettersString=strings.letters;const letters=lettersString.split(" ");if(pos<letters.length){return letters[pos]}return " "}function getUserInputFromSerializedState$e(props,unshuffle=true){if(props.choiceStates){const choiceStates=props.choiceStates;const choicesSelected=choiceStates.map(()=>false);for(let i=0;i<choicesSelected.length;i++){const index=unshuffle?props.choices[i].originalIndex:i;choicesSelected[index]=choiceStates[i].selected;}return {choicesSelected}}const{values}=props;if(values){const choicesSelected=[...values];const valuesLength=values.length;for(let i=0;i<valuesLength;i++){const index=unshuffle?props.choices[i].originalIndex:i;choicesSelected[index]=values[i];}return {choicesSelected}}return {choicesSelected:props.choices.map(()=>false)}}
1732
1726
 
1733
1727
  const CHOICE_ICON_SIZE$1=24;const styles$z=StyleSheet.create({iconWrapper:{display:"inline-block",position:"relative"}});
1734
1728
 
@@ -1742,7 +1736,7 @@ const ChoiceNoneAbove$1=function({content,forwardedRef,showContent=true,...rest}
1742
1736
 
1743
1737
  const{captureScratchpadTouchStart: captureScratchpadTouchStart$2}=Util;function getInstructionsText$1(multipleSelect,countChoices,numCorrect,strings){if(multipleSelect){if(usesNumCorrect(multipleSelect,!!countChoices,numCorrect)){return strings.chooseNumAnswers({numCorrect:String(numCorrect)})}return strings.chooseAllAnswers}return strings.chooseOneAnswer}const BaseRadio$1=function({apiOptions,reviewModeRubric,reviewMode,choices,editMode=false,multipleSelect=false,labelWrap,countChoices,numCorrect,isLastUsedWidget,onChange,registerFocusFunction}){const{strings}=usePerseusI18n();const prevReviewModeRubric=useRef();const choiceRefs=useRef([]);useEffect(()=>{if(apiOptions.canScrollPage&&isLastUsedWidget&&reviewModeRubric&&!prevReviewModeRubric.current){const checkedIndex=choices.findIndex(c=>c.checked);if(checkedIndex>=0){const ref=choiceRefs.current[checkedIndex];const anyNode=ReactDOM__default.findDOMNode(ref.current);const buttonNode=anyNode;if(buttonNode){scrollElementIntoView(buttonNode);}}}prevReviewModeRubric.current=reviewModeRubric;},[apiOptions,choices,isLastUsedWidget,reviewModeRubric]);function updateChoice(choiceIndex,newValues){let newCheckedList;if(newValues.checked&&!multipleSelect){newCheckedList=choices.map(_=>false);}else {newCheckedList=choices.map(c=>c.checked);}newCheckedList[choiceIndex]=newValues.checked;onChange({checked:newCheckedList});}registerFocusFunction?.(choiceIndex=>{const ref=choiceRefs.current[choiceIndex||0];const anyNode=ReactDOM__default.findDOMNode(ref.current);const buttonNode=anyNode;if(buttonNode){buttonNode.focus();}else {return false}return true});const isMobile=apiOptions.isMobile;const firstChoiceHighlighted=choices[0].highlighted;const lastChoiceHighlighted=choices[choices.length-1].highlighted;const className=classNames$1("perseus-widget-radio",!editMode&&"perseus-rendered-radio",css(styles$v.radio,styles$v.responsiveRadioContainer,firstChoiceHighlighted&&isMobile&&styles$v.radioContainerFirstHighlighted,lastChoiceHighlighted&&isMobile&&styles$v.radioContainerLastHighlighted));const instructionsClassName=classNames$1("instructions",css(styles$v.instructions,isMobile&&styles$v.instructionsMobile));const instructions=getInstructionsText$1(multipleSelect,countChoices,numCorrect,strings);const responsiveClassName=css(styles$v.responsiveFieldset);const fieldset=jsxRuntimeExports.jsxs("fieldset",{className:`perseus-widget-radio-fieldset ${responsiveClassName}`,children:[jsxRuntimeExports.jsx("legend",{className:"perseus-sr-only",children:instructions}),jsxRuntimeExports.jsx("div",{className:instructionsClassName,"aria-hidden":"true",children:instructions}),jsxRuntimeExports.jsx("ul",{className:className,style:{listStyle:"none"},children:choices.map((choice,i)=>{let Element=Choice$2;const ref=React.createRef();choiceRefs.current[i]=ref;const elementProps={apiOptions:apiOptions,multipleSelect:multipleSelect,checked:choice.checked,previouslyAnswered:choice.previouslyAnswered,reviewMode,correct:choice.correct,rationale:choice.rationale,content:choice.content,disabled:apiOptions.readOnly||choice.disabled,showCorrectness:reviewMode||!!choice.showCorrectness,showRationale:choice.hasRationale&&(reviewMode||choice.showRationale),pos:i,onChange:newValues=>{if(editMode){return}updateChoice(i,newValues);}};if(choice.isNoneOfTheAbove){Element=ChoiceNoneAbove$2;_.extend(elementProps,{showContent:choice.revealNoneOfTheAbove});}const nextChoice=choices[i+1];const nextChoiceHighlighted=!!nextChoice&&nextChoice.highlighted;const aphroditeClassName=checked=>{return css(styles$v.item,styles$v.responsiveItem,checked&&styles$v.selectedItem,checked&&choice.highlighted&&styles$v.aboveBackdrop,checked&&choice.highlighted&&apiOptions.isMobile&&styles$v.aboveBackdropMobile,nextChoiceHighlighted&&apiOptions.isMobile&&styles$v.nextHighlighted)};aphroditeClassName(true);let correctnessClass;if(reviewMode&&reviewModeRubric){correctnessClass=reviewModeRubric.choices[i].correct?ClassNames.CORRECT:ClassNames.INCORRECT;}const className=classNames$1(aphroditeClassName(choice.checked),ClassNames.RADIO.OPTION,choice.checked&&ClassNames.RADIO.SELECTED,correctnessClass);let listElem=null;let clickHandler;if(editMode){clickHandler=e=>{let elem=e.target;while(elem&&elem!==listElem){if(elem.getAttribute("data-is-radio-icon")){updateChoice(i,{checked:!choice.checked});return}elem=elem.parentNode;}};}return jsxRuntimeExports.jsx("li",{ref:e=>listElem=e,className:className,onClick:clickHandler,onTouchStart:labelWrap?undefined:captureScratchpadTouchStart$2,children:jsxRuntimeExports.jsx(Element,{...elementProps,ref:ref})},i)})})]});return jsxRuntimeExports.jsx("div",{className:css(styles$v.responsiveContainer),children:fieldset})};const styles$v=StyleSheet.create({instructions:{display:"block",color:gray17,fontSize:14,lineHeight:1.25,fontFamily:"inherit",fontStyle:"normal",fontWeight:"bold",marginBottom:16},instructionsMobile:{fontSize:18,[mediaQueries.smOrSmaller]:{fontSize:16},[mediaQueries.xl]:{fontSize:20}},radio:{padding:0},responsiveRadioContainer:{borderBottom:`1px solid ${radioBorderColor$1}`,borderTop:`1px solid ${radioBorderColor$1}`,width:"auto",[mediaQueries.smOrSmaller]:{marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin}},radioContainerFirstHighlighted:{borderTop:`1px solid rgba(0, 0, 0, 0)`},radioContainerLastHighlighted:{borderBottom:`1px solid rgba(0, 0, 0, 0)`},item:{marginLeft:20},responsiveItem:{marginLeft:0,padding:0,":not(:last-child)":{borderBottom:`1px solid ${radioBorderColor$1}`}},selectedItem:{background:"white"},aboveBackdrop:{position:"relative",zIndex:1062},aboveBackdropMobile:{boxShadow:"0 0 4px 0 rgba(0, 0, 0, 0.2),"+"0 0 2px 0 rgba(0, 0, 0, 0.1)",":not(:last-child)":{borderBottom:`1px solid rgba(0, 0, 0, 0)`}},nextHighlighted:{":not(:last-child)":{borderBottom:`1px solid rgba(0, 0, 0, 0)`}},responsiveContainer:{overflow:"auto",marginLeft:negativePhoneMargin,paddingLeft:phoneMargin},responsiveFieldset:{paddingRight:phoneMargin,minWidth:"auto"}});
1744
1738
 
1745
- let Radio$3 = class Radio extends React.Component{static getUserInputFromProps(props,unshuffle=true){if(props.choiceStates){const choiceStates=props.choiceStates;const choicesSelected=choiceStates.map(()=>false);for(let i=0;i<choicesSelected.length;i++){const index=unshuffle?props.choices[i].originalIndex:i;choicesSelected[index]=choiceStates[i].selected;}return {choicesSelected}}const{values}=props;if(values){const choicesSelected=[...values];const valuesLength=values.length;for(let i=0;i<valuesLength;i++){const index=unshuffle?props.choices[i].originalIndex:i;choicesSelected[index]=values[i];}return {choicesSelected}}return {choicesSelected:props.choices.map(()=>false)}}focus(choiceIndex){if(this.focusFunction){return this.focusFunction(choiceIndex)}return false}registerFocusFunction(fun){this.focusFunction=fun;}getUserInput(){return Radio.getUserInputFromProps(this.props)}getPromptJSON(){const userInput=Radio.getUserInputFromProps(this.props,false);return getPromptJSON$q(this.props,userInput)}render(){const{choices}=this.props;const{strings}=this.context;let choiceStates;if(this.props.static){choiceStates=choices.map(choice=>({selected:!!choice.correct,crossedOut:false,readOnly:true,highlighted:false,rationaleShown:true,correctnessShown:true,previouslyAnswered:false}));}else if(this.props.showSolutions==="all"){choiceStates=choices.map(({correct})=>({selected:!!correct,crossedOut:false,readOnly:true,highlighted:false,rationaleShown:true,correctnessShown:true,previouslyAnswered:false}));}else if(this.props.choiceStates){choiceStates=this.props.choiceStates;}else if(this.props.values){choiceStates=this.props.values.map(val=>({selected:val,crossedOut:false,readOnly:false,highlighted:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false}));}else {choiceStates=choices.map(()=>({selected:false,crossedOut:false,readOnly:false,highlighted:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false}));}const choicesProp=choices.map((choice,i)=>{const content=choice.isNoneOfTheAbove&&!choice.content?strings.noneOfTheAbove:choice.content;const{selected,rationaleShown,correctnessShown,readOnly,highlighted,previouslyAnswered}=choiceStates[i];const reviewChoice=this.props.reviewModeRubric?.choices[i];return {content:this._renderRenderer(content),checked:selected,correct:choice.correct===undefined?!!reviewChoice&&!!reviewChoice.correct:choice.correct,disabled:readOnly,hasRationale:!!choice.rationale,rationale:this._renderRenderer(choice.rationale),showRationale:rationaleShown,showCorrectness:correctnessShown,isNoneOfTheAbove:!!choice.isNoneOfTheAbove,revealNoneOfTheAbove:!!(this.props.questionCompleted&&selected),highlighted,previouslyAnswered:previouslyAnswered}});return jsxRuntimeExports.jsx(BaseRadio$1,{labelWrap:true,multipleSelect:this.props.multipleSelect,countChoices:this.props.countChoices,numCorrect:this.props.numCorrect,choices:choicesProp,onChange:this.updateChoices,reviewModeRubric:this.props.reviewModeRubric,reviewMode:this.props.reviewMode,deselectEnabled:this.props.deselectEnabled,apiOptions:this.props.apiOptions,isLastUsedWidget:this.props.isLastUsedWidget,registerFocusFunction:i=>this.registerFocusFunction(i)})}constructor(...args){super(...args),this._renderRenderer=(content="")=>{let nextPassageRefId=1;const widgets={};const modContent=content.replace(/\{\{passage-ref (\d+) (\d+)(?: "([^"]*)")?\}\}/g,(match,passageNum,refNum,summaryText)=>{const widgetId="passage-ref "+nextPassageRefId;nextPassageRefId++;widgets[widgetId]={type:"passage-ref",graded:false,options:{passageNumber:parseInt(passageNum),referenceNumber:parseInt(refNum),summaryText:summaryText},version:PassageRef$1.version};return "[["+Util.snowman+" "+widgetId+"]]"});return jsxRuntimeExports.jsx(Renderer,{content:modContent,widgets:widgets,findExternalWidgets:this.props.findWidgets,alwaysUpdate:true,linterContext:{...this.props.linterContext,blockHighlight:true},strings:this.context.strings},"choiceContentRenderer")},this.updateChoices=newValueLists=>{const{choiceStates,choices}=this.props;const newChoiceStates=choiceStates?choiceStates.map(state=>({...state})):choices.map(()=>({selected:false,highlighted:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false,readOnly:false}));newChoiceStates.forEach((choiceState,i)=>{choiceState.selected=newValueLists.checked[i];});this.props.onChange({choiceStates:newChoiceStates});this.props.trackInteraction();};}};Radio$3.contextType=PerseusI18nContext;Radio$3.defaultProps={choices:[],multipleSelect:false,countChoices:false,deselectEnabled:false,linterContext:linterContextDefault,showSolutions:"none"};
1739
+ let Radio$3 = class Radio extends React.Component{focus(choiceIndex){if(this.focusFunction){return this.focusFunction(choiceIndex)}return false}registerFocusFunction(fun){this.focusFunction=fun;}getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$q(this.props,this.props.userInput)}render(){const{choices}=this.props;const{strings}=this.context;let choiceStates;if(this.props.static){choiceStates=choices.map(choice=>({selected:!!choice.correct,crossedOut:false,readOnly:true,highlighted:false,rationaleShown:true,correctnessShown:true,previouslyAnswered:false}));}else if(this.props.showSolutions==="all"){choiceStates=choices.map(({correct})=>({selected:!!correct,crossedOut:false,readOnly:true,highlighted:false,rationaleShown:true,correctnessShown:true,previouslyAnswered:false}));}else if(this.props.choiceStates){choiceStates=this.props.choiceStates;}else if(this.props.values){choiceStates=this.props.values.map(val=>({selected:val,crossedOut:false,readOnly:false,highlighted:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false}));}else {choiceStates=choices.map(()=>({selected:false,crossedOut:false,readOnly:false,highlighted:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false}));}const choicesProp=choices.map((choice,i)=>{const content=choice.isNoneOfTheAbove&&!choice.content?strings.noneOfTheAbove:choice.content;const{selected,rationaleShown,correctnessShown,readOnly,highlighted,previouslyAnswered}=choiceStates[i];const reviewChoice=this.props.reviewModeRubric?.choices[i];return {content:this._renderRenderer(content),checked:selected,correct:choice.correct===undefined?!!reviewChoice&&!!reviewChoice.correct:choice.correct,disabled:readOnly,hasRationale:!!choice.rationale,rationale:this._renderRenderer(choice.rationale),showRationale:rationaleShown,showCorrectness:correctnessShown,isNoneOfTheAbove:!!choice.isNoneOfTheAbove,revealNoneOfTheAbove:!!(this.props.questionCompleted&&selected),highlighted,previouslyAnswered:previouslyAnswered}});return jsxRuntimeExports.jsx(BaseRadio$1,{labelWrap:true,multipleSelect:this.props.multipleSelect,countChoices:this.props.countChoices,numCorrect:this.props.numCorrect,choices:choicesProp,onChange:this.updateChoices,reviewModeRubric:this.props.reviewModeRubric,reviewMode:this.props.reviewMode,deselectEnabled:this.props.deselectEnabled,apiOptions:this.props.apiOptions,isLastUsedWidget:this.props.isLastUsedWidget,registerFocusFunction:i=>this.registerFocusFunction(i)})}constructor(...args){super(...args),this._renderRenderer=(content="")=>{let nextPassageRefId=1;const widgets={};const modContent=content.replace(/\{\{passage-ref (\d+) (\d+)(?: "([^"]*)")?\}\}/g,(match,passageNum,refNum,summaryText)=>{const widgetId="passage-ref "+nextPassageRefId;nextPassageRefId++;widgets[widgetId]={type:"passage-ref",graded:false,options:{passageNumber:parseInt(passageNum),referenceNumber:parseInt(refNum),summaryText:summaryText},version:PassageRef$1.version};return "[["+Util.snowman+" "+widgetId+"]]"});return jsxRuntimeExports.jsx(Renderer,{content:modContent,widgets:widgets,findExternalWidgets:this.props.findWidgets,alwaysUpdate:true,linterContext:{...this.props.linterContext,blockHighlight:true},strings:this.context.strings},"choiceContentRenderer")},this.updateChoices=newValueLists=>{const{choiceStates,choices}=this.props;const newChoiceStates=choiceStates?choiceStates.map(state=>({...state})):choices.map(()=>({selected:false,highlighted:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false,readOnly:false}));newChoiceStates.forEach((choiceState,i)=>{choiceState.selected=newValueLists.checked[i];});this.props.onChange({choiceStates:newChoiceStates});this.props.trackInteraction();};}};Radio$3.contextType=PerseusI18nContext;Radio$3.defaultProps={choices:[],multipleSelect:false,countChoices:false,deselectEnabled:false,linterContext:linterContextDefault,showSolutions:"none"};
1746
1740
 
1747
1741
  var styles$u = {"scrollButtonsContainer":"perseus_kjDHiWdD","scrollFade":"perseus_KB5rz4XY","scrollFadeLeft":"perseus_tP0ytdCp","scrollFadeRight":"perseus_t5yijnoR"};
1748
1742
 
@@ -1768,11 +1762,11 @@ const getChoiceStates=({choices,isStatic,showSolutions,choiceStates,values})=>{c
1768
1762
 
1769
1763
  const defaultProps={multipleSelect:false,countChoices:false,deselectEnabled:false,linterContext:linterContextDefault,showSolutions:"none"};const RadioComponent=({choices,multipleSelect=defaultProps.multipleSelect,countChoices=defaultProps.countChoices,deselectEnabled=defaultProps.deselectEnabled,linterContext=defaultProps.linterContext,showSolutions=defaultProps.showSolutions,static:isStatic,reviewModeRubric,reviewMode,apiOptions,isLastUsedWidget,questionCompleted,findWidgets,onChange,trackInteraction,numCorrect,choiceStates,values})=>{const{strings}=useContext(PerseusI18nContext);useEffect(()=>{console.log("New Radio Component");},[]);const renderRenderer=(content="")=>{const{parsedContent,extractedWidgets}=parseNestedWidgets(content);return jsxRuntimeExports.jsx(Renderer,{content:parsedContent,widgets:extractedWidgets,findExternalWidgets:findWidgets,alwaysUpdate:true,linterContext:{...linterContext,blockHighlight:true},strings:strings},"choiceContentRenderer")};const updateChoices=newValueLists=>{const newChoiceStates=choiceStates?choiceStates.map(state=>({...state})):choices.map(()=>({selected:false,highlighted:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false,readOnly:false}));newChoiceStates.forEach((choiceState,i)=>{choiceState.selected=newValueLists.checked[i];});onChange({choiceStates:newChoiceStates});trackInteraction();};const buildChoiceProps=(choices,choiceStates)=>{return choices.map((choice,i)=>{const content=choice.isNoneOfTheAbove&&!choice.content?strings.noneOfTheAbove:choice.content;const{selected,rationaleShown,correctnessShown,readOnly,highlighted,previouslyAnswered}=choiceStates[i];const reviewChoice=reviewModeRubric?.choices[i];return {content:renderRenderer(content),checked:selected,correct:choice.correct===undefined?!!reviewChoice&&!!reviewChoice.correct:choice.correct,disabled:readOnly,hasRationale:!!choice.rationale,rationale:renderRenderer(choice.rationale),showRationale:rationaleShown,showCorrectness:correctnessShown,isNoneOfTheAbove:!!choice.isNoneOfTheAbove,revealNoneOfTheAbove:!!(questionCompleted&&selected),highlighted,previouslyAnswered}})};const updatedChoiceStates=getChoiceStates({choices,isStatic,showSolutions,choiceStates,values});const updatedChoicesProp=buildChoiceProps(choices,updatedChoiceStates);return jsxRuntimeExports.jsx(BaseRadio,{labelWrap:true,multipleSelect:multipleSelect,countChoices:countChoices,numCorrect:numCorrect,choices:updatedChoicesProp,onChange:updateChoices,reviewModeRubric:reviewModeRubric,reviewMode:reviewMode,deselectEnabled:deselectEnabled,apiOptions:apiOptions,isLastUsedWidget:isLastUsedWidget})};
1770
1764
 
1771
- let Radio$2 = class Radio extends React.Component{static getUserInputFromProps(props,unshuffle=true){if(props.choiceStates){const choiceStates=props.choiceStates;const choicesSelected=choiceStates.map(()=>false);for(let i=0;i<choicesSelected.length;i++){const index=unshuffle?props.choices[i].originalIndex:i;choicesSelected[index]=choiceStates[i].selected;}return {choicesSelected}}const{values}=props;if(values){const choicesSelected=[...values];const valuesLength=values.length;for(let i=0;i<valuesLength;i++){const index=unshuffle?props.choices[i].originalIndex:i;choicesSelected[index]=values[i];}return {choicesSelected}}return {choicesSelected:props.choices.map(()=>false)}}getUserInput(){return Radio.getUserInputFromProps(this.props)}getPromptJSON(){const userInput=Radio.getUserInputFromProps(this.props,false);return getPromptJSON$q(this.props,userInput)}render(){return jsxRuntimeExports.jsx(RadioComponent,{...this.props})}};Radio$2.defaultProps={choices:[],multipleSelect:false,countChoices:false,deselectEnabled:false,linterContext:linterContextDefault,showSolutions:"none"};
1765
+ let Radio$2 = class Radio extends React.Component{getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$q(this.props,this.props.userInput)}render(){return jsxRuntimeExports.jsx(RadioComponent,{...this.props})}};Radio$2.defaultProps={choices:[],multipleSelect:false,countChoices:false,deselectEnabled:false,linterContext:linterContextDefault,showSolutions:"none"};
1772
1766
 
1773
- let Radio$1 = class Radio extends Radio$3{focus(choiceIndex){if(this.radioRef.current?.focus){return this.radioRef.current.focus(choiceIndex)}return false}render(){return this.ffIsOn?jsxRuntimeExports.jsx(Radio$2,{...this.props}):jsxRuntimeExports.jsx(Radio$3,{ref:this.radioRef,...this.props})}constructor(props){super(props),this.ffIsOn=false,this.radioRef=React.createRef();this.ffIsOn=props.apiOptions.flags?.["new-radio-widget"]??false;}};
1767
+ let Radio$1 = class Radio extends Radio$3{getSerializedState(){const{userInput:_,...rest}=this._mergePropsAndState();return {...rest}}_handleChange(arg){const newChoiceStates=arg.choiceStates;if(newChoiceStates){this.setState({choiceStates:newChoiceStates.map(choiceState=>{const{selected:_,...rest}=choiceState;return {...rest}})},()=>{const props=this._mergePropsAndState();const mergedProps={...props,choiceStates:deepClone(props.choiceStates||[]).map((choiceState,index)=>{return {...choiceState,selected:newChoiceStates[index].selected}})};const unshuffledUserInput=getUserInputFromSerializedState$e(mergedProps);this.props.handleUserInput(unshuffledUserInput);});}else {throw new Error("unhandled onChange call in Radio!")}}_mergePropsAndState(){return {...this.props,choiceStates:this.state.choiceStates?.map((choiceState,index)=>{const choice=this.props.choices[index];const selected=this.props.userInput.choicesSelected[choice.originalIndex];return {...choiceState,selected}}),onChange:arg=>this._handleChange(arg)}}focus(choiceIndex){if(this.radioRef.current?.focus){return this.radioRef.current.focus(choiceIndex)}return false}render(){const props=this._mergePropsAndState();return this.ffIsOn?jsxRuntimeExports.jsx(Radio$2,{...props}):jsxRuntimeExports.jsx(Radio$3,{ref:this.radioRef,...props})}constructor(props){super(props),this.ffIsOn=false,this.radioRef=React.createRef(),this.state={choiceStates:[]};this.ffIsOn=props.apiOptions.flags?.["new-radio-widget"]??false;this.state={choiceStates:props.choices.map(()=>({highlighted:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false,readOnly:false}))};}};
1774
1768
 
1775
- const _choiceTransform=(widgetOptions,strings,problemNum)=>{const _maybeRandomize=function(array){const randomSeed=problemNum===undefined?random:problemNum;return widgetOptions.randomize?shuffle(array,randomSeed??0):array};const _addNoneOfAbove=function(choices){let noneOfTheAbove=null;const newChoices=choices.filter((choice,index)=>{if(choice.isNoneOfTheAbove){noneOfTheAbove=choice;return false}return true});if(noneOfTheAbove){newChoices.push(noneOfTheAbove);}return newChoices};const enforceOrdering=choices=>{const ReversedChoices=[[strings.false,strings.true],[strings.no,strings.yes]];const content=choices.map(c=>c.content);if(ReversedChoices.some(reversed=>_.isEqual(content,reversed))){return [choices[1],choices[0]]}return choices};const choices=widgetOptions.choices.map((choice,i)=>{return {...choice,originalIndex:i,correct:Boolean(choice.correct)}});return _addNoneOfAbove(enforceOrdering(_maybeRandomize(choices)))};const transform$3=(widgetOptions,strings,problemNum)=>{const choices=_choiceTransform(widgetOptions,strings,problemNum);const{hasNoneOfTheAbove,multipleSelect,countChoices,deselectEnabled,numCorrect}=widgetOptions;return {numCorrect:numCorrect,hasNoneOfTheAbove,multipleSelect,countChoices,deselectEnabled,choices,selectedChoices:_.pluck(choices,"correct")}};var Radio = {name:"radio",displayName:"Radio / Multiple choice",widget:Radio$1,transform:transform$3,staticTransform:transform$3,version:radioLogic.version,isLintable:true};
1769
+ const _choiceTransform=(widgetOptions,strings,problemNum)=>{const _maybeRandomize=function(array){const randomSeed=problemNum===undefined?random:problemNum;return widgetOptions.randomize?shuffle(array,randomSeed??0):array};const _addNoneOfAbove=function(choices){let noneOfTheAbove=null;const newChoices=choices.filter((choice,index)=>{if(choice.isNoneOfTheAbove){noneOfTheAbove=choice;return false}return true});if(noneOfTheAbove){newChoices.push(noneOfTheAbove);}return newChoices};const enforceOrdering=choices=>{const ReversedChoices=[[strings.false,strings.true],[strings.no,strings.yes]];const content=choices.map(c=>c.content);if(ReversedChoices.some(reversed=>_.isEqual(content,reversed))){return [choices[1],choices[0]]}return choices};const choices=widgetOptions.choices.map((choice,i)=>{return {...choice,originalIndex:i,correct:Boolean(choice.correct)}});return _addNoneOfAbove(enforceOrdering(_maybeRandomize(choices)))};const transform$5=(widgetOptions,strings,problemNum)=>{const choices=_choiceTransform(widgetOptions,strings,problemNum);const{hasNoneOfTheAbove,multipleSelect,countChoices,deselectEnabled,numCorrect}=widgetOptions;return {numCorrect:numCorrect,hasNoneOfTheAbove,multipleSelect,countChoices,deselectEnabled,choices,selectedChoices:_.pluck(choices,"correct")}};function getStartUserInput$8(options){return {choicesSelected:options.choices.map(()=>false)}}var Radio = {name:"radio",displayName:"Radio / Multiple choice",widget:Radio$1,transform: transform$5,staticTransform:transform$5,getStartUserInput: getStartUserInput$8,version:radioLogic.version,isLintable:true,getUserInputFromSerializedState: getUserInputFromSerializedState$e};
1776
1770
 
1777
1771
  var basicWidgets = [Radio,InputNumber$1,NumericInput$1,Expression$1];
1778
1772
 
@@ -1780,7 +1774,7 @@ const{radioBorderColor,checkedColor,circleSize,radioMarginWidth}=constants;var s
1780
1774
 
1781
1775
  const getPromptJSON$n=(renderProps,userInput)=>{return {type:"categorizer",options:{items:renderProps.items,categories:renderProps.categories},userInput:{itemToCategoryMapping:userInput.values}}};
1782
1776
 
1783
- class Categorizer extends React.Component{static getUserInputFromProps(props){return {values:props.values}}getUserInput(){return Categorizer.getUserInputFromProps(this.props)}getPromptJSON(){return getPromptJSON$n(this.props,this.getUserInput())}onChange(itemNum,catNum){const values=[...this.props.values];values[itemNum]=catNum;this.change("values",values);this.props.trackInteraction();}render(){const self=this;const isMobile=this.props.apiOptions.isMobile;let indexedItems=this.props.items.map((item,n)=>[item,n]);if(this.props.randomizeItems){indexedItems=shuffle(indexedItems,this.props.problemNum);}const table=jsxRuntimeExports.jsxs("table",{className:"categorizer-table "+css(styles$o.mobileTable),children:[jsxRuntimeExports.jsx("thead",{children:jsxRuntimeExports.jsxs("tr",{children:[jsxRuntimeExports.jsx("td",{className:css(styles$o.emptyHeaderCell)}),this.props.categories.map((category,i)=>{return jsxRuntimeExports.jsx("th",{className:css(styles$o.header),children:jsxRuntimeExports.jsx(Renderer,{content:category,linterContext:this.props.linterContext,strings:this.context.strings})},i)})]})}),jsxRuntimeExports.jsx("tbody",{children:indexedItems.map(indexedItem=>{const item=indexedItem[0];const itemNum=indexedItem[1];const uniqueId=self.state.uniqueId+"_"+itemNum;return jsxRuntimeExports.jsxs("tr",{children:[jsxRuntimeExports.jsx("td",{children:jsxRuntimeExports.jsx(Renderer,{content:item,linterContext:this.props.linterContext,strings:this.context.strings})}),self.props.categories.map((catName,catNum)=>{const selected=self.props.values[itemNum]===catNum;return jsxRuntimeExports.jsx("td",{className:"category "+css(styles$o.cell),children:jsxRuntimeExports.jsxs("div",{role:"button","aria-label":catName,onClick:()=>this.onChange(itemNum,catNum),children:[isMobile&&jsxRuntimeExports.jsx("input",{type:"radio",name:uniqueId,className:css(sharedStyles.responsiveInput,sharedStyles.responsiveRadioInput),checked:selected,onChange:()=>this.onChange(itemNum,catNum),onClick:e=>e.stopPropagation()}),!isMobile&&jsxRuntimeExports.jsx("span",{className:css(styles$o.radioSpan,selected&&styles$o.checkedRadioSpan,this.props.static&&selected&&styles$o.staticCheckedRadioSpan),children:selected?jsxRuntimeExports.jsx(InlineIcon,{...iconCircle}):jsxRuntimeExports.jsx(InlineIcon,{...iconCircleThin})})]})},catNum)})]},itemNum)})})]});const extraClassNames=classNames$1({"categorizer-container":true,"static-mode":this.props.static});const inlineStyles=this.props.apiOptions.isMobile?[styles$o.fullBleedContainer]:[];return jsxRuntimeExports.jsx("div",{className:extraClassNames+" "+css(...inlineStyles),children:table})}constructor(...args){super(...args),this.state={uniqueId:_.uniqueId("perseus_radio_")},this.change=(...args)=>{return change.apply(this,args)};}}Categorizer.contextType=PerseusI18nContext;Categorizer.defaultProps={items:[],categories:[],values:[],linterContext:linterContextDefault};const styles$o=StyleSheet.create({mobileTable:{[mediaQueries.smOrSmaller]:{minWidth:"auto"}},fullBleedContainer:{[mediaQueries.mdOrSmaller]:{marginLeft:-16,marginRight:-16,overflowX:"auto"}},header:{textAlign:"center",verticalAlign:"bottom"},cell:{textAlign:"center",padding:0,color:"#ccc",verticalAlign:"middle"},emptyHeaderCell:{backgroundColor:"inherit",borderBottom:"2px solid #ccc"},radioSpan:{fontSize:30,paddingRight:3,":hover":{color:"#999"}},checkedRadioSpan:{color:"#333"},staticCheckedRadioSpan:{color:"#888"}});var Categorizer$1 = {name:"categorizer",displayName:"Categorizer",hidden:true,widget:Categorizer,transform:widgetOptions=>{return {items:widgetOptions.items,categories:widgetOptions.categories,randomizeItems:widgetOptions.randomizeItems}},staticTransform:editorProps=>{return {items:editorProps.items,categories:editorProps.categories,values:editorProps.values,randomizeItems:editorProps.randomizeItems}},isLintable:true};
1777
+ class Categorizer extends React.Component{getUserInput(){return this.props.userInput}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,values:userInput.values}}getPromptJSON(){return getPromptJSON$n(this.props,this.getUserInput())}_handleUserInput(itemNum,catNum){const values=[...this.props.userInput.values];values[itemNum]=catNum;this.props.handleUserInput({values});this.props.trackInteraction();}render(){const self=this;const isMobile=this.props.apiOptions.isMobile;let indexedItems=this.props.items.map((item,n)=>[item,n]);if(this.props.randomizeItems){indexedItems=shuffle(indexedItems,this.props.problemNum);}const table=jsxRuntimeExports.jsxs("table",{className:"categorizer-table "+css(styles$o.mobileTable),children:[jsxRuntimeExports.jsx("thead",{children:jsxRuntimeExports.jsxs("tr",{children:[jsxRuntimeExports.jsx("td",{className:css(styles$o.emptyHeaderCell)}),this.props.categories.map((category,i)=>{return jsxRuntimeExports.jsx("th",{className:css(styles$o.header),children:jsxRuntimeExports.jsx(Renderer,{content:category,linterContext:this.props.linterContext,strings:this.context.strings})},i)})]})}),jsxRuntimeExports.jsx("tbody",{children:indexedItems.map(indexedItem=>{const item=indexedItem[0];const itemNum=indexedItem[1];const uniqueId=self.state.uniqueId+"_"+itemNum;return jsxRuntimeExports.jsxs("tr",{children:[jsxRuntimeExports.jsx("td",{children:jsxRuntimeExports.jsx(Renderer,{content:item,linterContext:this.props.linterContext,strings:this.context.strings})}),self.props.categories.map((catName,catNum)=>{const selected=self.props.userInput.values[itemNum]===catNum;return jsxRuntimeExports.jsx("td",{className:"category "+css(styles$o.cell),children:jsxRuntimeExports.jsxs("div",{role:"button","aria-label":catName,onClick:()=>this._handleUserInput(itemNum,catNum),children:[isMobile&&jsxRuntimeExports.jsx("input",{type:"radio",name:uniqueId,className:css(sharedStyles.responsiveInput,sharedStyles.responsiveRadioInput),checked:selected,onChange:()=>this._handleUserInput(itemNum,catNum),onClick:e=>e.stopPropagation()}),!isMobile&&jsxRuntimeExports.jsx("span",{className:css(styles$o.radioSpan,selected&&styles$o.checkedRadioSpan,this.props.static&&selected&&styles$o.staticCheckedRadioSpan),children:selected?jsxRuntimeExports.jsx(InlineIcon,{...iconCircle}):jsxRuntimeExports.jsx(InlineIcon,{...iconCircleThin})})]})},catNum)})]},itemNum)})})]});const extraClassNames=classNames$1({"categorizer-container":true,"static-mode":this.props.static});const inlineStyles=this.props.apiOptions.isMobile?[styles$o.fullBleedContainer]:[];return jsxRuntimeExports.jsx("div",{className:extraClassNames+" "+css(...inlineStyles),children:table})}constructor(...args){super(...args),this.state={uniqueId:_.uniqueId("perseus_radio_")};}}Categorizer.contextType=PerseusI18nContext;Categorizer.defaultProps={items:[],categories:[],linterContext:linterContextDefault,userInput:{values:[]}};const styles$o=StyleSheet.create({mobileTable:{[mediaQueries.smOrSmaller]:{minWidth:"auto"}},fullBleedContainer:{[mediaQueries.mdOrSmaller]:{marginLeft:-16,marginRight:-16,overflowX:"auto"}},header:{textAlign:"center",verticalAlign:"bottom"},cell:{textAlign:"center",padding:0,color:"#ccc",verticalAlign:"middle"},emptyHeaderCell:{backgroundColor:"inherit",borderBottom:"2px solid #ccc"},radioSpan:{fontSize:30,paddingRight:3,":hover":{color:"#999"}},checkedRadioSpan:{color:"#333"},staticCheckedRadioSpan:{color:"#888"}});function getUserInputFromSerializedState$d(serializedState){return {values:serializedState.values}}function getCorrectUserInput$2(options){return {values:options.values}}function transform$4(widgetOptions){return {items:widgetOptions.items,categories:widgetOptions.categories,randomizeItems:widgetOptions.randomizeItems}}var Categorizer$1 = {name:"categorizer",displayName:"Categorizer",hidden:true,widget:Categorizer,transform: transform$4,staticTransform:transform$4,getUserInputFromSerializedState: getUserInputFromSerializedState$d,getCorrectUserInput: getCorrectUserInput$2,isLintable:true};
1784
1778
 
1785
1779
  const isFileProtocol=protocol=>{if(protocol&&protocol.toLowerCase()==="file:"){return true}return false};
1786
1780
 
@@ -1790,23 +1784,31 @@ const getUnsupportedPromptJSON=(widgetType,message="")=>{return {type:widgetType
1790
1784
 
1791
1785
  const getPromptJSON$m=()=>{return getUnsupportedPromptJSON("cs-program")};
1792
1786
 
1793
- const{updateQueryString: updateQueryString$1}=Util;function getUrlFromProgramID$1(programID){const{InitialRequestUrl}=getDependencies();const path="/computer-programming/program/"+`${programID}/embedded?embed=yes&author=no`;if(isFileProtocol(InitialRequestUrl.protocol)){return `https://khanacademy.org${path}`}return toAbsoluteUrl(path)}class CSProgram extends React.Component{componentDidMount(){$(window).on("message",this.handleMessageEvent);}componentWillUnmount(){$(window).off("message",this.handleMessageEvent);}getUserInput(){return {status:this.props.status,message:this.props.message}}getPromptJSON(){return getPromptJSON$m()}render(){if(!this.props.programID){return jsxRuntimeExports.jsx("div",{})}let styleContainer=false;let url=getUrlFromProgramID$1(this.props.programID);let className;const style={height:this.props.height,width:"100%"};if(this.props.showEditor){url+="&editor=yes";className="perseus-scratchpad-editor";}else {url+=`&editor=no&width=${articleMaxWidthInPx}`;className="perseus-scratchpad";if(this.props.programType!=="webpage"){styleContainer=true;}}if(this.props.showButtons){url+="&buttons=yes";style.height+=67;}else {url+="&buttons=no";}if(this.props.settings){const settings={};_.each(this.props.settings,function(setting){if(setting.name&&setting.value){settings[setting.name]=setting.value;}});url=updateQueryString$1(url,"settings",JSON.stringify(settings));}const sandboxOptions=["allow-popups","allow-same-origin","allow-scripts","allow-top-navigation"].join(" ");return jsxRuntimeExports.jsx("div",{className:css(styles$n.widthOverride,styleContainer&&styles$n.container),children:jsxRuntimeExports.jsx("iframe",{sandbox:sandboxOptions,src:url,style:style,className:className,allowFullScreen:true})})}constructor(...args){super(...args),this.handleMessageEvent=e=>{let data={};try{data=JSON.parse(e.originalEvent.data);}catch{return}if(_.isUndefined(data.testsPassed)){return}const status=data.testsPassed?"correct":"incorrect";this.change({status:status,message:data.message});},this.change=(...args)=>{return change.apply(this,args)};}}CSProgram.defaultProps={showEditor:false,showButtons:false,status:"incomplete",message:null};const styles$n=StyleSheet.create({widthOverride:{width:820},container:{margin:"auto"}});var CSProgram$1 = {name:"cs-program",displayName:"CS Program",widget:CSProgram,hidden:true};
1787
+ const{updateQueryString: updateQueryString$1}=Util;function getUrlFromProgramID$1(programID){const{InitialRequestUrl}=getDependencies();const path="/computer-programming/program/"+`${programID}/embedded?embed=yes&author=no`;if(isFileProtocol(InitialRequestUrl.protocol)){return `https://khanacademy.org${path}`}return toAbsoluteUrl(path)}class CSProgram extends React.Component{componentDidMount(){$(window).on("message",this.handleMessageEvent);}componentWillUnmount(){$(window).off("message",this.handleMessageEvent);}getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$m()}render(){if(!this.props.programID){return jsxRuntimeExports.jsx("div",{})}let styleContainer=false;let url=getUrlFromProgramID$1(this.props.programID);let className;const style={height:this.props.height,width:"100%"};if(this.props.showEditor){url+="&editor=yes";className="perseus-scratchpad-editor";}else {url+=`&editor=no&width=${articleMaxWidthInPx}`;className="perseus-scratchpad";if(this.props.programType!=="webpage"){styleContainer=true;}}if(this.props.showButtons){url+="&buttons=yes";style.height+=67;}else {url+="&buttons=no";}if(this.props.settings){const settings={};_.each(this.props.settings,function(setting){if(setting.name&&setting.value){settings[setting.name]=setting.value;}});url=updateQueryString$1(url,"settings",JSON.stringify(settings));}const sandboxOptions=["allow-popups","allow-same-origin","allow-scripts","allow-top-navigation"].join(" ");return jsxRuntimeExports.jsx("div",{className:css(styles$n.widthOverride,styleContainer&&styles$n.container),children:jsxRuntimeExports.jsx("iframe",{sandbox:sandboxOptions,src:url,style:style,className:className,allowFullScreen:true})})}constructor(...args){super(...args),this.handleMessageEvent=e=>{let data={};try{data=JSON.parse(e.originalEvent.data);}catch{return}if(_.isUndefined(data.testsPassed)){return}const status=data.testsPassed?"correct":"incorrect";this.props.handleUserInput({status:status,message:data.message});};}}CSProgram.defaultProps={showEditor:false,showButtons:false,userInput:{status:"incomplete",message:null}};const styles$n=StyleSheet.create({widthOverride:{width:820},container:{margin:"auto"}});function getUserInputFromSerializedState$c(serializedState){return {status:serializedState.status,message:serializedState.message}}var CSProgram$1 = {name:"cs-program",displayName:"CS Program",widget:CSProgram,hidden:true,getUserInputFromSerializedState: getUserInputFromSerializedState$c};
1794
1788
 
1795
1789
  const getPromptJSON$l=renderProps=>{return {type:"definition",definition:renderProps.definition,togglePrompt:renderProps.togglePrompt}};
1796
1790
 
1797
- class Definition extends React.Component{getPromptJSON(){return getPromptJSON$l(this.props)}render(){return jsxRuntimeExports.jsx(DefinitionConsumer,{children:({activeDefinitionId,setActiveDefinitionId})=>jsxRuntimeExports.jsx(Popover,{content:jsxRuntimeExports.jsx(PopoverContentCore,{style:styles$m.tooltipBody,closeButtonVisible:true,children:jsxRuntimeExports.jsx(Renderer,{apiOptions:this.props.apiOptions,content:this.props.definition,widgets:this.props.widgets,strings:this.context.strings})}),opened:activeDefinitionId===this.props.widgetId,onClose:()=>setActiveDefinitionId(null),placement:"top",children:jsxRuntimeExports.jsx(Clickable,{onClick:()=>{this.props.trackInteraction();setActiveDefinitionId(this.props.widgetId);},children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx("span",{style:{color:color.blue,borderBottom:hovered||focused||pressed?`2px solid ${color.blue}`:"none"},children:this.props.togglePrompt})})})})}constructor(...args){super(...args),this.isWidget=true;}}Definition.contextType=PerseusI18nContext;Definition.defaultProps={togglePrompt:"define me",definition:"definition goes here"};const styles$m={tooltipBody:{color:color.offBlack,fontSize:20,fontWeight:500,lineHeight:"30px"}};var Definition$1 = {name:"definition",displayName:"Definition",widget:Definition,transform:x=>x};
1791
+ class Definition extends React.Component{getPromptJSON(){return getPromptJSON$l(this.props)}render(){return jsxRuntimeExports.jsx(DefinitionConsumer,{children:({activeDefinitionId,setActiveDefinitionId})=>jsxRuntimeExports.jsx(Popover,{content:jsxRuntimeExports.jsx(PopoverContentCore,{style:styles$m.tooltipBody,closeButtonVisible:true,children:jsxRuntimeExports.jsx(Renderer,{apiOptions:this.props.apiOptions,content:this.props.definition,widgets:this.props.widgets,strings:this.context.strings})}),opened:activeDefinitionId===this.props.widgetId,onClose:()=>setActiveDefinitionId(null),placement:"top",children:jsxRuntimeExports.jsx(Clickable,{onClick:()=>{this.props.trackInteraction();setActiveDefinitionId(this.props.widgetId);},children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx("span",{style:{color:color.blue,borderBottom:hovered||focused||pressed?`2px solid ${color.blue}`:"none"},children:this.props.togglePrompt})})})})}constructor(...args){super(...args),this.isWidget=true;}}Definition.contextType=PerseusI18nContext;Definition.defaultProps={togglePrompt:"define me",definition:"definition goes here"};const styles$m={tooltipBody:{color:color.offBlack,fontSize:20,fontWeight:500,lineHeight:"30px"}};var Definition$1 = {name:"definition",displayName:"Definition",widget:Definition};
1798
1792
 
1799
1793
  class DeprecatedStandin extends React__default.Component{render(){return jsxRuntimeExports.jsx("div",{style:{paddingTop:8,paddingBottom:8},children:jsxRuntimeExports.jsx(Banner,{text:this.context.strings.deprecatedStandin,kind:"info",layout:"full-width"})})}constructor(...args){super(...args),this.isWidget=true;}}DeprecatedStandin.contextType=PerseusI18nContext;var DeprecatedStandin$1 = {name:"deprecated-standin",displayName:"Deprecated Standin",widget:DeprecatedStandin,hidden:true};
1800
1794
 
1801
1795
  const getPromptJSON$k=(renderProps,userInput)=>{return {type:"dropdown",options:{items:renderProps.choices},userInput:{selectedIndex:userInput.value-1}}};
1802
1796
 
1803
- class Dropdown extends React.Component{getUserInput(){return {value:this.props.selected}}getPromptJSON(){return getPromptJSON$k(this.props,this.getUserInput())}render(){const children=[jsxRuntimeExports.jsx(OptionItem,{value:"0",disabled:true,label:jsxRuntimeExports.jsx(Renderer,{content:this.props.placeholder,strings:this.context.strings}),labelAsText:this.props.placeholder},"placeholder"),...this.props.choices.map((choice,i)=>jsxRuntimeExports.jsx(OptionItem,{value:String(i+1),label:jsxRuntimeExports.jsx(Renderer,{content:choice,strings:this.context.strings}),labelAsText:choice},String(i+1)))];return jsxRuntimeExports.jsx(Id,{children:dropdownId=>jsxRuntimeExports.jsxs(View,{onClick:e=>{e.stopPropagation();},onTouchStart:e=>{e.stopPropagation();},children:[this.props.visibleLabel&&jsxRuntimeExports.jsx(LabelLarge,{tag:"label",htmlFor:dropdownId,children:this.props.visibleLabel}),jsxRuntimeExports.jsx(SingleSelect,{id:dropdownId,placeholder:"",className:"perseus-dropdown",onChange:value=>this._handleChange(parseInt(value)),selectedValue:String(this.props.selected),disabled:this.props.apiOptions.readOnly,"aria-label":this.props.ariaLabel||this.props.visibleLabel||this.context.strings.selectAnAnswer,showOpenerLabelAsText:false,children:children})]})})}constructor(...args){super(...args),this.focus=()=>{ReactDOM__default.findDOMNode(this).focus();return true},this._handleChangeEvent=e=>{this._handleChange(parseInt(e.target.value));},this._handleChange=selected=>{this.props.trackInteraction();this.props.onChange({selected:selected});};}}Dropdown.contextType=PerseusI18nContext;Dropdown.defaultProps={choices:[],selected:0,placeholder:"",apiOptions:ApiOptions.defaults};function transform$2(widgetOptions){return {placeholder:widgetOptions.placeholder,visibleLabel:widgetOptions.visibleLabel,ariaLabel:widgetOptions.ariaLabel,choices:widgetOptions.choices.map(choice=>choice.content)}}var Dropdown$1 = {name:"dropdown",displayName:"Drop down",widget:Dropdown,transform: transform$2};
1797
+ class Dropdown extends React.Component{getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$k(this.props,this.getUserInput())}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,selected:userInput.value}}render(){const children=[jsxRuntimeExports.jsx(OptionItem,{value:"0",disabled:true,label:jsxRuntimeExports.jsx(Renderer,{content:this.props.placeholder,strings:this.context.strings}),labelAsText:this.props.placeholder},"placeholder"),...this.props.choices.map((choice,i)=>jsxRuntimeExports.jsx(OptionItem,{value:String(i+1),label:jsxRuntimeExports.jsx(Renderer,{content:choice,strings:this.context.strings}),labelAsText:choice},String(i+1)))];return jsxRuntimeExports.jsx(Id,{children:dropdownId=>jsxRuntimeExports.jsxs(View,{onClick:e=>{e.stopPropagation();},onTouchStart:e=>{e.stopPropagation();},children:[this.props.visibleLabel&&jsxRuntimeExports.jsx(LabelLarge,{tag:"label",htmlFor:dropdownId,children:this.props.visibleLabel}),jsxRuntimeExports.jsx(SingleSelect,{id:dropdownId,placeholder:"",className:"perseus-dropdown",onChange:value=>this._handleChange(parseInt(value)),selectedValue:String(this.props.userInput.value),disabled:this.props.apiOptions.readOnly,"aria-label":this.props.ariaLabel||this.props.visibleLabel||this.context.strings.selectAnAnswer,showOpenerLabelAsText:false,children:children})]})})}constructor(...args){super(...args),this.focus=()=>{ReactDOM__default.findDOMNode(this).focus();return true},this._handleChangeEvent=e=>{this._handleChange(parseInt(e.target.value));},this._handleChange=selected=>{this.props.trackInteraction();this.props.handleUserInput({value:selected});};}}Dropdown.contextType=PerseusI18nContext;Dropdown.defaultProps={choices:[],placeholder:"",apiOptions:ApiOptions.defaults,userInput:{value:0}};function transform$3(widgetOptions){return {placeholder:widgetOptions.placeholder,visibleLabel:widgetOptions.visibleLabel,ariaLabel:widgetOptions.ariaLabel,choices:widgetOptions.choices.map(choice=>choice.content)}}function getUserInputFromSerializedState$b(serializedState){return {value:serializedState.selected}}var Dropdown$1 = {name:"dropdown",displayName:"Drop down",widget:Dropdown,transform: transform$3,getUserInputFromSerializedState: getUserInputFromSerializedState$b};
1798
+
1799
+ const USAGE="Usage:\n"+" this.change({propName: 5}, callback);\n"+' this.change("propName", 5, callback);\n'+' this.change("propName")';const _changeMultiple=function(component,newProps,callback){const currProps=excludeDenylistKeys(component.props);const nextProps=_.extend(currProps,newProps);component.props.onChange(nextProps,callback);};const _changeSingle=function(component,propName,value,callback){if(value===undefined){return _.partial(_changeSingle,component,propName)}const newProps={};newProps[propName]=value;_changeMultiple(component,newProps,callback);};const change=function(newPropsOrSinglePropName,propValue,callback){if(_.isObject(newPropsOrSinglePropName)&&callback===undefined){callback=propValue;return _changeMultiple(this,newPropsOrSinglePropName,callback)}if(typeof newPropsOrSinglePropName==="string"){return _changeSingle(this,newPropsOrSinglePropName,propValue,callback)}throw new PerseusError("Invalid types sent to this.change(): "+_.toArray(arguments).join()+"\n"+USAGE,Errors.Internal)};const propTypes={onChange:PropTypes.func.isRequired};
1800
+
1801
+ var changeable = /*#__PURE__*/Object.freeze({
1802
+ __proto__: null,
1803
+ change: change,
1804
+ propTypes: propTypes
1805
+ });
1804
1806
 
1805
1807
  const getPromptJSON$j=renderProps=>{return {type:"explanation",showPrompt:renderProps.showPrompt,explanation:renderProps.explanation}};
1806
1808
 
1807
- function mediaQueryIsMatched(mediaQuery){if(typeof window.matchMedia!=="function"){return false}return window.matchMedia(mediaQuery).matches}class Explanation extends React.Component{componentDidMount(){this._mounted=true;}componentWillUnmount(){this._mounted=false;}getPromptJSON(){return getPromptJSON$j(this.props)}render(){const promptText=this.state.expanded?this.props.hidePrompt:this.props.showPrompt;const caretIcon=this.state.expanded?caretUp:caretDown;const allowTransition=this._mounted&&mediaQueryIsMatched("(prefers-reduced-motion: no-preference)");const buttonStyleOverrides={height:"auto",lineHeight:"inherit",marginLeft:"-2px",marginRight:"2px",paddingLeft:"2px"};const labelStyle={fontSize:"18px",lineHeight:"inherit","text-align":"left",marginRight:"-6px","white-space":"normal"};const contentStyling=[styles$l.content,this.state.expanded?styles$l.contentExpanded:styles$l.contentCollapsed,allowTransition&&(this.state.expanded?styles$l.transitionExpanded:styles$l.transitionCollapsed)];return jsxRuntimeExports.jsx(Id,{children:contentId=>jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Button,{"aria-expanded":this.state.expanded,"aria-controls":contentId,endIcon:caretIcon,kind:"tertiary",labelStyle:labelStyle,onClick:this._onClick,size:"small",style:buttonStyleOverrides,children:promptText}),jsxRuntimeExports.jsx(View,{id:contentId,style:contentStyling,"aria-hidden":!this.state.expanded,testId:"content-container",children:jsxRuntimeExports.jsx(View,{style:styles$l.contentWrapper,children:jsxRuntimeExports.jsx(Renderer,{apiOptions:this.props.apiOptions,content:this.props.explanation,widgets:this.props.widgets,linterContext:this.props.linterContext,strings:this.context.strings})})})]})})}constructor(...args){super(...args),this.isWidget=true,this.state={expanded:false},this._mounted=false,this.change=(...args)=>{return change.apply(this,args)},this._onClick=()=>{this.setState({expanded:!this.state.expanded});this.props.trackInteraction();};}}Explanation.contextType=PerseusI18nContext;Explanation.defaultProps={showPrompt:"Explain",hidePrompt:"Hide explanation",explanation:"explanation goes here\n\nmore explanation",widgets:{},linterContext:linterContextDefault};const leftBorderSpacing=23;const verticalContentPadding=10;const arrowHeight=14;const styles$l=StyleSheet.create({content:{borderLeft:"0px solid #ccc",display:"inline-grid",position:"relative"},contentCollapsed:{gridTemplateColumns:"0fr",gridTemplateRows:"0fr",marginBottom:0,marginTop:0,minWidth:"0",paddingBottom:0,visibility:"hidden"},contentExpanded:{borderLeftWidth:"5px",gridTemplateColumns:"1fr",gridTemplateRows:"1fr",marginLeft:-23,minWidth:"100%",paddingLeft:leftBorderSpacing,paddingBottom:verticalContentPadding,visibility:"visible",marginBottom:arrowHeight,marginTop:arrowHeight},contentWrapper:{overflow:"hidden"},transitionCollapsed:{transition:"all 0.25s step-end, grid-template-rows 0.25s, margin-top 0.25s, margin-bottom 0.25s, padding-bottom 0.25s"},transitionExpanded:{transition:"grid-template-rows 0.5s, margin-top 0.5s, margin-bottom 0.5s, padding-bottom 0.5s"}});var Explanation$1 = {name:"explanation",displayName:"Explanation",widget:Explanation,transform:_.identity,isLintable:true};
1809
+ function mediaQueryIsMatched(mediaQuery){if(typeof window.matchMedia!=="function"){return false}return window.matchMedia(mediaQuery).matches}class Explanation extends React.Component{componentDidMount(){this._mounted=true;}componentWillUnmount(){this._mounted=false;}getPromptJSON(){return getPromptJSON$j(this.props)}render(){const promptText=this.state.expanded?this.props.hidePrompt:this.props.showPrompt;const caretIcon=this.state.expanded?caretUp:caretDown;const allowTransition=this._mounted&&mediaQueryIsMatched("(prefers-reduced-motion: no-preference)");const buttonStyleOverrides={height:"auto",lineHeight:"inherit",marginLeft:"-2px",marginRight:"2px",paddingLeft:"2px"};const labelStyle={fontSize:"18px",lineHeight:"inherit","text-align":"left",marginRight:"-6px","white-space":"normal"};const contentStyling=[styles$l.content,this.state.expanded?styles$l.contentExpanded:styles$l.contentCollapsed,allowTransition&&(this.state.expanded?styles$l.transitionExpanded:styles$l.transitionCollapsed)];return jsxRuntimeExports.jsx(Id,{children:contentId=>jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Button,{"aria-expanded":this.state.expanded,"aria-controls":contentId,endIcon:caretIcon,kind:"tertiary",labelStyle:labelStyle,onClick:this._onClick,size:"small",style:buttonStyleOverrides,children:promptText}),jsxRuntimeExports.jsx(View,{id:contentId,style:contentStyling,"aria-hidden":!this.state.expanded,testId:"content-container",children:jsxRuntimeExports.jsx(View,{style:styles$l.contentWrapper,children:jsxRuntimeExports.jsx(Renderer,{apiOptions:this.props.apiOptions,content:this.props.explanation,widgets:this.props.widgets,linterContext:this.props.linterContext,strings:this.context.strings})})})]})})}constructor(...args){super(...args),this.isWidget=true,this.state={expanded:false},this._mounted=false,this.change=(...args)=>{return change.apply(this,args)},this._onClick=()=>{this.setState({expanded:!this.state.expanded});this.props.trackInteraction();};}}Explanation.contextType=PerseusI18nContext;Explanation.defaultProps={showPrompt:"Explain",hidePrompt:"Hide explanation",explanation:"explanation goes here\n\nmore explanation",widgets:{},linterContext:linterContextDefault};const leftBorderSpacing=23;const verticalContentPadding=10;const arrowHeight=14;const styles$l=StyleSheet.create({content:{borderLeft:"0px solid #ccc",display:"inline-grid",position:"relative"},contentCollapsed:{gridTemplateColumns:"0fr",gridTemplateRows:"0fr",marginBottom:0,marginTop:0,minWidth:"0",paddingBottom:0,visibility:"hidden"},contentExpanded:{borderLeftWidth:"5px",gridTemplateColumns:"1fr",gridTemplateRows:"1fr",marginLeft:-23,minWidth:"100%",paddingLeft:leftBorderSpacing,paddingBottom:verticalContentPadding,visibility:"visible",marginBottom:arrowHeight,marginTop:arrowHeight},contentWrapper:{overflow:"hidden"},transitionCollapsed:{transition:"all 0.25s step-end, grid-template-rows 0.25s, margin-top 0.25s, margin-bottom 0.25s, padding-bottom 0.25s"},transitionExpanded:{transition:"grid-template-rows 0.5s, margin-top 0.5s, margin-bottom 0.5s, padding-bottom 0.5s"}});var Explanation$1 = {name:"explanation",displayName:"Explanation",widget:Explanation,isLintable:true};
1808
1810
 
1809
- class FreeResponse extends React.Component{getUserInput(){return {currentValue:this.state.currentValue}}isOverLimit(){return !this.props.allowUnlimitedCharacters&&this.characterCount()>this.props.characterLimit}renderCharacterCount(){if(this.props.allowUnlimitedCharacters){return null}const characterCountText=this.context.strings.characterCount({used:this.characterCount(),num:this.props.characterLimit});return jsxRuntimeExports.jsx(View,{children:jsxRuntimeExports.jsxs(Text$1,{role:"status",style:[styles$k.characterCountText,this.isOverLimit()?styles$k.overCharacterLimit:undefined],children:[this.isOverLimit()&&jsxRuntimeExports.jsx(PhosphorIcon,{icon:warningCircleIcon,size:"small",style:styles$k.warningCircleIcon}),characterCountText]})})}render(){return jsxRuntimeExports.jsxs(View,{style:styles$k.container,className:"free-response",children:[jsxRuntimeExports.jsx(LabeledField,{label:jsxRuntimeExports.jsx(View,{className:"free-response-question",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.question,strings:this.context.strings})}),field:jsxRuntimeExports.jsx(TextArea,{error:this.isOverLimit(),onChange:this.handleChange,placeholder:this.props.placeholder,style:styles$k.textarea,value:this.state.currentValue}),styles:{label:styles$k.questionLabel}}),this.renderCharacterCount()]})}constructor(...args){super(...args),this.state={currentValue:""},this.characterCount=()=>{return this.state.currentValue.replace(/\n/g,"").length},this.handleChange=newValue=>{this.setState({currentValue:newValue});};}}FreeResponse.contextType=PerseusI18nContext;var FreeResponse$1 = {name:"free-response",accessible:true,displayName:"Free Response (Assessments only)",widget:FreeResponse,hidden:false};const styles$k=StyleSheet.create({container:{gap:spacing.xSmall_8},characterCountText:{color:semanticColor.core.foreground.neutral.default,fontSize:font.size.small},overCharacterLimit:{color:color.red},questionLabel:{fontWeight:font.weight.bold},textarea:{padding:spacing.medium_16},warningCircleIcon:{marginInlineEnd:spacing.xSmall_8}});
1811
+ class FreeResponse extends React.Component{getUserInput(){return this.props.userInput}isOverLimit(){return !this.props.allowUnlimitedCharacters&&this.characterCount()>this.props.characterLimit}renderCharacterCount(){if(this.props.allowUnlimitedCharacters){return null}const characterCountText=this.context.strings.characterCount({used:this.characterCount(),num:this.props.characterLimit});return jsxRuntimeExports.jsx(View,{children:jsxRuntimeExports.jsxs(Text$1,{role:"status",style:[styles$k.characterCountText,this.isOverLimit()?styles$k.overCharacterLimit:undefined],children:[this.isOverLimit()&&jsxRuntimeExports.jsx(PhosphorIcon,{icon:warningCircleIcon,size:"small",style:styles$k.warningCircleIcon}),characterCountText]})})}render(){return jsxRuntimeExports.jsxs(View,{style:styles$k.container,className:"free-response",children:[jsxRuntimeExports.jsx(LabeledField,{label:jsxRuntimeExports.jsx(View,{className:"free-response-question",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.question,strings:this.context.strings})}),field:jsxRuntimeExports.jsx(TextArea,{error:this.isOverLimit(),onChange:this._handleUserInput,placeholder:this.props.placeholder,style:styles$k.textarea,value:this.props.userInput.currentValue}),styles:{label:styles$k.questionLabel}}),this.renderCharacterCount()]})}constructor(...args){super(...args),this.characterCount=()=>{return this.props.userInput.currentValue.replace(/\n/g,"").length},this._handleUserInput=newValue=>{this.props.handleUserInput({currentValue:newValue});};}}FreeResponse.contextType=PerseusI18nContext;FreeResponse.defaultProps={userInput:{currentValue:""}};var FreeResponse$1 = {name:"free-response",accessible:true,displayName:"Free Response (Assessments only)",widget:FreeResponse,hidden:false};const styles$k=StyleSheet.create({container:{gap:spacing.xSmall_8},characterCountText:{color:semanticColor.core.foreground.neutral.default,fontSize:font.size.small},overCharacterLimit:{color:color.red},questionLabel:{fontWeight:font.weight.bold},textarea:{padding:spacing.medium_16},warningCircleIcon:{marginInlineEnd:spacing.xSmall_8}});
1810
1812
 
1811
1813
  const getPromptJSON$i=(title,rendererJSON,hintRendererJSON)=>{if(!rendererJSON){return {type:"graded-group",title,content:"",widgets:{},hint:hintRendererJSON}}return {...rendererJSON,title,type:"graded-group",hint:hintRendererJSON}};
1812
1814
 
@@ -1816,15 +1818,15 @@ const GRADING_STATUSES={ungraded:"ungraded",correct:"correct",incorrect:"incorre
1816
1818
 
1817
1819
  const getPromptJSON$h=(renderProps,activeGroupJSON)=>{return {type:"graded-group-set",options:{groupCount:renderProps.gradedGroups.length,currentGroup:activeGroupJSON}}};
1818
1820
 
1819
- class Indicators extends React.Component{render(){return jsxRuntimeExports.jsx("ul",{className:classNames$1(css(styles$h.indicatorContainer),"indicatorContainer"),children:this.props.gradedGroups.map(({title},i)=>jsxRuntimeExports.jsx("li",{className:css(styles$h.indicator),children:jsxRuntimeExports.jsx(Clickable,{role:"button","aria-label":this.context.strings.skipToTitle({title}),style:styles$h.indicatorButton,onClick:()=>this.props.onChangeCurrentGroup(i),onKeyDown:e=>this.handleKeyDown(e,i),children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx(View,{style:[styles$h.indicatorDot,(hovered||focused||pressed)&&styles$h.indicatorDotFocused],children:i===this.props.currentGroup&&jsxRuntimeExports.jsx(View,{style:styles$h.indicatorDotActive,children:jsxRuntimeExports.jsx("span",{className:css(a11y.srOnly),children:this.context.strings.current})})})})},title))})}constructor(...args){super(...args),this.handleKeyDown=(e,i)=>{if(e.key==="Enter"||e.key===" "){this.props.onChangeCurrentGroup(i);}};}}Indicators.contextType=PerseusI18nContext;class GradedGroupSet extends React.Component{shouldComponentUpdate(nextProps,nextState){return nextProps!==this.props||nextState!==this.state}getPromptJSON(){const activeGroupPromptJSON=this._childGroup.getPromptJSON();return getPromptJSON$h(this.props,activeGroupPromptJSON)}render(){const{JIPT}=getDependencies();if(JIPT.useJIPT&&this.props.gradedGroups.length>1){return jsxRuntimeExports.jsx("div",{className:css(styles$h.container),children:this.props.gradedGroups.map((gradedGroup,i)=>{return jsxRuntimeExports.jsx(GradedGroup,{...this.props,...gradedGroup,inGradedGroupSet:false,linterContext:this.props.linterContext},i)})})}const currentGroup=this.props.gradedGroups[this.state.currentGroup];if(!currentGroup){return jsxRuntimeExports.jsx("span",{children:"No current group..."})}const numGroups=this.props.gradedGroups.length;const handleNextQuestion=this.state.currentGroup<numGroups-1?this.handleNextQuestion:null;return jsxRuntimeExports.jsxs("div",{className:css(styles$h.container),children:[jsxRuntimeExports.jsxs("div",{className:css(styles$h.top),children:[jsxRuntimeExports.jsx("div",{className:css(styles$h.title),children:currentGroup.title}),jsxRuntimeExports.jsx("div",{className:css(styles$h.spacer)}),jsxRuntimeExports.jsx(Indicators,{currentGroup:this.state.currentGroup,gradedGroups:this.props.gradedGroups,onChangeCurrentGroup:currentGroup=>this.setState({currentGroup})})]}),jsxRuntimeExports.jsx(GradedGroup,{ref:comp=>this._childGroup=comp,...this.props,...currentGroup,inGradedGroupSet:true,title:null,onNextQuestion:handleNextQuestion,linterContext:this.props.linterContext},this.state.currentGroup)]})}constructor(...args){super(...args),this.state={currentGroup:0},this.change=(...args)=>{return change.apply(this,args)},this.getInputPaths=()=>{return this._childGroup.getInputPaths()},this.setInputValue=(path,newValue,cb)=>{return this._childGroup.setInputValue(path,newValue,cb)},this.focus=()=>{return this._childGroup.focus()},this.focusInputPath=path=>{this._childGroup.focusInputPath(path);},this.blurInputPath=path=>{this._childGroup.blurInputPath(path);},this.handleNextQuestion=()=>{const{currentGroup}=this.state;const numGroups=this.props.gradedGroups.length;if(currentGroup<numGroups-1){this.setState({currentGroup:currentGroup+1});}};}}GradedGroupSet.defaultProps={gradedGroups:[],linterContext:linterContextDefault};var GradedGroupSet$1 = {name:"graded-group-set",displayName:"Graded group set (articles only)",widget:GradedGroupSet,hidden:false,tracking:"all",isLintable:true};const styles$h=StyleSheet.create({top:{display:"flex",flexDirection:"row"},spacer:{flex:1},title:{fontSize:12,color:color.offBlack64,textTransform:"uppercase",marginBottom:11,letterSpacing:.8},indicatorContainer:{display:"flex",flexDirection:"row",listStyle:"none",margin:"unset",paddingInlineStart:"unset",flexWrap:"wrap"},indicator:{width:24,height:24},indicatorButton:{width:"100%",height:"100%",display:"flex",flexWrap:"wrap",placeContent:"center",cursor:"pointer",":focus":{outline:"none"}},indicatorDot:{boxSizing:"content-box",width:10,height:10,borderRadius:"100%",borderWidth:2,borderColor:color.blue,borderStyle:"solid"},indicatorDotFocused:{borderWidth:5,borderStyle:"double"},indicatorDotActive:{backgroundColor:color.blue,width:"100%",height:"100%"},container:{borderTop:`1px solid ${gray76}`,borderBottom:`1px solid ${gray76}`,backgroundColor:tableBackgroundAccent,marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin,paddingBottom:phoneMargin,paddingLeft:phoneMargin,paddingRight:phoneMargin,paddingTop:10,width:"auto"}});
1821
+ class Indicators extends React.Component{render(){return jsxRuntimeExports.jsx("ul",{className:classNames$1(css(styles$h.indicatorContainer),"indicatorContainer"),children:this.props.gradedGroups.map(({title},i)=>jsxRuntimeExports.jsx("li",{className:css(styles$h.indicator),children:jsxRuntimeExports.jsx(Clickable,{role:"button","aria-label":this.context.strings.skipToTitle({title}),style:styles$h.indicatorButton,onClick:()=>this.props.onChangeCurrentGroup(i),onKeyDown:e=>this.handleKeyDown(e,i),children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx(View,{style:[styles$h.indicatorDot,(hovered||focused||pressed)&&styles$h.indicatorDotFocused],children:i===this.props.currentGroup&&jsxRuntimeExports.jsx(View,{style:styles$h.indicatorDotActive,children:jsxRuntimeExports.jsx("span",{className:css(a11y.srOnly),children:this.context.strings.current})})})})},title))})}constructor(...args){super(...args),this.handleKeyDown=(e,i)=>{if(e.key==="Enter"||e.key===" "){this.props.onChangeCurrentGroup(i);}};}}Indicators.contextType=PerseusI18nContext;class GradedGroupSet extends React.Component{shouldComponentUpdate(nextProps,nextState){return nextProps!==this.props||nextState!==this.state}getPromptJSON(){const activeGroupPromptJSON=this._childGroup.getPromptJSON();return getPromptJSON$h(this.props,activeGroupPromptJSON)}render(){const{JIPT}=getDependencies();if(JIPT.useJIPT&&this.props.gradedGroups.length>1){return jsxRuntimeExports.jsx("div",{className:css(styles$h.container),children:this.props.gradedGroups.map((gradedGroup,i)=>{return jsxRuntimeExports.jsx(GradedGroup,{...this.props,...gradedGroup,inGradedGroupSet:false,linterContext:this.props.linterContext},i)})})}const currentGroup=this.props.gradedGroups[this.state.currentGroup];if(!currentGroup){return jsxRuntimeExports.jsx("span",{children:"No current group..."})}const numGroups=this.props.gradedGroups.length;const handleNextQuestion=this.state.currentGroup<numGroups-1?this.handleNextQuestion:null;return jsxRuntimeExports.jsxs("div",{className:css(styles$h.container),children:[jsxRuntimeExports.jsxs("div",{className:css(styles$h.top),children:[jsxRuntimeExports.jsx("div",{className:css(styles$h.title),children:currentGroup.title}),jsxRuntimeExports.jsx("div",{className:css(styles$h.spacer)}),jsxRuntimeExports.jsx(Indicators,{currentGroup:this.state.currentGroup,gradedGroups:this.props.gradedGroups,onChangeCurrentGroup:currentGroup=>this.setState({currentGroup})})]}),jsxRuntimeExports.jsx(GradedGroup,{ref:comp=>this._childGroup=comp,...this.props,...currentGroup,inGradedGroupSet:true,title:null,onNextQuestion:handleNextQuestion,linterContext:this.props.linterContext},this.state.currentGroup)]})}constructor(...args){super(...args),this.state={currentGroup:0},this.getInputPaths=()=>{return this._childGroup.getInputPaths()},this.setInputValue=(path,newValue,cb)=>{return this._childGroup.setInputValue(path,newValue,cb)},this.focus=()=>{return this._childGroup.focus()},this.focusInputPath=path=>{this._childGroup.focusInputPath(path);},this.blurInputPath=path=>{this._childGroup.blurInputPath(path);},this.handleNextQuestion=()=>{const{currentGroup}=this.state;const numGroups=this.props.gradedGroups.length;if(currentGroup<numGroups-1){this.setState({currentGroup:currentGroup+1});}};}}GradedGroupSet.defaultProps={gradedGroups:[],linterContext:linterContextDefault};var GradedGroupSet$1 = {name:"graded-group-set",displayName:"Graded group set (articles only)",widget:GradedGroupSet,hidden:false,tracking:"all",isLintable:true};const styles$h=StyleSheet.create({top:{display:"flex",flexDirection:"row"},spacer:{flex:1},title:{fontSize:12,color:color.offBlack64,textTransform:"uppercase",marginBottom:11,letterSpacing:.8},indicatorContainer:{display:"flex",flexDirection:"row",listStyle:"none",margin:"unset",paddingInlineStart:"unset",flexWrap:"wrap"},indicator:{width:24,height:24},indicatorButton:{width:"100%",height:"100%",display:"flex",flexWrap:"wrap",placeContent:"center",cursor:"pointer",":focus":{outline:"none"}},indicatorDot:{boxSizing:"content-box",width:10,height:10,borderRadius:"100%",borderWidth:2,borderColor:color.blue,borderStyle:"solid"},indicatorDotFocused:{borderWidth:5,borderStyle:"double"},indicatorDotActive:{backgroundColor:color.blue,width:"100%",height:"100%"},container:{borderTop:`1px solid ${gray76}`,borderBottom:`1px solid ${gray76}`,backgroundColor:tableBackgroundAccent,marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin,paddingBottom:phoneMargin,paddingLeft:phoneMargin,paddingRight:phoneMargin,paddingTop:10,width:"auto"}});
1820
1822
 
1821
1823
  class ButtonGroup extends React.Component{componentWillUnmount(){this.container=null;}focus(){if(this.container){this.container.focus();return true}}toggleSelect(newValue){const value=this.props.value;if(this.props.allowEmpty){this.props.onChange(value!==newValue?newValue:null);}else {this.props.onChange(newValue);}}render(){const value=this.props.value;const buttons=this.props.buttons.map((button,i)=>{return jsxRuntimeExports.jsx("button",{title:button.title,type:"button",ref:"button"+i,className:css(styles$g.buttonStyle,button.value===value&&styles$g.selectedStyle,button.value===value&&this.props.selectedButtonStyle),onClick:()=>this.toggleSelect(button.value),children:button.content||""+button.value},""+i)});const outerStyle={display:"inline-block"};return jsxRuntimeExports.jsx("div",{style:outerStyle,ref:node=>this.container=node,children:buttons})}}ButtonGroup.defaultProps={value:null,allowEmpty:true};const styles$g=StyleSheet.create({buttonStyle:{backgroundColor:"white",border:"1px solid #ccc",borderLeft:"0",cursor:"pointer",margin:"0",padding:"5px 10px",position:"relative",":first-child":{borderLeft:"1px solid #ccc",borderTopLeftRadius:"3px",borderBottomLeftRadius:"3px"},":last-child":{borderRight:"1px solid #ccc",borderTopRightRadius:"3px",borderBottomRightRadius:"3px"},":hover":{backgroundColor:"#ccc"},":focus":{zIndex:2}},selectedStyle:{backgroundColor:"#ddd"}});
1822
1824
 
1823
1825
  const getPromptJSON$g=(renderProps,userInput)=>{const{type,coords}=userInput;const input={type,coords};if(userInput.type==="logarithm"||userInput.type==="exponential"){input["asymptote"]=userInput.asymptote;}return {type:"grapher",options:{availableTypes:renderProps.availableTypes,range:renderProps.graph.range,labels:renderProps.graph.labels,tickStep:renderProps.graph.step,gridStep:renderProps.graph.gridStep,snapStep:renderProps.graph.snapStep,backgroundImageUrl:renderProps.graph.backgroundImage.url},userInput:input}};
1824
1826
 
1825
- const movableTypeToComponent={PLOT:Graphie.Plot,PARABOLA:Graphie.Parabola,SINUSOID:Graphie.Sinusoid};const DEFAULT_BACKGROUND_IMAGE={url:null};const getEquationString=props=>{const plot=props.plot;if(plot.type&&plot.coords){const handler=GrapherUtil$1.functionForType(plot.type);const result=handler.getEquationString(plot.coords,plot.asymptote);return result||""}return ""};const pointsFromNormalized=(coordsList,range,step,snapStep)=>{const numSteps=function(range,step){return Math.floor((range[1]-range[0])/step)};return coordsList.map(coords=>{const unsnappedPoint=coords.map((coord,i)=>{const currRange=range[i];const currStep=step[i];const nSteps=numSteps(currRange,currStep);const tick=Math.round(coord*nSteps);return currRange[0]+currStep*tick});return point.roundTo(unsnappedPoint,snapStep)})};const maybePointsFromNormalized=(coordsList,range,step,snapStep)=>{if(coordsList){return pointsFromNormalized(coordsList,range,step,snapStep)}return coordsList};const defaultPlotProps=(type,graph)=>{const model=GrapherUtil$1.functionForType(type);const defaultAsymptote="defaultAsymptote"in model?model.defaultAsymptote:null;const gridStep=[1,1];const snapStep=Util.snapStepFromGridStep(gridStep);return {type,asymptote:maybePointsFromNormalized(defaultAsymptote,graph.range,graph.step,snapStep),coords:null}};const chooseType=_.first;const getGridAndSnapSteps=(options,boxSize)=>{const gridStep=options.gridStep||Util.getGridStep(options.range,options.step,boxSize);const snapStep=options.snapStep||Util.snapStepFromGridStep(gridStep);return {gridStep:gridStep,snapStep:snapStep}};const defaultGraph={labels:["x","y"],range:[[-10,10],[-10,10]],step:[1,1],backgroundImage:DEFAULT_BACKGROUND_IMAGE,markings:"graph",rulerLabel:"",rulerTicks:10,valid:true,showTooltips:false};const defaultPlot=defaultPlotProps("linear",defaultGraph);const DEFAULT_GRAPHER_PROPS={graph:defaultGraph,plot:defaultPlot,availableTypes:[defaultPlot.type]};const typeToButton=type=>{const capitalized=type.charAt(0).toUpperCase()+type.substring(1);const staticUrl=getDependencies().staticUrl;return {value:type,title:capitalized,content:jsxRuntimeExports.jsx("img",{src:staticUrl(GrapherUtil$1.functionForType(type).url),alt:capitalized})}};
1827
+ const movableTypeToComponent={PLOT:Graphie.Plot,PARABOLA:Graphie.Parabola,SINUSOID:Graphie.Sinusoid};const DEFAULT_BACKGROUND_IMAGE={url:null};const getEquationString=plot=>{if(plot.type&&plot.coords){const handler=GrapherUtil$1.functionForType(plot.type);const result=handler.getEquationString(plot.coords,plot.asymptote);return result||""}return ""};const pointsFromNormalized=(coordsList,range,step,snapStep)=>{const numSteps=function(range,step){return Math.floor((range[1]-range[0])/step)};return coordsList.map(coords=>{const unsnappedPoint=coords.map((coord,i)=>{const currRange=range[i];const currStep=step[i];const nSteps=numSteps(currRange,currStep);const tick=Math.round(coord*nSteps);return currRange[0]+currStep*tick});return point.roundTo(unsnappedPoint,snapStep)})};const maybePointsFromNormalized=(coordsList,range,step,snapStep)=>{if(coordsList){return pointsFromNormalized(coordsList,range,step,snapStep)}return coordsList};const defaultPlotProps=(type,graph)=>{const model=GrapherUtil$1.functionForType(type);const defaultAsymptote="defaultAsymptote"in model?model.defaultAsymptote:null;const gridStep=[1,1];const snapStep=Util.snapStepFromGridStep(gridStep);return {type,asymptote:maybePointsFromNormalized(defaultAsymptote,graph.range,graph.step,snapStep),coords:null}};const chooseType=_.first;const getGridAndSnapSteps=(options,boxSize)=>{const gridStep=options.gridStep||Util.getGridStep(options.range,options.step,boxSize);const snapStep=options.snapStep||Util.snapStepFromGridStep(gridStep);return {gridStep:gridStep,snapStep:snapStep}};const defaultGraph={labels:["x","y"],range:[[-10,10],[-10,10]],step:[1,1],backgroundImage:DEFAULT_BACKGROUND_IMAGE,markings:"graph",rulerLabel:"",rulerTicks:10,valid:true,showTooltips:false};const defaultPlot=defaultPlotProps("linear",defaultGraph);const DEFAULT_GRAPHER_PROPS={graph:defaultGraph,plot:defaultPlot,availableTypes:[defaultPlot.type]};const typeToButton=type=>{const capitalized=type.charAt(0).toUpperCase()+type.substring(1);const staticUrl=getDependencies().staticUrl;return {value:type,title:capitalized,content:jsxRuntimeExports.jsx("img",{src:staticUrl(GrapherUtil$1.functionForType(type).url),alt:capitalized})}};
1826
1828
 
1827
- const MovablePoint$3=Graphie.MovablePoint;const MovableLine$2=Graphie.MovableLine;function isFlipped(newCoord,oldCoord,line){const CCW=(a,b,c)=>{return (b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1])};return CCW(line[0],line[1],oldCoord)>0!==CCW(line[0],line[1],newCoord)>0}const typeSelectorStyle={padding:"5px 5px"};class FunctionGrapher extends React.Component{render(){const pointForCoord=(coord,i)=>{return jsxRuntimeExports.jsx(MovablePoint$3,{coord:coord,static:this.props.static,constraints:[Interactive2.MovablePoint.constraints.bound(),Interactive2.MovablePoint.constraints.snap(),coord=>{const isFunction=this._coords().every((otherCoord,j)=>{return i===j||!otherCoord||!number.equal(coord[0],otherCoord[0])});if(!isFunction){return false}if(this.props.model&&this.props.model.extraCoordConstraint){const extraConstraint=this.props.model.extraCoordConstraint;const proposedCoords=deepClone(this._coords());const oldCoord=deepClone(proposedCoords[i]);proposedCoords[i]=coord;return extraConstraint(coord,oldCoord,proposedCoords,this._asymptote(),this.props.graph)}return isFunction}],onMove:(newCoord,oldCoord)=>{let coords;const asymptote=this._asymptote();if(asymptote&&this.props.model.allowReflectOverAsymptote&&isFlipped(newCoord,oldCoord,asymptote)){coords=this._coords().map(coord=>{return point.reflectOverLine(coord,asymptote)});}else {coords=deepClone(this._coords());}coords[i]=newCoord;this.props.onChange({coords:coords});},showHairlines:this.props.showHairlines,hideHairlines:this.props.hideHairlines,showTooltips:this.props.showTooltips,isMobile:this.props.isMobile},i)};const points=this._coords().map(pointForCoord);const box=this.props.graph.box;const imageDescription=this.props.graph.backgroundImage;let image=null;if(imageDescription.url){const scale=box[0]/interactiveSizes$1.defaultBoxSize;image=jsxRuntimeExports.jsx(SvgImage,{src:imageDescription.url,width:imageDescription.width,height:imageDescription.height,scale:scale});}return jsxRuntimeExports.jsx("div",{className:"perseus-widget "+"perseus-widget-grapher",style:{width:box[0],height:box[1],boxSizing:"initial"},children:jsxRuntimeExports.jsxs("div",{className:"graphie-container blank-background",style:{width:box[0],height:box[1]},children:[image,jsxRuntimeExports.jsxs(Graphie,{...this.props.graph,setDrawingAreaAvailable:this.props.setDrawingAreaAvailable,children:[this.props.model&&this.renderPlot(),this.props.model&&this.renderAsymptote(),this.props.model&&points]})]})})}constructor(...args){super(...args),this._coords=()=>{const props=this.props;const graph=props.graph;const defaultModelCoords=props.model&&maybePointsFromNormalized(props.model.defaultCoords,graph.range,graph.step,graph.snapStep);return props.coords||defaultModelCoords||null},this._asymptote=()=>{return this.props.asymptote},this.change=(...args)=>{return change.apply(this,args)},this.renderPlot=()=>{const model=this.props.model;const xRange=this.props.graph.range[0];const style={stroke:this.props.isMobile?KhanColors.BLUE_C:KhanColors.DYNAMIC,...this.props.isMobile?{"stroke-width":3}:{}};const coeffs=model.getCoefficients(this._coords(),this._asymptote());if(!coeffs){return}const functionProps=model.getPropsForCoeffs(coeffs,xRange);const Movable=movableTypeToComponent[model.movable];return createElement(Movable,{...functionProps,key:this.props.model.url,range:xRange,style:style})},this.renderAsymptote=()=>{const model=this.props.model;const graph=this.props.graph;const asymptote=this._asymptote();const showAsymptote=asymptote?.length>0;const dashed={strokeDasharray:"- "};return showAsymptote&&jsxRuntimeExports.jsx(MovableLine$2,{onMove:(newCoord,oldCoord)=>{const delta=vector$3.subtract(newCoord,oldCoord);const newAsymptote=this._asymptote().map(coord=>vector$3.add(coord,delta));this.props.onChange({asymptote:newAsymptote});},constraints:[Interactive2.MovableLine.constraints.bound(),Interactive2.MovableLine.constraints.snap(),(newCoord,oldCoord)=>{const delta=vector$3.subtract(newCoord,oldCoord);const proposedAsymptote=this._asymptote().map(coord=>vector$3.add(coord,delta));if(model.extraAsymptoteConstraint){return model.extraAsymptoteConstraint(newCoord,oldCoord,this._coords(),proposedAsymptote,graph)}return true}],normalStyle:dashed,highlightStyle:dashed,children:asymptote.map((coord,i)=>jsxRuntimeExports.jsx(MovablePoint$3,{coord:coord,static:true,draw:null,extendLine:true,showHairlines:this.props.showHairlines,hideHairlines:this.props.hideHairlines,showTooltips:this.props.showTooltips,isMobile:this.props.isMobile},`asymptoteCoord-${i}`))})};}}FunctionGrapher.defaultProps={graph:{range:[[-10,10],[-10,10]],step:[1,1]},coords:null,asymptote:null,isMobile:false};class Grapher extends React.Component{static getUserInputFromProps(props){return props.plot}_getGridConfig(options){return options.step.map((step,i)=>{return Util.gridDimensionConfig(step,options.range[i],options.box[i],options.gridStep[i])})}_calculateMobileTickStep(gridStep,step,ranges){const tickStep=Util.constrainedTickStepsFromTickSteps(step,ranges);tickStep[0]=tickStep[0]/gridStep[0];tickStep[1]=tickStep[1]/gridStep[1];return tickStep}getUserInput(){return Grapher.getUserInputFromProps(this.props)}getPromptJSON(){return getPromptJSON$g(this.props,this.getUserInput())}render(){const type=this.props.plot.type;const coords=this.props.plot.coords;const asymptote="asymptote"in this.props.plot?this.props.plot.asymptote:undefined;const typeSelector=jsxRuntimeExports.jsx("div",{style:typeSelectorStyle,children:jsxRuntimeExports.jsx(ButtonGroup,{value:type,allowEmpty:true,buttons:this.props.availableTypes.map(typeToButton),onChange:this.handleActiveTypeChange})});const box=getInteractiveBoxFromSizeClass(this.props.containerSizeClass);const options={...this.props.graph,...getGridAndSnapSteps(this.props.graph,box[0]),gridConfig:this._getGridConfig({...this.props.graph,box:box,...getGridAndSnapSteps(this.props.graph,box[0])})};const grapherProps={graph:{box:box,range:options.range,step:options.step,snapStep:options.snapStep,backgroundImage:options.backgroundImage,options:options,setup:this._setupGraphie},onChange:this.handlePlotChanges,model:type&&GrapherUtil$1.functionForType(type),coords:coords,asymptote:asymptote,static:this.props.static,setDrawingAreaAvailable:this.props.apiOptions.setDrawingAreaAvailable,isMobile:this.props.apiOptions.isMobile,showTooltips:this.props.graph.showTooltips,showHairlines:this.showHairlines,hideHairlines:this.hideHairlines};return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx(FunctionGrapher,{...grapherProps}),this.props.availableTypes.length>1&&typeSelector]})}constructor(...args){super(...args),this.handlePlotChanges=newPlot=>{const plot={...this.props.plot,...newPlot};this.props.onChange({plot:plot});this.props.trackInteraction();},this.handleActiveTypeChange=newType=>{const graph=this.props.graph;const plot={...this.props.plot,...defaultPlotProps(newType,graph)};this.props.onChange({plot:plot});},this._setupGraphie=(graphie,options)=>{const isMobile=this.props.apiOptions.isMobile;if(options.markings==="graph"){graphie.graphInit({range:options.range,scale:options.gridConfig.map(e=>e.scale),axisArrows:"<->",labelFormat:function(s){return "\\small{"+s+"}"},gridStep:options.gridStep,snapStep:options.snapStep,tickStep:isMobile?this._calculateMobileTickStep(options.gridStep,options.step,options.range):options.gridConfig.map(e=>e.tickStep),labelStep:1,unityLabels:options.gridConfig.map(e=>e.unityLabel),isMobile:isMobile});graphie.label([0,options.range[1][1]],options.labels[1],isMobile?"below right":"above");graphie.label([options.range[0][1],0],options.labels[0],isMobile?"above left":"right");}else if(options.markings==="grid"){graphie.graphInit({range:options.range,scale:options.gridConfig.map(e=>e.scale),gridStep:options.gridStep,axes:false,ticks:false,labels:false,isMobile:isMobile});}else if(options.markings==="none"){graphie.init({range:options.range,scale:options.gridConfig.map(e=>e.scale)});}if(this.props.apiOptions.isMobile){const hairlineStyle={normalStyle:{strokeWidth:1}};this.horizHairline=new WrappedLine(graphie,[0,0],[0,0],hairlineStyle);this.horizHairline.attr({stroke:KhanColors.INTERACTIVE});this.horizHairline.hide();this.vertHairline=new WrappedLine(graphie,[0,0],[0,0],hairlineStyle);this.vertHairline.attr({stroke:KhanColors.INTERACTIVE});this.vertHairline.hide();}},this.showHairlines=point=>{if(this.props.apiOptions.isMobile){this.horizHairline.moveTo([this.props.graph.range[0][0],point[1]],[this.props.graph.range[0][1],point[1]]);this.horizHairline.show();this.vertHairline.moveTo([point[0],this.props.graph.range[1][0]],[point[0],this.props.graph.range[1][1]]);this.vertHairline.show();}},this.hideHairlines=()=>{if(this.props.apiOptions.isMobile){this.horizHairline.hide();this.vertHairline.hide();}};}}Grapher.defaultProps=DEFAULT_GRAPHER_PROPS;const transform$1=editorProps=>{const widgetProps={availableTypes:editorProps.availableTypes,graph:editorProps.graph};if(widgetProps.availableTypes.length===1){const graph=widgetProps.graph;const type=chooseType(widgetProps.availableTypes);widgetProps.plot=defaultPlotProps(type,graph);}return widgetProps};const staticTransform$4=editorProps=>{return {...transform$1(editorProps),availableTypes:[editorProps.correct.type],plot:editorProps.correct}};var Grapher$1 = {name:"grapher",displayName:"Grapher",hidden:true,widget:Grapher,transform: transform$1,staticTransform: staticTransform$4};
1829
+ const MovablePoint$3=Graphie.MovablePoint;const MovableLine$2=Graphie.MovableLine;function isFlipped(newCoord,oldCoord,line){const CCW=(a,b,c)=>{return (b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1])};return CCW(line[0],line[1],oldCoord)>0!==CCW(line[0],line[1],newCoord)>0}const typeSelectorStyle={padding:"5px 5px"};class FunctionGrapher extends React.Component{render(){const pointForCoord=(coord,i)=>{return jsxRuntimeExports.jsx(MovablePoint$3,{coord:coord,static:this.props.static,constraints:[Interactive2.MovablePoint.constraints.bound(),Interactive2.MovablePoint.constraints.snap(),coord=>{const isFunction=this._coords().every((otherCoord,j)=>{return i===j||!otherCoord||!number.equal(coord[0],otherCoord[0])});if(!isFunction){return false}if(this.props.model&&this.props.model.extraCoordConstraint){const extraConstraint=this.props.model.extraCoordConstraint;const proposedCoords=deepClone(this._coords());const oldCoord=deepClone(proposedCoords[i]);proposedCoords[i]=coord;return extraConstraint(coord,oldCoord,proposedCoords,this._asymptote(),this.props.graph)}return isFunction}],onMove:(newCoord,oldCoord)=>{let coords;const asymptote=this._asymptote();if(asymptote&&this.props.model.allowReflectOverAsymptote&&isFlipped(newCoord,oldCoord,asymptote)){coords=this._coords().map(coord=>{return point.reflectOverLine(coord,asymptote)});}else {coords=deepClone(this._coords());}coords[i]=newCoord;this.props.onChange({coords:coords});},showHairlines:this.props.showHairlines,hideHairlines:this.props.hideHairlines,showTooltips:this.props.showTooltips,isMobile:this.props.isMobile},i)};const points=this._coords().map(pointForCoord);const box=this.props.graph.box;const imageDescription=this.props.graph.backgroundImage;let image=null;if(imageDescription.url){const scale=box[0]/interactiveSizes$1.defaultBoxSize;image=jsxRuntimeExports.jsx(SvgImage,{src:imageDescription.url,width:imageDescription.width,height:imageDescription.height,scale:scale});}return jsxRuntimeExports.jsx("div",{className:"perseus-widget "+"perseus-widget-grapher",style:{width:box[0],height:box[1],boxSizing:"initial"},children:jsxRuntimeExports.jsxs("div",{className:"graphie-container blank-background",style:{width:box[0],height:box[1]},children:[image,jsxRuntimeExports.jsxs(Graphie,{...this.props.graph,setDrawingAreaAvailable:this.props.setDrawingAreaAvailable,children:[this.props.model&&this.renderPlot(),this.props.model&&this.renderAsymptote(),this.props.model&&points]})]})})}constructor(...args){super(...args),this._coords=()=>{const props=this.props;const graph=props.graph;const defaultModelCoords=props.model&&maybePointsFromNormalized(props.model.defaultCoords,graph.range,graph.step,graph.snapStep);return props.coords||defaultModelCoords||null},this._asymptote=()=>{return this.props.asymptote},this.renderPlot=()=>{const model=this.props.model;const xRange=this.props.graph.range[0];const style={stroke:this.props.isMobile?KhanColors.BLUE_C:KhanColors.DYNAMIC,...this.props.isMobile?{"stroke-width":3}:{}};const coeffs=model.getCoefficients(this._coords(),this._asymptote());if(!coeffs){return}const functionProps=model.getPropsForCoeffs(coeffs,xRange);const Movable=movableTypeToComponent[model.movable];return createElement(Movable,{...functionProps,key:this.props.model.url,range:xRange,style:style})},this.renderAsymptote=()=>{const model=this.props.model;const graph=this.props.graph;const asymptote=this._asymptote();const showAsymptote=asymptote?.length>0;const dashed={strokeDasharray:"- "};return showAsymptote&&jsxRuntimeExports.jsx(MovableLine$2,{onMove:(newCoord,oldCoord)=>{const delta=vector$3.subtract(newCoord,oldCoord);const newAsymptote=this._asymptote().map(coord=>vector$3.add(coord,delta));this.props.onChange({asymptote:newAsymptote});},constraints:[Interactive2.MovableLine.constraints.bound(),Interactive2.MovableLine.constraints.snap(),(newCoord,oldCoord)=>{const delta=vector$3.subtract(newCoord,oldCoord);const proposedAsymptote=this._asymptote().map(coord=>vector$3.add(coord,delta));if(model.extraAsymptoteConstraint){return model.extraAsymptoteConstraint(newCoord,oldCoord,this._coords(),proposedAsymptote,graph)}return true}],normalStyle:dashed,highlightStyle:dashed,children:asymptote.map((coord,i)=>jsxRuntimeExports.jsx(MovablePoint$3,{coord:coord,static:true,draw:null,extendLine:true,showHairlines:this.props.showHairlines,hideHairlines:this.props.hideHairlines,showTooltips:this.props.showTooltips,isMobile:this.props.isMobile},`asymptoteCoord-${i}`))})};}}FunctionGrapher.defaultProps={graph:{range:[[-10,10],[-10,10]],step:[1,1]},coords:null,asymptote:null,isMobile:false};class Grapher extends React.Component{_getGridConfig(options){return options.step.map((step,i)=>{return Util.gridDimensionConfig(step,options.range[i],options.box[i],options.gridStep[i])})}_calculateMobileTickStep(gridStep,step,ranges){const tickStep=Util.constrainedTickStepsFromTickSteps(step,ranges);tickStep[0]=tickStep[0]/gridStep[0];tickStep[1]=tickStep[1]/gridStep[1];return tickStep}getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$g(this.props,this.getUserInput())}getSerializedState(){const{userInput:_,...rest}=this.props;return {...rest,plot:this.props.userInput}}render(){const type=this.props.userInput.type;const coords=this.props.userInput.coords;const asymptote="asymptote"in this.props.userInput?this.props.userInput.asymptote:undefined;const typeSelector=jsxRuntimeExports.jsx("div",{style:typeSelectorStyle,children:jsxRuntimeExports.jsx(ButtonGroup,{value:type,allowEmpty:true,buttons:this.props.availableTypes.map(typeToButton),onChange:this.handleActiveTypeChange})});const box=getInteractiveBoxFromSizeClass(this.props.containerSizeClass);const options={...this.props.graph,...getGridAndSnapSteps(this.props.graph,box[0]),gridConfig:this._getGridConfig({...this.props.graph,box:box,...getGridAndSnapSteps(this.props.graph,box[0])})};const grapherProps={graph:{box:box,range:options.range,step:options.step,snapStep:options.snapStep,backgroundImage:options.backgroundImage,options:options,setup:this._setupGraphie},onChange:this.handlePlotChanges,model:type&&GrapherUtil$1.functionForType(type),coords:coords,asymptote:asymptote,static:this.props.static,setDrawingAreaAvailable:this.props.apiOptions.setDrawingAreaAvailable,isMobile:this.props.apiOptions.isMobile,showTooltips:this.props.graph.showTooltips,showHairlines:this.showHairlines,hideHairlines:this.hideHairlines};return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx(FunctionGrapher,{...grapherProps}),this.props.availableTypes.length>1&&typeSelector]})}constructor(...args){super(...args),this.handlePlotChanges=newPlot=>{const plot={...this.props.userInput,...newPlot};this.props.handleUserInput(plot);this.props.trackInteraction();},this.handleActiveTypeChange=newType=>{const graph=this.props.graph;const plot={...this.props.userInput,...defaultPlotProps(newType,graph)};this.props.handleUserInput(plot);},this._setupGraphie=(graphie,options)=>{const isMobile=this.props.apiOptions.isMobile;if(options.markings==="graph"){graphie.graphInit({range:options.range,scale:options.gridConfig.map(e=>e.scale),axisArrows:"<->",labelFormat:function(s){return "\\small{"+s+"}"},gridStep:options.gridStep,snapStep:options.snapStep,tickStep:isMobile?this._calculateMobileTickStep(options.gridStep,options.step,options.range):options.gridConfig.map(e=>e.tickStep),labelStep:1,unityLabels:options.gridConfig.map(e=>e.unityLabel),isMobile:isMobile});graphie.label([0,options.range[1][1]],options.labels[1],isMobile?"below right":"above");graphie.label([options.range[0][1],0],options.labels[0],isMobile?"above left":"right");}else if(options.markings==="grid"){graphie.graphInit({range:options.range,scale:options.gridConfig.map(e=>e.scale),gridStep:options.gridStep,axes:false,ticks:false,labels:false,isMobile:isMobile});}else if(options.markings==="none"){graphie.init({range:options.range,scale:options.gridConfig.map(e=>e.scale)});}if(this.props.apiOptions.isMobile){const hairlineStyle={normalStyle:{strokeWidth:1}};this.horizHairline=new WrappedLine(graphie,[0,0],[0,0],hairlineStyle);this.horizHairline.attr({stroke:KhanColors.INTERACTIVE});this.horizHairline.hide();this.vertHairline=new WrappedLine(graphie,[0,0],[0,0],hairlineStyle);this.vertHairline.attr({stroke:KhanColors.INTERACTIVE});this.vertHairline.hide();}},this.showHairlines=point=>{if(this.props.apiOptions.isMobile){this.horizHairline.moveTo([this.props.graph.range[0][0],point[1]],[this.props.graph.range[0][1],point[1]]);this.horizHairline.show();this.vertHairline.moveTo([point[0],this.props.graph.range[1][0]],[point[0],this.props.graph.range[1][1]]);this.vertHairline.show();}},this.hideHairlines=()=>{if(this.props.apiOptions.isMobile){this.horizHairline.hide();this.vertHairline.hide();}};}}function transform$2(options){const{availableTypes,graph}=options;return {availableTypes,graph}}function staticTransform$1(options){return {...transform$2(options),availableTypes:[options.correct.type],plot:options.correct}}function getUserInputFromSerializedState$a(serializedState){return serializedState.plot}function getStartUserInput$7(options){if(options.availableTypes.length===1){const graph=options.graph;const type=chooseType(options.availableTypes);if(type){return defaultPlotProps(type,graph)}}return DEFAULT_GRAPHER_PROPS.plot}var Grapher$1 = {name:"grapher",displayName:"Grapher",hidden:true,widget:Grapher,transform: transform$2,staticTransform: staticTransform$1,getUserInputFromSerializedState: getUserInputFromSerializedState$a,getStartUserInput: getStartUserInput$7};
1828
1830
 
1829
1831
  const getPromptJSON$f=rendererJSON=>{if(!rendererJSON){return {type:"group",content:"",widgets:{}}}return {...rendererJSON,type:"group"}};
1830
1832
 
@@ -1832,17 +1834,17 @@ class Group extends React.Component{componentDidMount(){this.forceUpdate();}getU
1832
1834
 
1833
1835
  const getPromptJSON$e=()=>{return getUnsupportedPromptJSON("iframe")};
1834
1836
 
1835
- const{updateQueryString}=Util;class Iframe extends React.Component{componentDidMount(){$(window).on("message",this.handleMessageEvent);}componentWillUnmount(){$(window).off("message",this.handleMessageEvent);}getUserInput(){return {status:this.props.status,message:this.props.message}}getPromptJSON(){return getPromptJSON$e()}render(){const style={width:String(this.props.width),height:String(this.props.height)};const{InitialRequestUrl}=getDependencies();Object.entries(style).forEach(([key,value])=>{if(!value.endsWith("%")&&!value.endsWith("px")){style[key]=value+"px";}});let url=this.props.url;if(url&&url.length&&url.indexOf("http")!==0){url="https://www.khanacademy.org/computer-programming/program/"+url+"/embedded?buttons=no&embed=yes&editor=no&author=no";url=updateQueryString(url,"width",this.props.width);url=updateQueryString(url,"height",this.props.height);url=updateQueryString(url,"origin",InitialRequestUrl.origin);}if(this.props.settings){const settings={};this.props.settings.forEach(setting=>{if(setting.name&&setting.value){settings[setting.name]=setting.value;}});url=updateQueryString(url,"settings",JSON.stringify(settings));}let sandboxProperties="allow-same-origin allow-scripts";sandboxProperties+=" allow-top-navigation";return jsxRuntimeExports.jsx("iframe",{sandbox:sandboxProperties,style:style,src:url,allowFullScreen:this.props.allowFullScreen})}constructor(...args){super(...args),this.handleMessageEvent=e=>{let data={};try{data=JSON.parse(e.originalEvent.data);}catch{return}if(data.testsPassed===undefined){return}const status=data.testsPassed?"correct":"incorrect";this.change({status:status,message:data.message});},this.change=(...args)=>{return change.apply(this,args)};}}Iframe.defaultProps={status:"incomplete",message:null,allowFullScreen:false,allowTopNavigation:false};var Iframe$1 = {name:"iframe",displayName:"Iframe (deprecated)",widget:Iframe,hidden:true};
1837
+ const{updateQueryString}=Util;class Iframe extends React.Component{componentDidMount(){$(window).on("message",this.handleMessageEvent);}componentWillUnmount(){$(window).off("message",this.handleMessageEvent);}getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$e()}render(){const style={width:String(this.props.width),height:String(this.props.height)};const{InitialRequestUrl}=getDependencies();Object.entries(style).forEach(([key,value])=>{if(!value.endsWith("%")&&!value.endsWith("px")){style[key]=value+"px";}});let url=this.props.url;if(url&&url.length&&url.indexOf("http")!==0){url="https://www.khanacademy.org/computer-programming/program/"+url+"/embedded?buttons=no&embed=yes&editor=no&author=no";url=updateQueryString(url,"width",this.props.width);url=updateQueryString(url,"height",this.props.height);url=updateQueryString(url,"origin",InitialRequestUrl.origin);}if(this.props.settings){const settings={};this.props.settings.forEach(setting=>{if(setting.name&&setting.value){settings[setting.name]=setting.value;}});url=updateQueryString(url,"settings",JSON.stringify(settings));}let sandboxProperties="allow-same-origin allow-scripts";sandboxProperties+=" allow-top-navigation";return jsxRuntimeExports.jsx("iframe",{sandbox:sandboxProperties,style:style,src:url,allowFullScreen:this.props.allowFullScreen})}constructor(...args){super(...args),this.handleMessageEvent=e=>{let data={};try{data=JSON.parse(e.originalEvent.data);}catch{return}if(data.testsPassed===undefined){return}const status=data.testsPassed?"correct":"incorrect";this.props.handleUserInput({status:status,message:data.message});};}}Iframe.defaultProps={allowFullScreen:false,allowTopNavigation:false,userInput:{status:"incomplete",message:null}};function getUserInputFromSerializedState$9(serializedState){return {status:serializedState.status,message:serializedState.message}}var Iframe$1 = {name:"iframe",displayName:"Iframe (deprecated)",widget:Iframe,hidden:true,getUserInputFromSerializedState: getUserInputFromSerializedState$9};
1836
1838
 
1837
1839
  const getPromptJSON$d=renderProps=>{return {type:"image",options:{altText:renderProps.alt,title:renderProps.title,caption:renderProps.caption,imageUrl:renderProps.backgroundImage.url}}};
1838
1840
 
1839
- const defaultBoxSize=400;const defaultRange=[0,10];const defaultBackgroundImage$1={url:null,width:0,height:0};class ImageWidget extends React.Component{getPromptJSON(){return getPromptJSON$d(this.props)}render(){let image;const alt=this.props.caption===this.props.alt?"":this.props.alt;const{apiOptions}=this.props;const backgroundImage=this.props.backgroundImage;if(backgroundImage.url){const url=backgroundImage.url;image=jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{src:url,alt:alt,width:backgroundImage.width,height:backgroundImage.height,preloader:apiOptions.imagePreloader,extraGraphie:{box:this.props.box,range:this.props.range,labels:this.props.labels},trackInteraction:this.props.trackInteraction,zoomToFullSizeOnMobile:apiOptions.isMobile,constrainHeight:apiOptions.isMobile,allowFullBleed:apiOptions.isMobile,setAssetStatus:setAssetStatus})});}if(apiOptions.isMobile){let titleAndCaption;if(this.props.title||this.props.caption){let title=this.props.title;if(title){if(this.props.caption&&!/[.?!"']\s*$/.test(title)){title+=".";}title=`**${title}** `;}const className=classNames$1({"perseus-image-caption":true,"has-title":!!title});const alignment=this.props.alignment;const isImageFullWidth=alignment==="block"||alignment==="full-width";const minWidth=isImageFullWidth?null:"0 !important";titleAndCaption=jsxRuntimeExports.jsx("figcaption",{className:className,style:{maxWidth:backgroundImage.width},children:jsxRuntimeExports.jsx("div",{style:{minWidth:minWidth},children:jsxRuntimeExports.jsx(Renderer,{content:title+this.props.caption,apiOptions:apiOptions,linterContext:this.props.linterContext,strings:this.context.strings})})});}return jsxRuntimeExports.jsxs("figure",{className:"perseus-image-widget",style:{maxWidth:backgroundImage.width},children:[image,titleAndCaption]})}let title;let caption;if(this.props.title){title=jsxRuntimeExports.jsx("div",{className:"perseus-image-title",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.title,apiOptions:apiOptions,linterContext:this.props.linterContext,strings:this.context.strings})});}if(this.props.caption){caption=jsxRuntimeExports.jsx("figcaption",{className:"perseus-image-caption",style:{maxWidth:backgroundImage.width},children:jsxRuntimeExports.jsx(Renderer,{content:this.props.caption,apiOptions:apiOptions,linterContext:this.props.linterContext,strings:this.context.strings})});}return jsxRuntimeExports.jsxs("figure",{className:"perseus-image-widget",style:{maxWidth:backgroundImage.width},children:[title,image,caption]})}constructor(...args){super(...args),this.isWidget=true,this.change=(...args)=>{return change.apply(this,args)};}}ImageWidget.contextType=PerseusI18nContext;ImageWidget.defaultProps={alignment:"block",title:"",range:[defaultRange,defaultRange],box:[defaultBoxSize,defaultBoxSize],backgroundImage:defaultBackgroundImage$1,labels:[],alt:"",caption:"",linterContext:linterContextDefault};var Image$1 = {name:"image",displayName:"Image",widget:ImageWidget,isLintable:true};
1841
+ const defaultBoxSize=400;const defaultRange=[0,10];const defaultBackgroundImage$1={url:null,width:0,height:0};class ImageWidget extends React.Component{getPromptJSON(){return getPromptJSON$d(this.props)}render(){let image;const alt=this.props.caption===this.props.alt?"":this.props.alt;const{apiOptions}=this.props;const backgroundImage=this.props.backgroundImage;if(backgroundImage.url){const url=backgroundImage.url;image=jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{src:url,alt:alt,width:backgroundImage.width,height:backgroundImage.height,preloader:apiOptions.imagePreloader,extraGraphie:{box:this.props.box,range:this.props.range,labels:this.props.labels},trackInteraction:this.props.trackInteraction,zoomToFullSizeOnMobile:apiOptions.isMobile,constrainHeight:apiOptions.isMobile,allowFullBleed:apiOptions.isMobile,setAssetStatus:setAssetStatus})});}if(apiOptions.isMobile){let titleAndCaption;if(this.props.title||this.props.caption){let title=this.props.title;if(title){if(this.props.caption&&!/[.?!"']\s*$/.test(title)){title+=".";}title=`**${title}** `;}const className=classNames$1({"perseus-image-caption":true,"has-title":!!title});const alignment=this.props.alignment;const isImageFullWidth=alignment==="block"||alignment==="full-width";const minWidth=isImageFullWidth?null:"0 !important";titleAndCaption=jsxRuntimeExports.jsx("figcaption",{className:className,style:{maxWidth:backgroundImage.width},children:jsxRuntimeExports.jsx("div",{style:{minWidth:minWidth},children:jsxRuntimeExports.jsx(Renderer,{content:title+this.props.caption,apiOptions:apiOptions,linterContext:this.props.linterContext,strings:this.context.strings})})});}return jsxRuntimeExports.jsxs("figure",{className:"perseus-image-widget",style:{maxWidth:backgroundImage.width},children:[image,titleAndCaption]})}let title;let caption;if(this.props.title){title=jsxRuntimeExports.jsx("div",{className:"perseus-image-title",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.title,apiOptions:apiOptions,linterContext:this.props.linterContext,strings:this.context.strings})});}if(this.props.caption){caption=jsxRuntimeExports.jsx("figcaption",{className:"perseus-image-caption",style:{maxWidth:backgroundImage.width},children:jsxRuntimeExports.jsx(Renderer,{content:this.props.caption,apiOptions:apiOptions,linterContext:this.props.linterContext,strings:this.context.strings})});}return jsxRuntimeExports.jsxs("figure",{className:"perseus-image-widget",style:{maxWidth:backgroundImage.width},children:[title,image,caption]})}constructor(...args){super(...args),this.isWidget=true;}}ImageWidget.contextType=PerseusI18nContext;ImageWidget.defaultProps={alignment:"block",title:"",range:[defaultRange,defaultRange],box:[defaultBoxSize,defaultBoxSize],backgroundImage:defaultBackgroundImage$1,labels:[],alt:"",caption:"",linterContext:linterContextDefault};var Image$1 = {name:"image",displayName:"Image",widget:ImageWidget,isLintable:true};
1840
1842
 
1841
1843
  const getPromptJSON$c=()=>{return getUnsupportedPromptJSON("interaction")};
1842
1844
 
1843
- const Label=Graphie.Label;const Line$2=Graphie.Line;const MovablePoint$2=Graphie.MovablePoint;const MovableLine$1=Graphie.MovableLine;const Plot=Graphie.Plot;const PlotParametric=Graphie.PlotParametric;const Point=Graphie.Point;const Rect=Graphie.Rect;const{unescapeMathMode}=Util;const KAShashFunc=(expr,options)=>{options=options||{};let result=expr+"||"+options.decimal_separator+"||";const functions=options.functions;const functionsLength=functions?functions.length:0;for(let i=0;i<functionsLength;i++){result+=functions[i]+"|";}return result};const _parseCache=Object.create(null);const KASparse=(expr,options)=>{const hash=KAShashFunc(expr,options);let cached=_parseCache[hash];if(cached){return cached}cached=KAS.parse(expr,options);_parseCache[hash]=cached;return cached};const _compileCache=Object.create(null);const KAScompile=(expr,options)=>{const hash=KAShashFunc(expr,options);let cached=_compileCache[hash];if(cached){return cached}const parsed=KAS.parse(expr,options).expr;cached=parsed?parsed.compile():function(){return 0};_compileCache[hash]=cached;return cached};class Interaction extends React.Component{UNSAFE_componentWillReceiveProps(nextProps){if(!_.isEqual(this.props.elements,nextProps.elements)){this.setState({variables:_getInitialVariables(nextProps.elements),functions:_getInitialFunctions(nextProps.elements)});}}getPromptJSON(){return getPromptJSON$c()}render(){const range=this.props.graph.range;let labels=this.props.graph.labels;if(this.props.graph.markings==="graph"){labels=this.props.graph.labels.map(label=>label.startsWith("$")&&label.endsWith("$")?label.slice(1,-1):label);}return jsxRuntimeExports.jsxs(Graphie,{box:this.props.graph.box,range:this.props.graph.range,options:this.props.graph,setup:this._setupGraphie,setDrawingAreaAvailable:this.props.apiOptions.setDrawingAreaAvailable,children:[this.props.graph.markings==="graph"&&jsxRuntimeExports.jsx(Label,{coord:[0,range[1][1]],text:labels[1],direction:"above"}),this.props.graph.markings==="graph"&&jsxRuntimeExports.jsx(Label,{coord:[range[0][1],0],text:labels[0],direction:"right"}),_.map(this.props.elements,function(element,n){if(element.type==="point"){return jsxRuntimeExports.jsx(Point,{coord:[this._eval(element.options.coordX),this._eval(element.options.coordY)],color:element.options.color},element.key)}if(element.type==="line"){const start=[this._eval(element.options.startX),this._eval(element.options.startY)];const end=[this._eval(element.options.endX),this._eval(element.options.endY)];return jsxRuntimeExports.jsx(Line$2,{start:start,end:end,style:{stroke:element.options.color,strokeWidth:element.options.strokeWidth,strokeDasharray:element.options.strokeDasharray,arrows:element.options.arrows}},element.key)}if(element.type==="movable-point"){const constraints=[coord=>{const coordX=Math.max(this._eval(element.options.constraintXMin),Math.min(this._eval(element.options.constraintXMax),coord[0]));const coordY=Math.max(this._eval(element.options.constraintYMin),Math.min(this._eval(element.options.constraintYMax),coord[1]));return [coordX,coordY]}];if(element.options.constraint==="snap"){constraints.push(MovablePoint$2.constraints.snap(element.options.snap));}else if(element.options.constraint==="x"){constraints.push(coord=>{return [this._eval(element.options.constraintFn,{y:coord[1]}),coord[1]]});}else if(element.options.constraint==="y"){constraints.push(coord=>{return [coord[0],this._eval(element.options.constraintFn,{x:coord[0]})]});}return jsxRuntimeExports.jsx(MovablePoint$2,{coord:[this.state.variables["x_"+element.options.varSubscript],this.state.variables["y_"+element.options.varSubscript]],constraints:constraints,foo_x:element.options.constraint,foo_y:element.options.constraintFn,foo_z:element.options.snap,onMove:_.partial(this._updatePointLocation,element.options.varSubscript)},element.key)}if(element.type==="movable-line"){const constraints=[coord=>{const coordX=Math.max(this._eval(element.options.constraintXMin),Math.min(this._eval(element.options.constraintXMax),coord[0]));const coordY=Math.max(this._eval(element.options.constraintYMin),Math.min(this._eval(element.options.constraintYMax),coord[1]));return [coordX,coordY]}];if(element.options.constraint==="snap"){constraints.push(MovablePoint$2.constraints.snap(element.options.snap));}else if(element.options.constraint==="x"){constraints.push(coord=>{return [this._eval(element.options.constraintFn,{y:coord[1]}),coord[1]]});}else if(element.options.constraint==="y"){constraints.push(coord=>{return [coord[0],this._eval(element.options.constraintFn,{x:coord[0]})]});}const start=[this.state.variables["x_"+element.options.startSubscript],this.state.variables["y_"+element.options.startSubscript]];const end=[this.state.variables["x_"+element.options.endSubscript],this.state.variables["y_"+element.options.endSubscript]];return jsxRuntimeExports.jsxs(MovableLine$1,{constraints:constraints,onMove:_.bind(this._updateLineLocation,this,element.options),foo_x:element.options.constraint,foo_y:element.options.constraintFn,foo_z:element.options.snap,children:[jsxRuntimeExports.jsx(MovablePoint$2,{coord:start,static:true,normalStyle:{stroke:"none",fill:"none"}}),jsxRuntimeExports.jsx(MovablePoint$2,{coord:end,static:true,normalStyle:{stroke:"none",fill:"none"}})]},element.key)}if(element.type==="function"){const fn=x=>{return this._eval(element.options.value,{x:x})};const vars=_.without(this._extractVars(KASparse(element.options.value).expr),"x");const varValues=_.object(vars,_.map(vars,v=>this.state.variables[v]));const range=[this._eval(element.options.rangeMin,this.state.variables),this._eval(element.options.rangeMax,this.state.variables)];return jsxRuntimeExports.jsx(Plot,{fn:fn,foo_fn:element.options.value,foo_varvalues:varValues,range:range,style:{stroke:element.options.color,strokeWidth:element.options.strokeWidth,strokeDasharray:element.options.strokeDasharray,plotPoints:100}},element.key)}if(element.type==="parametric"){const fn=t=>{return [this._eval(element.options.x,{t:t}),this._eval(element.options.y,{t:t})]};const vars=_.without(this._extractVars(KASparse(element.options.x).expr).concat(this._extractVars(KASparse(element.options.y).expr)),"t");const varValues=_.object(vars,_.map(vars,v=>this.state.variables[v]));const range=[this._eval(element.options.rangeMin,this.state.variables),this._eval(element.options.rangeMax,this.state.variables)];return jsxRuntimeExports.jsx(PlotParametric,{fn:fn,foo_fnx:element.options.x,foo_fny:element.options.y,foo_varvalues:varValues,range:range,style:{stroke:element.options.color,strokeWidth:element.options.strokeWidth,strokeDasharray:element.options.strokeDasharray,plotPoints:100}},element.key)}if(element.type==="label"){const coord=[this._eval(element.options.coordX),this._eval(element.options.coordY)];return jsxRuntimeExports.jsx(Label,{coord:coord,text:unescapeMathMode(element.options.label),style:{color:element.options.color}},n+1)}if(element.type==="rectangle"){return jsxRuntimeExports.jsx(Rect,{x:this._eval(element.options.coordX),y:this._eval(element.options.coordY),width:_.max([this._eval(element.options.width),0]),height:_.max([this._eval(element.options.height),0]),style:{stroke:"none",fill:element.options.color}},n+1)}},this)]})}constructor(...args){super(...args),this.isWidget=true,this.state={variables:_getInitialVariables(this.props.elements),functions:_getInitialFunctions(this.props.elements)},this._setupGraphie=(graphie,options)=>{graphie.graphInit(_.extend({},options,{grid:_.contains(["graph","grid"],this.props.graph.markings),axes:_.contains(["graph"],this.props.graph.markings),ticks:_.contains(["graph"],this.props.graph.markings),labels:_.contains(["graph"],this.props.graph.markings),labelFormat:function(s){return "\\small{"+s+"}"},axisArrows:"<->",unityLabels:false}));},this._updatePointLocation=(subscript,coord)=>{const variables=_.clone(this.state.variables);variables["x_"+subscript]=coord[0];variables["y_"+subscript]=coord[1];this.setState({variables:variables});this.props.trackInteraction();},this._updateLineLocation=(options,startCoord)=>{const xDiff=this._eval("("+options.endX+")-("+options.startX+")");const yDiff=this._eval("("+options.endY+")-("+options.startY+")");const endCoord=vector$3.add(startCoord,[xDiff,yDiff]);const variables=_.clone(this.state.variables);variables["x_"+options.startSubscript]=startCoord[0];variables["y_"+options.startSubscript]=startCoord[1];variables["x_"+options.endSubscript]=endCoord[0];variables["y_"+options.endSubscript]=endCoord[1];this.setState({variables:variables});this.props.trackInteraction();},this._eval=(expression,variables)=>{const func=KAScompile(expression,{functions:this.state.functions});const compiledVars=_.extend({},this.state.variables,variables);_.each(_.keys(compiledVars),name=>{if(_.isString(compiledVars[name])){const func=KAScompile(compiledVars[name],{functions:this.state.functions});compiledVars[name]=function(x){return func(_.extend({},compiledVars,{x:x}))};}});return func(compiledVars)||0},this._extractVars=expr=>{if(expr==null){return []}let vars=[];_.each(expr.args(),function(arg){if(arg&&arg.constructor.name==="Expr"){vars=vars.concat(this._extractVars(arg));}},this);if(expr.name()==="Var"){vars.push(expr.prettyPrint());}return vars},this.change=(...args)=>{return change.apply(this,args)};}}Interaction.defaultProps={graph:{box:[400,400],labels:["x","y"],range:[[-10,10],[-10,10]],tickStep:[1,1],gridStep:[1,1],markings:"graph"},elements:[]};const _getInitialVariables=elements=>{const variables={};_.each(_.where(elements,{type:"movable-point"}),element=>{const subscript=element.options.varSubscript;const startXExpr=KASparse(element.options.startX||"0").expr;const startYExpr=KASparse(element.options.startY||"0").expr;let startX=0;let startY=0;if(startXExpr){startX=startXExpr.eval({})||0;}if(startYExpr){startY=startYExpr.eval({})||0;}variables["x_"+subscript]=startX;variables["y_"+subscript]=startY;});_.each(_.where(elements,{type:"movable-line"}),element=>{const startSubscript=element.options.startSubscript;const endSubscript=element.options.endSubscript;const startXExpr=KASparse(element.options.startX||"0").expr;const startYExpr=KASparse(element.options.startY||"0").expr;const endXExpr=KASparse(element.options.endX||"0").expr;const endYExpr=KASparse(element.options.endY||"0").expr;let startX=0;let startY=0;let endX=0;let endY=0;if(startXExpr){startX=startXExpr.eval({})||0;}if(startYExpr){startY=startYExpr.eval({})||0;}if(endXExpr){endX=endXExpr.eval({})||0;}if(endYExpr){endY=endYExpr.eval({})||0;}variables["x_"+startSubscript]=startX;variables["y_"+startSubscript]=startY;variables["x_"+endSubscript]=endX;variables["y_"+endSubscript]=endY;});_.each(_.where(elements,{type:"function"}),element=>{variables[element.options.funcName]=element.options.value;});return variables};const _getInitialFunctions=elements=>{return _.map(_.where(elements,{type:"function"}),element=>element.options.funcName)};var Interactive = {name:"interaction",displayName:"Interaction",widget:Interaction,transform:_.identity,hidden:true};
1845
+ const Label=Graphie.Label;const Line$2=Graphie.Line;const MovablePoint$2=Graphie.MovablePoint;const MovableLine$1=Graphie.MovableLine;const Plot=Graphie.Plot;const PlotParametric=Graphie.PlotParametric;const Point=Graphie.Point;const Rect=Graphie.Rect;const{unescapeMathMode}=Util;const KAShashFunc=(expr,options)=>{options=options||{};let result=expr+"||"+options.decimal_separator+"||";const functions=options.functions;const functionsLength=functions?functions.length:0;for(let i=0;i<functionsLength;i++){result+=functions[i]+"|";}return result};const _parseCache=Object.create(null);const KASparse=(expr,options)=>{const hash=KAShashFunc(expr,options);let cached=_parseCache[hash];if(cached){return cached}cached=KAS.parse(expr,options);_parseCache[hash]=cached;return cached};const _compileCache=Object.create(null);const KAScompile=(expr,options)=>{const hash=KAShashFunc(expr,options);let cached=_compileCache[hash];if(cached){return cached}const parsed=KAS.parse(expr,options).expr;cached=parsed?parsed.compile():function(){return 0};_compileCache[hash]=cached;return cached};class Interaction extends React.Component{UNSAFE_componentWillReceiveProps(nextProps){if(!_.isEqual(this.props.elements,nextProps.elements)){this.setState({variables:_getInitialVariables(nextProps.elements),functions:_getInitialFunctions(nextProps.elements)});}}getPromptJSON(){return getPromptJSON$c()}render(){const range=this.props.graph.range;let labels=this.props.graph.labels;if(this.props.graph.markings==="graph"){labels=this.props.graph.labels.map(label=>label.startsWith("$")&&label.endsWith("$")?label.slice(1,-1):label);}return jsxRuntimeExports.jsxs(Graphie,{box:this.props.graph.box,range:this.props.graph.range,options:this.props.graph,setup:this._setupGraphie,setDrawingAreaAvailable:this.props.apiOptions.setDrawingAreaAvailable,children:[this.props.graph.markings==="graph"&&jsxRuntimeExports.jsx(Label,{coord:[0,range[1][1]],text:labels[1],direction:"above"}),this.props.graph.markings==="graph"&&jsxRuntimeExports.jsx(Label,{coord:[range[0][1],0],text:labels[0],direction:"right"}),_.map(this.props.elements,function(element,n){if(element.type==="point"){return jsxRuntimeExports.jsx(Point,{coord:[this._eval(element.options.coordX),this._eval(element.options.coordY)],color:element.options.color},element.key)}if(element.type==="line"){const start=[this._eval(element.options.startX),this._eval(element.options.startY)];const end=[this._eval(element.options.endX),this._eval(element.options.endY)];return jsxRuntimeExports.jsx(Line$2,{start:start,end:end,style:{stroke:element.options.color,strokeWidth:element.options.strokeWidth,strokeDasharray:element.options.strokeDasharray,arrows:element.options.arrows}},element.key)}if(element.type==="movable-point"){const constraints=[coord=>{const coordX=Math.max(this._eval(element.options.constraintXMin),Math.min(this._eval(element.options.constraintXMax),coord[0]));const coordY=Math.max(this._eval(element.options.constraintYMin),Math.min(this._eval(element.options.constraintYMax),coord[1]));return [coordX,coordY]}];if(element.options.constraint==="snap"){constraints.push(MovablePoint$2.constraints.snap(element.options.snap));}else if(element.options.constraint==="x"){constraints.push(coord=>{return [this._eval(element.options.constraintFn,{y:coord[1]}),coord[1]]});}else if(element.options.constraint==="y"){constraints.push(coord=>{return [coord[0],this._eval(element.options.constraintFn,{x:coord[0]})]});}return jsxRuntimeExports.jsx(MovablePoint$2,{coord:[this.state.variables["x_"+element.options.varSubscript],this.state.variables["y_"+element.options.varSubscript]],constraints:constraints,foo_x:element.options.constraint,foo_y:element.options.constraintFn,foo_z:element.options.snap,onMove:_.partial(this._updatePointLocation,element.options.varSubscript)},element.key)}if(element.type==="movable-line"){const constraints=[coord=>{const coordX=Math.max(this._eval(element.options.constraintXMin),Math.min(this._eval(element.options.constraintXMax),coord[0]));const coordY=Math.max(this._eval(element.options.constraintYMin),Math.min(this._eval(element.options.constraintYMax),coord[1]));return [coordX,coordY]}];if(element.options.constraint==="snap"){constraints.push(MovablePoint$2.constraints.snap(element.options.snap));}else if(element.options.constraint==="x"){constraints.push(coord=>{return [this._eval(element.options.constraintFn,{y:coord[1]}),coord[1]]});}else if(element.options.constraint==="y"){constraints.push(coord=>{return [coord[0],this._eval(element.options.constraintFn,{x:coord[0]})]});}const start=[this.state.variables["x_"+element.options.startSubscript],this.state.variables["y_"+element.options.startSubscript]];const end=[this.state.variables["x_"+element.options.endSubscript],this.state.variables["y_"+element.options.endSubscript]];return jsxRuntimeExports.jsxs(MovableLine$1,{constraints:constraints,onMove:_.bind(this._updateLineLocation,this,element.options),foo_x:element.options.constraint,foo_y:element.options.constraintFn,foo_z:element.options.snap,children:[jsxRuntimeExports.jsx(MovablePoint$2,{coord:start,static:true,normalStyle:{stroke:"none",fill:"none"}}),jsxRuntimeExports.jsx(MovablePoint$2,{coord:end,static:true,normalStyle:{stroke:"none",fill:"none"}})]},element.key)}if(element.type==="function"){const fn=x=>{return this._eval(element.options.value,{x:x})};const vars=_.without(this._extractVars(KASparse(element.options.value).expr),"x");const varValues=_.object(vars,_.map(vars,v=>this.state.variables[v]));const range=[this._eval(element.options.rangeMin,this.state.variables),this._eval(element.options.rangeMax,this.state.variables)];return jsxRuntimeExports.jsx(Plot,{fn:fn,foo_fn:element.options.value,foo_varvalues:varValues,range:range,style:{stroke:element.options.color,strokeWidth:element.options.strokeWidth,strokeDasharray:element.options.strokeDasharray,plotPoints:100}},element.key)}if(element.type==="parametric"){const fn=t=>{return [this._eval(element.options.x,{t:t}),this._eval(element.options.y,{t:t})]};const vars=_.without(this._extractVars(KASparse(element.options.x).expr).concat(this._extractVars(KASparse(element.options.y).expr)),"t");const varValues=_.object(vars,_.map(vars,v=>this.state.variables[v]));const range=[this._eval(element.options.rangeMin,this.state.variables),this._eval(element.options.rangeMax,this.state.variables)];return jsxRuntimeExports.jsx(PlotParametric,{fn:fn,foo_fnx:element.options.x,foo_fny:element.options.y,foo_varvalues:varValues,range:range,style:{stroke:element.options.color,strokeWidth:element.options.strokeWidth,strokeDasharray:element.options.strokeDasharray,plotPoints:100}},element.key)}if(element.type==="label"){const coord=[this._eval(element.options.coordX),this._eval(element.options.coordY)];return jsxRuntimeExports.jsx(Label,{coord:coord,text:unescapeMathMode(element.options.label),style:{color:element.options.color}},n+1)}if(element.type==="rectangle"){return jsxRuntimeExports.jsx(Rect,{x:this._eval(element.options.coordX),y:this._eval(element.options.coordY),width:_.max([this._eval(element.options.width),0]),height:_.max([this._eval(element.options.height),0]),style:{stroke:"none",fill:element.options.color}},n+1)}},this)]})}constructor(...args){super(...args),this.isWidget=true,this.state={variables:_getInitialVariables(this.props.elements),functions:_getInitialFunctions(this.props.elements)},this._setupGraphie=(graphie,options)=>{graphie.graphInit(_.extend({},options,{grid:_.contains(["graph","grid"],this.props.graph.markings),axes:_.contains(["graph"],this.props.graph.markings),ticks:_.contains(["graph"],this.props.graph.markings),labels:_.contains(["graph"],this.props.graph.markings),labelFormat:function(s){return "\\small{"+s+"}"},axisArrows:"<->",unityLabels:false}));},this._updatePointLocation=(subscript,coord)=>{const variables=_.clone(this.state.variables);variables["x_"+subscript]=coord[0];variables["y_"+subscript]=coord[1];this.setState({variables:variables});this.props.trackInteraction();},this._updateLineLocation=(options,startCoord)=>{const xDiff=this._eval("("+options.endX+")-("+options.startX+")");const yDiff=this._eval("("+options.endY+")-("+options.startY+")");const endCoord=vector$3.add(startCoord,[xDiff,yDiff]);const variables=_.clone(this.state.variables);variables["x_"+options.startSubscript]=startCoord[0];variables["y_"+options.startSubscript]=startCoord[1];variables["x_"+options.endSubscript]=endCoord[0];variables["y_"+options.endSubscript]=endCoord[1];this.setState({variables:variables});this.props.trackInteraction();},this._eval=(expression,variables)=>{const func=KAScompile(expression,{functions:this.state.functions});const compiledVars=_.extend({},this.state.variables,variables);_.each(_.keys(compiledVars),name=>{if(_.isString(compiledVars[name])){const func=KAScompile(compiledVars[name],{functions:this.state.functions});compiledVars[name]=function(x){return func(_.extend({},compiledVars,{x:x}))};}});return func(compiledVars)||0},this._extractVars=expr=>{if(expr==null){return []}let vars=[];_.each(expr.args(),function(arg){if(arg&&arg.constructor.name==="Expr"){vars=vars.concat(this._extractVars(arg));}},this);if(expr.name()==="Var"){vars.push(expr.prettyPrint());}return vars};}}Interaction.defaultProps={graph:{box:[400,400],labels:["x","y"],range:[[-10,10],[-10,10]],tickStep:[1,1],gridStep:[1,1],markings:"graph"},elements:[]};const _getInitialVariables=elements=>{const variables={};_.each(_.where(elements,{type:"movable-point"}),element=>{const subscript=element.options.varSubscript;const startXExpr=KASparse(element.options.startX||"0").expr;const startYExpr=KASparse(element.options.startY||"0").expr;let startX=0;let startY=0;if(startXExpr){startX=startXExpr.eval({})||0;}if(startYExpr){startY=startYExpr.eval({})||0;}variables["x_"+subscript]=startX;variables["y_"+subscript]=startY;});_.each(_.where(elements,{type:"movable-line"}),element=>{const startSubscript=element.options.startSubscript;const endSubscript=element.options.endSubscript;const startXExpr=KASparse(element.options.startX||"0").expr;const startYExpr=KASparse(element.options.startY||"0").expr;const endXExpr=KASparse(element.options.endX||"0").expr;const endYExpr=KASparse(element.options.endY||"0").expr;let startX=0;let startY=0;let endX=0;let endY=0;if(startXExpr){startX=startXExpr.eval({})||0;}if(startYExpr){startY=startYExpr.eval({})||0;}if(endXExpr){endX=endXExpr.eval({})||0;}if(endYExpr){endY=endYExpr.eval({})||0;}variables["x_"+startSubscript]=startX;variables["y_"+startSubscript]=startY;variables["x_"+endSubscript]=endX;variables["y_"+endSubscript]=endY;});_.each(_.where(elements,{type:"function"}),element=>{variables[element.options.funcName]=element.options.value;});return variables};const _getInitialFunctions=elements=>{return _.map(_.where(elements,{type:"function"}),element=>element.options.funcName)};var Interactive = {name:"interaction",displayName:"Interaction",widget:Interaction,hidden:true};
1844
1846
 
1845
- const getPromptJSON$b=(props,userInput)=>{return {type:"interactive-graph",options:{graph:getGraphOptionsForProps(props),backgroundImageUrl:props.backgroundImage?.url,range:props.range,labels:props.labels},userInput:getUserInput(userInput)}};const getGraphOptionsForProps=props=>{const type=props.graph.type;switch(type){case "angle":return {type:props.graph.type,angleOffsetDegrees:props.graph.angleOffsetDeg,startCoords:props.graph.startCoords};case "circle":return {type:props.graph.type,startParams:{center:props.graph.startCoords?.center,radius:props.graph.startCoords?.radius}};case "linear":return {type:props.graph.type,startCoords:props.graph.startCoords};case "linear-system":return {type:props.graph.type,startCoords:props.graph.startCoords};case "point":return {type:props.graph.type,numPoints:props.graph.numPoints,startCoords:props.graph.startCoords};case "polygon":return {type:props.graph.type,match:props.graph.match,numSides:props.graph.numSides,startCoords:props.graph.startCoords};case "quadratic":return {type:props.graph.type,startCoords:props.graph.startCoords};case "ray":return {type:props.graph.type,startCoords:props.graph.startCoords};case "segment":return {type:props.graph.type,numSegments:props.graph.numSegments,startCoords:props.graph.startCoords};case "sinusoid":return {type:props.graph.type,startCoords:props.graph.startCoords};case "none":return {};default:throw new UnreachableCaseError(type)}};const getUserInput=userInput=>{const type=userInput.type;switch(type){case "angle":return {coords:userInput.coords,angleOffsetDegrees:userInput.angleOffsetDeg};case "circle":return {center:userInput.center,radius:userInput.radius};case "linear":return {coords:userInput.coords};case "linear-system":return {coords:userInput.coords};case "point":return {coords:userInput.coords};case "polygon":return {coords:userInput.coords};case "quadratic":return {coords:userInput.coords};case "ray":return {coords:userInput.coords};case "segment":return {coords:userInput.coords};case "sinusoid":return {coords:userInput.coords};case "none":return {};default:throw new UnreachableCaseError(type)}};
1847
+ const getPromptJSON$b=(props,userInput)=>{return {type:"interactive-graph",options:{graph:getGraphOptionsForProps(props),backgroundImageUrl:props.backgroundImage?.url,range:props.range,labels:props.labels},userInput:getUserInput(userInput)}};const getGraphOptionsForProps=props=>{const type=props.userInput.type;switch(type){case "angle":return {type:props.userInput.type,angleOffsetDegrees:props.userInput.angleOffsetDeg,startCoords:props.userInput.startCoords};case "circle":return {type:props.userInput.type,startParams:{center:props.userInput.startCoords?.center,radius:props.userInput.startCoords?.radius}};case "linear":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "linear-system":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "point":return {type:props.userInput.type,numPoints:props.userInput.numPoints,startCoords:props.userInput.startCoords};case "polygon":return {type:props.userInput.type,match:props.userInput.match,numSides:props.userInput.numSides,startCoords:props.userInput.startCoords};case "quadratic":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "ray":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "segment":return {type:props.userInput.type,numSegments:props.userInput.numSegments,startCoords:props.userInput.startCoords};case "sinusoid":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "none":return {};default:throw new UnreachableCaseError(type)}};const getUserInput=userInput=>{const type=userInput.type;switch(type){case "angle":return {coords:userInput.coords,angleOffsetDegrees:userInput.angleOffsetDeg};case "circle":return {center:userInput.center,radius:userInput.radius};case "linear":return {coords:userInput.coords};case "linear-system":return {coords:userInput.coords};case "point":return {coords:userInput.coords};case "polygon":return {coords:userInput.coords};case "quadratic":return {coords:userInput.coords};case "ray":return {coords:userInput.coords};case "segment":return {coords:userInput.coords};case "sinusoid":return {coords:userInput.coords};case "none":return {};default:throw new UnreachableCaseError(type)}};
1846
1848
 
1847
1849
  function pathBuilder(){return new PathBuilder}class PathBuilder{build(){return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("")}move(x,y){this.path.push({action:"M",args:[x,y]});return this}line(x,y){this.path.push({action:"L",args:[x,y]});return this}circularArc(radius,toX,toY,{sweep=false,largeArc=false}={}){this.path.push({action:"A",args:[radius,radius,0,largeArc?1:0,sweep?1:0,toX,toY]});return this}curve(control1X,control1Y,control2X,control2Y,endX,endY){this.path.push({action:"C",args:[control1X,control1Y,control2X,control2Y,endX,endY]});return this}scale(factor){this.scaleFactor*=factor;return this}constructor(){this.path=[];this.scaleFactor=1;}}function commandToString(command){return `${command.action}${command.args.join(" ")}`}function scaleCommandBy(scaleFactor){return command=>{switch(command.action){case "A":return {...command,args:[command.args[0]*scaleFactor,command.args[1]*scaleFactor,command.args[2],command.args[3],command.args[4],command.args[5]*scaleFactor,command.args[6]*scaleFactor]};default:return {...command,args:command.args.map(c=>c*scaleFactor)}}}}
1848
1850
 
@@ -1868,11 +1870,13 @@ const arrowPath=pathBuilder().move(-3,4).curve(-2.75,2.5,0,.25,.75,0).curve(0,-0
1868
1870
 
1869
1871
  function AxisArrows(){const{range:[[xMin,xMax],[yMin,yMax]]}=useGraphConfig();const axisColor="var(--mafs-fg)";return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[!(yMin>0||yMax<0)&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Arrowhead,{color:axisColor,tip:[xMin,0],angle:180}),jsxRuntimeExports.jsx(Arrowhead,{color:axisColor,tip:[xMax,0],angle:0})]}),!(xMin>0||xMax<0)&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Arrowhead,{color:axisColor,tip:[0,yMin],angle:90}),jsxRuntimeExports.jsx(Arrowhead,{color:axisColor,tip:[0,yMax],angle:270})]})]})}
1870
1872
 
1871
- const TARGET_SIZE=44;const REMOVE_BUTTON_ID="perseus_mafs_remove_button";const normalizePoints=(range,step,coordsList,noSnap)=>coordsList.map(coords=>coords.map((coord,i)=>{const axisRange=range[i];if(noSnap){return axisRange[MIN]+size(axisRange)*coord}const axisStep=step[i];const nSteps=Math.floor(size(axisRange)/axisStep);const tick=Math.round(coord*nSteps);return axisRange[MIN]+axisStep*tick}));const normalizeCoords=(coordsList,ranges)=>coordsList.map(coords=>coords.map((coord,i)=>{return (coord+ranges[i][1])/size(ranges[i])}));function bound$1({snapStep,range,point}){const boundingBox=inset(snapStep,range);return clampToBox(boundingBox,point)}function isUnlimitedGraphState(state){return state.type==="point"&&state.numPoints==="unlimited"||state.type==="polygon"&&state.numSides==="unlimited"}const mathOnlyParser=SimpleMarkdown.parserFor({math:{...pureMarkdownRules.math,order:0},text:{order:1,match:SimpleMarkdown.anyScopeRegex(/^([^$\\{}]+)/),parse:capture=>({content:capture[0]})},specialCharacter:{order:2,match:SimpleMarkdown.anyScopeRegex(/^(\\[\S\s]|\$|\\$|{|})/),parse:capture=>({content:capture[0]})}},{inline:true});function replaceOutsideTeX(mathString){const parsed=mathOnlyParser(mathString);let result="";for(const piece of parsed){piece.type==="math"?result+="$"+piece.content+"$":piece.type==="specialCharacter"?result+=escapeIfUnescaped(piece.content):result+=piece.content;}return `\\text{${result}}`}function escapeIfUnescaped(character){if(character.length===1){return "\\"+character}else {return character}}
1873
+ const TARGET_SIZE=44;const REMOVE_BUTTON_ID="perseus_mafs_remove_button";const normalizePoints=(range,step,coordsList,noSnap)=>coordsList.map(coords=>coords.map((coord,i)=>{const axisRange=range[i];if(noSnap){return axisRange[MIN]+size(axisRange)*coord}const axisStep=step[i];const nSteps=Math.floor(size(axisRange)/axisStep);const tick=Math.round(coord*nSteps);return axisRange[MIN]+axisStep*tick}));const normalizeCoords=(coordsList,ranges)=>coordsList.map(coords=>coords.map((coord,i)=>{return (coord+ranges[i][1])/size(ranges[i])}));function bound$1({snapStep,range,point}){const boundingBox=inset(snapStep,range);return clampToBox(boundingBox,point)}function isUnlimitedGraphState(state){return state.type==="point"&&state.numPoints==="unlimited"||state.type==="polygon"&&state.numSides==="unlimited"}const mathOnlyParser=SimpleMarkdown.parserFor({math:{...pureMarkdownRules.math,order:0},text:{order:1,match:SimpleMarkdown.anyScopeRegex(/^([^$\\{}]+)/),parse:capture=>({content:capture[0]})},specialCharacter:{order:2,match:SimpleMarkdown.anyScopeRegex(/^(\\[\S\s]|\$|\\$|{|})/),parse:capture=>({content:capture[0]})}},{inline:true});function replaceOutsideTeX(mathString){const parsed=mathOnlyParser(mathString);let result="";for(const piece of parsed){piece.type==="math"?result+="$"+piece.content+"$":piece.type==="specialCharacter"?result+=escapeIfUnescaped(piece.content):result+=piece.content;}return `\\text{${result}}`}function escapeIfUnescaped(character){if(character.length===1){return "\\"+character}else {return character}}const getRangeDiff=range=>{const[min,max]=range;return Math.abs(max-min)};const calculateNestedSVGCoords=(range,width,height)=>{let viewboxX=0;const totalXRange=getRangeDiff(range[X]);const gridCellWidth=width/totalXRange;const minX=range[X][MIN];if(minX>0){viewboxX=gridCellWidth*Math.abs(minX);}if(minX<0){viewboxX=-gridCellWidth*Math.abs(minX);}let viewboxY=-height;const totalYRange=getRangeDiff(range[Y]);const gridCellHeight=height/totalYRange;const minY=range[Y][MIN];if(minY>0){viewboxY=-height-gridCellHeight*Math.abs(minY);}if(minY<0){viewboxY=gridCellHeight*Math.abs(minY)-height;}return {viewboxX,viewboxY}};
1872
1874
 
1873
- const fontSize=14;function AxisLabels({i18n}){const{range,labels,width,height,labelLocation}=useGraphConfig();const graphInfo={range,width,height};const[xAxisLabelLocation,yAxisLabelLocation]=getLabelPosition(graphInfo,labelLocation);const[xAxisLabelText,yAxisLabelText]=labels;const{xLabelTransform,yLabelTransform}=getLabelTransform(labelLocation);const{TeX}=getDependencies();return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx("span",{"aria-hidden":true,style:{position:"absolute",left:xAxisLabelLocation[X],top:xAxisLabelLocation[Y],fontSize:fontSize+"px",transform:xLabelTransform},children:jsxRuntimeExports.jsx(TeX,{children:replaceOutsideTeX(xAxisLabelText)})}),jsxRuntimeExports.jsx("span",{"aria-hidden":true,style:{position:"absolute",left:yAxisLabelLocation[X],top:yAxisLabelLocation[Y],fontSize:fontSize+"px",transform:yLabelTransform},children:jsxRuntimeExports.jsx(TeX,{children:replaceOutsideTeX(yAxisLabelText)})})]})}const getLabelTransform=labelLocation=>{const isOnAxis=labelLocation===undefined||labelLocation==="onAxis";const xLabelTransform=isOnAxis?"translate(7px, -50%)":"translate(-50%, -50%)";const yLabelTransform=isOnAxis?"translate(-50%, 0px)":"translate(-50%, 0px) rotate(-90deg)";return {xLabelTransform,yLabelTransform}};const clampLabelPosition=(labelPosition,graphInfo)=>{const x=Math.max(Math.min(labelPosition[X],graphInfo.width+fontSize*1.25),-14*1.5);const y=Math.max(Math.min(labelPosition[Y],graphInfo.height+fontSize*1.25),-14*2);return [x,y]};const getLabelPosition=(graphInfo,labelLocation)=>{if(labelLocation==="alongEdge"){const xAxisLabelOffset=graphInfo.range[Y][MIN]>=0?[0,fontSize*3]:[0,fontSize];const yAxisLabelOffset=graphInfo.range[X][MIN]>=0?[-14*3,-14]:[-14,-14];const xAxisLabelLocation=[(graphInfo.range[X][MIN]+graphInfo.range[X][MAX])/2,graphInfo.range[Y][MIN]];const yAxisLabelLocation=[graphInfo.range[X][MIN],(graphInfo.range[Y][MIN]+graphInfo.range[Y][MAX])/2];const xLabel=vec.add(pointToPixel(xAxisLabelLocation,graphInfo),xAxisLabelOffset);const yLabel=vec.add(pointToPixel(yAxisLabelLocation,graphInfo),yAxisLabelOffset);return [xLabel,yLabel]}const xLabelInitial=[graphInfo.range[X][MAX],0];const yLabelInitial=[0,graphInfo.range[Y][MAX]];const yLabelOffset=[0,-14*2];let xLabel=pointToPixel(xLabelInitial,graphInfo);let yLabel=vec.add(pointToPixel(yLabelInitial,graphInfo),yLabelOffset);xLabel=clampLabelPosition(xLabel,graphInfo);yLabel=clampLabelPosition(yLabel,graphInfo);return [xLabel,yLabel]};
1875
+ const fontSize=14;const clampLabelPosition=(labelPosition,graphInfo)=>{const x=Math.max(Math.min(labelPosition[X],graphInfo.width+fontSize*1.25),-14*1.5);const y=Math.max(Math.min(labelPosition[Y],graphInfo.height+fontSize*1.25),-14*2);return [x,y]};const getLabelTransform=labelLocation=>{const isOnAxis=labelLocation===undefined||labelLocation==="onAxis";const xLabelTransform=isOnAxis?"translate(7px, -50%)":"translate(-50%, -50%)";const yLabelTransform=isOnAxis?"translate(-50%, 0px)":"translate(-50%, 0px) rotate(-90deg)";return {xLabelTransform,yLabelTransform}};const calculateMaxDigitsInRange=(range,tickStep)=>{const maxDigitsInRange=Math.max(String(Math.abs(Math.floor(range[MIN]))).length,String(Math.abs(Math.ceil(range[MAX]))).length);const tickStepSigFigs=countSignificantDecimals(tickStep)+2;return Math.max(maxDigitsInRange,tickStepSigFigs)};const getLabelPosition=(graphInfo,labelLocation,tickStep)=>{if(labelLocation==="alongEdge"){const xAxisLabelOffset=graphInfo.range[Y][MIN]>=0?[0,fontSize*3]:[0,fontSize*1.5];const isRelativelyCloseToZero=graphInfo.range[X][MIN]<0&&Math.abs(graphInfo.range[X][MIN])<(graphInfo.range[X][MAX]-graphInfo.range[X][MIN])*.07;const needsExtraSpacing=graphInfo.range[X][MIN]>=0||isRelativelyCloseToZero;let paddingRequiredForTickLabels=0;if(needsExtraSpacing){const maxDigits=calculateMaxDigitsInRange(graphInfo.range[Y],tickStep[Y]);paddingRequiredForTickLabels=maxDigits*(fontSize*.75)+(graphInfo.range[Y][MIN]<0&&graphInfo.range[X][MIN]<=0?fontSize*.5:0);}const yAxisLabelOffset=[-14*1.25-paddingRequiredForTickLabels,-14];const xAxisLabelLocation=[(graphInfo.range[X][MIN]+graphInfo.range[X][MAX])/2,graphInfo.range[Y][MIN]];const yAxisLabelLocation=[graphInfo.range[X][MIN],(graphInfo.range[Y][MIN]+graphInfo.range[Y][MAX])/2];if(isRelativelyCloseToZero){yAxisLabelLocation[X]=yAxisLabelLocation[X]-graphInfo.range[X][MIN];}const xLabel=vec.add(pointToPixel(xAxisLabelLocation,graphInfo),xAxisLabelOffset);const yLabel=vec.add(pointToPixel(yAxisLabelLocation,graphInfo),yAxisLabelOffset);return [xLabel,yLabel]}const xLabelInitial=[graphInfo.range[X][MAX],0];const yLabelInitial=[0,graphInfo.range[Y][MAX]];const yLabelOffset=[0,-14*2];let xLabel=pointToPixel(xLabelInitial,graphInfo);let yLabel=vec.add(pointToPixel(yLabelInitial,graphInfo),yLabelOffset);xLabel=clampLabelPosition(xLabel,graphInfo);yLabel=clampLabelPosition(yLabel,graphInfo);return [xLabel,yLabel]};const shouldShowLabel=(currentTick,range,tickStep)=>{let showLabel=true;if(range[X][MIN]<-tickStep&&range[X][MAX]>0&&currentTick===-tickStep){showLabel=false;}return showLabel};function generateTickLocations(tickStep,min,max){const ticks=[];const decimalSigFigs=countSignificantDecimals(tickStep);const start=Math.max(min,0);for(let i=start+tickStep;i<max;i+=tickStep){ticks.push(parseFloat(i.toFixed(decimalSigFigs)));}let i=Math.min(max,0)-tickStep;for(i;i>min;i-=tickStep){ticks.push(i);}return ticks}const countSignificantDecimals=number=>{const numStr=number.toString();if(!numStr.includes(".")){return 0}return numStr.split(".")[1].length};function divideByAndShowPi(value){const dividedValue=value/Math.PI;switch(dividedValue){case 1:return "π";case -1:return "-π";case 0:return "0";default:return dividedValue+"π"}}
1874
1876
 
1875
- const tickSize=10;const tickLabelSize=14;const YGridTick=({y,range,tickStep,showPi})=>{let xPointOnAxis=0;if(range[X][MIN]>0){xPointOnAxis=range[X][MIN];}if(range[X][MAX]<0){xPointOnAxis=range[X][MAX];}const pointOnAxis=[xPointOnAxis,y];const[[xPosition,yPosition]]=useTransformVectorsToPixels(pointOnAxis);const x1=xPosition-tickSize/2;const y1=yPosition;const x2=xPosition+tickSize/2;const y2=yPosition;const xAdjustment=range[X][MAX]<=0?tickLabelSize*1.5:-14*1.1;const xPositionText=xPosition+xAdjustment;const yPositionText=yPosition+tickLabelSize*.25;const showLabel=shouldShowLabel(y,range,tickStep);const yLabel=showPi?divideByAndShowPi(y):y.toString();return jsxRuntimeExports.jsxs("g",{className:"tick","aria-hidden":true,children:[jsxRuntimeExports.jsx("line",{x1:x1,y1:y1,x2:x2,y2:y2,className:"axis-tick"}),showLabel&&jsxRuntimeExports.jsx("text",{className:"axis-tick-label",style:{fontSize:tickLabelSize},textAnchor:"end",x:xPositionText,y:yPositionText,children:yLabel})]})};const XGridTick=({x,range,showPi})=>{let yPointOnAxis=0;if(range[Y][MIN]>0){yPointOnAxis=range[Y][MIN];}if(range[Y][MAX]<0){yPointOnAxis=range[Y][MAX];}const pointOnAxis=[x,yPointOnAxis];const[[xPosition,yPosition]]=useTransformVectorsToPixels(pointOnAxis);const x1=xPosition;const y1=yPosition+tickSize/2;const x2=xPosition;const y2=yPosition-tickSize/2;const yAdjustment=range[Y][MAX]<0?-14:tickLabelSize*1.75;const xAdjustment=x<0?-2:0;const xPositionText=xPosition+xAdjustment;const yPositionText=yPosition+yAdjustment;const xLabel=showPi?divideByAndShowPi(x):x.toString();return jsxRuntimeExports.jsxs("g",{className:"tick","aria-hidden":true,children:[jsxRuntimeExports.jsx("line",{x1:x1,y1:y1,x2:x2,y2:y2,className:"axis-tick"}),jsxRuntimeExports.jsx("text",{className:"axis-tick-label",style:{fontSize:tickLabelSize},textAnchor:"middle",x:xPositionText,y:yPositionText,children:xLabel})]})};const shouldShowLabel=(currentTick,range,tickStep)=>{let showLabel=true;if(range[X][MIN]<-tickStep&&range[X][MAX]>0&&currentTick===-tickStep){showLabel=false;}return showLabel};function generateTickLocations(tickStep,min,max){const ticks=[];const decimalSigFigs=countSignificantDecimals(tickStep);const start=Math.max(min,0);for(let i=start+tickStep;i<max;i+=tickStep){ticks.push(parseFloat(i.toFixed(decimalSigFigs)));}let i=Math.min(max,0)-tickStep;for(i;i>min;i-=tickStep){ticks.push(i);}return ticks}const countSignificantDecimals=number=>{const numStr=number.toString();if(!numStr.includes(".")){return 0}return numStr.split(".")[1].length};function divideByAndShowPi(value){const dividedValue=value/Math.PI;switch(dividedValue){case 1:return "π";case -1:return "-π";case 0:return "0";default:return dividedValue+"π"}}const AxisTicks=()=>{const{tickStep,range}=useGraphConfig();const[[xMin,xMax],[yMin,yMax]]=range;const[xTickStep,yTickStep]=tickStep;const yGridTicks=generateTickLocations(yTickStep,yMin,yMax);const xGridTicks=generateTickLocations(xTickStep,xMin,xMax);return jsxRuntimeExports.jsxs("g",{className:"axis-ticks",role:"presentation",children:[jsxRuntimeExports.jsx("g",{className:"y-axis-ticks",children:yGridTicks.map(y=>{return jsxRuntimeExports.jsx(YGridTick,{y:y,range:range,tickStep:tickStep[Y],showPi:tickStep[Y]%Math.PI===0},`y-grid-tick-${y}`)})}),jsxRuntimeExports.jsx("g",{className:"x-axis-ticks",children:xGridTicks.map(x=>{return jsxRuntimeExports.jsx(XGridTick,{x:x,range:range,showPi:tickStep[X]%Math.PI===0},`x-grid-tick-${x}`)})})]})};
1877
+ function AxisLabels({i18n}){const{range,labels,width,height,labelLocation,tickStep}=useGraphConfig();const graphInfo={range,width,height};const[xAxisLabelLocation,yAxisLabelLocation]=getLabelPosition(graphInfo,labelLocation,tickStep);const[xAxisLabelText,yAxisLabelText]=labels;const{xLabelTransform,yLabelTransform}=getLabelTransform(labelLocation);const{TeX}=getDependencies();return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx("span",{"aria-hidden":true,style:{position:"absolute",left:xAxisLabelLocation[X],top:xAxisLabelLocation[Y],fontSize:fontSize+"px",transform:xLabelTransform},children:jsxRuntimeExports.jsx(TeX,{children:replaceOutsideTeX(xAxisLabelText)})}),jsxRuntimeExports.jsx("span",{"aria-hidden":true,style:{position:"absolute",left:yAxisLabelLocation[X],top:yAxisLabelLocation[Y],fontSize:fontSize+"px",transform:yLabelTransform},children:jsxRuntimeExports.jsx(TeX,{children:replaceOutsideTeX(yAxisLabelText)})})]})}
1878
+
1879
+ const tickSize=10;const tickLabelSize=14;const YGridTick=({y,range,tickStep,showPi})=>{let xPointOnAxis=0;if(range[X][MIN]>0){xPointOnAxis=range[X][MIN];}if(range[X][MAX]<0){xPointOnAxis=range[X][MAX];}const pointOnAxis=[xPointOnAxis,y];const[[xPosition,yPosition]]=useTransformVectorsToPixels(pointOnAxis);const x1=xPosition-tickSize/2;const y1=yPosition;const x2=xPosition+tickSize/2;const y2=yPosition;const xAdjustment=range[X][MAX]<=0?tickLabelSize*1.5:-14*1.1;const xPositionText=xPosition+xAdjustment;const yPositionText=yPosition+tickLabelSize*.25;const showLabel=shouldShowLabel(y,range,tickStep);const yLabel=showPi?divideByAndShowPi(y):y.toString();return jsxRuntimeExports.jsxs("g",{className:"tick","aria-hidden":true,children:[jsxRuntimeExports.jsx("line",{x1:x1,y1:y1,x2:x2,y2:y2,className:"axis-tick"}),showLabel&&jsxRuntimeExports.jsx("text",{className:"axis-tick-label",style:{fontSize:tickLabelSize},textAnchor:"end",x:xPositionText,y:yPositionText,children:yLabel})]})};const XGridTick=({x,range,showPi})=>{let yPointOnAxis=0;if(range[Y][MIN]>0){yPointOnAxis=range[Y][MIN];}if(range[Y][MAX]<0){yPointOnAxis=range[Y][MAX];}const pointOnAxis=[x,yPointOnAxis];const[[xPosition,yPosition]]=useTransformVectorsToPixels(pointOnAxis);const x1=xPosition;const y1=yPosition+tickSize/2;const x2=xPosition;const y2=yPosition-tickSize/2;const yAdjustment=range[Y][MAX]<0?-14:tickLabelSize*1.75;const xAdjustment=x<0?-2:0;const xPositionText=xPosition+xAdjustment;const yPositionText=yPosition+yAdjustment;const xLabel=showPi?divideByAndShowPi(x):x.toString();return jsxRuntimeExports.jsxs("g",{className:"tick","aria-hidden":true,children:[jsxRuntimeExports.jsx("line",{x1:x1,y1:y1,x2:x2,y2:y2,className:"axis-tick"}),jsxRuntimeExports.jsx("text",{className:"axis-tick-label",style:{fontSize:tickLabelSize},textAnchor:"middle",x:xPositionText,y:yPositionText,children:xLabel})]})};const AxisTicks=()=>{const{tickStep,range}=useGraphConfig();const[[xMin,xMax],[yMin,yMax]]=range;const[xTickStep,yTickStep]=tickStep;const yGridTicks=generateTickLocations(yTickStep,yMin,yMax);const xGridTicks=generateTickLocations(xTickStep,xMin,xMax);return jsxRuntimeExports.jsxs("g",{className:"axis-ticks",role:"presentation",children:[jsxRuntimeExports.jsx("g",{className:"y-axis-ticks",children:yGridTicks.map(y=>{return jsxRuntimeExports.jsx(YGridTick,{y:y,range:range,tickStep:tickStep[Y],showPi:tickStep[Y]%Math.PI===0},`y-grid-tick-${y}`)})}),jsxRuntimeExports.jsx("g",{className:"x-axis-ticks",children:xGridTicks.map(x=>{return jsxRuntimeExports.jsx(XGridTick,{x:x,range:range,showPi:tickStep[X]%Math.PI===0},`x-grid-tick-${x}`)})})]})};
1876
1880
 
1877
1881
  const axisOptions=(props,axisIndex)=>{const lines=props.markings==="axes"?false:props.gridStep[axisIndex];return {axis:props.markings==="graph"||props.markings==="axes",lines:lines,labels:false}};const Grid=props=>{return props.markings==="none"?null:jsxRuntimeExports.jsx(Coordinates.Cartesian,{xAxis:axisOptions(props,X),yAxis:axisOptions(props,Y)})};
1878
1882
 
@@ -1989,13 +1993,13 @@ function renderSinusoidGraph(state,dispatch,i18n){return {graph:jsxRuntimeExport
1989
1993
 
1990
1994
  const{calculateAngleInDegrees,convertDegreesToRadians}=angles;const protractorImage="https://cdn.kastatic.org/images/perseus/protractor.svg";const centerToTopLeft=[-195,-190];const centerToRotationHandle=[-201,-15];function Protractor(){const staticUrl=getDependencies().staticUrl;const{range,snapStep}=useGraphConfig();const[[xMin,xMax],[yMin,yMax]]=range;const initialCenter=[lerp(xMin,xMax,.5),lerp(yMin,yMax,.25)];const[center,setCenter]=useState(initialCenter);const[rotationHandleOffset,setRotationHandleOffset]=useState(centerToRotationHandle);const draggableRef=useRef(null);const{dragging}=useDraggable({gestureTarget:draggableRef,onMove:setCenter,point:center,constrainKeyboardMovement:point=>bound$1({snapStep,range,point})});const rotationHandleRef=useRef(null);useDraggablePx({gestureTarget:rotationHandleRef,onMove:setRotationHandleOffset,point:rotationHandleOffset,constrain:constrainToCircle});const[centerPx]=useTransformVectorsToPixels(center);const topLeftPx=vec.add(centerPx,centerToTopLeft);const angle=calculateAngleInDegrees(rotationHandleOffset)-calculateAngleInDegrees(centerToRotationHandle);return jsxRuntimeExports.jsxs("g",{ref:draggableRef,transform:`translate(${topLeftPx[X]}, ${topLeftPx[Y]}), rotate(${angle})`,style:{transformOrigin:`${-centerToTopLeft[X]}px ${-centerToTopLeft[Y]}px`,cursor:dragging?"grabbing":"grab"},children:[jsxRuntimeExports.jsx("image",{href:staticUrl(protractorImage)}),jsxRuntimeExports.jsx("g",{transform:`translate(5, ${-centerToTopLeft[1]})`,ref:rotationHandleRef,children:jsxRuntimeExports.jsx(RotationArrow,{})})]})}function RotationArrow(){const radius=175;const angleDeg=10;const angleRad=convertDegreesToRadians(angleDeg);const endX=radius*(1-Math.cos(angleRad));const endY=radius*-Math.sin(angleRad);const rotationArrow=pathBuilder().move(0,0).circularArc(radius,endX,endY,{sweep:true}).build();const arrowhead=pathBuilder().move(-8,0).line(0,10).line(8,0).build();const targetRadius=TARGET_SIZE/2;return jsxRuntimeExports.jsxs("g",{className:"protractor-rotation-handle",children:[jsxRuntimeExports.jsx("path",{className:"protractor-rotation-handle-arrow-arc",d:rotationArrow}),jsxRuntimeExports.jsx("path",{className:"protractor-rotation-handle-arrowhead",d:arrowhead}),jsxRuntimeExports.jsx("path",{className:"protractor-rotation-handle-arrowhead",d:arrowhead,transform:`translate(${endX}, ${endY}), rotate(${180+angleDeg})`}),jsxRuntimeExports.jsx("ellipse",{cx:"0px",cy:"-15px",rx:targetRadius,ry:targetRadius,fill:"none"})]})}const protractorRadius=vec.mag(centerToRotationHandle);function constrainToCircle(edgePoint){return vec.withMag(edgePoint,protractorRadius)}function useDraggablePx(args){const{gestureTarget:target,onMove,point,constrain=p=>p}=args;const pickupPx=React.useRef([0,0]);useDrag(state=>{const{event,first,movement:pixelMovement}=state;event?.stopPropagation();if(first){pickupPx.current=point;}if(vec.mag(pixelMovement)===0){return}onMove?.(constrain(vec.add(pickupPx.current,pixelMovement)));},{target,eventOptions:{passive:false}});}
1991
1995
 
1992
- const MafsGraph=props=>{const{state,dispatch,labels,labelLocation,readOnly,fullGraphAriaLabel,fullGraphAriaDescription}=props;const{type}=state;const[width,height]=props.box;const tickStep=props.step;const uniqueId=React.useId();const descriptionId=`interactive-graph-description-${uniqueId}`;const interactiveElementsDescriptionId=`interactive-graph-interactive-elements-description-${uniqueId}`;const unlimitedGraphKeyboardPromptId=`unlimited-graph-keyboard-prompt-${uniqueId}`;const instructionsId=`instructions-${uniqueId}`;const graphRef=React.useRef(null);const{analytics}=useDependencies();const{viewboxX,viewboxY}=calculateNestedSVGCoords(state.range,width,height);const viewBox=`${viewboxX} ${viewboxY} ${width} ${height}`;const nestedSVGAttributes={width,height,viewBox,preserveAspectRatio:"xMidYMin",x:viewboxX,y:viewboxY};const i18n=usePerseusI18n();const{strings}=i18n;const interactionPrompt=isUnlimitedGraphState(state)&&state.showKeyboardInteractionInvitation;useOnMountEffect(()=>{analytics.onAnalyticsEvent({type:"perseus:interactive-graph-widget:rendered",payload:{type,widgetType:"INTERACTIVE_GRAPH",widgetId:"interactive-graph"}});analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:type,widgetType:"INTERACTIVE_GRAPH",widgetId:"interactive-graph"}});});const{graph,interactiveElementsDescription}=renderGraphElements({state,dispatch,i18n,markings:props.markings});const disableInteraction=readOnly||!!props.static;return jsxRuntimeExports.jsx(GraphConfigContext.Provider,{value:{range:state.range,snapStep:state.snapStep,markings:props.markings,tickStep:tickStep,gridStep:props.gridStep,showTooltips:!!props.showTooltips,graphDimensionsInPixels:props.box,width,height,labels,labelLocation,disableKeyboardInteraction:disableInteraction,interactiveColor:disableInteraction?"var(--static-gray)":"var(--mafs-blue)"},children:jsxRuntimeExports.jsxs(View,{className:"mafs-graph-container",children:[jsxRuntimeExports.jsxs(View,{className:"mafs-graph",style:{position:"relative",padding:"25px 25px 0 0",boxSizing:"content-box",marginLeft:"20px",marginBottom:"30px",pointerEvents:props.static?"none":"auto",userSelect:"none",width,height},onKeyUp:event=>{handleKeyboardEvent(event,state,dispatch);},"aria-label":fullGraphAriaLabel,"aria-describedby":describedByIds(fullGraphAriaDescription&&descriptionId,interactiveElementsDescription&&interactiveElementsDescriptionId,isUnlimitedGraphState(state)&&unlimitedGraphKeyboardPromptId,state.type!=="none"&&!disableInteraction&&instructionsId),ref:graphRef,tabIndex:0,onFocus:event=>{handleFocusEvent(event,state,dispatch);},onBlur:event=>{handleBlurEvent(event,state,dispatch);},children:[fullGraphAriaDescription&&jsxRuntimeExports.jsx(View,{id:descriptionId,tabIndex:-1,className:"mafs-sr-only",children:fullGraphAriaDescription}),interactiveElementsDescription&&jsxRuntimeExports.jsx(View,{id:interactiveElementsDescriptionId,tabIndex:-1,className:"mafs-sr-only",children:interactiveElementsDescription}),state.type!=="none"&&jsxRuntimeExports.jsx(View,{id:instructionsId,tabIndex:-1,className:"mafs-sr-only",children:isUnlimitedGraphState(state)?strings.srUnlimitedGraphInstructions:strings.srGraphInstructions}),jsxRuntimeExports.jsx(LegacyGrid,{box:props.box,backgroundImage:props.backgroundImage}),jsxRuntimeExports.jsxs(View,{style:{position:"absolute",bottom:0,left:0},children:[(props.markings==="graph"||props.markings==="axes")&&jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:jsxRuntimeExports.jsx(AxisLabels,{i18n:i18n})}),jsxRuntimeExports.jsx(View,{"aria-hidden":props.lockedFigures.length===0,children:jsxRuntimeExports.jsxs(Mafs,{preserveAspectRatio:false,viewBox:{x:state.range[X],y:state.range[Y],padding:0},pan:false,zoom:false,width:width,height:height,children:[jsxRuntimeExports.jsx(SvgDefs,{}),jsxRuntimeExports.jsx("svg",{...nestedSVGAttributes,children:jsxRuntimeExports.jsx(Grid,{gridStep:props.gridStep,range:state.range,containerSizeClass:props.containerSizeClass,markings:props.markings,width:width,height:height})}),(props.markings==="graph"||props.markings==="axes")&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(AxisTicks,{}),jsxRuntimeExports.jsx(AxisArrows,{})]}),props.lockedFigures.length>0&&jsxRuntimeExports.jsx("svg",{...nestedSVGAttributes,children:jsxRuntimeExports.jsx(GraphLockedLayer,{lockedFigures:props.lockedFigures,range:state.range})})]})}),jsxRuntimeExports.jsx(GraphLockedLabelsLayer,{lockedFigures:props.lockedFigures}),jsxRuntimeExports.jsx(View,{style:{position:"absolute"},children:jsxRuntimeExports.jsx(Mafs,{preserveAspectRatio:false,viewBox:{x:state.range[X],y:state.range[Y],padding:0},pan:false,zoom:false,width:width,height:height,children:jsxRuntimeExports.jsxs("svg",{...nestedSVGAttributes,children:[props.showProtractor&&jsxRuntimeExports.jsx(Protractor,{}),graph]})})})]}),interactionPrompt&&jsxRuntimeExports.jsx(View,{style:{display:interactionPrompt?undefined:"hidden",textAlign:"center",backgroundColor:"white",border:"1px solid #21242C52",padding:"16px 0",boxShadow:"0px 8px 8px 0px #21242C14",top:"50%",transform:"translateY(-50%)"},children:jsxRuntimeExports.jsx(LabelMedium,{id:unlimitedGraphKeyboardPromptId,children:strings.graphKeyboardPrompt})})]}),renderGraphControls({state,dispatch,width,perseusStrings:strings})]})})};const renderPointGraphControls=props=>{const{interactionMode,showRemovePointButton,focusedPointIndex}=props.state;const{perseusStrings}=props;const shouldShowRemoveButton=showRemovePointButton&&focusedPointIndex!==null;return jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row",width:props.width},children:[interactionMode==="keyboard"&&jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},tabIndex:0,onClick:()=>{props.dispatch(actions.pointGraph.addPoint([0,0]));},children:perseusStrings.addPoint}),interactionMode==="mouse"&&jsxRuntimeExports.jsx(Button,{id:REMOVE_BUTTON_ID,kind:"secondary",actionType:"destructive",tabIndex:-1,style:{width:"100%",marginLeft:"20px",visibility:shouldShowRemoveButton?"visible":"hidden"},onClick:_event=>{props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));},children:perseusStrings.removePoint})]})};const renderPolygonGraphControls=props=>{const{interactionMode,showRemovePointButton,focusedPointIndex,closedPolygon,coords}=props.state;const{perseusStrings}=props;const shouldShowRemoveButton=showRemovePointButton&&focusedPointIndex!==null;const disableCloseButton=getArrayWithoutDuplicates(coords).length<3;const polygonButton=closedPolygon?jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},tabIndex:0,onClick:()=>{props.dispatch(actions.polygon.openPolygon());},children:perseusStrings.openPolygon}):jsxRuntimeExports.jsx(Button,{kind:"secondary",disabled:disableCloseButton,style:{width:"100%",marginLeft:"20px"},tabIndex:disableCloseButton?-1:0,onClick:()=>{props.dispatch(actions.polygon.closePolygon());},children:perseusStrings.closePolygon});return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row",width:props.width},children:[interactionMode==="keyboard"&&jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},disabled:closedPolygon,tabIndex:closedPolygon?-1:0,onClick:()=>{props.dispatch(actions.polygon.addPoint([0,0]));},children:perseusStrings.addPoint}),interactionMode==="mouse"&&jsxRuntimeExports.jsx(Button,{id:REMOVE_BUTTON_ID,kind:"secondary",actionType:"destructive",disabled:closedPolygon||!shouldShowRemoveButton,tabIndex:-1,style:{width:"100%",marginLeft:"20px"},onClick:_event=>{props.dispatch(actions.polygon.removePoint(props.state.focusedPointIndex));},children:perseusStrings.removePoint}),polygonButton]})})};const renderGraphControls=props=>{const{state,dispatch,width,perseusStrings}=props;const{type}=state;switch(type){case "point":if(state.numPoints==="unlimited"){return renderPointGraphControls({state,dispatch,width,perseusStrings})}return null;case "polygon":if(state.numSides==="unlimited"){return renderPolygonGraphControls({state,dispatch,width,perseusStrings})}return null;default:return null}};function handleFocusEvent(event,state,dispatch){if(isUnlimitedGraphState(state)){if(event.target.classList.contains("mafs-graph")&&state.interactionMode==="mouse"){dispatch(actions.global.changeKeyboardInvitationVisibility(true));}}}function handleBlurEvent(_event,state,dispatch){if(isUnlimitedGraphState(state)){dispatch(actions.global.changeKeyboardInvitationVisibility(false));}}function handleKeyboardEvent(event,state,dispatch){if(isUnlimitedGraphState(state)){if(event.key==="Backspace"||event.key==="Delete"){if(document.activeElement?.classList.contains("movable-point__focusable-handle")){if(state.type==="point"||state.type==="polygon"&&!state.closedPolygon){dispatch(actions.global.deleteIntent());}}document.activeElement.blur();}else if(event.shiftKey&&event.key==="Enter"){dispatch(actions.global.changeInteractionMode("keyboard"));}else if(state.interactionMode==="keyboard"&&event.key==="a"){dispatch(actions.pointGraph.addPoint([0,0]));}}}const getRangeDiff=range=>{const[min,max]=range;return Math.abs(max-min)};const calculateNestedSVGCoords=(range,width,height)=>{let viewboxX=0;const totalXRange=getRangeDiff(range[X]);const gridCellWidth=width/totalXRange;const minX=range[X][MIN];if(minX>0){viewboxX=gridCellWidth*Math.abs(minX);}if(minX<0){viewboxX=-gridCellWidth*Math.abs(minX);}let viewboxY=-height;const totalYRange=getRangeDiff(range[Y]);const gridCellHeight=height/totalYRange;const minY=range[Y][MIN];if(minY>0){viewboxY=-height-gridCellHeight*Math.abs(minY);}if(minY<0){viewboxY=gridCellHeight*Math.abs(minY)-height;}return {viewboxX,viewboxY}};const renderGraphElements=props=>{const{state,dispatch,i18n,markings}=props;const{type}=state;switch(type){case "angle":return renderAngleGraph(state,dispatch,i18n);case "segment":return renderSegmentGraph(state,dispatch,i18n);case "linear-system":return renderLinearSystemGraph(state,dispatch,i18n);case "linear":return renderLinearGraph(state,dispatch,i18n);case "ray":return renderRayGraph(state,dispatch,i18n);case "polygon":return renderPolygonGraph(state,dispatch,i18n,markings);case "point":return renderPointGraph(state,dispatch,i18n);case "circle":return renderCircleGraph(state,dispatch,i18n);case "quadratic":return renderQuadraticGraph(state,dispatch,i18n);case "sinusoid":return renderSinusoidGraph(state,dispatch,i18n);case "none":return {graph:null,interactiveElementsDescription:null};default:throw new UnreachableCaseError(type)}};function describedByIds(...args){return args.filter(Boolean).join(" ")||undefined}
1996
+ const MafsGraph=props=>{const{state,dispatch,labels,labelLocation,readOnly,fullGraphAriaLabel,fullGraphAriaDescription}=props;const{type}=state;const[width,height]=props.box;const tickStep=props.step;const uniqueId=React.useId();const descriptionId=`interactive-graph-description-${uniqueId}`;const interactiveElementsDescriptionId=`interactive-graph-interactive-elements-description-${uniqueId}`;const unlimitedGraphKeyboardPromptId=`unlimited-graph-keyboard-prompt-${uniqueId}`;const instructionsId=`instructions-${uniqueId}`;const graphRef=React.useRef(null);const{analytics}=useDependencies();const{viewboxX,viewboxY}=calculateNestedSVGCoords(state.range,width,height);const viewBox=`${viewboxX} ${viewboxY} ${width} ${height}`;const nestedSVGAttributes={width,height,viewBox,preserveAspectRatio:"xMidYMin",x:viewboxX,y:viewboxY};const i18n=usePerseusI18n();const{strings}=i18n;const interactionPrompt=isUnlimitedGraphState(state)&&state.showKeyboardInteractionInvitation;useOnMountEffect(()=>{analytics.onAnalyticsEvent({type:"perseus:interactive-graph-widget:rendered",payload:{type,widgetType:"INTERACTIVE_GRAPH",widgetId:"interactive-graph"}});analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:type,widgetType:"INTERACTIVE_GRAPH",widgetId:"interactive-graph"}});});const{graph,interactiveElementsDescription}=renderGraphElements({state,dispatch,i18n,markings:props.markings});const disableInteraction=readOnly||!!props.static;return jsxRuntimeExports.jsx(GraphConfigContext.Provider,{value:{range:state.range,snapStep:state.snapStep,markings:props.markings,tickStep:tickStep,gridStep:props.gridStep,showTooltips:!!props.showTooltips,graphDimensionsInPixels:props.box,width,height,labels,labelLocation,disableKeyboardInteraction:disableInteraction,interactiveColor:disableInteraction?"var(--static-gray)":"var(--mafs-blue)"},children:jsxRuntimeExports.jsxs(View,{className:"mafs-graph-container",children:[jsxRuntimeExports.jsxs(View,{className:"mafs-graph",style:{position:"relative",padding:"25px 25px 0 0",boxSizing:"content-box",marginLeft:"20px",marginBottom:"30px",pointerEvents:props.static?"none":"auto",userSelect:"none",width,height},onKeyUp:event=>{handleKeyboardEvent(event,state,dispatch);},"aria-label":fullGraphAriaLabel,"aria-describedby":describedByIds(fullGraphAriaDescription&&descriptionId,interactiveElementsDescription&&interactiveElementsDescriptionId,isUnlimitedGraphState(state)&&unlimitedGraphKeyboardPromptId,state.type!=="none"&&!disableInteraction&&instructionsId),ref:graphRef,tabIndex:0,onFocus:event=>{handleFocusEvent(event,state,dispatch);},onBlur:event=>{handleBlurEvent(event,state,dispatch);},children:[fullGraphAriaDescription&&jsxRuntimeExports.jsx(View,{id:descriptionId,tabIndex:-1,className:"mafs-sr-only",children:fullGraphAriaDescription}),interactiveElementsDescription&&jsxRuntimeExports.jsx(View,{id:interactiveElementsDescriptionId,tabIndex:-1,className:"mafs-sr-only",children:interactiveElementsDescription}),state.type!=="none"&&jsxRuntimeExports.jsx(View,{id:instructionsId,tabIndex:-1,className:"mafs-sr-only",children:isUnlimitedGraphState(state)?strings.srUnlimitedGraphInstructions:strings.srGraphInstructions}),jsxRuntimeExports.jsx(LegacyGrid,{box:props.box,backgroundImage:props.backgroundImage}),jsxRuntimeExports.jsxs(View,{style:{position:"absolute",bottom:0,left:0},children:[(props.markings==="graph"||props.markings==="axes")&&jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:jsxRuntimeExports.jsx(AxisLabels,{i18n:i18n})}),jsxRuntimeExports.jsx(View,{"aria-hidden":props.lockedFigures.length===0,children:jsxRuntimeExports.jsxs(Mafs,{preserveAspectRatio:false,viewBox:{x:state.range[X],y:state.range[Y],padding:0},pan:false,zoom:false,width:width,height:height,children:[jsxRuntimeExports.jsx(SvgDefs,{}),jsxRuntimeExports.jsx("svg",{...nestedSVGAttributes,children:jsxRuntimeExports.jsx(Grid,{gridStep:props.gridStep,range:state.range,containerSizeClass:props.containerSizeClass,markings:props.markings,width:width,height:height})}),(props.markings==="graph"||props.markings==="axes")&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(AxisTicks,{}),jsxRuntimeExports.jsx(AxisArrows,{})]}),props.lockedFigures.length>0&&jsxRuntimeExports.jsx("svg",{...nestedSVGAttributes,children:jsxRuntimeExports.jsx(GraphLockedLayer,{lockedFigures:props.lockedFigures,range:state.range})})]})}),jsxRuntimeExports.jsx(GraphLockedLabelsLayer,{lockedFigures:props.lockedFigures}),jsxRuntimeExports.jsx(View,{style:{position:"absolute"},children:jsxRuntimeExports.jsx(Mafs,{preserveAspectRatio:false,viewBox:{x:state.range[X],y:state.range[Y],padding:0},pan:false,zoom:false,width:width,height:height,children:jsxRuntimeExports.jsxs("svg",{...nestedSVGAttributes,children:[props.showProtractor&&jsxRuntimeExports.jsx(Protractor,{}),graph]})})})]}),interactionPrompt&&jsxRuntimeExports.jsx(View,{style:{display:interactionPrompt?undefined:"hidden",textAlign:"center",backgroundColor:"white",border:"1px solid #21242C52",padding:"16px 0",boxShadow:"0px 8px 8px 0px #21242C14",top:"50%",transform:"translateY(-50%)"},children:jsxRuntimeExports.jsx(LabelMedium,{id:unlimitedGraphKeyboardPromptId,children:strings.graphKeyboardPrompt})})]}),renderGraphControls({state,dispatch,width,perseusStrings:strings})]})})};const renderPointGraphControls=props=>{const{interactionMode,showRemovePointButton,focusedPointIndex}=props.state;const{perseusStrings}=props;const shouldShowRemoveButton=showRemovePointButton&&focusedPointIndex!==null;return jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row",width:props.width},children:[interactionMode==="keyboard"&&jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},tabIndex:0,onClick:()=>{props.dispatch(actions.pointGraph.addPoint([0,0]));},children:perseusStrings.addPoint}),interactionMode==="mouse"&&jsxRuntimeExports.jsx(Button,{id:REMOVE_BUTTON_ID,kind:"secondary",actionType:"destructive",tabIndex:-1,style:{width:"100%",marginLeft:"20px",visibility:shouldShowRemoveButton?"visible":"hidden"},onClick:_event=>{props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));},children:perseusStrings.removePoint})]})};const renderPolygonGraphControls=props=>{const{interactionMode,showRemovePointButton,focusedPointIndex,closedPolygon,coords}=props.state;const{perseusStrings}=props;const shouldShowRemoveButton=showRemovePointButton&&focusedPointIndex!==null;const disableCloseButton=getArrayWithoutDuplicates(coords).length<3;const polygonButton=closedPolygon?jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},tabIndex:0,onClick:()=>{props.dispatch(actions.polygon.openPolygon());},children:perseusStrings.openPolygon}):jsxRuntimeExports.jsx(Button,{kind:"secondary",disabled:disableCloseButton,style:{width:"100%",marginLeft:"20px"},tabIndex:disableCloseButton?-1:0,onClick:()=>{props.dispatch(actions.polygon.closePolygon());},children:perseusStrings.closePolygon});return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row",width:props.width},children:[interactionMode==="keyboard"&&jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},disabled:closedPolygon,tabIndex:closedPolygon?-1:0,onClick:()=>{props.dispatch(actions.polygon.addPoint([0,0]));},children:perseusStrings.addPoint}),interactionMode==="mouse"&&jsxRuntimeExports.jsx(Button,{id:REMOVE_BUTTON_ID,kind:"secondary",actionType:"destructive",disabled:closedPolygon||!shouldShowRemoveButton,tabIndex:-1,style:{width:"100%",marginLeft:"20px"},onClick:_event=>{props.dispatch(actions.polygon.removePoint(props.state.focusedPointIndex));},children:perseusStrings.removePoint}),polygonButton]})})};const renderGraphControls=props=>{const{state,dispatch,width,perseusStrings}=props;const{type}=state;switch(type){case "point":if(state.numPoints==="unlimited"){return renderPointGraphControls({state,dispatch,width,perseusStrings})}return null;case "polygon":if(state.numSides==="unlimited"){return renderPolygonGraphControls({state,dispatch,width,perseusStrings})}return null;default:return null}};function handleFocusEvent(event,state,dispatch){if(isUnlimitedGraphState(state)){if(event.target.classList.contains("mafs-graph")&&state.interactionMode==="mouse"){dispatch(actions.global.changeKeyboardInvitationVisibility(true));}}}function handleBlurEvent(_event,state,dispatch){if(isUnlimitedGraphState(state)){dispatch(actions.global.changeKeyboardInvitationVisibility(false));}}function handleKeyboardEvent(event,state,dispatch){if(isUnlimitedGraphState(state)){if(event.key==="Backspace"||event.key==="Delete"){if(document.activeElement?.classList.contains("movable-point__focusable-handle")){if(state.type==="point"||state.type==="polygon"&&!state.closedPolygon){dispatch(actions.global.deleteIntent());}}document.activeElement.blur();}else if(event.shiftKey&&event.key==="Enter"){dispatch(actions.global.changeInteractionMode("keyboard"));}else if(state.interactionMode==="keyboard"&&event.key==="a"){dispatch(actions.pointGraph.addPoint([0,0]));}}}const renderGraphElements=props=>{const{state,dispatch,i18n,markings}=props;const{type}=state;switch(type){case "angle":return renderAngleGraph(state,dispatch,i18n);case "segment":return renderSegmentGraph(state,dispatch,i18n);case "linear-system":return renderLinearSystemGraph(state,dispatch,i18n);case "linear":return renderLinearGraph(state,dispatch,i18n);case "ray":return renderRayGraph(state,dispatch,i18n);case "polygon":return renderPolygonGraph(state,dispatch,i18n,markings);case "point":return renderPointGraph(state,dispatch,i18n);case "circle":return renderCircleGraph(state,dispatch,i18n);case "quadratic":return renderQuadraticGraph(state,dispatch,i18n);case "sinusoid":return renderSinusoidGraph(state,dispatch,i18n);case "none":return {graph:null,interactiveElementsDescription:null};default:throw new UnreachableCaseError(type)}};function describedByIds(...args){return args.filter(Boolean).join(" ")||undefined}
1993
1997
 
1994
1998
  function mafsStateToInteractiveGraph(state,originalGraph){switch(state.type){case "angle":invariant(originalGraph.type==="angle");return {...originalGraph,coords:state.coords};case "quadratic":invariant(originalGraph.type==="quadratic");return {...originalGraph,coords:state.coords};case "circle":invariant(originalGraph.type==="circle");return {...originalGraph,center:state.center,radius:getRadius(state)};case "linear":invariant(originalGraph.type==="linear");return {...originalGraph,coords:state.coords};case "ray":invariant(originalGraph.type==="ray");return {...originalGraph,coords:state.coords};case "sinusoid":invariant(originalGraph.type==="sinusoid");return {...originalGraph,coords:state.coords};case "segment":invariant(originalGraph.type==="segment");return {...originalGraph,coords:state.coords};case "linear-system":invariant(originalGraph.type==="linear-system");return {...originalGraph,coords:state.coords};case "polygon":invariant(originalGraph.type==="polygon");return {...originalGraph,coords:state.coords};case "point":invariant(originalGraph.type==="point");return {...originalGraph,coords:state.coords};case "none":invariant(originalGraph.type==="none");return {...originalGraph};default:throw new UnreachableCaseError(state)}}
1995
1999
 
1996
- const StatefulMafsGraph=React.forwardRef((props,ref)=>{const{onChange,graph}=props;const[state,dispatch]=React.useReducer(interactiveGraphReducer,props,initializeGraphState);useImperativeHandle(ref,()=>({getUserInput:()=>getGradableGraph(state,graph)}));const prevState=useRef(state);useEffect(()=>{if(prevState.current!==state){onChange({graph:mafsStateToInteractiveGraph(state,graph)});}prevState.current=state;},[onChange,state,graph]);const[xSnap,ySnap]=props.snapStep;useEffect(()=>{dispatch(changeSnapStep([xSnap,ySnap]));},[dispatch,xSnap,ySnap]);const[[xMinRange,xMaxRange],[yMinRange,yMaxRange]]=props.range;useEffect(()=>{dispatch(changeRange([[xMinRange,xMaxRange],[yMinRange,yMaxRange]]));},[dispatch,xMinRange,xMaxRange,yMinRange,yMaxRange]);const numSegments=graph.type==="segment"?graph.numSegments:null;const numPoints=graph.type==="point"?graph.numPoints:null;const numSides=graph.type==="polygon"?graph.numSides:null;const snapTo=graph.type==="polygon"?graph.snapTo:null;const showAngles=graph.type==="polygon"||graph.type==="angle"?graph.showAngles:null;const allowReflexAngles=graph.type==="angle"?graph.allowReflexAngles:null;const showSides=graph.type==="polygon"?graph.showSides:null;const startCoords="startCoords"in graph?graph.startCoords:undefined;const originalPropsRef=useRef(props);const latestPropsRef=useLatestRef(props);useEffect(()=>{if(latestPropsRef.current!==originalPropsRef.current){dispatch(reinitialize(latestPropsRef.current));}},[graph.type,numPoints,numSegments,numSides,snapTo,showAngles,showSides,latestPropsRef,startCoords,allowReflexAngles]);if(props.static&&props.correct){return jsxRuntimeExports.jsx(MafsGraph,{...props,state:initializeGraphState({...props,graph:props.correct}),dispatch:dispatch})}return jsxRuntimeExports.jsx(MafsGraph,{...props,state:state,dispatch:dispatch})});
2000
+ const StatefulMafsGraph=React.forwardRef((props,ref)=>{const{onChange,graph}=props;const[state,dispatch]=React.useReducer(interactiveGraphReducer,props,initializeGraphState);useImperativeHandle(ref,()=>({getUserInput:()=>getGradableGraph(state,graph)}));const prevState=useRef(state);useEffect(()=>{if(prevState.current!==state){onChange(mafsStateToInteractiveGraph(state,graph));}prevState.current=state;},[onChange,state,graph]);const[xSnap,ySnap]=props.snapStep;useEffect(()=>{dispatch(changeSnapStep([xSnap,ySnap]));},[dispatch,xSnap,ySnap]);const[[xMinRange,xMaxRange],[yMinRange,yMaxRange]]=props.range;useEffect(()=>{dispatch(changeRange([[xMinRange,xMaxRange],[yMinRange,yMaxRange]]));},[dispatch,xMinRange,xMaxRange,yMinRange,yMaxRange]);const numSegments=graph.type==="segment"?graph.numSegments:null;const numPoints=graph.type==="point"?graph.numPoints:null;const numSides=graph.type==="polygon"?graph.numSides:null;const snapTo=graph.type==="polygon"?graph.snapTo:null;const showAngles=graph.type==="polygon"||graph.type==="angle"?graph.showAngles:null;const allowReflexAngles=graph.type==="angle"?graph.allowReflexAngles:null;const showSides=graph.type==="polygon"?graph.showSides:null;const startCoords="startCoords"in graph?graph.startCoords:undefined;const originalPropsRef=useRef(props);const latestPropsRef=useLatestRef(props);useEffect(()=>{if(latestPropsRef.current!==originalPropsRef.current){dispatch(reinitialize(latestPropsRef.current));}},[graph.type,numPoints,numSegments,numSides,snapTo,showAngles,showSides,latestPropsRef,startCoords,allowReflexAngles]);if(props.static&&props.correct){return jsxRuntimeExports.jsx(MafsGraph,{...props,state:initializeGraphState({...props,graph:props.correct}),dispatch:dispatch})}return jsxRuntimeExports.jsx(MafsGraph,{...props,state:state,dispatch:dispatch})});
1997
2001
 
1998
- const{getClockwiseAngle}=angles;const{getLineEquation,getLineIntersectionString,magnitude,vector}=geometry;const defaultBackgroundImage={url:null};const UNLIMITED="unlimited";function numSteps(range,step){return Math.floor((range[1]-range[0])/step)}const makeInvalidTypeError=(functionName,graphType)=>{return new PerseusError(`${functionName} called but current graph type is not a '${graphType}'`,Errors.NotAllowed,{metadata:{graphType}})};function getSinusoidCoefficients(coords){const p1=coords[0];const p2=coords[1];const amplitude=p2[1]-p1[1];const angularFrequency=Math.PI/(2*(p2[0]-p1[0]));const phase=p1[0]*angularFrequency;const verticalOffset=p1[1];return [amplitude,angularFrequency,phase,verticalOffset]}function getQuadraticCoefficients(coords){const p1=coords[0];const p2=coords[1];const p3=coords[2];const denom=(p1[0]-p2[0])*(p1[0]-p3[0])*(p2[0]-p3[0]);if(denom===0){return}const a=(p3[0]*(p2[1]-p1[1])+p2[0]*(p1[1]-p3[1])+p1[0]*(p3[1]-p2[1]))/denom;const b=(p3[0]*p3[0]*(p1[1]-p2[1])+p2[0]*p2[0]*(p3[1]-p1[1])+p1[0]*p1[0]*(p2[1]-p3[1]))/denom;const c=(p2[0]*p3[0]*(p2[0]-p3[0])*p1[1]+p3[0]*p1[0]*(p3[0]-p1[0])*p2[1]+p1[0]*p2[0]*(p1[0]-p2[0])*p3[1])/denom;return [a,b,c]}class InteractiveGraph extends React.Component{getUserInput(){if(this.mafsRef.current?.getUserInput){return this.mafsRef.current.getUserInput()}throw new PerseusError("Cannot getUserInput from a graph that has never rendered",Errors.NotAllowed)}getPromptJSON(){return getPromptJSON$b(this.props,this.getUserInput())}render(){const box=getInteractiveBoxFromSizeClass(this.props.containerSizeClass);const gridStep=this.props.gridStep||Util.getGridStep(this.props.range,this.props.step,box[0]);const snapStep=this.props.snapStep||Util.snapStepFromGridStep(gridStep);return jsxRuntimeExports.jsx(StatefulMafsGraph,{...this.props,ref:this.mafsRef,gridStep:gridStep,snapStep:snapStep,box:box,showTooltips:!!this.props.showTooltips,readOnly:this.props.apiOptions?.readOnly})}static getLineCoords(graph,props){return graph.coords||InteractiveGraph.pointsFromNormalized(props,[[.25,.75],[.75,.75]])}static getPointCoords(graph,props){const numPoints=graph.numPoints||1;let coords=graph.coords;if(coords){return coords}switch(numPoints){case 1:coords=[graph.coord||[0,0]];break;case 2:coords=[[-5,0],[5,0]];break;case 3:coords=[[-5,0],[0,0],[5,0]];break;case 4:coords=[[-6,0],[-2,0],[2,0],[6,0]];break;case 5:coords=[[-6,0],[-3,0],[0,0],[3,0],[6,0]];break;case 6:coords=[[-5,0],[-3,0],[-1,0],[1,0],[3,0],[5,0]];break;case UNLIMITED:coords=[];break}const range=[[-10,10],[-10,10]];const newCoords=InteractiveGraph.normalizeCoords(coords,range);return InteractiveGraph.pointsFromNormalized(props,newCoords)}static getLinearSystemCoords(graph,props){return graph.coords||_.map([[[.25,.75],[.75,.75]],[[.25,.25],[.75,.25]]],coords=>{return InteractiveGraph.pointsFromNormalized(props,coords)})}static getPolygonCoords(graph,props){if(graph.type!=="polygon"){throw makeInvalidTypeError("toggleShowSides","polygon")}let coords=graph.coords;if(coords){return coords}const n=graph.numSides||3;if(n===UNLIMITED){coords=[];}else {const angle=2*Math.PI/n;const offset=(1/n-1/2)*Math.PI;const radius=graph.snapTo==="sides"?Math.sqrt(3)/3*7:4;coords=_.times(n,function(i){return [radius*Math.cos(i*angle+offset),radius*Math.sin(i*angle+offset)]});}const ranges=[[-10,10],[-10,10]];coords=InteractiveGraph.normalizeCoords(coords,ranges);const snapToGrid=!_.contains(["angles","sides"],graph.snapTo);coords=InteractiveGraph.pointsFromNormalized(props,coords,!snapToGrid);return coords}static getSegmentCoords(graph,props){const coords=graph.coords;if(coords){return coords}const n=graph.numSegments||1;const ys={1:[5],2:[5,-5],3:[5,0,-5],4:[6,2,-2,-6],5:[6,3,0,-3,-6],6:[5,3,1,-1,-3,-5]}[n];const range=[[-10,10],[-10,10]];return ys.map(function(y){let segment=[[-5,y],[5,y]];segment=InteractiveGraph.normalizeCoords(segment,range);segment=InteractiveGraph.pointsFromNormalized(props,segment);return segment})}static getAngleCoords(graph,props){let coords=graph.coords;if(coords){return coords}const snap=graph.snapDegrees||1;let angle=snap;while(angle<20){angle+=snap;}angle=angle*Math.PI/180;const offset=(graph.angleOffsetDeg||0)*Math.PI/180;coords=InteractiveGraph.pointsFromNormalized(props,[[.85,.5],[.5,.5]]);const radius=magnitude(vector(...coords));coords[0]=[coords[1][0]+radius*Math.cos(offset),coords[1][1]+radius*Math.sin(offset)];coords[2]=[coords[1][0]+radius*Math.cos(angle+offset),coords[1][1]+radius*Math.sin(angle+offset)];return coords}static normalizeCoords(coordsList,ranges){return _.map(coordsList,function(coords){return _.map(coords,function(coord,i){const extent=ranges[i][1]-ranges[i][0];return (coord+ranges[i][1])/extent})})}static getEquationString(props){const type=props.graph.type;switch(type){case "none":return InteractiveGraph.getNoneEquationString();case "linear":return InteractiveGraph.getLinearEquationString(props);case "quadratic":return InteractiveGraph.getQuadraticEquationString(props);case "sinusoid":return InteractiveGraph.getSinusoidEquationString(props);case "circle":return InteractiveGraph.getCircleEquationString(props);case "linear-system":return InteractiveGraph.getLinearSystemEquationString(props);case "point":return InteractiveGraph.getPointEquationString(props);case "segment":return InteractiveGraph.getSegmentEquationString(props);case "ray":return InteractiveGraph.getRayEquationString(props);case "polygon":return InteractiveGraph.getPolygonEquationString(props);case "angle":return InteractiveGraph.getAngleEquationString(props);default:throw new UnreachableCaseError(type)}}static pointsFromNormalized(props,coordsList,noSnap){return _.map(coordsList,function(coords){return _.map(coords,function(coord,i){const range=props.range[i];if(noSnap){return range[0]+(range[1]-range[0])*coord}const step=props.step[i];const nSteps=numSteps(range,step);const tick=Math.round(coord*nSteps);return range[0]+step*tick})})}static getNoneEquationString(){return ""}static getLinearEquationString(props){const coords=InteractiveGraph.getLineCoords(props.graph,props);if(approximateEqual(coords[0][0],coords[1][0])){return "x = "+coords[0][0].toFixed(3)}const m=(coords[1][1]-coords[0][1])/(coords[1][0]-coords[0][0]);const b=coords[0][1]-m*coords[0][0];if(approximateEqual(m,0)){return "y = "+b.toFixed(3)}return "y = "+m.toFixed(3)+"x + "+b.toFixed(3)}static getCurrentQuadraticCoefficients(props){const coords=props.graph.coords||InteractiveGraph.defaultQuadraticCoords(props);return getQuadraticCoefficients(coords)}static defaultQuadraticCoords(props){const coords=[[.25,.75],[.5,.25],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getQuadraticEquationString(props){const coeffs=InteractiveGraph.getCurrentQuadraticCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"x^2 + "+coeffs[1].toFixed(3)+"x + "+coeffs[2].toFixed(3)}static getCurrentSinusoidCoefficients(props){const coords=props.graph.coords||InteractiveGraph.defaultSinusoidCoords(props);return getSinusoidCoefficients(coords)}static defaultSinusoidCoords(props){const coords=[[.5,.5],[.65,.6]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getSinusoidEquationString(props){const coeffs=InteractiveGraph.getCurrentSinusoidCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"sin("+coeffs[1].toFixed(3)+"x - "+coeffs[2].toFixed(3)+") + "+coeffs[3].toFixed(3)}static getCircleEquationString(props){const graph=props.graph;const center=graph.center||[0,0];const radius=graph.radius||2;return "center ("+center[0]+", "+center[1]+"), radius "+radius}static getLinearSystemEquationString(props){const coords=InteractiveGraph.getLinearSystemCoords(props.graph,props);return "\n"+getLineEquation(coords[0][0],coords[0][1])+"\n"+getLineEquation(coords[1][0],coords[1][1])+"\n"+getLineIntersectionString(coords[0],coords[1])}static getPointEquationString(props){if(props.graph.type!=="point"){throw makeInvalidTypeError("getPointEquationString","point")}const coords=InteractiveGraph.getPointCoords(props.graph,props);return coords.map(function(coord){return "("+coord[0]+", "+coord[1]+")"}).join(", ")}static getSegmentEquationString(props){if(props.graph.type!=="segment"){throw makeInvalidTypeError("getSegmentEquationString","segment")}const segments=InteractiveGraph.getSegmentCoords(props.graph,props);return _.map(segments,function(segment){return "["+_.map(segment,function(coord){return "("+coord.join(", ")+")"}).join(" ")+"]"}).join(" ")}static getRayEquationString(props){if(props.graph.type!=="ray"){throw makeInvalidTypeError("createPointForPolygonType","ray")}const coords=InteractiveGraph.getLineCoords(props.graph,props);const a=coords[0];const b=coords[1];let eq=InteractiveGraph.getLinearEquationString(props);if(a[0]>b[0]){eq+=" (for x <= "+a[0].toFixed(3)+")";}else if(a[0]<b[0]){eq+=" (for x >= "+a[0].toFixed(3)+")";}else if(a[1]>b[1]){eq+=" (for y <= "+a[1].toFixed(3)+")";}else {eq+=" (for y >= "+a[1].toFixed(3)+")";}return eq}static getPolygonEquationString(props){if(props.graph.type!=="polygon"){throw makeInvalidTypeError("getPolygonEquationString","polygon")}const coords=InteractiveGraph.getPolygonCoords(props.graph,props);return _.map(coords,function(coord){return "("+coord.join(", ")+")"}).join(" ")}static getAngleEquationString(props){if(props.graph.type!=="angle"){throw makeInvalidTypeError("getAngleEquationString","angle")}const coords=InteractiveGraph.getAngleCoords(props.graph,props);const allowReflexAngles=props.graph.allowReflexAngles;const angle=getClockwiseAngle(coords,allowReflexAngles);return angle.toFixed(0)+"° angle"+" at ("+coords[1].join(", ")+")"}static getUserInputFromProps(props){return props.graph}constructor(...args){super(...args),this.mafsRef=React.createRef();}}InteractiveGraph.defaultProps={labels:["x","y"],labelLocation:"onAxis",range:[[-10,10],[-10,10]],step:[1,1],backgroundImage:defaultBackgroundImage,markings:"graph",showTooltips:false,showProtractor:false,graph:{type:"linear"}};const staticTransform$3=_.identity;var InteractiveGraph$1 = {name:"interactive-graph",displayName:"Interactive graph",widget:InteractiveGraph,staticTransform:staticTransform$3};
2002
+ const{getClockwiseAngle}=angles;const{getLineEquation,getLineIntersectionString,magnitude,vector}=geometry;const defaultBackgroundImage={url:null};const UNLIMITED="unlimited";function numSteps(range,step){return Math.floor((range[1]-range[0])/step)}const makeInvalidTypeError=(functionName,graphType)=>{return new PerseusError(`${functionName} called but current graph type is not a '${graphType}'`,Errors.NotAllowed,{metadata:{graphType}})};function getSinusoidCoefficients(coords){const p1=coords[0];const p2=coords[1];const amplitude=p2[1]-p1[1];const angularFrequency=Math.PI/(2*(p2[0]-p1[0]));const phase=p1[0]*angularFrequency;const verticalOffset=p1[1];return [amplitude,angularFrequency,phase,verticalOffset]}function getQuadraticCoefficients(coords){const p1=coords[0];const p2=coords[1];const p3=coords[2];const denom=(p1[0]-p2[0])*(p1[0]-p3[0])*(p2[0]-p3[0]);if(denom===0){return}const a=(p3[0]*(p2[1]-p1[1])+p2[0]*(p1[1]-p3[1])+p1[0]*(p3[1]-p2[1]))/denom;const b=(p3[0]*p3[0]*(p1[1]-p2[1])+p2[0]*p2[0]*(p3[1]-p1[1])+p1[0]*p1[0]*(p2[1]-p3[1]))/denom;const c=(p2[0]*p3[0]*(p2[0]-p3[0])*p1[1]+p3[0]*p1[0]*(p3[0]-p1[0])*p2[1]+p1[0]*p2[0]*(p1[0]-p2[0])*p3[1])/denom;return [a,b,c]}class InteractiveGraph extends React.Component{getUserInput(){if(this.mafsRef.current?.getUserInput){return this.mafsRef.current.getUserInput()}throw new PerseusError("Cannot getUserInput from a graph that has never rendered",Errors.NotAllowed)}getPromptJSON(){return getPromptJSON$b(this.props,this.getUserInput())}getSerializedState(){const{userInput:_,...rest}=this.props;return {...rest,graph:this.props.userInput}}render(){const box=getInteractiveBoxFromSizeClass(this.props.containerSizeClass);const gridStep=this.props.gridStep||Util.getGridStep(this.props.range,this.props.step,box[0]);const snapStep=this.props.snapStep||Util.snapStepFromGridStep(gridStep);const mafsProps={...this.props,graph:this.props.userInput,onChange:()=>this.props.handleUserInput(this.mafsRef.current?.getUserInput())};return jsxRuntimeExports.jsx(StatefulMafsGraph,{...mafsProps,ref:this.mafsRef,gridStep:gridStep,snapStep:snapStep,box:box,showTooltips:!!this.props.showTooltips,readOnly:this.props.apiOptions?.readOnly})}static getLineCoords(graph,props){return graph.coords||InteractiveGraph.pointsFromNormalized(props,[[.25,.75],[.75,.75]])}static getPointCoords(graph,props){const numPoints=graph.numPoints||1;let coords=graph.coords;if(coords){return coords}switch(numPoints){case 1:coords=[graph.coord||[0,0]];break;case 2:coords=[[-5,0],[5,0]];break;case 3:coords=[[-5,0],[0,0],[5,0]];break;case 4:coords=[[-6,0],[-2,0],[2,0],[6,0]];break;case 5:coords=[[-6,0],[-3,0],[0,0],[3,0],[6,0]];break;case 6:coords=[[-5,0],[-3,0],[-1,0],[1,0],[3,0],[5,0]];break;case UNLIMITED:coords=[];break}const range=[[-10,10],[-10,10]];const newCoords=InteractiveGraph.normalizeCoords(coords,range);return InteractiveGraph.pointsFromNormalized(props,newCoords)}static getLinearSystemCoords(graph,props){return graph.coords||_.map([[[.25,.75],[.75,.75]],[[.25,.25],[.75,.25]]],coords=>{return InteractiveGraph.pointsFromNormalized(props,coords)})}static getPolygonCoords(graph,props){if(graph.type!=="polygon"){throw makeInvalidTypeError("toggleShowSides","polygon")}let coords=graph.coords;if(coords){return coords}const n=graph.numSides||3;if(n===UNLIMITED){coords=[];}else {const angle=2*Math.PI/n;const offset=(1/n-1/2)*Math.PI;const radius=graph.snapTo==="sides"?Math.sqrt(3)/3*7:4;coords=_.times(n,function(i){return [radius*Math.cos(i*angle+offset),radius*Math.sin(i*angle+offset)]});}const ranges=[[-10,10],[-10,10]];coords=InteractiveGraph.normalizeCoords(coords,ranges);const snapToGrid=!_.contains(["angles","sides"],graph.snapTo);coords=InteractiveGraph.pointsFromNormalized(props,coords,!snapToGrid);return coords}static getSegmentCoords(graph,props){const coords=graph.coords;if(coords){return coords}const n=graph.numSegments||1;const ys={1:[5],2:[5,-5],3:[5,0,-5],4:[6,2,-2,-6],5:[6,3,0,-3,-6],6:[5,3,1,-1,-3,-5]}[n];const range=[[-10,10],[-10,10]];return ys.map(function(y){let segment=[[-5,y],[5,y]];segment=InteractiveGraph.normalizeCoords(segment,range);segment=InteractiveGraph.pointsFromNormalized(props,segment);return segment})}static getAngleCoords(graph,props){let coords=graph.coords;if(coords){return coords}const snap=graph.snapDegrees||1;let angle=snap;while(angle<20){angle+=snap;}angle=angle*Math.PI/180;const offset=(graph.angleOffsetDeg||0)*Math.PI/180;coords=InteractiveGraph.pointsFromNormalized(props,[[.85,.5],[.5,.5]]);const radius=magnitude(vector(...coords));coords[0]=[coords[1][0]+radius*Math.cos(offset),coords[1][1]+radius*Math.sin(offset)];coords[2]=[coords[1][0]+radius*Math.cos(angle+offset),coords[1][1]+radius*Math.sin(angle+offset)];return coords}static normalizeCoords(coordsList,ranges){return _.map(coordsList,function(coords){return _.map(coords,function(coord,i){const extent=ranges[i][1]-ranges[i][0];return (coord+ranges[i][1])/extent})})}static getEquationString(props){const type=props.userInput.type;switch(type){case "none":return InteractiveGraph.getNoneEquationString();case "linear":return InteractiveGraph.getLinearEquationString(props);case "quadratic":return InteractiveGraph.getQuadraticEquationString(props);case "sinusoid":return InteractiveGraph.getSinusoidEquationString(props);case "circle":return InteractiveGraph.getCircleEquationString(props);case "linear-system":return InteractiveGraph.getLinearSystemEquationString(props);case "point":return InteractiveGraph.getPointEquationString(props);case "segment":return InteractiveGraph.getSegmentEquationString(props);case "ray":return InteractiveGraph.getRayEquationString(props);case "polygon":return InteractiveGraph.getPolygonEquationString(props);case "angle":return InteractiveGraph.getAngleEquationString(props);default:throw new UnreachableCaseError(type)}}static pointsFromNormalized(props,coordsList,noSnap){return _.map(coordsList,function(coords){return _.map(coords,function(coord,i){const range=props.range[i];if(noSnap){return range[0]+(range[1]-range[0])*coord}const step=props.step[i];const nSteps=numSteps(range,step);const tick=Math.round(coord*nSteps);return range[0]+step*tick})})}static getNoneEquationString(){return ""}static getLinearEquationString(props){const coords=InteractiveGraph.getLineCoords(props.userInput,props);if(approximateEqual(coords[0][0],coords[1][0])){return "x = "+coords[0][0].toFixed(3)}const m=(coords[1][1]-coords[0][1])/(coords[1][0]-coords[0][0]);const b=coords[0][1]-m*coords[0][0];if(approximateEqual(m,0)){return "y = "+b.toFixed(3)}return "y = "+m.toFixed(3)+"x + "+b.toFixed(3)}static getCurrentQuadraticCoefficients(props){const coords=props.userInput.coords||InteractiveGraph.defaultQuadraticCoords(props);return getQuadraticCoefficients(coords)}static defaultQuadraticCoords(props){const coords=[[.25,.75],[.5,.25],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getQuadraticEquationString(props){const coeffs=InteractiveGraph.getCurrentQuadraticCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"x^2 + "+coeffs[1].toFixed(3)+"x + "+coeffs[2].toFixed(3)}static getCurrentSinusoidCoefficients(props){const coords=props.userInput.coords||InteractiveGraph.defaultSinusoidCoords(props);return getSinusoidCoefficients(coords)}static defaultSinusoidCoords(props){const coords=[[.5,.5],[.65,.6]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getSinusoidEquationString(props){const coeffs=InteractiveGraph.getCurrentSinusoidCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"sin("+coeffs[1].toFixed(3)+"x - "+coeffs[2].toFixed(3)+") + "+coeffs[3].toFixed(3)}static getCircleEquationString(props){const graph=props.userInput;const center=graph.center||[0,0];const radius=graph.radius||2;return "center ("+center[0]+", "+center[1]+"), radius "+radius}static getLinearSystemEquationString(props){const coords=InteractiveGraph.getLinearSystemCoords(props.userInput,props);return "\n"+getLineEquation(coords[0][0],coords[0][1])+"\n"+getLineEquation(coords[1][0],coords[1][1])+"\n"+getLineIntersectionString(coords[0],coords[1])}static getPointEquationString(props){if(props.userInput.type!=="point"){throw makeInvalidTypeError("getPointEquationString","point")}const coords=InteractiveGraph.getPointCoords(props.userInput,props);return coords.map(function(coord){return "("+coord[0]+", "+coord[1]+")"}).join(", ")}static getSegmentEquationString(props){if(props.userInput.type!=="segment"){throw makeInvalidTypeError("getSegmentEquationString","segment")}const segments=InteractiveGraph.getSegmentCoords(props.userInput,props);return _.map(segments,function(segment){return "["+_.map(segment,function(coord){return "("+coord.join(", ")+")"}).join(" ")+"]"}).join(" ")}static getRayEquationString(props){if(props.userInput.type!=="ray"){throw makeInvalidTypeError("createPointForPolygonType","ray")}const coords=InteractiveGraph.getLineCoords(props.userInput,props);const a=coords[0];const b=coords[1];let eq=InteractiveGraph.getLinearEquationString(props);if(a[0]>b[0]){eq+=" (for x <= "+a[0].toFixed(3)+")";}else if(a[0]<b[0]){eq+=" (for x >= "+a[0].toFixed(3)+")";}else if(a[1]>b[1]){eq+=" (for y <= "+a[1].toFixed(3)+")";}else {eq+=" (for y >= "+a[1].toFixed(3)+")";}return eq}static getPolygonEquationString(props){if(props.userInput.type!=="polygon"){throw makeInvalidTypeError("getPolygonEquationString","polygon")}const coords=InteractiveGraph.getPolygonCoords(props.userInput,props);return _.map(coords,function(coord){return "("+coord.join(", ")+")"}).join(" ")}static getAngleEquationString(props){if(props.userInput.type!=="angle"){throw makeInvalidTypeError("getAngleEquationString","angle")}const coords=InteractiveGraph.getAngleCoords(props.userInput,props);const allowReflexAngles=props.userInput.allowReflexAngles;const angle=getClockwiseAngle(coords,allowReflexAngles);return angle.toFixed(0)+"° angle"+" at ("+coords[1].join(", ")+")"}constructor(...args){super(...args),this.mafsRef=React.createRef();}}InteractiveGraph.defaultProps={labels:["x","y"],labelLocation:"onAxis",range:[[-10,10],[-10,10]],step:[1,1],backgroundImage:defaultBackgroundImage,markings:"graph",showTooltips:false,showProtractor:false,userInput:{type:"linear"}};function getUserInputFromSerializedState$8(serializedState){return serializedState.graph}function getStartUserInput$6(options){return options.graph}var InteractiveGraph$1 = {name:"interactive-graph",displayName:"Interactive graph",widget:InteractiveGraph,staticTransform:_.identity,getStartUserInput: getStartUserInput$6,getUserInputFromSerializedState: getUserInputFromSerializedState$8};
1999
2003
 
2000
2004
  const bodyXsmallBold={fontFamily:"inherit",fontSize:15,fontWeight:"bold",lineHeight:"22px"};
2001
2005
 
@@ -2009,17 +2013,17 @@ const BringToFront={boxShadow:`0 8px 8px ${color.offBlack64}`,zIndex:1e3};const
2009
2013
 
2010
2014
  function shouldReduceMotion(){if(typeof window.matchMedia!=="function"){return true}const mediaQuery=window.matchMedia("(prefers-reduced-motion: reduce)");return !mediaQuery||mediaQuery.matches}const MARKER_SIZE=24;class Marker extends React.Component{componentDidMount(){this._mounted=true;}componentWillUnmount(){this._mounted=false;}renderIcon(){const{selected,showCorrectness,showSelected,showPulsate}=this.props;const isOpen=showSelected;const selectedAnswers=selected;let iconStyles;const iconNull={path:"",height:1,width:1};let args={size:MARKER_SIZE,color:color.white,icon:iconNull};if(showCorrectness){iconStyles=[styles$a.markerGraded,showCorrectness==="correct"?styles$a.markerCorrect:styles$a.markerIncorrect,isOpen&&styles$a.markerSelected];args={...args,icon:showCorrectness==="correct"?iconCheck:iconMinus};}else if(selectedAnswers&&selectedAnswers.length>0){iconStyles=[styles$a.markerFilled,isOpen&&styles$a.markerSelected];}else if(isOpen){iconStyles=[styles$a.markerSelected];args={...args,icon:iconChevronDown,size:8};}else if(showPulsate){iconStyles=[styles$a.markerPulsateBase,this._mounted&&shouldReduceMotion()?showPulsate&&styles$a.markerUnfilledPulsateOnce:showPulsate&&styles$a.markerUnfilledPulsateInfinite];}return jsxRuntimeExports.jsx(View,{style:[styles$a.markerIcon,iconStyles],ref:node=>this._icon=node,children:jsxRuntimeExports.jsx(Icon,{...args})})}render(){const{showCorrectness,selected,showAnswer,answerSide,answerStyles,hovered,focused,label}=this.props;const markerDisabled=showCorrectness==="correct";const active=hovered||focused;return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(View,{style:[styles$a.marker,active&&!markerDisabled&&styles$a.markerActive],"aria-label":markerDisabled?this.context.strings.correctExcited:label,children:this.renderIcon()}),!!selected&&showAnswer&&jsxRuntimeExports.jsx(AnswerPill,{selectedAnswers:selected,showCorrectness:showCorrectness,side:answerSide,style:answerStyles,markerRef:this._icon??undefined,hovered:hovered,focused:focused})]})}constructor(...args){super(...args),this._mounted=false;}}Marker.contextType=PerseusI18nContext;Marker.defaultProps={selected:[]};const styles$a=StyleSheet.create({marker:{position:"absolute",backgroundColor:color.white,borderRadius:MARKER_SIZE,width:MARKER_SIZE,height:MARKER_SIZE,marginLeft:MARKER_SIZE/-2,marginTop:MARKER_SIZE/-2,boxShadow:`0 8px 8px ${color.offBlack8}`},markerIcon:{display:"flex",alignItems:"center",justifyContent:"center",boxSizing:"border-box",width:MARKER_SIZE,height:MARKER_SIZE,border:`2px solid ${color.offBlack64}`,borderRadius:MARKER_SIZE},markerPulsateBase:{animationName:{"0%":{transform:"scale(1)",backgroundColor:color.blue},"100%":{transform:"scale(1.3)",backgroundColor:color.blue}},animationDirection:"alternate",animationDuration:"0.8s",animationTimingFunction:"ease-in",transformOrigin:"50% 50%",animationIterationCount:"0"},markerUnfilledPulsateInfinite:{animationIterationCount:"infinite"},markerUnfilledPulsateOnce:{animationIterationCount:"2"},markerActive:{outline:`2px solid ${color.blue}`,outlineOffset:2},markerSelected:{boxShadow:`0 8px 8px ${color.offBlack8}`,border:`solid 4px ${color.white}`,backgroundColor:color.blue,borderRadius:MARKER_SIZE,transform:"rotate(180deg)"},markerFilled:{backgroundColor:"#ECF3FE",border:`4px solid ${color.blue}`},markerGraded:{width:MARKER_SIZE,height:MARKER_SIZE,justifyContent:"center",alignItems:"center",border:`2px solid ${color.white}`},markerCorrect:{background:"#00880b"},markerIncorrect:{background:color.offBlack64}});
2011
2015
 
2012
- function isAnswerful(marker){return marker.answers!=null}function getUpdatedMarkerState(marker,reviewMode,showSolutions){const shouldShowFeedback=showSolutions==="all"||reviewMode;if(!shouldShowFeedback){return marker}if(isAnswerful(marker)){return {...marker,selected:marker.answers}}else {return {...marker,selected:undefined}}}class LabelImage extends React.Component{static pointInTriangle(p,a,b,c){const sign=(p1,p2,p3)=>(p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y);const b1=sign(p,a,b)<0;const b2=sign(p,b,c)<0;const b3=sign(p,c,a)<0;return b1===b2&&b2===b3}static imageSideForMarkerPosition(x,y,preferredDirection){if(preferredDirection&&preferredDirection!=="NONE"){if(preferredDirection==="LEFT"&&x>20){return "right"}else if(preferredDirection==="RIGHT"&&x<80){return "left"}else if(preferredDirection==="UP"&&y>20){return "bottom"}else if(preferredDirection==="DOWN"&&y<80){return "top"}}if(x<20){return "left"}if(x>80){return "right"}const tl={x:20,y:0};const tr={x:80,y:0};const br={x:80,y:100};const bl={x:20,y:100};const cp={x:50,y:50};const sides=["top","right","bottom","left"];const triangles={top:[tl,tr,cp],right:[cp,tr,br],bottom:[bl,cp,br],left:[tl,cp,bl]};const p={x,y};for(const side of sides){const corners=triangles[side];if(LabelImage.pointInTriangle(p,...corners)){return side}}return "center"}static navigateToMarkerIndex(navigateDirection,markers,thisIndex){const thisMarker=markers[thisIndex];const sortedMarkers=markers.map((otherMarker,index)=>{const x=otherMarker.x-thisMarker.x;const y=otherMarker.y-thisMarker.y;const dist=Math.sqrt(x**2+y**2);return {index,dist,dir:{x:dist!==0?x/dist:0,y:dist!==0?y/dist:0}}}).filter(marker=>{if(marker.index===thisIndex){return false}return markers[marker.index].showCorrectness!=="correct"}).sort((a,b)=>{const distA=Math.round(a.dist*(navigateDirection.x*a.dir.x+navigateDirection.y*a.dir.y));const distB=Math.round(b.dist*(navigateDirection.x*b.dir.x+navigateDirection.y*b.dir.y));let dirA;let dirB;if(navigateDirection.x>0){dirA=a.dir.x>0&&distA!==0;dirB=b.dir.x>0&&distB!==0;}else if(navigateDirection.x<0){dirA=a.dir.x<0&&distA!==0;dirB=b.dir.x<0&&distB!==0;}else if(navigateDirection.y>0){dirA=a.dir.y>0&&distA!==0;dirB=b.dir.y>0&&distB!==0;}else if(navigateDirection.y<0){dirA=a.dir.y<0&&distA!==0;dirB=b.dir.y<0&&distB!==0;}if(dirA!==dirB){if(dirA){return -1}return 1}return distA-distB});return sortedMarkers.length>0?sortedMarkers[0].index:thisIndex}componentDidMount(){this._mounted=true;}componentWillUnmount(){this._mounted=false;}getUserInput(){return {markers:this.props.markers.map(marker=>({selected:marker.selected,label:marker.label}))}}getPromptJSON(){return getPromptJSON$a(this.props,this.getUserInput())}handleMarkerChange(index,marker){const{markers,onChange}=this.props;const updatedMarkers=[...markers.slice(0,index),{...marker,showCorrectness:undefined},...markers.slice(index+1)];onChange({markers:updatedMarkers});}activateMarker(index,opened){this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:marker-interacted-with",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:marker-interacted-with:ti",payload:null});const{activeMarkerIndex}=this.state;if(activeMarkerIndex!==index&&opened){this.setState({activeMarkerIndex:index,markersInteracted:true});}else {this.setState({activeMarkerIndex:-1});}}handleMarkerKeyDown(index,e){const{markers}=this.props;if(markers.length<2){return}const directions={ArrowUp:{x:0,y:-1},ArrowRight:{x:1,y:0},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0}};if(!(e.key in directions)){return}const navigateDirection=directions[e.key];e.preventDefault();const marker=this._markers[LabelImage.navigateToMarkerIndex(navigateDirection,markers,index)];if(marker){ReactDOM.findDOMNode(marker).focus();}}handleAnswerChoicesChangeForMarker(index,selection){const{choices,markers}=this.props;const selected=choices.filter((_,index)=>selection[index]);this.handleMarkerChange(index,{...markers[index],selected:selected.length?selected:undefined});}renderMarkers(){const{markers,preferredPopoverDirection}=this.props;const{markersInteracted,activeMarkerIndex}=this.state;const isNarrowPage=this._mounted&&window.matchMedia(mediaQueries.xsOrSmaller.replace("@media ","")).matches;const isWideImage=this.props.imageWidth/2>this.props.imageHeight;return markers.map((marker,index)=>{let side;let markerPosition;if(isNarrowPage||isWideImage){side=marker.y>50?"top":"bottom";markerPosition=marker.y>50?"bottom":"top";}else {markerPosition=LabelImage.imageSideForMarkerPosition(marker.x,marker.y,preferredPopoverDirection);if(markerPosition==="center"){markerPosition="bottom";}side=({left:"right",top:"bottom",right:"left",bottom:"top"})[markerPosition];}const updatedMarkerState=getUpdatedMarkerState(marker,this.props.reviewMode,this.props.showSolutions);let score;if(isAnswerful(updatedMarkerState)){score=scoreLabelImageMarker(updatedMarkerState.selected,updatedMarkerState.answers);}else {score={hasAnswers:false,isCorrect:false};}const shouldShowFeedback=this.props.showSolutions==="all"||this.props.reviewMode;const showCorrectness=shouldShowFeedback&&score.isCorrect?"correct":marker.showCorrectness;const disabled=shouldShowFeedback;const isActiveAnswerChoice=activeMarkerIndex===index;const showAnswerChoice=updatedMarkerState.selected&&!this.state.hideAnswers&&!isActiveAnswerChoice;const adjustPillDistance={[`margin${markerPosition.charAt(0).toUpperCase()+markerPosition.slice(1)}`]:10};return jsxRuntimeExports.jsx(View,{style:{position:"absolute",left:`${marker.x}%`,top:`${marker.y}%`,zIndex:"unset"},children:jsxRuntimeExports.jsx(AnswerChoices,{choices:this.props.choices.map(choice=>({content:choice,checked:updatedMarkerState.selected?updatedMarkerState.selected.includes(choice):false})),multipleSelect:this.props.multipleAnswers,onChange:selection=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:choiced-interacted-with",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:choiced-interacted-with:ti",payload:null});this.handleAnswerChoicesChangeForMarker(index,selection);},onToggle:opened=>this.activateMarker(index,opened),disabled:disabled,opener:({opened})=>jsxRuntimeExports.jsx(Clickable,{role:"button","aria-expanded":opened,children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx(Marker,{label:marker.label,showCorrectness:showCorrectness,showSelected:opened,showPulsate:!markersInteracted,ref:node=>this._markers[index]=node,showAnswer:showAnswerChoice,answerSide:side,answerStyles:adjustPillDistance,focused:focused||pressed,hovered:hovered,selected:updatedMarkerState.selected})},`marker-${marker.x}.${marker.y}`)},`answers-${marker.x}.${marker.y}`)},index)})}renderInstructions(){const{apiOptions:{isMobile},choices,multipleAnswers,hideChoicesFromInstructions:hideChoices}=this.props;const{strings}=this.context;const promptString=isMobile?multipleAnswers?strings.tapMultiple:strings.tapSingle:multipleAnswers?strings.clickMultiple:strings.clickSingle;const choicesString=strings.choices;return jsxRuntimeExports.jsxs("div",{className:classNames$1("perseus-label-image-widget-instructions",css(styles$9.instructions)),children:[jsxRuntimeExports.jsxs("div",{className:css(styles$9.instructionsCaption),children:[promptString," ",!hideChoices&&choicesString]}),!hideChoices&&jsxRuntimeExports.jsx("div",{className:css(styles$9.instructionsChoices),children:choices.map((choice,index)=>jsxRuntimeExports.jsx("div",{className:css(styles$9.instructionsChoice),children:jsxRuntimeExports.jsx(Renderer,{content:choice,strings:strings})},index))})]})}render(){const{imageAlt,imageUrl,imageWidth,imageHeight}=this.props;const{activeMarkerIndex}=this.state;return jsxRuntimeExports.jsxs("div",{children:[this.renderInstructions(),jsxRuntimeExports.jsxs("div",{className:css(styles$9.markersCanvas),style:{maxWidth:imageWidth,maxHeight:imageHeight},children:[jsxRuntimeExports.jsx("div",{className:css(styles$9.imageContainer,activeMarkerIndex!==-1&&styles$9.imageInteractionDisabled),children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{alt:imageAlt,src:imageUrl,width:imageWidth,height:imageHeight,setAssetStatus:setAssetStatus})})}),this.renderMarkers()]}),jsxRuntimeExports.jsx(HideAnswersToggle,{areAnswersHidden:this.state.hideAnswers,onChange:hideAnswers=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:toggle-answers-hidden",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:toggle-answers-hidden:ti",payload:null});this.setState({hideAnswers});}})]})}constructor(props){super(props),this._mounted=false;this._markers=[];this.state={activeMarkerIndex:-1,markersInteracted:false,hideAnswers:false};}}LabelImage.contextType=PerseusI18nContext;const styles$9=StyleSheet.create({instructions:{paddingBottom:16},instructionsCaption:{...bodyXsmallBold,paddingBottom:16},instructionsChoices:{display:"flex",flexWrap:"wrap",margin:"-8px 0"},instructionsChoice:{display:"flex",alignItems:"center",margin:"8px 0",":not(:last-child)":{"::after":{content:"''",display:"inline-block",position:"relative",width:2,height:2,marginLeft:5,marginRight:5,background:"rgba(33, 36, 44, 0.32)",borderRadius:2}}},markersCanvas:{position:"relative"},imageContainer:{display:"flex"},imageInteractionDisabled:{pointerEvents:"none"}});const LabelImageWithDependencies=React.forwardRef((props,ref)=>{const deps=useDependencies();return jsxRuntimeExports.jsx(LabelImage,{ref:ref,analytics:deps.analytics,...props})});var LabelImage$1 = {name:"label-image",displayName:"Label Image",widget:LabelImageWithDependencies,isLintable:true};
2016
+ function isAnswerful(marker){return marker.answers!=null}function getComputedSelectedState(marker,userInputMarker,reviewMode,showSolutions){const shouldShowFeedback=showSolutions==="all"||reviewMode;if(!shouldShowFeedback){return userInputMarker}if(isAnswerful(marker)){return {...userInputMarker,selected:marker.answers}}else {return {...userInputMarker,selected:undefined}}}class LabelImage extends React.Component{static pointInTriangle(p,a,b,c){const sign=(p1,p2,p3)=>(p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y);const b1=sign(p,a,b)<0;const b2=sign(p,b,c)<0;const b3=sign(p,c,a)<0;return b1===b2&&b2===b3}static imageSideForMarkerPosition(x,y,preferredDirection){if(preferredDirection&&preferredDirection!=="NONE"){if(preferredDirection==="LEFT"&&x>20){return "right"}else if(preferredDirection==="RIGHT"&&x<80){return "left"}else if(preferredDirection==="UP"&&y>20){return "bottom"}else if(preferredDirection==="DOWN"&&y<80){return "top"}}if(x<20){return "left"}if(x>80){return "right"}const tl={x:20,y:0};const tr={x:80,y:0};const br={x:80,y:100};const bl={x:20,y:100};const cp={x:50,y:50};const sides=["top","right","bottom","left"];const triangles={top:[tl,tr,cp],right:[cp,tr,br],bottom:[bl,cp,br],left:[tl,cp,bl]};const p={x,y};for(const side of sides){const corners=triangles[side];if(LabelImage.pointInTriangle(p,...corners)){return side}}return "center"}static navigateToMarkerIndex(navigateDirection,markers,thisIndex){const thisMarker=markers[thisIndex];const sortedMarkers=markers.map((otherMarker,index)=>{const x=otherMarker.x-thisMarker.x;const y=otherMarker.y-thisMarker.y;const dist=Math.sqrt(x**2+y**2);return {index,dist,dir:{x:dist!==0?x/dist:0,y:dist!==0?y/dist:0}}}).filter(marker=>{if(marker.index===thisIndex){return false}return markers[marker.index].showCorrectness!=="correct"}).sort((a,b)=>{const distA=Math.round(a.dist*(navigateDirection.x*a.dir.x+navigateDirection.y*a.dir.y));const distB=Math.round(b.dist*(navigateDirection.x*b.dir.x+navigateDirection.y*b.dir.y));let dirA;let dirB;if(navigateDirection.x>0){dirA=a.dir.x>0&&distA!==0;dirB=b.dir.x>0&&distB!==0;}else if(navigateDirection.x<0){dirA=a.dir.x<0&&distA!==0;dirB=b.dir.x<0&&distB!==0;}else if(navigateDirection.y>0){dirA=a.dir.y>0&&distA!==0;dirB=b.dir.y>0&&distB!==0;}else if(navigateDirection.y<0){dirA=a.dir.y<0&&distA!==0;dirB=b.dir.y<0&&distB!==0;}if(dirA!==dirB){if(dirA){return -1}return 1}return distA-distB});return sortedMarkers.length>0?sortedMarkers[0].index:thisIndex}componentDidMount(){this._mounted=true;}componentWillUnmount(){this._mounted=false;}getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$a(this.props,this.getUserInput())}handleMarkerChange(index,marker){const{markers,onChange,userInput,handleUserInput}=this.props;const updatedRenderProps=[...markers.slice(0,index),{...markers[index],showCorrectness:undefined},...markers.slice(index+1)];const updatedUserInput=[...userInput.markers.slice(0,index),{label:marker.label,selected:marker.selected},...userInput.markers.slice(index+1)];onChange({markers:updatedRenderProps});handleUserInput({markers:updatedUserInput});}activateMarker(index,opened){this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:marker-interacted-with",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:marker-interacted-with:ti",payload:null});const{activeMarkerIndex}=this.state;if(activeMarkerIndex!==index&&opened){this.setState({activeMarkerIndex:index,markersInteracted:true});}else {this.setState({activeMarkerIndex:-1});}}handleMarkerKeyDown(index,e){const{markers}=this.props;if(markers.length<2){return}const directions={ArrowUp:{x:0,y:-1},ArrowRight:{x:1,y:0},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0}};if(!(e.key in directions)){return}const navigateDirection=directions[e.key];e.preventDefault();const marker=this._markers[LabelImage.navigateToMarkerIndex(navigateDirection,markers,index)];if(marker){ReactDOM.findDOMNode(marker).focus();}}handleAnswerChoicesChangeForMarker(index,selection){const{choices,markers}=this.props;const selected=choices.filter((_,index)=>selection[index]);this.handleMarkerChange(index,{...markers[index],selected:selected.length>0?selected:undefined});}renderMarkers(){const{markers,preferredPopoverDirection,userInput}=this.props;const{markersInteracted,activeMarkerIndex}=this.state;const isNarrowPage=this._mounted&&window.matchMedia(mediaQueries.xsOrSmaller.replace("@media ","")).matches;const isWideImage=this.props.imageWidth/2>this.props.imageHeight;return markers.map((marker,index)=>{const userInputMarker=userInput.markers[index];let side;let markerPosition;if(isNarrowPage||isWideImage){side=marker.y>50?"top":"bottom";markerPosition=marker.y>50?"bottom":"top";}else {markerPosition=LabelImage.imageSideForMarkerPosition(marker.x,marker.y,preferredPopoverDirection);if(markerPosition==="center"){markerPosition="bottom";}side=({left:"right",top:"bottom",right:"left",bottom:"top"})[markerPosition];}const computedSelectedState=getComputedSelectedState(marker,userInputMarker,this.props.reviewMode,this.props.showSolutions);let score;if(isAnswerful(marker)){score=scoreLabelImageMarker(computedSelectedState.selected,marker.answers);}else {score={hasAnswers:false,isCorrect:false};}const shouldShowFeedback=this.props.showSolutions==="all"||this.props.reviewMode;const showCorrectness=shouldShowFeedback&&score.isCorrect?"correct":marker.showCorrectness;const disabled=shouldShowFeedback;const isActiveAnswerChoice=activeMarkerIndex===index;const showAnswerChoice=computedSelectedState.selected&&!this.state.hideAnswers&&!isActiveAnswerChoice;const adjustPillDistance={[`margin${markerPosition.charAt(0).toUpperCase()+markerPosition.slice(1)}`]:10};return jsxRuntimeExports.jsx(View,{style:{position:"absolute",left:`${marker.x}%`,top:`${marker.y}%`,zIndex:"unset"},children:jsxRuntimeExports.jsx(AnswerChoices,{choices:this.props.choices.map(choice=>({content:choice,checked:computedSelectedState.selected?computedSelectedState.selected.includes(choice):false})),multipleSelect:this.props.multipleAnswers,onChange:selection=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:choiced-interacted-with",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:choiced-interacted-with:ti",payload:null});this.handleAnswerChoicesChangeForMarker(index,selection);},onToggle:opened=>this.activateMarker(index,opened),disabled:disabled,opener:({opened})=>jsxRuntimeExports.jsx(Clickable,{role:"button","aria-expanded":opened,children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx(Marker,{label:marker.label,showCorrectness:showCorrectness,showSelected:opened,showPulsate:!markersInteracted,ref:node=>this._markers[index]=node,showAnswer:showAnswerChoice,answerSide:side,answerStyles:adjustPillDistance,focused:focused||pressed,hovered:hovered,selected:computedSelectedState.selected})},`marker-${marker.x}.${marker.y}`)},`answers-${marker.x}.${marker.y}`)},index)})}renderInstructions(){const{apiOptions:{isMobile},choices,multipleAnswers,hideChoicesFromInstructions:hideChoices}=this.props;const{strings}=this.context;const promptString=isMobile?multipleAnswers?strings.tapMultiple:strings.tapSingle:multipleAnswers?strings.clickMultiple:strings.clickSingle;const choicesString=strings.choices;return jsxRuntimeExports.jsxs("div",{className:classNames$1("perseus-label-image-widget-instructions",css(styles$9.instructions)),children:[jsxRuntimeExports.jsxs("div",{className:css(styles$9.instructionsCaption),children:[promptString," ",!hideChoices&&choicesString]}),!hideChoices&&jsxRuntimeExports.jsx("div",{className:css(styles$9.instructionsChoices),children:choices.map((choice,index)=>jsxRuntimeExports.jsx("div",{className:css(styles$9.instructionsChoice),children:jsxRuntimeExports.jsx(Renderer,{content:choice,strings:strings})},index))})]})}getSerializedState(){const{userInput,markers,...rest}=this.props;return {...rest,markers:markers.map((marker,index)=>({...marker,selected:userInput.markers[index].selected}))}}render(){const{imageAlt,imageUrl,imageWidth,imageHeight}=this.props;const{activeMarkerIndex}=this.state;return jsxRuntimeExports.jsxs("div",{children:[this.renderInstructions(),jsxRuntimeExports.jsxs("div",{className:css(styles$9.markersCanvas),style:{maxWidth:imageWidth,maxHeight:imageHeight},children:[jsxRuntimeExports.jsx("div",{className:css(styles$9.imageContainer,activeMarkerIndex!==-1&&styles$9.imageInteractionDisabled),children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{alt:imageAlt,src:imageUrl,width:imageWidth,height:imageHeight,setAssetStatus:setAssetStatus})})}),this.renderMarkers()]}),jsxRuntimeExports.jsx(HideAnswersToggle,{areAnswersHidden:this.state.hideAnswers,onChange:hideAnswers=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:toggle-answers-hidden",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:toggle-answers-hidden:ti",payload:null});this.setState({hideAnswers});}})]})}constructor(props){super(props),this._mounted=false;this._markers=[];this.state={activeMarkerIndex:-1,markersInteracted:false,hideAnswers:false};}}LabelImage.contextType=PerseusI18nContext;const LabelImageWithDependencies=React.forwardRef((props,ref)=>{const deps=useDependencies();return jsxRuntimeExports.jsx(LabelImage,{ref:ref,analytics:deps.analytics,...props})});function getStartUserInput$5(options){return {markers:options.markers.map(m=>({label:m.label}))}}function getUserInputFromSerializedState$7(serializedState){return {markers:serializedState.markers.map(m=>({label:m.label,selected:m.selected}))}}var LabelImage$1 = {name:"label-image",displayName:"Label Image",widget:LabelImageWithDependencies,isLintable:true,getStartUserInput: getStartUserInput$5,getUserInputFromSerializedState: getUserInputFromSerializedState$7};const styles$9=StyleSheet.create({instructions:{paddingBottom:16},instructionsCaption:{...bodyXsmallBold,paddingBottom:16},instructionsChoices:{display:"flex",flexWrap:"wrap",margin:"-8px 0"},instructionsChoice:{display:"flex",alignItems:"center",margin:"8px 0",":not(:last-child)":{"::after":{content:"''",display:"inline-block",position:"relative",width:2,height:2,marginLeft:5,marginRight:5,background:"rgba(33, 36, 44, 0.32)",borderRadius:2}}},markersCanvas:{position:"relative"},imageContainer:{display:"flex"},imageInteractionDisabled:{pointerEvents:"none"}});
2013
2017
 
2014
- const addOffsetParentScroll=($el,position)=>{const $offsetParent=$el.offsetParent();return {top:position.top+$offsetParent.scrollTop(),left:position.left+$offsetParent.scrollLeft()}};class Placeholder extends React.Component{render(){const{layout}=this.props;const className=css(styles$8.card,styles$8.placeholder,layout==="horizontal"&&styles$8.horizontalCard);const style={width:this.props.width,height:this.props.height};if(this.props.margin!=null){style.margin=this.props.margin;}return jsxRuntimeExports.jsx("li",{className:className,style:style})}}class Draggable extends React.Component{componentDidMount(){this._mounted=true;this.isMouseMoveUpBound=false;document.addEventListener("touchmove",this.onMouseMove,Util.supportsPassiveEvents()?{passive:false}:false);}componentDidUpdate(prevProps){if(this.props.state===prevProps.state){return}if(this.props.state==="animating"&&this.props.endPosition){const current=this.getCurrentPosition();const duration=15*Math.sqrt(Math.sqrt(Math.pow(this.props.endPosition.left-current.left,2)+Math.pow(this.props.endPosition.top-current.top,2)));$(ReactDOM__default.findDOMNode(this)).animate(this.props.endPosition,{duration:Math.max(duration,1),complete:this.props.onAnimationEnd});}else if(this.props.state==="static"){$(ReactDOM__default.findDOMNode(this)).finish();}}componentWillUnmount(){this._mounted=false;if(this.isMouseMoveUpBound){this.unbindMouseMoveUp();}if(this.animationFrameRequest){cancelAnimationFrame(this.animationFrameRequest);}document.removeEventListener("touchmove",this.onMouseMove);}render(){const{includePadding,layout,state:type}=this.props;let className=css(styles$8.card,styles$8.draggable,layout==="horizontal"&&styles$8.horizontalCard,layout==="vertical"&&styles$8.verticalCard,type==="dragging"&&styles$8.dragging,type==="disabled"&&styles$8.disabled,!includePadding&&styles$8.unpaddedCard)+" "+"perseus-sortable-draggable";if(!includePadding){className+=" perseus-sortable-draggable-unpadded";}const style={position:"static"};if(this.props.state==="dragging"||this.props.state==="animating"){_.extend(style,{position:"absolute"},this.getCurrentPosition());}if(this.props.width){style.width=this.props.width+1;}if(this.props.height){style.height=this.props.height;}if(this.props.margin!=null){style.margin=this.props.margin;}return jsxRuntimeExports.jsx("li",{className:className,style:style,onMouseDown:this.onMouseDown,onTouchStart:this.onMouseDown,onTouchMove:this.onMouseMove,onTouchEnd:this.onMouseUp,onTouchCancel:this.onMouseUp,children:jsxRuntimeExports.jsx(Renderer,{content:this.props.content,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"draggable"),onRender:this.props.onRender,strings:this.context.strings})})}constructor(...args){super(...args),this.animationFrameRequest=null,this.state={startPosition:{left:0,top:0},startMouse:{left:0,top:0},mouse:{left:0,top:0},dragging:false},this.getCurrentPosition=()=>{return {left:this.state.startPosition.left+this.state.mouse.left-this.state.startMouse.left,top:this.state.startPosition.top+this.state.mouse.top-this.state.startMouse.top}},this.bindMouseMoveUp=()=>{this.isMouseMoveUpBound=true;$(document).on("mousemove",this.onMouseMove);$(document).on("mouseup",this.onMouseUp);},this.unbindMouseMoveUp=()=>{this.isMouseMoveUpBound=false;$(document).off("mousemove",this.onMouseMove);$(document).off("mouseup",this.onMouseUp);},this.onMouseDown=event=>{if(this.props.state!=="static"){return}if(!(event.button===0||event.touches!=null&&event.touches.length===1)){return}event.preventDefault();const loc=Util.extractPointerLocation(event);this.animationFrameRequest=requestAnimationFrame(()=>{const $el=$(ReactDOM__default.findDOMNode(this));const position=$el.position();const startPosition=addOffsetParentScroll($el,position);if(loc&&this._mounted){this.setState({startPosition,startMouse:loc,mouse:loc,dragging:true},function(){this.bindMouseMoveUp();this.props.onMouseDown();});}});},this.onMouseMove=event=>{const notDragging=this.props.state!=="dragging"||!this.state.dragging;if(notDragging){return}event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.animationFrameRequest=requestAnimationFrame(()=>{this.setState({mouse:loc},this.props.onMouseMove);});}},this.onMouseUp=event=>{const notDragging=this.props.state!=="dragging"||!this.state.dragging;if(notDragging){return}event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.setState({dragging:false});this.unbindMouseMoveUp();this.props.onMouseUp();}};}}Draggable.contextType=PerseusI18nContext;Draggable.defaultProps={includePadding:true,type:"static",linterContext:PerseusLinter.linterContextDefault};class Sortable extends React.Component{static itemsFromProps(props){const state=props.disabled?"disabled":"static";return props.options.map((option,i)=>{return {option:option,key:i,state,width:0,height:0}})}static clearItemMeasurements(items){return items.map(item=>{return {...item,width:0,height:0}})}UNSAFE_componentWillReceiveProps(nextProps){const prevProps=this.props;if(!_.isEqual(nextProps.options,prevProps.options)){this.setState({items:Sortable.itemsFromProps(nextProps)});}else if(nextProps.layout!==prevProps.layout||nextProps.padding!==prevProps.padding||nextProps.disabled!==prevProps.disabled||!_.isEqual(nextProps.constraints,prevProps.constraints)){this.setState({items:Sortable.clearItemMeasurements(this.state.items)});}}componentDidUpdate(){if(this.state.items.length&&!this.state.items[0].width&&!this.state.items[0].height){setTimeout(()=>{this.measureItems();},0);}}measureItems(){let items=[...this.state.items];const $items=_.map(items,function(item){return $(ReactDOM__default.findDOMNode(this.refs[item.key]))},this);const widths=_.invoke($items,"outerWidth");const heights=_.invoke($items,"outerHeight");const{constraints,layout}=this.props;let syncWidth=null;if(constraints?.width){syncWidth=_.max(widths.concat(constraints.width));}else if(layout==="vertical"){syncWidth=_.max(widths);}let syncHeight=null;if(constraints?.height){syncHeight=_.max(heights.concat(constraints.height));}else if(layout==="horizontal"){syncHeight=_.max(heights);}items=_.map(items,function(item,i){item.width=syncWidth||widths[i];item.height=syncHeight||heights[i];return item});this.setState({items},()=>{this.props.onMeasure?.({widths:widths,heights:heights});});}onMouseDown(key){const items=_.map(this.state.items,function(item){if(item.key===key){item.state="dragging";}return item});this.setState({items:items});}moveOptionToIndex(option,index){const{items}=this.state;if(index<0||index>items.length){throw new Error(`index ${index} out of bounds`)}const nextItems=_.clone(items);const item=items.filter(item=>{return item.option===option})[0];if(item==null){throw new Error(`option ${option} not found`)}const currentIndex=items.findIndex(i=>{return i.key===item.key});nextItems.splice(currentIndex,1);nextItems.splice(index,0,item);this.setState({items:nextItems});this.props.onChange?.({});}onMouseMove(key){const $draggable=$(ReactDOM__default.findDOMNode(this.refs[key]));const $sortable=$(ReactDOM__default.findDOMNode(this));const items=_.clone(this.state.items);const item=_.findWhere(this.state.items,{key:key});const margin=this.props.margin||0;const currentIndex=_.indexOf(items,item);let newIndex=0;items.splice(currentIndex,1);if(this.props.layout==="horizontal"){const midWidth=$draggable.offset().left-$sortable.offset().left;let sumWidth=0;let cardWidth;_.each(items,function(item){cardWidth=item.width;if(midWidth>sumWidth+cardWidth/2){newIndex+=1;}sumWidth+=cardWidth+margin;});}else {const midHeight=$draggable.offset().top-$sortable.offset().top;let sumHeight=0;let cardHeight;_.each(items,function(item){cardHeight=item.height;if(midHeight>sumHeight+cardHeight/2){newIndex+=1;}sumHeight+=cardHeight+margin;});}if(newIndex!==currentIndex){items.splice(newIndex,0,item);this.setState({items:items});}}onMouseUp(key){const nextAnimationFrame=requestAnimationFrame(()=>{const items=_.map(this.state.items,function(item){if(item.key===key){item.state="animating";const $placeholder=$(ReactDOM__default.findDOMNode(this.refs["placeholder_"+key]));const position=$placeholder.position();const endPosition=addOffsetParentScroll($placeholder,position);item.endPosition=endPosition;}return item},this);this.setState({items:items});this.props.onChange?.({});});this.animationFrameRequest=nextAnimationFrame;}onAnimationEnd(key){const items=_.map(this.state.items,function(item){if(item.key===key){item.state="static";}return item});this.setState({items:items});}getOptions(){return _.pluck(this.state.items,"option")}render(){if(this.props.waitForTexRendererToLoad&&!this.state.texRendererLoaded){const{TeX}=getDependencies();return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(CircularSpinner,{}),jsxRuntimeExports.jsx("div",{style:{display:"none"},children:jsxRuntimeExports.jsx(TeX,{onRender:()=>this.setState({texRendererLoaded:true}),children:"1"})})]})}const cards=[];const{layout}=this.props;const className=css(styles$8.sortable)+" perseus-sortable";const syncWidth=this.props.constraints?.width||layout==="vertical";const syncHeight=this.props.constraints?.height||layout==="horizontal";_.each(this.state.items,function(item,i,items){const isLast=i===items.length-1;const isStatic=item.state==="static"||item.state==="disabled";let margin;if(this.props.layout==="horizontal"){margin="0 "+this.props.margin+"px 0 0";}else if(this.props.layout==="vertical"){margin="0 0 "+this.props.margin+"px 0";}cards.push(jsxRuntimeExports.jsx(Draggable,{content:item.option,state:item.state,ref:item.key,width:syncWidth?item.width:undefined,height:syncHeight?item.height:undefined,layout:layout,includePadding:this.props.padding,margin:isLast&&isStatic?0:margin,endPosition:item.endPosition,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"sortable"),onRender:this.remeasureItems,onMouseDown:this.onMouseDown.bind(this,item.key),onMouseMove:this.onMouseMove.bind(this,item.key),onMouseUp:this.onMouseUp.bind(this,item.key),onTouchMove:this.onMouseMove.bind(this,item.key),onTouchEnd:this.onMouseUp.bind(this,item.key),onTouchCancel:this.onMouseUp.bind(this,item.key),onAnimationEnd:this.onAnimationEnd.bind(this,item.key)},item.key));if(item.state==="dragging"||item.state==="animating"){cards.push(jsxRuntimeExports.jsx(Placeholder,{ref:"placeholder_"+item.key,width:item.width,height:item.height,layout:layout,margin:isLast?0:margin},"placeholder_"+item.key));}},this);return jsxRuntimeExports.jsx("ul",{className:className,children:cards})}constructor(props){super(props),this.remeasureItems=_.debounce(()=>{this.setState({items:Sortable.clearItemMeasurements(this.state.items)});},20);this.state={items:Sortable.itemsFromProps(this.props),texRendererLoaded:false};}}Sortable.defaultProps={layout:"horizontal",padding:true,disabled:false,constraints:{},onMeasure:function(){},margin:5,onChange:function(){},linterContext:PerseusLinter.linterContextDefault,waitForTexRendererToLoad:true};const styles$8=StyleSheet.create({sortable:{boxSizing:"border-box",float:"left",padding:0,margin:0},card:{boxSizing:"border-box",background:"#fff",border:"1px solid #ddd",borderRadius:4,cursor:"pointer",minWidth:25,minHeight:44,padding:10,listStyleType:"none",userSelect:"none",touchAction:"none"},placeholder:{background:"#ddd",border:"1px solid #ccc"},draggable:{textAlign:"center"},horizontalCard:{float:"left",cursor:"ew-resize"},verticalCard:{maxWidth:"100%",cursor:"ns-resize"},unpaddedCard:{padding:0},dragging:{background:"#ffedcd",opacity:.8},disabled:{backgroundColor:"inherit",border:"1px solid transparent",cursor:"default"}});
2018
+ const addOffsetParentScroll=($el,position)=>{const $offsetParent=$el.offsetParent();return {top:position.top+$offsetParent.scrollTop(),left:position.left+$offsetParent.scrollLeft()}};class Placeholder extends React.Component{render(){const{layout}=this.props;const className=css(styles$8.card,styles$8.placeholder,layout==="horizontal"&&styles$8.horizontalCard);const style={width:this.props.width,height:this.props.height};if(this.props.margin!=null){style.margin=this.props.margin;}return jsxRuntimeExports.jsx("li",{className:className,style:style})}}class Draggable extends React.Component{componentDidMount(){this._mounted=true;this.isMouseMoveUpBound=false;document.addEventListener("touchmove",this.onMouseMove,Util.supportsPassiveEvents()?{passive:false}:false);}componentDidUpdate(prevProps){if(this.props.state===prevProps.state){return}if(this.props.state==="animating"&&this.props.endPosition){const current=this.getCurrentPosition();const duration=15*Math.sqrt(Math.sqrt(Math.pow(this.props.endPosition.left-current.left,2)+Math.pow(this.props.endPosition.top-current.top,2)));$(ReactDOM__default.findDOMNode(this)).animate(this.props.endPosition,{duration:Math.max(duration,1),complete:this.props.onAnimationEnd});}else if(this.props.state==="static"){$(ReactDOM__default.findDOMNode(this)).finish();}}componentWillUnmount(){this._mounted=false;if(this.isMouseMoveUpBound){this.unbindMouseMoveUp();}if(this.animationFrameRequest){cancelAnimationFrame(this.animationFrameRequest);}document.removeEventListener("touchmove",this.onMouseMove);}render(){const{includePadding,layout,state:type}=this.props;let className=css(styles$8.card,styles$8.draggable,layout==="horizontal"&&styles$8.horizontalCard,layout==="vertical"&&styles$8.verticalCard,type==="dragging"&&styles$8.dragging,type==="disabled"&&styles$8.disabled,!includePadding&&styles$8.unpaddedCard)+" "+"perseus-sortable-draggable";if(!includePadding){className+=" perseus-sortable-draggable-unpadded";}const style={position:"static"};if(this.props.state==="dragging"||this.props.state==="animating"){_.extend(style,{position:"absolute"},this.getCurrentPosition());}if(this.props.width){style.width=this.props.width+1;}if(this.props.height){style.height=this.props.height;}if(this.props.margin!=null){style.margin=this.props.margin;}return jsxRuntimeExports.jsx("li",{className:className,style:style,onMouseDown:this.onMouseDown,onTouchStart:this.onMouseDown,onTouchMove:this.onMouseMove,onTouchEnd:this.onMouseUp,onTouchCancel:this.onMouseUp,children:jsxRuntimeExports.jsx(Renderer,{content:this.props.content,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"draggable"),onRender:this.props.onRender,strings:this.context.strings})})}constructor(...args){super(...args),this.animationFrameRequest=null,this.state={startPosition:{left:0,top:0},startMouse:{left:0,top:0},mouse:{left:0,top:0},dragging:false},this.getCurrentPosition=()=>{return {left:this.state.startPosition.left+this.state.mouse.left-this.state.startMouse.left,top:this.state.startPosition.top+this.state.mouse.top-this.state.startMouse.top}},this.bindMouseMoveUp=()=>{this.isMouseMoveUpBound=true;$(document).on("mousemove",this.onMouseMove);$(document).on("mouseup",this.onMouseUp);},this.unbindMouseMoveUp=()=>{this.isMouseMoveUpBound=false;$(document).off("mousemove",this.onMouseMove);$(document).off("mouseup",this.onMouseUp);},this.onMouseDown=event=>{if(this.props.state!=="static"){return}if(!(event.button===0||event.touches!=null&&event.touches.length===1)){return}event.preventDefault();const loc=Util.extractPointerLocation(event);this.animationFrameRequest=requestAnimationFrame(()=>{const $el=$(ReactDOM__default.findDOMNode(this));const position=$el.position();const startPosition=addOffsetParentScroll($el,position);if(loc&&this._mounted){this.setState({startPosition,startMouse:loc,mouse:loc,dragging:true},function(){this.bindMouseMoveUp();this.props.onMouseDown();});}});},this.onMouseMove=event=>{const notDragging=this.props.state!=="dragging"||!this.state.dragging;if(notDragging){return}event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.animationFrameRequest=requestAnimationFrame(()=>{this.setState({mouse:loc},this.props.onMouseMove);});}},this.onMouseUp=event=>{const notDragging=this.props.state!=="dragging"||!this.state.dragging;if(notDragging){return}event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.setState({dragging:false});this.unbindMouseMoveUp();this.props.onMouseUp();}};}}Draggable.contextType=PerseusI18nContext;Draggable.defaultProps={includePadding:true,type:"static",linterContext:PerseusLinter.linterContextDefault};class Sortable extends React.Component{static itemsFromProps(props){const state=props.disabled?"disabled":"static";return props.options.map((option,i)=>{return {option:option,key:i,state,width:0,height:0}})}static clearItemMeasurements(items){return items.map(item=>{return {...item,width:0,height:0}})}UNSAFE_componentWillReceiveProps(nextProps){const prevProps=this.props;if(!_.isEqual(nextProps.options,prevProps.options)){this.setState({items:Sortable.itemsFromProps(nextProps)});}else if(nextProps.layout!==prevProps.layout||nextProps.padding!==prevProps.padding||nextProps.disabled!==prevProps.disabled||!_.isEqual(nextProps.constraints,prevProps.constraints)){this.setState({items:Sortable.clearItemMeasurements(this.state.items)});}}componentDidUpdate(){if(this.state.items.length&&!this.state.items[0].width&&!this.state.items[0].height){setTimeout(()=>{this.measureItems();},0);}}measureItems(){let items=[...this.state.items];const $items=_.map(items,function(item){return $(ReactDOM__default.findDOMNode(this.refs[item.key]))},this);const widths=_.invoke($items,"outerWidth");const heights=_.invoke($items,"outerHeight");const{constraints,layout}=this.props;let syncWidth=null;if(constraints?.width){syncWidth=_.max(widths.concat(constraints.width));}else if(layout==="vertical"){syncWidth=_.max(widths);}let syncHeight=null;if(constraints?.height){syncHeight=_.max(heights.concat(constraints.height));}else if(layout==="horizontal"){syncHeight=_.max(heights);}items=_.map(items,function(item,i){item.width=syncWidth||widths[i];item.height=syncHeight||heights[i];return item});this.setState({items},()=>{this.props.onMeasure?.({widths:widths,heights:heights});});}onMouseDown(key){const items=_.map(this.state.items,function(item){if(item.key===key){item.state="dragging";}return item});this.setState({items:items});}moveOptionToIndex(option,index){const{items}=this.state;if(index<0||index>items.length){throw new Error(`index ${index} out of bounds`)}const nextItems=_.clone(items);const item=items.filter(item=>{return item.option===option})[0];if(item==null){throw new Error(`option ${option} not found`)}const currentIndex=items.findIndex(i=>{return i.key===item.key});nextItems.splice(currentIndex,1);nextItems.splice(index,0,item);this.setState({items:nextItems},()=>{this.props.onChange?.({});});}onMouseMove(key){const $draggable=$(ReactDOM__default.findDOMNode(this.refs[key]));const $sortable=$(ReactDOM__default.findDOMNode(this));const items=_.clone(this.state.items);const item=_.findWhere(this.state.items,{key:key});const margin=this.props.margin||0;const currentIndex=_.indexOf(items,item);let newIndex=0;items.splice(currentIndex,1);if(this.props.layout==="horizontal"){const midWidth=$draggable.offset().left-$sortable.offset().left;let sumWidth=0;let cardWidth;_.each(items,function(item){cardWidth=item.width;if(midWidth>sumWidth+cardWidth/2){newIndex+=1;}sumWidth+=cardWidth+margin;});}else {const midHeight=$draggable.offset().top-$sortable.offset().top;let sumHeight=0;let cardHeight;_.each(items,function(item){cardHeight=item.height;if(midHeight>sumHeight+cardHeight/2){newIndex+=1;}sumHeight+=cardHeight+margin;});}if(newIndex!==currentIndex){items.splice(newIndex,0,item);this.setState({items:items});}}onMouseUp(key){const nextAnimationFrame=requestAnimationFrame(()=>{const items=_.map(this.state.items,function(item){if(item.key===key){item.state="animating";const $placeholder=$(ReactDOM__default.findDOMNode(this.refs["placeholder_"+key]));const position=$placeholder.position();const endPosition=addOffsetParentScroll($placeholder,position);item.endPosition=endPosition;}return item},this);this.setState({items:items},()=>{this.props.onChange?.({});});});this.animationFrameRequest=nextAnimationFrame;}onAnimationEnd(key){const items=_.map(this.state.items,function(item){if(item.key===key){item.state="static";}return item});this.setState({items:items});}getOptions(){return _.pluck(this.state.items,"option")}render(){if(this.props.waitForTexRendererToLoad&&!this.state.texRendererLoaded){const{TeX}=getDependencies();return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(CircularSpinner,{}),jsxRuntimeExports.jsx("div",{style:{display:"none"},children:jsxRuntimeExports.jsx(TeX,{onRender:()=>this.setState({texRendererLoaded:true}),children:"1"})})]})}const cards=[];const{layout}=this.props;const className=css(styles$8.sortable)+" perseus-sortable";const syncWidth=this.props.constraints?.width||layout==="vertical";const syncHeight=this.props.constraints?.height||layout==="horizontal";_.each(this.state.items,function(item,i,items){const isLast=i===items.length-1;const isStatic=item.state==="static"||item.state==="disabled";let margin;if(this.props.layout==="horizontal"){margin="0 "+this.props.margin+"px 0 0";}else if(this.props.layout==="vertical"){margin="0 0 "+this.props.margin+"px 0";}cards.push(jsxRuntimeExports.jsx(Draggable,{content:item.option,state:item.state,ref:item.key,width:syncWidth?item.width:undefined,height:syncHeight?item.height:undefined,layout:layout,includePadding:this.props.padding,margin:isLast&&isStatic?0:margin,endPosition:item.endPosition,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"sortable"),onRender:this.remeasureItems,onMouseDown:this.onMouseDown.bind(this,item.key),onMouseMove:this.onMouseMove.bind(this,item.key),onMouseUp:this.onMouseUp.bind(this,item.key),onTouchMove:this.onMouseMove.bind(this,item.key),onTouchEnd:this.onMouseUp.bind(this,item.key),onTouchCancel:this.onMouseUp.bind(this,item.key),onAnimationEnd:this.onAnimationEnd.bind(this,item.key)},item.key));if(item.state==="dragging"||item.state==="animating"){cards.push(jsxRuntimeExports.jsx(Placeholder,{ref:"placeholder_"+item.key,width:item.width,height:item.height,layout:layout,margin:isLast?0:margin},"placeholder_"+item.key));}},this);return jsxRuntimeExports.jsx("ul",{className:className,children:cards})}constructor(props){super(props),this.remeasureItems=_.debounce(()=>{this.setState({items:Sortable.clearItemMeasurements(this.state.items)});},20);this.state={items:Sortable.itemsFromProps(this.props),texRendererLoaded:false};}}Sortable.defaultProps={layout:"horizontal",padding:true,disabled:false,constraints:{},onMeasure:function(){},margin:5,onChange:function(){},linterContext:PerseusLinter.linterContextDefault,waitForTexRendererToLoad:true};const styles$8=StyleSheet.create({sortable:{boxSizing:"border-box",float:"left",padding:0,margin:0},card:{boxSizing:"border-box",background:"#fff",border:"1px solid #ddd",borderRadius:4,cursor:"pointer",minWidth:25,minHeight:44,padding:10,listStyleType:"none",userSelect:"none",touchAction:"none"},placeholder:{background:"#ddd",border:"1px solid #ccc"},draggable:{textAlign:"center"},horizontalCard:{float:"left",cursor:"ew-resize"},verticalCard:{maxWidth:"100%",cursor:"ns-resize"},unpaddedCard:{padding:0},dragging:{background:"#ffedcd",opacity:.8},disabled:{backgroundColor:"inherit",border:"1px solid transparent",cursor:"default"}});
2015
2019
 
2016
- const getPromptJSON$9=(renderProps,userInput)=>{return {type:"matcher",options:{labels:renderProps.labels,left:renderProps.left,right:renderProps.right,orderMatters:renderProps.orderMatters},userInput:{left:userInput.left,right:userInput.right}}};
2020
+ const getPromptJSON$9=(renderProps,userInput)=>{return {type:"matcher",options:{labels:renderProps.labels,left:renderProps.userInput.left,right:renderProps.userInput.right,orderMatters:renderProps.orderMatters},userInput:{left:userInput.left,right:userInput.right}}};
2017
2021
 
2018
- const HACKY_CSS_CLASSNAME="perseus-widget-matcher";class Matcher extends React.Component{getPromptJSON(){return getPromptJSON$9(this.props,this.getUserInput())}render(){if(!this.state.texRendererLoaded){const{TeX}=getDependencies();return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(CircularSpinner,{}),jsxRuntimeExports.jsx("div",{style:{display:"none"},children:jsxRuntimeExports.jsx(TeX,{onRender:()=>{this.setState({texRendererLoaded:true});},children:"1"})})]})}const{left,right}=shuffleMatcher(this.props);const showLabels=_.any(this.props.labels);const constraints={height:_.max([this.state.leftHeight,this.state.rightHeight])};const cellMarginPx=this.props.apiOptions.isMobile?8:5;return jsxRuntimeExports.jsx("table",{className:css(styles$7.widget)+" "+HACKY_CSS_CLASSNAME,children:jsxRuntimeExports.jsxs("tbody",{children:[showLabels&&jsxRuntimeExports.jsxs("tr",{className:css(styles$7.row),children:[jsxRuntimeExports.jsx("th",{className:css(styles$7.column,styles$7.columnLabel),children:jsxRuntimeExports.jsx(Renderer,{content:this.props.labels[0]||"...",linterContext:this.props.linterContext,strings:this.context.strings})}),jsxRuntimeExports.jsx("th",{className:css(styles$7.column,styles$7.columnRight,styles$7.columnLabel),children:jsxRuntimeExports.jsx(Renderer,{content:this.props.labels[1]||"...",linterContext:this.props.linterContext,strings:this.context.strings})})]}),jsxRuntimeExports.jsxs("tr",{className:css(styles$7.row),children:[jsxRuntimeExports.jsx("td",{className:css(styles$7.column),children:jsxRuntimeExports.jsx(Sortable,{options:left,layout:"vertical",padding:this.props.padding,disabled:!this.props.orderMatters,constraints:constraints,onMeasure:this.onMeasureLeft,onChange:this.changeAndTrack,margin:cellMarginPx,linterContext:this.props.linterContext,ref:"left"})}),jsxRuntimeExports.jsx("td",{className:css(styles$7.column,styles$7.columnRight),children:jsxRuntimeExports.jsx(Sortable,{options:right,layout:"vertical",padding:this.props.padding,constraints:constraints,onMeasure:this.onMeasureRight,onChange:this.changeAndTrack,margin:cellMarginPx,linterContext:this.props.linterContext,ref:"right"})})]})]})})}constructor(...args){super(...args),this.state={leftHeight:0,rightHeight:0,texRendererLoaded:false},this.changeAndTrack=e=>{this.props.onChange(e);this.props.trackInteraction();},this.onMeasureLeft=dimensions=>{const height=_.max(dimensions.heights);this.setState({leftHeight:height});},this.onMeasureRight=dimensions=>{const height=_.max(dimensions.heights);this.setState({rightHeight:height});},this.getUserInput=()=>{if(!this.state.texRendererLoaded){return {left:[],right:[]}}return {left:this.refs.left.getOptions(),right:this.refs.right.getOptions()}},this.moveLeftOptionToIndex=(option,index)=>{this.refs.left.moveOptionToIndex(option,index);},this.moveRightOptionToIndex=(option,index)=>{this.refs.right.moveOptionToIndex(option,index);};}}Matcher.contextType=PerseusI18nContext;Matcher.defaultProps={left:[],right:[],labels:["",""],orderMatters:false,padding:true,problemNum:0,onChange:function(){},linterContext:linterContextDefault};const padding=5;const border="1px solid #444";const styles$7=StyleSheet.create({widget:{paddingTop:padding,maxWidth:"100%",minWidth:"auto"},row:{border:0},column:{padding:0,border:0},columnRight:{borderLeft:border},columnLabel:{fontWeight:"inherit",borderBottom:border,padding:`0 ${padding}px ${padding}px ${padding}px`,textAlign:"center"}});var Matcher$1 = {name:"matcher",displayName:"Matcher (two column)",widget:Matcher,isLintable:true};
2022
+ const HACKY_CSS_CLASSNAME="perseus-widget-matcher";class Matcher extends React.Component{getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$9(this.props,this.props.userInput)}render(){if(!this.state.texRendererLoaded){const{TeX}=getDependencies();return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(CircularSpinner,{}),jsxRuntimeExports.jsx("div",{style:{display:"none"},children:jsxRuntimeExports.jsx(TeX,{onRender:()=>{this.setState({texRendererLoaded:true});},children:"1"})})]})}const showLabels=_.any(this.props.labels);const constraints={height:_.max([this.state.leftHeight,this.state.rightHeight])};const cellMarginPx=this.props.apiOptions.isMobile?8:5;return jsxRuntimeExports.jsx("table",{className:css(styles$7.widget)+" "+HACKY_CSS_CLASSNAME,children:jsxRuntimeExports.jsxs("tbody",{children:[showLabels&&jsxRuntimeExports.jsxs("tr",{className:css(styles$7.row),children:[jsxRuntimeExports.jsx("th",{className:css(styles$7.column,styles$7.columnLabel),children:jsxRuntimeExports.jsx(Renderer,{content:this.props.labels[0]||"...",linterContext:this.props.linterContext,strings:this.context.strings})}),jsxRuntimeExports.jsx("th",{className:css(styles$7.column,styles$7.columnRight,styles$7.columnLabel),children:jsxRuntimeExports.jsx(Renderer,{content:this.props.labels[1]||"...",linterContext:this.props.linterContext,strings:this.context.strings})})]}),jsxRuntimeExports.jsxs("tr",{className:css(styles$7.row),children:[jsxRuntimeExports.jsx("td",{className:css(styles$7.column),children:jsxRuntimeExports.jsx(Sortable,{options:this.props.userInput.left,layout:"vertical",padding:this.props.padding,disabled:!this.props.orderMatters,constraints:constraints,onMeasure:this.onMeasureLeft,onChange:this.changeAndTrack,margin:cellMarginPx,linterContext:this.props.linterContext,ref:"left"})}),jsxRuntimeExports.jsx("td",{className:css(styles$7.column,styles$7.columnRight),children:jsxRuntimeExports.jsx(Sortable,{options:this.props.userInput.right,layout:"vertical",padding:this.props.padding,constraints:constraints,onMeasure:this.onMeasureRight,onChange:this.changeAndTrack,margin:cellMarginPx,linterContext:this.props.linterContext,ref:"right"})})]})]})})}constructor(...args){super(...args),this.state={leftHeight:0,rightHeight:0,texRendererLoaded:false},this.changeAndTrack=()=>{const nextUserInput=this._getUserInputFromSortable();this.props.handleUserInput(nextUserInput);this.props.trackInteraction();},this.onMeasureLeft=dimensions=>{const height=_.max(dimensions.heights);this.setState({leftHeight:height});},this.onMeasureRight=dimensions=>{const height=_.max(dimensions.heights);this.setState({rightHeight:height});},this._getUserInputFromSortable=()=>{if(!this.state.texRendererLoaded){return {left:[],right:[]}}return {left:this.refs.left.getOptions(),right:this.refs.right.getOptions()}},this.moveLeftOptionToIndex=(option,index)=>{this.refs.left.moveOptionToIndex(option,index);},this.moveRightOptionToIndex=(option,index)=>{this.refs.right.moveOptionToIndex(option,index);};}}Matcher.contextType=PerseusI18nContext;Matcher.defaultProps={labels:["",""],orderMatters:false,padding:true,problemNum:0,onChange:function(){},linterContext:linterContextDefault,userInput:{left:[],right:[]}};function getStartUserInput$4(options,problemNum){const shuffled=shuffleMatcher(options,problemNum);return shuffled}function getUserInputFromSerializedState$6(serializedState){return {left:serializedState.left,right:serializedState.right}}var Matcher$1 = {name:"matcher",displayName:"Matcher (two column)",widget:Matcher,isLintable:true,getStartUserInput: getStartUserInput$4,getUserInputFromSerializedState: getUserInputFromSerializedState$6};const padding=5;const border="1px solid #444";const styles$7=StyleSheet.create({widget:{paddingTop:padding,maxWidth:"100%",minWidth:"auto"},row:{border:0},column:{padding:0,border:0},columnRight:{borderLeft:border},columnLabel:{fontWeight:"inherit",borderBottom:border,padding:`0 ${padding}px ${padding}px ${padding}px`,textAlign:"center"}});
2019
2023
 
2020
2024
  const getPromptJSON$8=(renderProps,userInput)=>{return {type:"matrix",options:{height:renderProps.matrixBoardSize[0],width:renderProps.matrixBoardSize[1]},userInput:{answerRows:userInput.answers}}};
2021
2025
 
2022
- const{assert: assert$2}=InteractiveUtil;const NORMAL_DIMENSIONS={INPUT_MARGIN:3,INPUT_HEIGHT:30,INPUT_WIDTH:40};const KEYPAD_INPUT_DIMENSIONS={INPUT_MARGIN:4,INPUT_HEIGHT:36,INPUT_WIDTH:64};function getInputPath$1(row,column){return [""+row,""+column]}const getDefaultPath$1=function(){return getInputPath$1(0,0)};const getRowFromPath$1=function(path){assert$2(Array.isArray(path)&&path.length===2);return +path[0]};const getColumnFromPath$1=function(path){assert$2(Array.isArray(path)&&path.length===2);return +path[1]};const getRefForPath$1=function(path){const row=getRowFromPath$1(path);const column=getColumnFromPath$1(path);return "answer"+row+","+column};class Matrix extends React.Component{componentDidMount(){this.cursorPosition=[0,0];}getDOMNodeForPath(path){const inputID=getRefForPath$1(path);return ReactDOM__default.findDOMNode(this.refs[inputID])}getUserInput(){return {answers:this.props.answers}}getPromptJSON(){return getPromptJSON$8(this.props,this.getUserInput())}render(){let dimensions;if(this.props.apiOptions.customKeypad){dimensions=KEYPAD_INPUT_DIMENSIONS;}else {dimensions=NORMAL_DIMENSIONS;}const{INPUT_MARGIN,INPUT_HEIGHT,INPUT_WIDTH}=dimensions;const matrixSize=getMatrixSize(this.props.answers);const maxRows=this.props.matrixBoardSize[0];const maxCols=this.props.matrixBoardSize[1];const cursorRow=this.props.cursorPosition[0];const cursorCol=this.props.cursorPosition[1];const highlightedRow=Math.max(cursorRow,matrixSize[0]-1);const highlightedCol=Math.max(cursorCol,matrixSize[1]-1);const bracketHeight=(highlightedRow+1)*(INPUT_HEIGHT+2*INPUT_MARGIN);const bracketOffset=(highlightedCol+1)*(INPUT_WIDTH+2*INPUT_MARGIN);const className=classNames$1({"perseus-matrix":true,"static-mode":this.props.static,"the-matrix":this.state.enterTheMatrix>=5});return jsxRuntimeExports.jsxs("div",{className:className,children:[this.props.prefix&&jsxRuntimeExports.jsx("div",{className:"matrix-prefix",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.prefix,linterContext:this.props.linterContext,strings:this.context.strings})}),jsxRuntimeExports.jsxs("div",{className:"matrix-input",children:[jsxRuntimeExports.jsx("div",{className:"matrix-bracket bracket-left",style:{height:bracketHeight}}),jsxRuntimeExports.jsx("div",{className:"matrix-bracket bracket-right",style:{height:bracketHeight,left:bracketOffset}}),_(maxRows).times(row=>{const rowVals=this.props.answers[row];return jsxRuntimeExports.jsx("div",{className:"matrix-row",children:_(maxCols).times(col=>{const outside=row>highlightedRow||col>highlightedCol;const inputProps={className:outside?"outside":"inside",ref:getRefForPath$1(getInputPath$1(row,col)),value:rowVals?rowVals[col]:null,style:{height:INPUT_HEIGHT,width:INPUT_WIDTH,margin:INPUT_MARGIN},disabled:this.props.apiOptions.readOnly,onFocus:()=>{this.cursorPosition=[row,col];this.props.onChange({cursorPosition:[row,col]},()=>{return false});this._handleFocus(row,col);},onBlur:()=>{if(row===this.cursorPosition[0]&&col===this.cursorPosition[1]){this.props.onChange({cursorPosition:[0,0]},()=>{return false});}this._handleBlur(row,col);},onKeyDown:e=>{this.handleKeyDown(row,col,e);},onChange:(value,cb)=>{this.onValueChange(row,col,value,cb);}};let MatrixInput;if(this.props.apiOptions.customKeypad){const style={margin:INPUT_MARGIN,minWidth:INPUT_WIDTH,minHeight:INPUT_HEIGHT,boxSizing:"border-box",backgroundColor:outside?"#f3f3f3":"#fff"};MatrixInput=jsxRuntimeExports.jsx(SimpleKeypadInput,{...inputProps,style:style,scrollable:true,keypadElement:this.props.keypadElement});}else if(this.props.numericInput){MatrixInput=jsxRuntimeExports.jsx(NumberInput,{...inputProps});}else {const updatedProps={...inputProps,style:StyleSheet.create({input:{...inputProps.style,display:"inline-block",padding:0,backgroundColor:outside?"#f3f3f3":"#fff"}}).input};MatrixInput=jsxRuntimeExports.jsx(TextInput,{...updatedProps});}return jsxRuntimeExports.jsx("span",{className:"matrix-input-field",children:MatrixInput},col)})},row)})]}),this.props.suffix&&jsxRuntimeExports.jsx("div",{className:"matrix-suffix",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.suffix,linterContext:this.props.linterContext,strings:this.context.strings})})]})}constructor(...args){super(...args),this.state={enterTheMatrix:0},this.getInputPaths=()=>{const inputPaths=[];const maxRows=this.props.matrixBoardSize[0];const maxCols=this.props.matrixBoardSize[1];for(let row=0;row<maxRows;row++){for(let col=0;col<maxCols;col++){const inputPath=getInputPath$1(row,col);inputPaths.push(inputPath);}}return inputPaths},this._handleFocus=(row,col)=>{this.props.onFocus(getInputPath$1(row,col));},this._handleBlur=(row,col)=>{this.props.onBlur(getInputPath$1(row,col));},this.focus=()=>{this.focusInputPath(getDefaultPath$1());return true},this.focusInputPath=path=>{const inputID=getRefForPath$1(path);this.refs[inputID].focus();},this.blurInputPath=path=>{if(path.length===0){path=getDefaultPath$1();}const inputID=getRefForPath$1(path);this.refs[inputID].blur();},this.setInputValue=(inputPath,value,callback)=>{const row=getRowFromPath$1(inputPath);const col=getColumnFromPath$1(inputPath);this.onValueChange(row,col,value,callback);},this.handleKeyDown=(row,col,e)=>{const maxRow=this.props.matrixBoardSize[0];const maxCol=this.props.matrixBoardSize[1];let enterTheMatrix=null;const curInput=this.refs[getRefForPath$1(getInputPath$1(row,col))];const curValueString=curInput.getStringValue();const cursorStartPosition=curInput.getSelectionStart();const cursorEndPosition=curInput.getSelectionEnd();let nextPath=null;if(e.key==="ArrowUp"&&row>0){nextPath=getInputPath$1(row-1,col);}else if(e.key==="ArrowDown"&&row+1<maxRow){nextPath=getInputPath$1(row+1,col);}else if(e.key==="ArrowLeft"&&col>0){if(cursorStartPosition===0&&cursorEndPosition===0){nextPath=getInputPath$1(row,col-1);}}else if(e.key==="ArrowRight"&&col+1<maxCol){if(cursorStartPosition===curValueString.length){nextPath=getInputPath$1(row,col+1);}}else if(e.key==="Enter"){enterTheMatrix=this.state.enterTheMatrix+1;}else if(e.key==="Escape"){enterTheMatrix=0;}if(nextPath){e.preventDefault();const input=this.refs[getRefForPath$1(nextPath)];const inputValString=input.getStringValue();const valueLength=inputValString.length*2;input.focus();if(e.key==="ArrowRight"){input.setSelectionRange(0,0);}else {input.setSelectionRange(valueLength,valueLength);}}if(enterTheMatrix!=null){this.setState({enterTheMatrix:enterTheMatrix});}},this.onValueChange=(row,column,value,cb)=>{const answers=this.props.answers.map(answer=>[...answer]);if(!answers[row]){answers[row]=[];}answers[row][column]=value;this.props.onChange({answers},cb);this.props.trackInteraction();};}}Matrix.contextType=PerseusI18nContext;Matrix.defaultProps={matrixBoardSize:[3,3],answers:[[]],prefix:"",suffix:"",cursorPosition:[0,0],apiOptions:ApiOptions.defaults,linterContext:linterContextDefault};function transform(widgetOptions){const rows=widgetOptions.matrixBoardSize[0];const columns=widgetOptions.matrixBoardSize[1];const blankInput=stringArrayOfSize2D({rows,columns});const{matrixBoardSize,prefix,suffix}=widgetOptions;return {matrixBoardSize,prefix,suffix,emptyMatrix:blankInput}}const staticTransform$2=editorProps=>{const widgetProps=_.pick(editorProps,"matrixBoardSize","prefix","suffix");widgetProps.answers=_.map(editorProps.answers,row=>{return _.map(row,cell=>cell!=null?String(cell):"")});return widgetProps};var Matrix$1 = {name:"matrix",displayName:"Matrix",hidden:true,widget:Matrix,transform,staticTransform:staticTransform$2,isLintable:true};
2026
+ const{assert: assert$2}=InteractiveUtil;const NORMAL_DIMENSIONS={INPUT_MARGIN:3,INPUT_HEIGHT:30,INPUT_WIDTH:40};const KEYPAD_INPUT_DIMENSIONS={INPUT_MARGIN:4,INPUT_HEIGHT:36,INPUT_WIDTH:64};function getInputPath$1(row,column){return [""+row,""+column]}const getDefaultPath$1=function(){return getInputPath$1(0,0)};const getRowFromPath$1=function(path){assert$2(Array.isArray(path)&&path.length===2);return +path[0]};const getColumnFromPath$1=function(path){assert$2(Array.isArray(path)&&path.length===2);return +path[1]};const getRefForPath$1=function(path){const row=getRowFromPath$1(path);const column=getColumnFromPath$1(path);return "answer"+row+","+column};class Matrix extends React.Component{componentDidMount(){this.cursorPosition=[0,0];}getDOMNodeForPath(path){const inputID=getRefForPath$1(path);return ReactDOM__default.findDOMNode(this.refs[inputID])}getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$8(this.props,this.getUserInput())}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,answers:userInput.answers}}render(){let dimensions;if(this.props.apiOptions.customKeypad){dimensions=KEYPAD_INPUT_DIMENSIONS;}else {dimensions=NORMAL_DIMENSIONS;}const{INPUT_MARGIN,INPUT_HEIGHT,INPUT_WIDTH}=dimensions;const matrixSize=getMatrixSize(this.props.userInput.answers);const maxRows=this.props.matrixBoardSize[0];const maxCols=this.props.matrixBoardSize[1];const cursorRow=this.props.cursorPosition[0];const cursorCol=this.props.cursorPosition[1];const highlightedRow=Math.max(cursorRow,matrixSize[0]-1);const highlightedCol=Math.max(cursorCol,matrixSize[1]-1);const bracketHeight=(highlightedRow+1)*(INPUT_HEIGHT+2*INPUT_MARGIN);const bracketOffset=(highlightedCol+1)*(INPUT_WIDTH+2*INPUT_MARGIN);const className=classNames$1({"perseus-matrix":true,"static-mode":this.props.static,"the-matrix":this.state.enterTheMatrix>=5});return jsxRuntimeExports.jsxs("div",{className:className,children:[this.props.prefix&&jsxRuntimeExports.jsx("div",{className:"matrix-prefix",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.prefix,linterContext:this.props.linterContext,strings:this.context.strings})}),jsxRuntimeExports.jsxs("div",{className:"matrix-input",children:[jsxRuntimeExports.jsx("div",{className:"matrix-bracket bracket-left",style:{height:bracketHeight}}),jsxRuntimeExports.jsx("div",{className:"matrix-bracket bracket-right",style:{height:bracketHeight,left:bracketOffset}}),_(maxRows).times(row=>{const rowVals=this.props.userInput.answers[row];return jsxRuntimeExports.jsx("div",{className:"matrix-row",children:_(maxCols).times(col=>{const outside=row>highlightedRow||col>highlightedCol;const inputProps={className:outside?"outside":"inside",ref:getRefForPath$1(getInputPath$1(row,col)),value:rowVals?rowVals[col]:null,style:{height:INPUT_HEIGHT,width:INPUT_WIDTH,margin:INPUT_MARGIN},disabled:this.props.apiOptions.readOnly,onFocus:()=>{this.cursorPosition=[row,col];this.props.onChange({cursorPosition:[row,col]},()=>{return false});this._handleFocus(row,col);},onBlur:()=>{if(row===this.cursorPosition[0]&&col===this.cursorPosition[1]){this.props.onChange({cursorPosition:[0,0]},()=>{return false});}this._handleBlur(row,col);},onKeyDown:e=>{this.handleKeyDown(row,col,e);},onChange:(value,cb)=>{this.onValueChange(row,col,value,cb);}};let MatrixInput;if(this.props.apiOptions.customKeypad){const style={margin:INPUT_MARGIN,minWidth:INPUT_WIDTH,minHeight:INPUT_HEIGHT,boxSizing:"border-box",backgroundColor:outside?"#f3f3f3":"#fff"};MatrixInput=jsxRuntimeExports.jsx(SimpleKeypadInput,{...inputProps,style:style,scrollable:true,keypadElement:this.props.keypadElement});}else {const updatedProps={...inputProps,style:StyleSheet.create({input:{...inputProps.style,display:"inline-block",padding:0,backgroundColor:outside?"#f3f3f3":"#fff"}}).input};MatrixInput=jsxRuntimeExports.jsx(TextInput,{...updatedProps});}return jsxRuntimeExports.jsx("span",{className:"matrix-input-field",children:MatrixInput},col)})},row)})]}),this.props.suffix&&jsxRuntimeExports.jsx("div",{className:"matrix-suffix",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.suffix,linterContext:this.props.linterContext,strings:this.context.strings})})]})}constructor(...args){super(...args),this.state={enterTheMatrix:0},this.getInputPaths=()=>{const inputPaths=[];const maxRows=this.props.matrixBoardSize[0];const maxCols=this.props.matrixBoardSize[1];for(let row=0;row<maxRows;row++){for(let col=0;col<maxCols;col++){const inputPath=getInputPath$1(row,col);inputPaths.push(inputPath);}}return inputPaths},this._handleFocus=(row,col)=>{this.props.onFocus(getInputPath$1(row,col));},this._handleBlur=(row,col)=>{this.props.onBlur(getInputPath$1(row,col));},this.focus=()=>{this.focusInputPath(getDefaultPath$1());return true},this.focusInputPath=path=>{const inputID=getRefForPath$1(path);this.refs[inputID].focus();},this.blurInputPath=path=>{if(path.length===0){path=getDefaultPath$1();}const inputID=getRefForPath$1(path);this.refs[inputID].blur();},this.setInputValue=(inputPath,value,callback)=>{const row=getRowFromPath$1(inputPath);const col=getColumnFromPath$1(inputPath);this.onValueChange(row,col,value,callback);},this.handleKeyDown=(row,col,e)=>{const maxRow=this.props.matrixBoardSize[0];const maxCol=this.props.matrixBoardSize[1];let enterTheMatrix=null;const curInput=this.refs[getRefForPath$1(getInputPath$1(row,col))];const curValueString=curInput.getStringValue();const cursorStartPosition=curInput.getSelectionStart();const cursorEndPosition=curInput.getSelectionEnd();let nextPath=null;if(e.key==="ArrowUp"&&row>0){nextPath=getInputPath$1(row-1,col);}else if(e.key==="ArrowDown"&&row+1<maxRow){nextPath=getInputPath$1(row+1,col);}else if(e.key==="ArrowLeft"&&col>0){if(cursorStartPosition===0&&cursorEndPosition===0){nextPath=getInputPath$1(row,col-1);}}else if(e.key==="ArrowRight"&&col+1<maxCol){if(cursorStartPosition===curValueString.length){nextPath=getInputPath$1(row,col+1);}}else if(e.key==="Enter"){enterTheMatrix=this.state.enterTheMatrix+1;}else if(e.key==="Escape"){enterTheMatrix=0;}if(nextPath){e.preventDefault();const input=this.refs[getRefForPath$1(nextPath)];const inputValString=input.getStringValue();const valueLength=inputValString.length*2;input.focus();if(e.key==="ArrowRight"){input.setSelectionRange(0,0);}else {input.setSelectionRange(valueLength,valueLength);}}if(enterTheMatrix!=null){this.setState({enterTheMatrix:enterTheMatrix});}},this.onValueChange=(row,column,value,cb)=>{const answers=this.props.userInput.answers.map(answer=>[...answer]);if(!answers[row]){answers[row]=[];}answers[row][column]=value;this.props.handleUserInput({answers},cb);this.props.trackInteraction();};}}Matrix.contextType=PerseusI18nContext;Matrix.defaultProps={matrixBoardSize:[3,3],prefix:"",suffix:"",cursorPosition:[0,0],apiOptions:ApiOptions.defaults,linterContext:linterContextDefault,userInput:{answers:[[]]}};function transform$1(widgetOptions){const{matrixBoardSize,prefix,suffix}=widgetOptions;return {matrixBoardSize,prefix,suffix}}function getCorrectUserInput$1(options){return {answers:options.answers}}function getUserInputFromSerializedState$5(serializedState){return {answers:serializedState.answers}}var Matrix$1 = {name:"matrix",displayName:"Matrix",hidden:true,widget:Matrix,transform: transform$1,staticTransform:transform$1,isLintable:true,getCorrectUserInput: getCorrectUserInput$1,getUserInputFromSerializedState: getUserInputFromSerializedState$5};
2023
2027
 
2024
2028
  const getPromptJSON$7=()=>{return getUnsupportedPromptJSON("measurer")};
2025
2029
 
@@ -2033,15 +2037,15 @@ const smilesRe=new RegExp("^[A-Za-z\\[\\]()=#+-]*$");const atomRe=new RegExp("^(
2033
2037
 
2034
2038
  const{layout}=MoleculeLayout;const parse=SmilesParser.parse;const ParseError=SmilesParser.ParseError;const borderSize=30;class Molecule extends React.Component{UNSAFE_componentWillMount(){this.stateFromSmiles(this.props.smiles);}componentDidMount(){this.canvasRender();}UNSAFE_componentWillReceiveProps(nextProps){this.stateFromSmiles(nextProps.smiles);}componentDidUpdate(){this.canvasRender();}render(){let content=jsxRuntimeExports.jsx("canvas",{className:"molecule-canvas",id:this.props.widgetId+"-molecule",ref:"canvas",children:this.context.strings.molecularDrawing({content:this.props.smiles||""})});if(this.state.error){content=jsxRuntimeExports.jsx("div",{className:"error",children:this.state.error});}return jsxRuntimeExports.jsx("div",{className:"molecule-canvas",children:content})}constructor(...args){super(...args),this.state={parsedSmiles:null,error:null},this.stateFromSmiles=smiles=>{try{this.setState({parsedSmiles:parse(smiles),error:null});}catch(e){if(e instanceof ParseError){this.setState({error:e.message});}else {throw e}}},this.setCanvasBounds=(canvas,items)=>{const xmax=Math.max(...items.map(item=>item.pos?item.pos[0]:-Infinity));const ymax=Math.max(...items.map(item=>item.pos?item.pos[1]:-Infinity));const xmin=Math.min(...items.map(item=>item.pos?item.pos[0]:Infinity));const ymin=Math.min(...items.map(item=>item.pos?item.pos[1]:Infinity));const width=xmax-xmin+2*borderSize;const height=ymax-ymin+2*borderSize;canvas.width=width;canvas.height=height;return [borderSize-xmin,borderSize-ymin]},this.canvasRender=()=>{if(!!this.state.error||!this.state.parsedSmiles){return}const items=layout(this.state.parsedSmiles,this.props.rotationAngle);const canvas=this.refs.canvas;const translation=this.setCanvasBounds(canvas,items);const ctx=canvas.getContext("2d");ctx.clearRect(0,0,canvas.width,canvas.height);ctx.save();ctx.translate(translation[0],translation[1]);draw(ctx,items);ctx.restore();};}}Molecule.contextType=PerseusI18nContext;class MoleculeWidget extends React.Component{render(){return jsxRuntimeExports.jsx(Molecule,{widgetId:this.props.widgetId,smiles:this.props.smiles,rotationAngle:this.props.rotationAngle})}constructor(...args){super(...args),this.isWidget=true;}}MoleculeWidget.defaultProps={rotationAngle:0};var Molecule$1 = {name:"molecule-renderer",displayName:"Molecule renderer",hidden:true,widget:MoleculeWidget};
2035
2039
 
2036
- const getPromptJSON$6=(renderProps,userInput)=>{return {type:"number-line",options:{range:renderProps.range,numDivisions:renderProps.numDivisions,snapDivisions:renderProps.snapDivisions},userInput:{numLinePosition:userInput.numLinePosition,numDivisions:userInput.numDivisions}}};
2040
+ const getPromptJSON$6=(renderProps,userInput)=>{return {type:"number-line",options:{range:renderProps.range,snapDivisions:renderProps.snapDivisions},userInput:{numLinePosition:userInput.numLinePosition,numDivisions:userInput.numDivisions,rel:userInput.rel}}};
2037
2041
 
2038
- const MovablePoint=Graphie.MovablePoint;const Line=Graphie.Line;const{assert: assert$1}=InteractiveUtil;const bound=(x,gt,lt)=>Math.min(Math.max(x,gt),lt);const EN_DASH="–";const horizontalPadding=30;const reverseRel={eq:"eq",ge:"le",gt:"lt",le:"ge",lt:"gt"};const toggleStrictRel={eq:"eq",ge:"gt",gt:"ge",le:"lt",lt:"le"};function formatImproper(n,d){if(d===1){return ""+n}return `\\dfrac{${n}}{${d}}`}function formatMixed(n,d){if(n<0){return "-"+formatMixed(-n,d)}const w=Math.floor(n/d);if(w===0){return formatImproper(n,d)}if(n-w*d===0){return ""+w}return w+formatImproper(n-w*d,d)}function formatNonReduced(n,d,base){const factor=Math.floor(base/d);return formatImproper(n*factor,base)}const _label=(graphie,labelStyle,pos,value,base)=>{value=value||pos;if(labelStyle==="decimal"||labelStyle==="decimal ticks"){return graphie.label([pos,-0.53],Math.round(value*100)/100,"center")}if(labelStyle==="improper"){const frac=KhanMath.toFraction(value);return graphie.label([pos,-0.17],formatImproper(frac[0],frac[1]),"below")}if(labelStyle==="mixed"){const frac=KhanMath.toFraction(value);return graphie.label([pos,-0.17],formatMixed(frac[0],frac[1]),"below")}if(labelStyle==="non-reduced"){const frac=KhanMath.toFraction(value);return graphie.label([pos,-0.17],formatNonReduced(frac[0],frac[1],base),"below")}};const TickMarks=Graphie.createSimpleClass((graphie,props)=>{if(!_.isFinite(props.tickStep)||props.tickStep<=0){return []}const results=[];const{range,labelRange,labelStyle,labelTicks,tickStep,numDivisions}=props;const leftLabel=labelRange[0]==null?range[0]:labelRange[0];const rightLabel=labelRange[1]==null?range[1]:labelRange[1];let base;if(labelStyle==="non-reduced"){const fractions=[leftLabel,rightLabel];for(let i=0;i<=numDivisions;i++){const x=range[0]+i*tickStep;fractions.push(x);}const getDenom=x=>number.toFraction(x)[1];const denoms=_.map(fractions,getDenom);base=_.reduce(denoms,(x,y)=>KhanMath.getLCM(x,y));}else {base=undefined;}const highlightedLineStyle={stroke:KhanColors.BLUE,strokeWidth:3.5};const highlightedTextStyle={color:KhanColors.BLUE};const initialTicks=[...Array(Math.round(numDivisions)).keys()].map(index=>range[0]+index*tickStep);const byNumericAscending=(a,b)=>a-b;const allTicks=[...new Set([...initialTicks,leftLabel,rightLabel,...range])].sort(byNumericAscending);allTicks.forEach(tick=>{const tickIsHighlighted=tick===leftLabel||tick===rightLabel;const lineStyle=tickIsHighlighted?highlightedLineStyle:null;const textStyle=tickIsHighlighted?highlightedTextStyle:null;graphie.style(lineStyle,()=>{results.push(graphie.line([tick,-0.2],[tick,.2]));});if(labelTicks||tickIsHighlighted||labelStyle==="decimal ticks"){graphie.style(textStyle,()=>{results.push(_label(graphie,labelStyle,tick,tick,base));});}});return results});class NumberLine extends React.Component{focus(){if(this.props.isTickCtrl){this.refs["tick-ctrl"].focus();return true}return false}getDOMNodeForPath(inputPath){if(inputPath?.length===1){return ReactDOM__default.findDOMNode(this.refs[inputPath[0]])}return null}getUserInput(){return {numLinePosition:this.props.numLinePosition,rel:this.props.isInequality?this.props.rel:"eq",numDivisions:this.props.numDivisions,divisionRange:this.props.divisionRange}}getPromptJSON(){return getPromptJSON$6(this.props,this.getUserInput())}render(){const{strings}=this.context;const divisionRange=this.props.divisionRange;const divRangeString=divisionRange[0]+EN_DASH+divisionRange[1];const invalidNumDivisions=this.props.numDivisions<divisionRange[0]||this.props.numDivisions>divisionRange[1];const inequalityControls=jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx("input",{type:"button",className:"simple-button",value:strings.switchDirection,onClick:this.handleReverse}),jsxRuntimeExports.jsx("input",{type:"button",className:"simple-button",value:_(["le","ge"]).contains(this.props.rel)?strings.circleOpen:strings.circleFilled,onClick:this.handleToggleStrict})]});let tickCtrl;if(this.props.isTickCtrl){let Input;if(this.props.apiOptions.customKeypad){Input=SimpleKeypadInput;}else {Input=NumberInput;}tickCtrl=jsxRuntimeExports.jsxs("label",{children:[strings.numDivisions," ",jsxRuntimeExports.jsx(Input,{ref:"tick-ctrl",value:this.state.numDivisionsEmpty?null:this.props.numDivisions||divisionRange[0],checkValidity:val=>val>=divisionRange[0]&&val<=divisionRange[1],onChange:this.onNumDivisionsChange,onFocus:this._handleTickCtrlFocus,onBlur:this._handleTickCtrlBlur,useArrowKeys:true,keypadElement:this.props.keypadElement})]});}return jsxRuntimeExports.jsxs("div",{className:"perseus-widget "+"perseus-widget-interactive-number-line",children:[tickCtrl,!this.isValid()?jsxRuntimeExports.jsx("div",{className:"perseus-error",children:"Invalid number line configuration."}):this.props.isTickCtrl&&invalidNumDivisions?jsxRuntimeExports.jsx("div",{className:"perseus-error",children:strings.divisions({divRangeString:divRangeString})}):this._renderGraphie(),!this.props.static&&this.props.isInequality&&inequalityControls]})}constructor(...args){super(...args),this.state={numDivisionsEmpty:false},this.change=(...args)=>{return change.apply(this,args)},this.isValid=()=>{const range=this.props.range;let initialX=this.props.numLinePosition;const divisionRange=this.props.divisionRange;initialX=initialX==null?range[0]:initialX;return range[0]<range[1]&&number.sign(initialX-range[0])>=0&&number.sign(initialX-range[1])<=0&&divisionRange[0]<divisionRange[1]&&0<this.props.numDivisions&&0<this.props.snapDivisions},this.onNumDivisionsChange=(numDivisions,cb)=>{const divRange=this.props.divisionRange.slice();const width=this.props.range[1]-this.props.range[0];numDivisions=Math.round(numDivisions);numDivisions=numDivisions<0?numDivisions*-1:numDivisions;if(numDivisions){const nextProps=_.extend({},this.props,{tickStep:width/numDivisions});const newNumLinePosition=this.snapNumLinePosition(nextProps,this.props.numLinePosition);this.setState({numDivisionsEmpty:false},()=>{this.props.onChange({divisionRange:divRange,numDivisions:numDivisions,numLinePosition:newNumLinePosition},cb);});}else {this.setState({numDivisionsEmpty:true},cb);}},this._handleTickCtrlFocus=()=>{this.props.onFocus(["tick-ctrl"]);},this._handleTickCtrlBlur=()=>{this.props.onBlur(["tick-ctrl"]);},this.focusInputPath=path=>{if(path.length===1){this.refs[path[0]].focus();}},this.blurInputPath=path=>{if(path.length===1){this.refs[path[0]].blur();}},this.getInputPaths=()=>{if(this.props.isTickCtrl){return [["tick-ctrl"]]}return []},this.setInputValue=(inputPath,value,callback)=>{if(inputPath.length===1&&inputPath[0]==="tick-ctrl"){this.onNumDivisionsChange(value,callback);}},this._renderGraphie=()=>{const range=this.props.range;const width=range[1]-range[0];const options=_.pick(this.props,["range","isTickCtrl"]);const props=_.extend({},this.props,{tickStep:width/this.props.numDivisions});return jsxRuntimeExports.jsxs(Graphie,{ref:"graphie",box:[this.props.apiOptions.isMobile?288:460,80],options:options,onMouseDown:coord=>{this.refs.graphie.movables.numberLinePoint.grab(coord);},setup:this._setupGraphie,setDrawingAreaAvailable:this.props.apiOptions.setDrawingAreaAvailable,isMobile:this.props.apiOptions.isMobile,children:[jsxRuntimeExports.jsx(TickMarks,{..._.pick(props,["range","numDivisions","labelTicks","labelStyle","labelRange","tickStep"]),isMobile:this.props.apiOptions.isMobile}),this._renderInequality(props),this._renderNumberLinePoint(props)]},this.props.labelStyle)},this.snapNumLinePosition=(props,numLinePosition)=>{const left=props.range[0];const right=props.range[1];const snapX=props.tickStep/props.snapDivisions;let x=bound(numLinePosition,left,right);x=left+number.roundTo(x-left,snapX);assert$1(_.isFinite(x));return x},this.movePosition=targetPosition=>{this.change({numLinePosition:targetPosition});this.props.trackInteraction();},this._renderNumberLinePoint=props=>{const isOpen=_(["lt","gt"]).contains(props.rel);let fill;if(isOpen){fill=KhanColors._BACKGROUND;}else if(props.static){fill=KhanColors.BLUE;}else {fill=KhanColors.GREEN;}const normalStyle={fill:fill,stroke:props.static?KhanColors.BLUE:KhanColors.GREEN,"stroke-width":isOpen?3:1};const highlightStyle={fill:isOpen?KhanColors._BACKGROUND:KhanColors.GREEN,"stroke-width":isOpen?3:1};const mobileDotStyle=props.isInequality?{stroke:KhanColors.GREEN,"fill-opacity":isOpen?0:1}:{};return jsxRuntimeExports.jsx(MovablePoint,{ref:"numberLinePoint",pointSize:6,coord:[props.numLinePosition,0],constraints:[(coord,prevCoord)=>{return [coord[0],prevCoord[1]]},(coord,prevCoord)=>{const x=this.snapNumLinePosition(props,coord[0]);return [x,coord[1]]}],normalStyle:normalStyle,highlightStyle:highlightStyle,onMove:coord=>{this.movePosition(coord[0]);},isMobile:this.props.apiOptions.isMobile,mobileStyleOverride:mobileDotStyle,showTooltips:this.props.showTooltips,xOnlyTooltip:true})},this.handleReverse=()=>{const newRel=reverseRel[this.props.rel];this.props.onChange({rel:newRel});},this.handleToggleStrict=()=>{const newRel=toggleStrictRel[this.props.rel];this.props.onChange({rel:newRel});},this._getInequalityEndpoint=props=>{const isGreater=_(["ge","gt"]).contains(props.rel);const widthInPixels=400;const range=props.range;const scale=(range[1]-range[0])/widthInPixels;const buffer=horizontalPadding*scale;const left=range[0]-buffer;const right=range[1]+buffer;const end=isGreater?[right,0]:[left,0];return end},this._renderInequality=props=>{if(props.isInequality){const end=this._getInequalityEndpoint(props);const style={arrows:"->",stroke:this.props.apiOptions.isMobile?KhanColors.GREEN:KhanColors.BLUE,strokeWidth:3.5};const isGreater=["ge","gt"].includes(props.rel);return jsxRuntimeExports.jsx(Line,{start:[(isGreater?.4:-0.4)+props.numLinePosition,0],end:end,style:style})}return null},this._setupGraphie=(graphie,options)=>{if(!this.isValid()){return}const widthInPixels=this.props.apiOptions.isMobile?288-horizontalPadding*2:400;const range=options.range;const scale=(range[1]-range[0])/widthInPixels;const buffer=horizontalPadding*scale;const left=range[0]-buffer;const right=range[1]+buffer;const hasFractionalLabels=this.props.labelStyle==="improper"||this.props.labelStyle==="mixed"||this.props.labelStyle==="non-reduced";const bottom=hasFractionalLabels?-1.5:-1;const top=1;graphie.init({range:[[left,right],[bottom,top]],scale:[1/scale,40],isMobile:this.props.apiOptions.isMobile});const center=(range[0]+range[1])/2;graphie.line([center,0],[right,0],{arrows:"->"});graphie.line([center,0],[left,0],{arrows:"->"});};}}NumberLine.contextType=PerseusI18nContext;NumberLine.defaultProps={range:[0,10],labelStyle:"decimal",labelRange:[null,null],divisionRange:[1,12],labelTicks:true,isTickCtrl:false,isInequality:false,numLinePosition:0,snapDivisions:2,showTooltips:false,rel:"ge",apiOptions:ApiOptions.defaults};function numberLineTransform(editorProps){const props=_.pick(editorProps,["range","labelRange","labelStyle","labelTicks","divisionRange","snapDivisions","isTickCtrl","isInequality","showTooltips"]);const numLinePosition=editorProps.initialX!=null?editorProps.initialX:editorProps.range[0];const width=editorProps.range[1]-editorProps.range[0];let numDivisions;if(editorProps.numDivisions!=null){numDivisions=editorProps.numDivisions;}else if(editorProps.tickStep!=null){numDivisions=width/editorProps.tickStep;}else {numDivisions=undefined;}return {...props,isTickCtrl:editorProps.isTickCtrl??undefined,numLinePosition:numLinePosition,numDivisions:numDivisions,snapDivisions:props.snapDivisions||undefined}}function staticTransform$1(editorProps){const props=_.pick(editorProps,["range","labelRange","labelStyle","labelTicks","divisionRange","snapDivisions","isInequality"]);const numLinePosition=editorProps.correctX!=null?editorProps.correctX:editorProps.range[0];const width=editorProps.range[1]-editorProps.range[0];let numDivisions;if(editorProps.numDivisions!=null){numDivisions=editorProps.numDivisions;}else if(editorProps.tickStep!=null){numDivisions=width/editorProps.tickStep;}else {numDivisions=undefined;}return {...props,numLinePosition:numLinePosition,numDivisions:numDivisions,rel:editorProps.isInequality?editorProps.correctRel:undefined,snapDivisions:props.snapDivisions||undefined}}var NumberLine$1 = {name:"number-line",displayName:"Number line",widget:NumberLine,transform:numberLineTransform,staticTransform:staticTransform$1};
2042
+ const MovablePoint=Graphie.MovablePoint;const Line=Graphie.Line;const{assert: assert$1}=InteractiveUtil;const bound=(x,gt,lt)=>Math.min(Math.max(x,gt),lt);const EN_DASH="–";const horizontalPadding=30;const reverseRel={eq:"eq",ge:"le",gt:"lt",le:"ge",lt:"gt"};const toggleStrictRel={eq:"eq",ge:"gt",gt:"ge",le:"lt",lt:"le"};function formatImproper(n,d){if(d===1){return ""+n}return `\\dfrac{${n}}{${d}}`}function formatMixed(n,d){if(n<0){return "-"+formatMixed(-n,d)}const w=Math.floor(n/d);if(w===0){return formatImproper(n,d)}if(n-w*d===0){return ""+w}return w+formatImproper(n-w*d,d)}function formatNonReduced(n,d,base){const factor=Math.floor(base/d);return formatImproper(n*factor,base)}const _label=(graphie,labelStyle,pos,value,base)=>{value=value||pos;if(labelStyle==="decimal"||labelStyle==="decimal ticks"){return graphie.label([pos,-0.53],Math.round(value*100)/100,"center")}if(labelStyle==="improper"){const frac=KhanMath.toFraction(value);return graphie.label([pos,-0.17],formatImproper(frac[0],frac[1]),"below")}if(labelStyle==="mixed"){const frac=KhanMath.toFraction(value);return graphie.label([pos,-0.17],formatMixed(frac[0],frac[1]),"below")}if(labelStyle==="non-reduced"){const frac=KhanMath.toFraction(value);return graphie.label([pos,-0.17],formatNonReduced(frac[0],frac[1],base),"below")}};const TickMarks=Graphie.createSimpleClass((graphie,props)=>{if(!_.isFinite(props.tickStep)||props.tickStep<=0){return []}const results=[];const{range,labelRange,labelStyle,labelTicks,tickStep,numDivisions}=props;const leftLabel=labelRange[0]==null?range[0]:labelRange[0];const rightLabel=labelRange[1]==null?range[1]:labelRange[1];let base;if(labelStyle==="non-reduced"){const fractions=[leftLabel,rightLabel];for(let i=0;i<=numDivisions;i++){const x=range[0]+i*tickStep;fractions.push(x);}const getDenom=x=>number.toFraction(x)[1];const denoms=_.map(fractions,getDenom);base=_.reduce(denoms,(x,y)=>KhanMath.getLCM(x,y));}else {base=undefined;}const highlightedLineStyle={stroke:KhanColors.BLUE,strokeWidth:3.5};const highlightedTextStyle={color:KhanColors.BLUE};const initialTicks=[...Array(Math.round(numDivisions)).keys()].map(index=>range[0]+index*tickStep);const byNumericAscending=(a,b)=>a-b;const allTicks=[...new Set([...initialTicks,leftLabel,rightLabel,...range])].sort(byNumericAscending);allTicks.forEach(tick=>{const tickIsHighlighted=tick===leftLabel||tick===rightLabel;const lineStyle=tickIsHighlighted?highlightedLineStyle:null;const textStyle=tickIsHighlighted?highlightedTextStyle:null;graphie.style(lineStyle,()=>{results.push(graphie.line([tick,-0.2],[tick,.2]));});if(labelTicks||tickIsHighlighted||labelStyle==="decimal ticks"){graphie.style(textStyle,()=>{results.push(_label(graphie,labelStyle,tick,tick,base));});}});return results});class NumberLine extends React.Component{focus(){if(this.props.isTickCtrl){this.refs["tick-ctrl"].focus();return true}return false}getDOMNodeForPath(inputPath){if(inputPath?.length===1){return ReactDOM__default.findDOMNode(this.refs[inputPath[0]])}return null}_getInequalityEndpoint(props){const isGreater=_(["ge","gt"]).contains(this.props.userInput.rel);const widthInPixels=400;const range=props.range;const scale=(range[1]-range[0])/widthInPixels;const buffer=horizontalPadding*scale;const left=range[0]-buffer;const right=range[1]+buffer;const end=isGreater?[right,0]:[left,0];return end}_renderInequality(props){if(props.isInequality){const end=this._getInequalityEndpoint(props);const style={arrows:"->",stroke:this.props.apiOptions.isMobile?KhanColors.GREEN:KhanColors.BLUE,strokeWidth:3.5};return jsxRuntimeExports.jsx(Line,{start:[props.userInput.numLinePosition,0],end:end,style:style})}return null}getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$6(this.props,this.getUserInput())}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,numDivisions:userInput.numDivisions,numLinePosition:userInput.numLinePosition,rel:"ge"}}render(){const{strings}=this.context;const divisionRange=this.props.divisionRange;const divRangeString=divisionRange[0]+EN_DASH+divisionRange[1];const invalidNumDivisions=this.props.userInput.numDivisions<divisionRange[0]||this.props.userInput.numDivisions>divisionRange[1];const inequalityControls=jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx("input",{type:"button",className:"simple-button",value:strings.switchDirection,onClick:this.handleReverse}),jsxRuntimeExports.jsx("input",{type:"button",className:"simple-button",value:["le","ge"].includes(this.props.userInput.rel)?strings.circleOpen:strings.circleFilled,onClick:this.handleToggleStrict})]});let tickCtrl;if(this.props.isTickCtrl){let Input;if(this.props.apiOptions.customKeypad){Input=SimpleKeypadInput;}else {Input=NumberInput;}tickCtrl=jsxRuntimeExports.jsxs("label",{children:[strings.numDivisions," ",jsxRuntimeExports.jsx(Input,{ref:"tick-ctrl",value:this.state.numDivisionsEmpty?null:this.props.userInput.numDivisions||divisionRange[0],checkValidity:val=>val>=divisionRange[0]&&val<=divisionRange[1],onChange:this.onNumDivisionsChange,onFocus:this._handleTickCtrlFocus,onBlur:this._handleTickCtrlBlur,useArrowKeys:true,keypadElement:this.props.keypadElement})]});}return jsxRuntimeExports.jsxs("div",{className:"perseus-widget "+"perseus-widget-interactive-number-line",children:[tickCtrl,!this.isValid()?jsxRuntimeExports.jsx("div",{className:"perseus-error",children:"Invalid number line configuration."}):this.props.isTickCtrl&&invalidNumDivisions?jsxRuntimeExports.jsx("div",{className:"perseus-error",children:strings.divisions({divRangeString:divRangeString})}):this._renderGraphie(),!this.props.static&&this.props.isInequality&&inequalityControls]})}constructor(...args){super(...args),this.state={numDivisionsEmpty:false},this.isValid=()=>{const range=this.props.range;let initialX=this.props.userInput.numLinePosition;const divisionRange=this.props.divisionRange;initialX=initialX==null?range[0]:initialX;return range[0]<range[1]&&number.sign(initialX-range[0])>=0&&number.sign(initialX-range[1])<=0&&divisionRange[0]<divisionRange[1]&&0<this.props.userInput.numDivisions&&0<this.props.snapDivisions},this.onNumDivisionsChange=(numDivisions,cb)=>{const divRange=this.props.divisionRange.slice();const width=this.props.range[1]-this.props.range[0];numDivisions=Math.round(numDivisions);numDivisions=numDivisions<0?numDivisions*-1:numDivisions;if(numDivisions){const nextProps={...this.props,tickStep:width/numDivisions};const newNumLinePosition=this.snapNumLinePosition(nextProps,this.props.userInput.numLinePosition);this.setState({numDivisionsEmpty:false},()=>{this.props.onChange({divisionRange:divRange},cb);this.props.handleUserInput({...this.props.userInput,numDivisions:numDivisions,numLinePosition:newNumLinePosition},cb);});}else {this.setState({numDivisionsEmpty:true},cb);}},this._handleTickCtrlFocus=()=>{this.props.onFocus(["tick-ctrl"]);},this._handleTickCtrlBlur=()=>{this.props.onBlur(["tick-ctrl"]);},this.focusInputPath=path=>{if(path.length===1){this.refs[path[0]].focus();}},this.blurInputPath=path=>{if(path.length===1){this.refs[path[0]].blur();}},this.getInputPaths=()=>{if(this.props.isTickCtrl){return [["tick-ctrl"]]}return []},this.setInputValue=(inputPath,value,callback)=>{if(inputPath.length===1&&inputPath[0]==="tick-ctrl"){this.onNumDivisionsChange(value,callback);}},this._renderGraphie=()=>{const range=this.props.range;const width=range[1]-range[0];const options={range:this.props.range,isTickCtrl:this.props.isTickCtrl};const props={...this.props,tickStep:width/this.props.userInput.numDivisions};return jsxRuntimeExports.jsxs(Graphie,{ref:"graphie",box:[this.props.apiOptions.isMobile?288:460,80],options:options,onMouseDown:coord=>{this.refs.graphie.movables.numberLinePoint.grab(coord);},setup:this._setupGraphie,setDrawingAreaAvailable:this.props.apiOptions.setDrawingAreaAvailable,isMobile:this.props.apiOptions.isMobile,children:[jsxRuntimeExports.jsx(TickMarks,{range:props.range,labelTicks:props.labelTicks,labelStyle:props.labelStyle,labelRange:props.labelRange,tickStep:props.tickStep,numDivisions:props.userInput.numDivisions,isMobile:props.apiOptions.isMobile}),this._renderInequality(props),this._renderNumberLinePoint(props)]},this.props.labelStyle)},this.snapNumLinePosition=(props,numLinePosition)=>{const left=props.range[0];const right=props.range[1];const snapX=props.tickStep/props.snapDivisions;let x=bound(numLinePosition,left,right);x=left+number.roundTo(x-left,snapX);assert$1(_.isFinite(x));return x},this.movePosition=targetPosition=>{this.props.handleUserInput({...this.props.userInput,numLinePosition:targetPosition});this.props.trackInteraction();},this._renderNumberLinePoint=props=>{const isOpen=_(["lt","gt"]).contains(props.userInput.rel);let fill;if(isOpen){fill=KhanColors._BACKGROUND;}else if(props.static){fill=KhanColors.BLUE;}else {fill=KhanColors.GREEN;}const normalStyle={fill:fill,stroke:props.static?KhanColors.BLUE:KhanColors.GREEN,"stroke-width":isOpen?3:1};const highlightStyle={fill:isOpen?KhanColors._BACKGROUND:KhanColors.GREEN,"stroke-width":isOpen?3:1};const mobileDotStyle=props.isInequality?{stroke:KhanColors.GREEN,"fill-opacity":isOpen?0:1}:{};return jsxRuntimeExports.jsx(MovablePoint,{ref:"numberLinePoint",pointSize:6,coord:[props.userInput.numLinePosition,0],constraints:[(coord,prevCoord)=>{return [coord[0],prevCoord[1]]},(coord,prevCoord)=>{const x=this.snapNumLinePosition(props,coord[0]);return [x,coord[1]]}],normalStyle:normalStyle,highlightStyle:highlightStyle,onMove:coord=>{this.movePosition(coord[0]);},isMobile:this.props.apiOptions.isMobile,mobileStyleOverride:mobileDotStyle,showTooltips:this.props.showTooltips,xOnlyTooltip:true})},this.handleReverse=()=>{const newRel=reverseRel[this.props.userInput.rel];this.props.handleUserInput({...this.props.userInput,rel:newRel});},this.handleToggleStrict=()=>{const newRel=toggleStrictRel[this.props.userInput.rel];this.props.handleUserInput({...this.props.userInput,rel:newRel});},this._setupGraphie=(graphie,options)=>{if(!this.isValid()){return}const widthInPixels=this.props.apiOptions.isMobile?288-horizontalPadding*2:400;const range=options.range;const scale=(range[1]-range[0])/widthInPixels;const buffer=horizontalPadding*scale;const left=range[0]-buffer;const right=range[1]+buffer;const hasFractionalLabels=this.props.labelStyle==="improper"||this.props.labelStyle==="mixed"||this.props.labelStyle==="non-reduced";const bottom=hasFractionalLabels?-1.5:-1;const top=1;graphie.init({range:[[left,right],[bottom,top]],scale:[1/scale,40],isMobile:this.props.apiOptions.isMobile});const center=(range[0]+range[1])/2;graphie.line([center,0],[right,0],{arrows:"->"});graphie.line([center,0],[left,0],{arrows:"->"});};}}NumberLine.contextType=PerseusI18nContext;NumberLine.defaultProps={range:[0,10],labelStyle:"decimal",labelRange:[null,null],divisionRange:[1,12],labelTicks:true,isTickCtrl:false,isInequality:false,snapDivisions:2,showTooltips:false,apiOptions:ApiOptions.defaults};function transform(editorProps){const props=_.pick(editorProps,["range","labelRange","labelStyle","labelTicks","divisionRange","snapDivisions","isTickCtrl","isInequality","showTooltips"]);return {...props,isTickCtrl:editorProps.isTickCtrl??undefined,snapDivisions:props.snapDivisions||undefined}}function staticTransform(editorProps){const props=_.pick(editorProps,["range","labelRange","labelStyle","labelTicks","divisionRange","snapDivisions","isInequality"]);return {...props,snapDivisions:props.snapDivisions||undefined}}function getUserInputFromSerializedState$4(serializedState){return {numDivisions:serializedState.numDivisions,numLinePosition:serializedState.numLinePosition,rel:"eq"}}function getStartNumDivisions(options){const width=options.range[1]-options.range[0];let numDivisions;if(options.numDivisions!=null){numDivisions=options.numDivisions;}else if(options.tickStep!=null){numDivisions=width/options.tickStep;}else {numDivisions=undefined;}return numDivisions}function getCorrectUserInput(options){const numLinePosition=options.correctX!=null?options.correctX:options.range[0];return {numDivisions:getStartNumDivisions(options),numLinePosition,rel:options.isInequality&&options.correctRel||"eq"}}function getStartUserInput$3(options){const numLinePosition=options.initialX!=null?options.initialX:options.range[0];return {numDivisions:getStartNumDivisions(options),numLinePosition,rel:options.isInequality?"ge":"eq"}}var NumberLine$1 = {name:"number-line",displayName:"Number line",widget:NumberLine,transform,staticTransform:staticTransform,getCorrectUserInput,getStartUserInput: getStartUserInput$3,getUserInputFromSerializedState: getUserInputFromSerializedState$4};
2039
2043
 
2040
2044
  const getPromptJSON$5=(renderProps,userInput)=>{return {type:"orderer",options:{options:renderProps.options.map(option=>option.content)},userInput:{values:userInput.current}}};
2041
2045
 
2042
- class PlaceholderCard extends React.Component{render(){return jsxRuntimeExports.jsx("div",{className:"card-wrap",style:{width:this.props.width},children:jsxRuntimeExports.jsx("div",{className:"card placeholder",style:{height:this.props.width}})})}}class DragHintCard extends React.Component{render(){return jsxRuntimeExports.jsx("div",{className:"card-wrap",children:jsxRuntimeExports.jsx("div",{className:"card drag-hint"})})}}class Card extends React.Component{componentDidMount(){this.mouseMoveUpBound=false;document.addEventListener("touchmove",this.onMouseMove,Util.supportsPassiveEvents()?{passive:false}:false);}shouldComponentUpdate(nextProps,nextState){return this.props.floating||nextProps.floating||this.props.content!==nextProps.content||this.props.fakeRef!==nextProps.fakeRef}componentDidUpdate(prevProps,prevState){if(this.props.animating&&!prevProps.animating&&this.props.animateTo&&this.props.startOffset){const ms=15*Math.sqrt(Math.sqrt(Math.pow(this.props.animateTo.left-this.props.startOffset.left,2)+Math.pow(this.props.animateTo.top-this.props.startOffset.top,2)));$(ReactDOM__default.findDOMNode(this)).animate(this.props.animateTo,Math.max(ms,1),this.props.onAnimationEnd);}}componentWillUnmount(){if(this.mouseMoveUpBound){Log.error("Removing an element with bound event handlers.",Errors.Internal);this.unbindMouseMoveUp();Util.resetTouchHandlers();}document.removeEventListener("touchmove",this.onMouseMove);}render(){let style={};if(this.props.floating){style={position:"absolute",left:this.props.startOffset?.left,top:this.props.startOffset?.top};}if(this.props.width){style.width=this.props.width;}const className=["card"];if(this.props.stack){className.push("stack");}if(this.props.floating&&!this.props.animating&&this.props.mouse&&this.props.startMouse){className.push("dragging");style.left+=this.props.mouse.left-this.props.startMouse.left;style.top+=this.props.mouse.top-this.props.startMouse.top;}const rendererProps={content:this.props.content};const onMouseDown=this.props.animating?$.noop:this.onMouseDown;return jsxRuntimeExports.jsx("div",{className:"card-wrap",style:style,onMouseDown:onMouseDown,onTouchStart:onMouseDown,onTouchEnd:this.onMouseUp,onTouchCancel:this.onMouseUp,children:jsxRuntimeExports.jsx("div",{className:className.join(" "),children:jsxRuntimeExports.jsx(Renderer,{...rendererProps,linterContext:this.props.linterContext,strings:this.context.strings})})})}constructor(...args){super(...args),this.state={dragging:false},this.bindMouseMoveUp=()=>{this.mouseMoveUpBound=true;$(document).on("mousemove",this.onMouseMove);$(document).on("mouseup",this.onMouseUp);},this.unbindMouseMoveUp=()=>{this.mouseMoveUpBound=false;$(document).off("mousemove",this.onMouseMove);$(document).off("mouseup",this.onMouseUp);},this.onMouseDown=event=>{event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.setState({dragging:true});this.bindMouseMoveUp();this.props.onMouseDown?.(loc,this);}},this.onMouseMove=event=>{if(!this.state.dragging){return}event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.props.onMouseMove?.(loc);}},this.onMouseUp=event=>{event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.setState({dragging:false});this.unbindMouseMoveUp();this.props.onMouseUp?.(loc);}};}}Card.contextType=PerseusI18nContext;Card.defaultProps={stack:false,animating:false,linterContext:linterContextDefault};class Orderer extends React.Component{UNSAFE_componentWillReceiveProps(nextProps){if(!_.isEqual(this.props.current,nextProps.current)){this.setState({current:nextProps.current});}}getUserInput(){return {current:this.props.current.map(function(v){return v.content})}}getPromptJSON(){return getPromptJSON$5(this.props,this.getUserInput())}render(){const dragging=this.state.dragging&&jsxRuntimeExports.jsx(Card,{ref:"dragging",floating:true,content:this.state.dragContent,startOffset:this.state.offsetPos,startMouse:this.state.grabPos,mouse:this.state.mousePos,width:this.state.dragWidth,onMouseUp:this.onRelease,onMouseMove:this.onMouseMove,linterContext:this.props.linterContext},this.state.dragKey||"draggingCard");const animating=this.state.animating&&jsxRuntimeExports.jsx(Card,{floating:true,animating:true,content:this.state.dragContent,startOffset:this.state.offsetPos,width:this.state.dragWidth,animateTo:this.state.animateTo,onAnimationEnd:this.state.onAnimationEnd,linterContext:this.props.linterContext},this.state.dragKey||"draggingCard");const sortableCards=this.state.current.map((opt,i)=>{return jsxRuntimeExports.jsx(Card,{ref:"sortable"+i,fakeRef:"sortable"+i,floating:false,content:opt.content,width:opt.width,linterContext:this.props.linterContext,onMouseDown:this.state.animating?$.noop:this.onClick.bind(null,"current",i)},`sortableCard${i}`)});if(this.state.placeholderIndex!=null){const placeholder=jsxRuntimeExports.jsx(PlaceholderCard,{ref:"placeholder",width:this.state.dragWidth,height:this.state.dragHeight},"placeholder");sortableCards.splice(this.state.placeholderIndex,0,placeholder);}const anySortableCards=sortableCards.length>0;sortableCards.push(dragging,animating);const sortable=jsxRuntimeExports.jsxs("div",{className:"perseus-clearfix draggable-box",children:[!anySortableCards&&jsxRuntimeExports.jsx(DragHintCard,{}),jsxRuntimeExports.jsx("div",{ref:"dragList",children:sortableCards})]});const bank=jsxRuntimeExports.jsx("div",{ref:"bank",className:"bank perseus-clearfix",children:this.props.options.map((opt,i)=>{return jsxRuntimeExports.jsx(Card,{ref:"bank"+i,floating:false,content:opt.content,stack:true,linterContext:this.props.linterContext,onMouseDown:this.state.animating?$.noop:this.onClick.bind(null,"bank",i),onMouseMove:this.onMouseMove,onMouseUp:this.onRelease},i)})});return jsxRuntimeExports.jsxs("div",{className:"draggy-boxy-thing orderer "+"height-"+this.props.height+" "+"layout-"+this.props.layout+" "+"blank-background "+"perseus-clearfix ",ref:"orderer",children:[bank,sortable]})}constructor(...args){super(...args),this.state={current:[],dragging:false,placeholderIndex:null,dragKey:null,animating:false,dragContent:null,dragWidth:null,dragHeight:null,offsetPos:null,animateTo:null,grabPos:null},this.onClick=(type,index,loc,draggable)=>{const $draggable=$(ReactDOM__default.findDOMNode(draggable));const list=this.state.current.slice();let opt;let placeholderIndex=null;if(type==="current"){list.splice(index,1);opt=this.state.current[index];placeholderIndex=index;}else if(type==="bank"){opt=this.props.options[index];}this.setState({current:list,dragging:true,placeholderIndex:placeholderIndex,dragKey:opt.key,dragContent:opt.content,dragWidth:$draggable.width(),dragHeight:$draggable.height(),grabPos:loc,mousePos:loc,offsetPos:$draggable.position()});},this.onRelease=loc=>{const draggable=this.refs.dragging;if(draggable==null){return}const inCardBank=this.isCardInBank(draggable);const index=this.state.placeholderIndex||0;const onAnimationEnd=()=>{const list=this.state.current.slice();if(!inCardBank){const newCard={content:this.state.dragContent,key:_.uniqueId("perseus_draggable_card_"),width:this.state.dragWidth};list.splice(index,0,newCard);}this.props.onChange({current:list});this.setState({current:list,dragging:false,placeholderIndex:null,animating:false});this.props.trackInteraction();};const offset=$(ReactDOM__default.findDOMNode(draggable)).position();let finalOffset=null;if(inCardBank){this.props.options.forEach((opt,i)=>{if(opt.content===this.state.dragContent){const card=ReactDOM__default.findDOMNode(this.refs["bank"+i]);finalOffset=$(card).position();}});}else if(this.refs.placeholder!=null){finalOffset=$(ReactDOM__default.findDOMNode(this.refs.placeholder)).position();}if(finalOffset==null){onAnimationEnd();}else {this.setState({offsetPos:offset,animateTo:finalOffset,onAnimationEnd:onAnimationEnd,animating:true,dragging:false});}},this.onMouseMove=loc=>{const draggable=this.refs.dragging;if(draggable==null){return}let index;if(this.isCardInBank(draggable)){index=null;}else {index=this.findCorrectIndex(draggable,this.state.current);}this.setState({mousePos:loc,placeholderIndex:index});},this.findCorrectIndex=(draggable,list)=>{const isHorizontal=this.props.layout==="horizontal";const $dragList=$(ReactDOM__default.findDOMNode(this.refs.dragList));const leftEdge=$dragList.offset().left;const topEdge=$dragList.offset().top;const midWidth=$(ReactDOM__default.findDOMNode(draggable)).offset().left-leftEdge;const midHeight=$(ReactDOM__default.findDOMNode(draggable)).offset().top-topEdge;let index=0;let sumWidth=0;let sumHeight=0;if(isHorizontal){list.forEach((opt,i)=>{const card=ReactDOM__default.findDOMNode(this.refs["sortable"+i]);const outerWidth=$(card).outerWidth(true);if(midWidth>sumWidth+outerWidth/2){index+=1;}sumWidth+=outerWidth;});}else {list.forEach((_,i)=>{const card=ReactDOM__default.findDOMNode(this.refs["sortable"+i]);const outerHeight=$(card).outerHeight(true);if(midHeight>sumHeight+outerHeight/2){index+=1;}sumHeight+=outerHeight;});}return index},this.isCardInBank=draggable=>{if(draggable==null){return false}const isHorizontal=this.props.layout==="horizontal";const $draggable=$(ReactDOM__default.findDOMNode(draggable));const $bank=$(ReactDOM__default.findDOMNode(this.refs.bank));const draggableOffset=$draggable.offset();const bankOffset=$bank.offset();const draggableHeight=$draggable.outerHeight(true);const bankHeight=$bank.outerHeight(true);const bankWidth=$bank.outerWidth(true);const draggableWidth=$draggable.outerWidth(true);if(isHorizontal){return draggableOffset.top+draggableHeight/2<bankOffset.top+bankHeight}return draggableOffset.left+draggableWidth/2<bankOffset.left+bankWidth},this.setListValues=values=>{const list=values.map(value=>{return {content:value}});this.props.onChange({current:list});this.setState({current:list});};}}Orderer.defaultProps={current:[],options:[],height:"normal",layout:"horizontal",linterContext:linterContextDefault};var Orderer$1 = {name:"orderer",displayName:"Orderer",hidden:true,widget:Orderer,isLintable:true};
2046
+ class PlaceholderCard extends React.Component{render(){return jsxRuntimeExports.jsx("div",{className:"card-wrap",style:{width:this.props.width},children:jsxRuntimeExports.jsx("div",{className:"card placeholder",style:{height:this.props.width}})})}}class DragHintCard extends React.Component{render(){return jsxRuntimeExports.jsx("div",{className:"card-wrap",children:jsxRuntimeExports.jsx("div",{className:"card drag-hint"})})}}class Card extends React.Component{componentDidMount(){this.mouseMoveUpBound=false;document.addEventListener("touchmove",this.onMouseMove,Util.supportsPassiveEvents()?{passive:false}:false);}shouldComponentUpdate(nextProps,nextState){return this.props.floating||nextProps.floating||this.props.content!==nextProps.content||this.props.fakeRef!==nextProps.fakeRef}componentDidUpdate(prevProps,prevState){if(this.props.animating&&!prevProps.animating&&this.props.animateTo&&this.props.startOffset){const ms=15*Math.sqrt(Math.sqrt(Math.pow(this.props.animateTo.left-this.props.startOffset.left,2)+Math.pow(this.props.animateTo.top-this.props.startOffset.top,2)));$(ReactDOM__default.findDOMNode(this)).animate(this.props.animateTo,Math.max(ms,1),this.props.onAnimationEnd);}}componentWillUnmount(){if(this.mouseMoveUpBound){Log.error("Removing an element with bound event handlers.",Errors.Internal);this.unbindMouseMoveUp();Util.resetTouchHandlers();}document.removeEventListener("touchmove",this.onMouseMove);}render(){let style={};if(this.props.floating){style={position:"absolute",left:this.props.startOffset?.left,top:this.props.startOffset?.top};}if(this.props.width){style.width=this.props.width;}const className=["card"];if(this.props.stack){className.push("stack");}if(this.props.floating&&!this.props.animating&&this.props.mouse&&this.props.startMouse){className.push("dragging");style.left+=this.props.mouse.left-this.props.startMouse.left;style.top+=this.props.mouse.top-this.props.startMouse.top;}const rendererProps={content:this.props.content};const onMouseDown=this.props.animating?$.noop:this.onMouseDown;return jsxRuntimeExports.jsx("div",{className:"card-wrap",style:style,onMouseDown:onMouseDown,onTouchStart:onMouseDown,onTouchEnd:this.onMouseUp,onTouchCancel:this.onMouseUp,children:jsxRuntimeExports.jsx("div",{className:className.join(" "),children:jsxRuntimeExports.jsx(Renderer,{...rendererProps,linterContext:this.props.linterContext,strings:this.context.strings})})})}constructor(...args){super(...args),this.state={dragging:false},this.bindMouseMoveUp=()=>{this.mouseMoveUpBound=true;$(document).on("mousemove",this.onMouseMove);$(document).on("mouseup",this.onMouseUp);},this.unbindMouseMoveUp=()=>{this.mouseMoveUpBound=false;$(document).off("mousemove",this.onMouseMove);$(document).off("mouseup",this.onMouseUp);},this.onMouseDown=event=>{event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.setState({dragging:true});this.bindMouseMoveUp();this.props.onMouseDown?.(loc,this);}},this.onMouseMove=event=>{if(!this.state.dragging){return}event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.props.onMouseMove?.(loc);}},this.onMouseUp=event=>{event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.setState({dragging:false});this.unbindMouseMoveUp();this.props.onMouseUp?.(loc);}};}}Card.contextType=PerseusI18nContext;Card.defaultProps={stack:false,animating:false,linterContext:linterContextDefault};class Orderer extends React.Component{getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$5(this.props,this.getUserInput())}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,current:userInput.current.map(e=>({content:e}))}}render(){const dragging=this.state.dragging&&jsxRuntimeExports.jsx(Card,{ref:"dragging",floating:true,content:this.state.dragContent,startOffset:this.state.offsetPos,startMouse:this.state.grabPos,mouse:this.state.mousePos,width:this.state.dragWidth,onMouseUp:this.onRelease,onMouseMove:this.onMouseMove,linterContext:this.props.linterContext},this.state.dragKey||"draggingCard");const animating=this.state.animating&&jsxRuntimeExports.jsx(Card,{floating:true,animating:true,content:this.state.dragContent,startOffset:this.state.offsetPos,width:this.state.dragWidth,animateTo:this.state.animateTo,onAnimationEnd:this.state.onAnimationEnd,linterContext:this.props.linterContext},this.state.dragKey||"draggingCard");const sortableCards=this.props.userInput.current.map((opt,i)=>{return jsxRuntimeExports.jsx(Card,{ref:"sortable"+i,fakeRef:"sortable"+i,floating:false,content:opt,linterContext:this.props.linterContext,onMouseDown:this.state.animating?$.noop:this.onClick.bind(null,"current",i)},`sortableCard${i}`)});if(this.state.placeholderIndex!=null){const placeholder=jsxRuntimeExports.jsx(PlaceholderCard,{ref:"placeholder",width:this.state.dragWidth,height:this.state.dragHeight},"placeholder");sortableCards.splice(this.state.placeholderIndex,0,placeholder);}const anySortableCards=sortableCards.length>0;sortableCards.push(dragging,animating);const sortable=jsxRuntimeExports.jsxs("div",{className:"perseus-clearfix draggable-box",children:[!anySortableCards&&jsxRuntimeExports.jsx(DragHintCard,{}),jsxRuntimeExports.jsx("div",{ref:"dragList",children:sortableCards})]});const bank=jsxRuntimeExports.jsx("div",{ref:"bank",className:"bank perseus-clearfix",children:this.props.options.map((opt,i)=>{return jsxRuntimeExports.jsx(Card,{ref:"bank"+i,floating:false,content:opt.content,stack:true,linterContext:this.props.linterContext,onMouseDown:this.state.animating?$.noop:this.onClick.bind(null,"bank",i),onMouseMove:this.onMouseMove,onMouseUp:this.onRelease},i)})});return jsxRuntimeExports.jsxs("div",{className:"draggy-boxy-thing orderer "+"height-"+this.props.height+" "+"layout-"+this.props.layout+" "+"blank-background "+"perseus-clearfix ",ref:"orderer",children:[bank,sortable]})}constructor(...args){super(...args),this.state={dragging:false,placeholderIndex:null,dragKey:null,animating:false,dragContent:null,dragWidth:null,dragHeight:null,offsetPos:null,animateTo:null,grabPos:null},this.onClick=(type,index,loc,draggable)=>{const $draggable=$(ReactDOM__default.findDOMNode(draggable));const list=this.props.userInput.current.slice();let opt;let placeholderIndex=null;if(type==="current"){list.splice(index,1);opt=this.props.userInput.current[index];placeholderIndex=index;}else if(type==="bank"){opt=this.props.options[index];}this.props.handleUserInput({current:list});this.setState({dragging:true,placeholderIndex:placeholderIndex,dragKey:opt.key,dragContent:opt.content,dragWidth:$draggable.width(),dragHeight:$draggable.height(),grabPos:loc,mousePos:loc,offsetPos:$draggable.position()});},this.onRelease=loc=>{const draggable=this.refs.dragging;if(draggable==null){return}const inCardBank=this.isCardInBank(draggable);const index=this.state.placeholderIndex||0;const onAnimationEnd=()=>{const list=this.props.userInput.current.slice();if(!inCardBank){const newCard={content:this.state.dragContent,key:_.uniqueId("perseus_draggable_card_"),width:this.state.dragWidth};list.splice(index,0,newCard.content);}this.props.handleUserInput({current:list});this.setState({dragging:false,placeholderIndex:null,animating:false});this.props.trackInteraction();};const offset=$(ReactDOM__default.findDOMNode(draggable)).position();let finalOffset=null;if(inCardBank){this.props.options.forEach((opt,i)=>{if(opt.content===this.state.dragContent){const card=ReactDOM__default.findDOMNode(this.refs["bank"+i]);finalOffset=$(card).position();}});}else if(this.refs.placeholder!=null){finalOffset=$(ReactDOM__default.findDOMNode(this.refs.placeholder)).position();}if(finalOffset==null){onAnimationEnd();}else {this.setState({offsetPos:offset,animateTo:finalOffset,onAnimationEnd:onAnimationEnd,animating:true,dragging:false});}},this.onMouseMove=loc=>{const draggable=this.refs.dragging;if(draggable==null){return}let index;if(this.isCardInBank(draggable)){index=null;}else {index=this.findCorrectIndex(draggable,this.props.userInput.current);}this.setState({mousePos:loc,placeholderIndex:index});},this.findCorrectIndex=(draggable,list)=>{const isHorizontal=this.props.layout==="horizontal";const $dragList=$(ReactDOM__default.findDOMNode(this.refs.dragList));const leftEdge=$dragList.offset().left;const topEdge=$dragList.offset().top;const midWidth=$(ReactDOM__default.findDOMNode(draggable)).offset().left-leftEdge;const midHeight=$(ReactDOM__default.findDOMNode(draggable)).offset().top-topEdge;let index=0;let sumWidth=0;let sumHeight=0;if(isHorizontal){list.forEach((opt,i)=>{const card=ReactDOM__default.findDOMNode(this.refs["sortable"+i]);const outerWidth=$(card).outerWidth(true);if(midWidth>sumWidth+outerWidth/2){index+=1;}sumWidth+=outerWidth;});}else {list.forEach((_,i)=>{const card=ReactDOM__default.findDOMNode(this.refs["sortable"+i]);const outerHeight=$(card).outerHeight(true);if(midHeight>sumHeight+outerHeight/2){index+=1;}sumHeight+=outerHeight;});}return index},this.isCardInBank=draggable=>{if(draggable==null){return false}const isHorizontal=this.props.layout==="horizontal";const $draggable=$(ReactDOM__default.findDOMNode(draggable));const $bank=$(ReactDOM__default.findDOMNode(this.refs.bank));const draggableOffset=$draggable.offset();const bankOffset=$bank.offset();const draggableHeight=$draggable.outerHeight(true);const bankHeight=$bank.outerHeight(true);const bankWidth=$bank.outerWidth(true);const draggableWidth=$draggable.outerWidth(true);if(isHorizontal){return draggableOffset.top+draggableHeight/2<bankOffset.top+bankHeight}return draggableOffset.left+draggableWidth/2<bankOffset.left+bankWidth},this.setListValues=values=>{this.props.handleUserInput({current:values});};}}Orderer.defaultProps={options:[],height:"normal",layout:"horizontal",linterContext:linterContextDefault,userInput:{current:[]}};function getUserInputFromSerializedState$3(serializedState){return {current:serializedState.current.map(e=>e.content)}}var Orderer$1 = {name:"orderer",displayName:"Orderer",hidden:true,widget:Orderer,isLintable:true,getUserInputFromSerializedState: getUserInputFromSerializedState$3};
2043
2047
 
2044
- class PassageRefTarget extends React.Component{render(){return jsxRuntimeExports.jsx(Renderer,{content:this.props.content,apiOptions:this.props.apiOptions,linterContext:this.props.linterContext,strings:this.context.strings})}constructor(...args){super(...args),this.isWidget=true,this.change=(...args)=>{return change.apply(this,args)};}}PassageRefTarget.contextType=PerseusI18nContext;PassageRefTarget.defaultProps={content:"",linterContext:linterContextDefault};var PassageRefTarget$1 = {name:"passage-ref-target",displayName:"PassageRefTarget",widget:PassageRefTarget,hidden:true,transform:editorProps=>{return _.pick(editorProps,"content")},isLintable:true};
2048
+ class PassageRefTarget extends React.Component{render(){return jsxRuntimeExports.jsx(Renderer,{content:this.props.content,apiOptions:this.props.apiOptions,linterContext:this.props.linterContext,strings:this.context.strings})}constructor(...args){super(...args),this.isWidget=true;}}PassageRefTarget.contextType=PerseusI18nContext;PassageRefTarget.defaultProps={content:"",linterContext:linterContextDefault};var PassageRefTarget$1 = {name:"passage-ref-target",displayName:"PassageRefTarget",widget:PassageRefTarget,hidden:true,transform:editorProps=>{return _.pick(editorProps,"content")},isLintable:true};
2045
2049
 
2046
2050
  const getPromptJSON$4=()=>{return getUnsupportedPromptJSON("phet-simulation")};
2047
2051
 
@@ -2049,7 +2053,7 @@ const MOBILE_APP_BOTTOM_BAR_HEIGHT=66;class PhetSimulation extends React.Compone
2049
2053
 
2050
2054
  const getPromptJSON$3=()=>{return getUnsupportedPromptJSON("plotter")};
2051
2055
 
2052
- class Plotter extends React.Component{componentDidMount(){this._isMounted=true;this.setupGraphie(this.state);}UNSAFE_componentWillReceiveProps(nextProps){const props=["type","labels","categories","scaleY","maxY","snapsPerLine","picUrl","labelInterval","static"];this.shouldSetupGraphie=_.any(props,prop=>!_.isEqual(this.props[prop],nextProps[prop]),this);if(!_.isEqual(this.props.starting,nextProps.starting)&&!_.isEqual(this.state.values,nextProps.starting)){this.shouldSetupGraphie=true;this.setState({values:nextProps.starting});}}componentDidUpdate(prevProps,prevState){this.shouldSetupGraphie=this.shouldSetupGraphie||!_.isEqual(this.state.categoryHeights,prevState.categoryHeights);if(this.shouldSetupGraphie){this.setupGraphie(prevState);}}componentWillUnmount(){this._isMounted=false;}getUserInput(){return this.state.values}getPromptJSON(){return getPromptJSON$3()}render(){const paddingForBottomLabel=75;const style={marginBottom:this.props.labels[0]?paddingForBottomLabel:0};return jsxRuntimeExports.jsx("div",{className:"perseus-widget-plotter graphie",ref:this.graphieDiv,style:style})}constructor(...args){super(...args),this._isMounted=false,this.graphieDiv=React.createRef(),this.state={values:this.props.starting||[1],categoryHeights:{}},this.DOT_PLOT_POINT_SIZE=()=>{return this.props.apiOptions.isMobile?6:4},this.DOT_PLOT_POINT_PADDING=()=>{return 8},this.DOT_TICK_POINT_SIZE=()=>{return 2},this.setupGraphie=prevState=>{const self=this;self.shouldSetupGraphie=false;$(this.graphieDiv.current).empty();const graphie=GraphUtils.createGraphie(this.graphieDiv.current);self.graphie=graphie;self.graphie.pics=[];self.graphie.dotTicks=[];const isBar=self.props.type==="bar";const isLine=self.props.type==="line";const isPic=self.props.type==="pic";const isHistogram=self.props.type==="histogram";const isDotplot=self.props.type==="dotplot";const isTiledPlot=isPic||isDotplot;const config={};const c=config;const isMobile=this.props.apiOptions.isMobile;c.graph={lines:[],bars:[],points:[],dividers:[]};c.scaleY=self.props.scaleY;c.dimX=self.props.categories.length;const plotDimensions=isMobile?[288,336]:self.props.plotDimensions;if(isLine){c.dimX+=isMobile?-0.2:1;}else if(isHistogram){c.barPad=0;c.barWidth=1;}else if(isBar){c.barPad=isMobile?.08:.15;c.barWidth=1-2*c.barPad;c.dimX+=(isMobile?-2:2)*c.barPad;}else if(isTiledPlot){c.picBoxHeight=self.props.picBoxHeight;c.picBoxWidthPx=plotDimensions[0]/self.props.categories.length;const picPadAllWidth=plotDimensions[0]-c.dimX*c.picBoxWidthPx;c.picPad=picPadAllWidth/(2*c.dimX+2);const picFullWidth=c.picBoxWidthPx+2*c.picPad;c.picPad=c.picPad/picFullWidth;c.picBoxWidth=c.picBoxWidthPx/picFullWidth;c.dimX+=2*c.picPad;}if(isDotplot){c.picBoxHeight=this.DOT_PLOT_POINT_SIZE()*2+this.DOT_PLOT_POINT_PADDING();}c.dimY=Math.ceil(self.props.maxY/c.scaleY)*c.scaleY;let padX=25;if((isBar||isLine)&&isMobile){padX=self.props.labels[1].length!==0?17:11;}if(isDotplot){padX/=2;}if(isMobile&&isTiledPlot&&self.props.labels[1].length===0){padX=0;}let padTop=25;let padBottom=25*3;if(isMobile&&(isBar||isTiledPlot)){const maxCategoryHeight=Math.max(0,...Object.values(self.state.categoryHeights));if(maxCategoryHeight){let offsetY=25;if(isTiledPlot){offsetY+=10;}padBottom=offsetY+maxCategoryHeight;}}if(isMobile){c.scale=[(plotDimensions[0]-padX*4)/c.dimX,(plotDimensions[1]-(padTop+padBottom))/c.dimY];}else {c.scale=_.map([c.dimX,c.dimY],function(dim,i){return plotDimensions[i]/dim});}if(isTiledPlot){c.scale[1]=c.picBoxHeight/c.scaleY;}padX/=c.scale[0];padTop/=c.scale[1];padBottom/=c.scale[1];graphie.init({range:[[-3*padX,c.dimX+padX],[-padBottom,c.dimY+padTop]],scale:c.scale,isMobile:this.props.apiOptions.isMobile});graphie.addMouseLayer({allowScratchpad:true,setDrawingAreaAvailable:this.props.apiOptions.setDrawingAreaAvailable});if(!isTiledPlot){const initialY=isMobile?c.scaleY:0;for(let y=initialY;y<=c.dimY;y+=c.scaleY){graphie.label([0,y],KhanMath.roundToApprox(y,2),"left",true);graphie.style({stroke:isMobile?"#e9ebec":"#000",strokeWidth:1,opacity:isMobile?1:.3},function(){graphie.line([0,y],[c.dimX,y]);});}}if((isBar||isLine)&&isMobile&&!this.props.static){self.graphie.dragPrompt=graphie.label([c.dimX/2,c.dimY/2],this.context.strings.dragHandles,"center",false).css("font-weight","bold").css("color",KhanColors.KA_GREEN).css("display","none");}self.setupCategories(config);if(isTiledPlot&&isMobile){self.graphie.dotPrompt=graphie.label([c.dimX/2,c.dimY/2],this.context.strings.tapAddPoints,"center",false).css("font-weight","bold").css("color",KhanColors.KA_GREEN).css("display","none");}if(isTiledPlot){self.drawPicHeights(self.state.values,prevState.values);}graphie.style({stroke:"#000",strokeWidth:2,opacity:1},function(){if(isTiledPlot){if(isDotplot){graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2},()=>graphie.line([isMobile?0:.5,0],[c.dimX-(isMobile?0:.5),0]));}else {graphie.line([0,0],[c.dimX,0]);if(self.props.labels[1].length!==0||!isMobile){graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2},()=>graphie.line([0,0],[0,c.dimY]));}}}else {graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2},()=>graphie.line([isMobile?-padX*3:0,0],[c.dimX+(isMobile?padX:0),0]));if(!((isBar||isLine)&&isMobile)){graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2},()=>graphie.line([0,0],[0,c.dimY]));}}});graphie.label([c.dimX/2,isMobile?-padBottom:-35/c.scale[1]],self.props.labels[0],isMobile?"above":"below",false).css("font-weight","bold").css("color",isMobile&&KhanColors.GRAY_F);graphie.label([(isMobile?-35:-60)/c.scale[0],c.dimY/2],self.props.labels[1],"center",false).css("font-weight","bold").css("color",isMobile&&KhanColors.GRAY_F).addClass("rotate");if(this.props.apiOptions.isMobile){this.horizHairline=new WrappedLine(this.graphie,[0,0],[0,0],{normalStyle:{strokeWidth:1}});this.horizHairline.attr({stroke:KhanColors.INTERACTIVE});this.horizHairline.hide();this.hairlineRange=[[0,c.dimX],[0,c.dimY]];}},this.showHairlines=point=>{if(this.props.apiOptions.isMobile){this.horizHairline.moveTo([this.hairlineRange[0][0],point[1]],[this.hairlineRange[0][1],point[1]]);this.horizHairline.show();}},this.hideHairlines=()=>{if(this.props.apiOptions.isMobile){this.horizHairline.hide();}},this.labelCategory=(x,category)=>{const isMobile=this.props.apiOptions.isMobile;const graphie=this.graphie;category=category+"";let isTeX=false;const mathyCategory=category.match(/^\$(.*)\$$/);if(mathyCategory){category=mathyCategory[1];isTeX=true;}const translateX=5;const rotationDeg=45;const rotationRad=rotationDeg*(Math.PI/180);const labelRotation=`translateX(-50%) translateX(${translateX}px) `+`translateY(-50%) rotate(-${rotationDeg}deg)`;const shouldRotate=isMobile&&!mathyCategory;return new Promise(resolve=>{graphie.style({color:isMobile?KhanColors.GRAY_G:"inherit",transform:shouldRotate?labelRotation:"none",transformOrigin:"100%"},()=>{const $span=graphie.label([x,isMobile?-0.5:0],category,"below",isTeX);const height=14+(shouldRotate?Math.round($span.height()*Math.cos(rotationRad)+($span.width()+translateX)*Math.sin(rotationRad)):$span.height());resolve({category,height});});})},this.setupCategories=config=>{const self=this;const c=config;const graphie=self.graphie;const isMobile=this.props.apiOptions.isMobile;const categoryHeightPromises=[];if(self.props.type==="histogram"){_.times(self.props.categories.length-1,function(i){self.setupBar({index:i,startHeight:self.state.values[i],config:config,isHistogram:true});});_.each(self.props.categories,function(category,i){const x=.5+i*c.barWidth;categoryHeightPromises.push(self.labelCategory(x,category));const tickHeight=6/c.scale[1];graphie.style({stroke:"#000",strokeWidth:isMobile?1:2,opacity:1},function(){graphie.line([x,-tickHeight],[x,0]);});});}else {_.each(self.props.categories,function(category,i){const startHeight=self.state.values[i];let x;if(self.props.type==="bar"){x=self.setupBar({index:i,startHeight:startHeight,config:config,isHistogram:false});}else if(self.props.type==="line"){x=self.setupLine(i,startHeight,config);}else if(self.props.type==="pic"){x=self.setupPic(i,config);}else if(self.props.type==="dotplot"){x=self.setupDotplot(i,config);}let tickStart=0;let tickEnd=-6/c.scale[1];if(self.props.type==="dotplot"&&!isMobile){tickStart=-tickEnd;}if(self.props.type==="dotplot"){if(i%self.props.labelInterval===0||i===self.props.categories.length-1){categoryHeightPromises.push(self.labelCategory(x,category));tickStart*=1.5;tickEnd*=1.5;}}else {categoryHeightPromises.push(self.labelCategory(x,category));}graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2,opacity:1},function(){graphie.line([x,tickStart],[x,tickEnd]);});});}Promise.all(categoryHeightPromises).then(measurements=>{if(self._isMounted){const categoryHeights={};measurements.forEach(({category,height})=>categoryHeights[category]=height);self.setState({categoryHeights});}});},this._clampValue=(v,min,max)=>{return Math.max(Math.min(v,max),min)},this._maybeShowDragPrompt=()=>{if(this.graphie.dragPrompt!=null){this.graphie.dragPrompt[0].style.display="inline";}},this._maybeHideDragPrompt=()=>{if(this.graphie.dragPrompt!=null){this.graphie.dragPrompt[0].style.display="none";}},this.setupBar=args=>{const isMobile=this.props.apiOptions.isMobile;const i=args.index;const startHeight=args.startHeight;const config=args.config;const isHistogram=args.isHistogram;const self=this;const graphie=self.graphie;const barHalfWidth=config.barWidth/2;let x;if(isHistogram){x=.5+i*config.barWidth+barHalfWidth;}else {x=(isMobile?barHalfWidth:.5+config.barPad)+i;}const scaleBar=function(i,height){const center=graphie.scalePoint(0);config.graph.bars[i].scale(1,Math.max(isMobile?.2:.01,height/config.scaleY),center[0],center[1]);if(isHistogram){const leftDivider=config.graph.dividers[i-1];const rightDivider=config.graph.dividers[i];if(leftDivider){const divHeight=Math.min(self.state.values[i-1],height);leftDivider.scale(1,Math.max(.01,divHeight/config.scaleY),center[0],center[1]);}if(rightDivider){const divHeight=Math.min(self.state.values[i+1],height);rightDivider.scale(1,Math.max(.01,divHeight/config.scaleY),center[0],center[1]);}}};graphie.style({stroke:"none",fill:isMobile?KhanColors.BLUE_C:KhanColors.LIGHT_BLUE,opacity:1},function(){config.graph.bars[i]=graphie.path([[x-barHalfWidth,0],[x-barHalfWidth,config.scaleY],[x+barHalfWidth,config.scaleY],[x+barHalfWidth,0],[x-barHalfWidth,0]]);});if(isHistogram){if(i>0){graphie.style({stroke:"#000",strokeWidth:1,opacity:.3},function(){config.graph.dividers.push(graphie.path([[x-barHalfWidth,0],[x-barHalfWidth,config.scaleY]]));});}}if(isMobile){const snap=config.scaleY/self.props.snapsPerLine;config.graph.lines[i]=Interactive2.addMaybeMobileMovablePoint(this,{coord:[x,startHeight],constraints:[(coord,prev,options)=>{return [x,this._clampValue(Math.round(coord[1]/snap)*snap,0,config.dimY)]}],onMoveStart:function(){config.graph.bars[i].attr({fill:KhanColors.INTERACTIVE});},onMove:function(){const y=config.graph.lines[i].coord()[1];const values=[...self.state.values];values[i]=y;self.setState({values:values});self.changeAndTrack({values:values});self._maybeHideDragPrompt();scaleBar(i,y);},onMoveEnd:function(){config.graph.bars[i].attr({fill:KhanColors.BLUE_C});}});config.graph.lines[i].state.visibleShape.wrapper.style.zIndex="1";self._maybeShowDragPrompt();}else {config.graph.lines[i]=graphie.addMovableLineSegment({coordA:[x-barHalfWidth,startHeight],coordZ:[x+barHalfWidth,startHeight],snapY:config.scaleY/self.props.snapsPerLine,constraints:{constrainX:true},normalStyle:{stroke:KhanColors.INTERACTIVE,"stroke-width":this.props.static?0:4}});config.graph.lines[i].onMove=function(dx,dy){let y=this.coordA[1];if(y<0||y>config.dimY){y=Math.min(Math.max(y,0),config.dimY);this.coordA[1]=this.coordZ[1]=y;this.transform();}const values=[...self.state.values];values[i]=y;self.setState({values:values});self.changeAndTrack({values:values});scaleBar(i,y);};}scaleBar(i,startHeight);return x},this.setupLine=(i,startHeight,config)=>{const isMobile=this.props.apiOptions.isMobile;const self=this;const c=config;const graphie=self.graphie;const x=i+(isMobile?.4:1);if(isMobile){const snap=config.scaleY/self.props.snapsPerLine;c.graph.points[i]=Interactive2.addMaybeMobileMovablePoint(this,{coord:[x,startHeight],constraints:[(coord,prev,options)=>{return [x,this._clampValue(Math.round(coord[1]/snap)*snap,0,config.dimY)]}],onMove:function(){const y=c.graph.points[i].coord()[1];const values=[...self.state.values];values[i]=y;self.setState({values:values});self.changeAndTrack({values:values});self._maybeHideDragPrompt();}});self._maybeShowDragPrompt();if(i>0){c.graph.lines[i]=Interactive2.addMovableLine(graphie,{points:[c.graph.points[i-1],c.graph.points[i]],constraints:Interactive2.MovablePoint.constraints.fixed(),normalStyle:{stroke:KhanColors.BLUE_C,"stroke-width":2},highlightStyle:{stroke:KhanColors.BLUE_C,"stroke-width":2}});}}else {c.graph.points[i]=graphie.addMovablePoint({coord:[x,startHeight],constraints:{constrainX:true},normalStyle:{fill:KhanColors.INTERACTIVE,stroke:KhanColors.INTERACTIVE},snapY:c.scaleY/self.props.snapsPerLine});c.graph.points[i].onMove=function(x,y){y=Math.min(Math.max(y,0),c.dimY);const values=[...self.state.values];values[i]=y;self.setState({values:values});self.changeAndTrack({values:values});return [x,y]};if(i>0){c.graph.lines[i]=graphie.addMovableLineSegment({pointA:c.graph.points[i-1],pointZ:c.graph.points[i],constraints:{fixed:true},normalStyle:{stroke:"#9ab8ed","stroke-width":2}});}}return x},this.setupDotplot=(i,config)=>{const graphie=this.graphie;const isMobile=this.props.apiOptions.isMobile;return this.setupTiledPlot(i,isMobile?.5:1,config,(x,y)=>{return graphie.ellipse([x,y],[this.DOT_PLOT_POINT_SIZE()/graphie.scale[0],this.DOT_PLOT_POINT_SIZE()/graphie.scale[1]],{fill:KhanColors.INTERACTIVE,stroke:KhanColors.INTERACTIVE})})},this.setupPic=(i,config)=>{const graphie=this.graphie;return this.setupTiledPlot(i,0,config,(x,y)=>{const scaledCenter=graphie.scalePoint([x,y]);const size=this.props.picSize;return graphie.raphael.image(this.props.picUrl,scaledCenter[0]-size/2,scaledCenter[1]-size/2,size,size)})},this.setupTiledPlot=(i,bottomMargin,config,createImage)=>{const self=this;const c=config;const graphie=self.graphie;const pics=graphie.pics;const dotTicks=graphie.dotTicks;const x=i+.5+c.picPad;graphie.mouselayer.canvas.style.touchAction="none";pics[i]=[];dotTicks[i]=[];const n=Math.round(c.dimY/c.scaleY)+1;_(n).times(function(j){j-=1;const midY=(j+.5)*c.scaleY;const leftX=x-c.picBoxWidth/2;const topY=midY+.5*c.scaleY;const coord=graphie.scalePoint([leftX,topY+bottomMargin]);const mouseRect=graphie.mouselayer.rect(coord[0],coord[1],c.picBoxWidthPx,c.picBoxHeight);$(mouseRect[0]).css({fill:"#000",opacity:0,cursor:"pointer"}).on("vmousedown",function(e){e.preventDefault();self.whichPicClicked=i;self.setPicHeight(i,topY);$(document).on("vmouseup.plotTile",function(e){$(document).unbind(".plotTile");});$(document).on("vmousemove.plotTile",function(e){e.preventDefault();const yCoord=graphie.getMouseCoord(e)[1];const adjustedCoord=Math.floor(yCoord-bottomMargin);const newJ=Math.max(-1,Math.floor(adjustedCoord/c.scaleY));const newMidY=(newJ+.5)*c.scaleY;const newTopY=Math.min(newMidY+.5*c.scaleY,c.dimY);self.setPicHeight(self.whichPicClicked,newTopY);});});if(j<0){return}pics[i][j]=createImage(x,midY+bottomMargin);dotTicks[i][j]=graphie.ellipse([x,midY+bottomMargin],[self.DOT_TICK_POINT_SIZE()/graphie.scale[0],self.DOT_TICK_POINT_SIZE()/graphie.scale[1]],{fill:"#dee1e3",stroke:"#dee1e3"});});return x},this.setPicHeight=(i,y)=>{const values=[...this.state.values];values[i]=y;this.drawPicHeights(values,this.state.values);this.setState({values:values});this.changeAndTrack({values:values});},this.changeAndTrack=data=>{this.props.onChange(data);this.props.trackInteraction();},this.drawPicHeights=(values,prevValues)=>{const self=this;const graphie=self.graphie;const pics=graphie.pics;const isMobile=this.props.apiOptions.isMobile;if(isMobile){const shouldDisplay=values.every(v=>v===0);graphie.dotPrompt[0].style.display=shouldDisplay?"inline":"none";}_.each(pics,function(ps,i){_.each(ps,function(pic,j){const y=(j+1)*self.props.scaleY;const show=y<=values[i];if(self.props.type==="dotplot"){const wasShown=y<=prevValues[i];const wasJustShown=show&&!wasShown;if(wasJustShown){pic.animate({"stroke-width":8},75,()=>pic.animate({"stroke-width":2},75));}}$(pic[0]).css({display:show?"inline":"none"});graphie.dotTicks[i][j][0].style.display=show||!isMobile?"none":"inline";});});};}}Plotter.contextType=PerseusI18nContext;Plotter.defaultProps={type:"bar",labels:["",""],categories:[""],scaleY:1,maxY:10,snapsPerLine:2,picSize:40,picBoxHeight:48,picUrl:"",plotDimensions:[380,300],labelInterval:1};const staticTransform=_.identity;var Plotter$1 = {name:"plotter",displayName:"Plotter",hidden:true,widget:Plotter,staticTransform:staticTransform};
2056
+ class Plotter extends React.Component{componentDidMount(){this._isMounted=true;this.setupGraphie(this.props.userInput);}UNSAFE_componentWillReceiveProps(nextProps){const props=["type","labels","categories","scaleY","maxY","snapsPerLine","picUrl","labelInterval","static"];this.shouldSetupGraphie=_.any(props,prop=>!_.isEqual(this.props[prop],nextProps[prop]),this);if(!_.isEqual(this.props.starting,nextProps.starting)&&!_.isEqual(this.props.userInput,nextProps.starting)){this.shouldSetupGraphie=true;this.props.handleUserInput(nextProps.starting);}}componentDidUpdate(prevProps,prevState){this.shouldSetupGraphie=this.shouldSetupGraphie||!_.isEqual(this.state.categoryHeights,prevState.categoryHeights);if(this.shouldSetupGraphie){this.setupGraphie(prevProps.userInput);}}componentWillUnmount(){this._isMounted=false;}setupGraphie(prevValues){const self=this;self.shouldSetupGraphie=false;$(this.graphieDiv.current).empty();const graphie=GraphUtils.createGraphie(this.graphieDiv.current);self.graphie=graphie;self.graphie.pics=[];self.graphie.dotTicks=[];const isBar=self.props.type==="bar";const isLine=self.props.type==="line";const isPic=self.props.type==="pic";const isHistogram=self.props.type==="histogram";const isDotplot=self.props.type==="dotplot";const isTiledPlot=isPic||isDotplot;const config={};const c=config;const isMobile=this.props.apiOptions.isMobile;c.graph={lines:[],bars:[],points:[],dividers:[]};c.scaleY=self.props.scaleY;c.dimX=self.props.categories.length;const plotDimensions=isMobile?[288,336]:self.props.plotDimensions;if(isLine){c.dimX+=isMobile?-0.2:1;}else if(isHistogram){c.barPad=0;c.barWidth=1;}else if(isBar){c.barPad=isMobile?.08:.15;c.barWidth=1-2*c.barPad;c.dimX+=(isMobile?-2:2)*c.barPad;}else if(isTiledPlot){c.picBoxHeight=self.props.picBoxHeight;c.picBoxWidthPx=plotDimensions[0]/self.props.categories.length;const picPadAllWidth=plotDimensions[0]-c.dimX*c.picBoxWidthPx;c.picPad=picPadAllWidth/(2*c.dimX+2);const picFullWidth=c.picBoxWidthPx+2*c.picPad;c.picPad=c.picPad/picFullWidth;c.picBoxWidth=c.picBoxWidthPx/picFullWidth;c.dimX+=2*c.picPad;}if(isDotplot){c.picBoxHeight=this.DOT_PLOT_POINT_SIZE()*2+this.DOT_PLOT_POINT_PADDING();}c.dimY=Math.ceil(self.props.maxY/c.scaleY)*c.scaleY;let padX=25;if((isBar||isLine)&&isMobile){padX=self.props.labels[1].length!==0?17:11;}if(isDotplot){padX/=2;}if(isMobile&&isTiledPlot&&self.props.labels[1].length===0){padX=0;}let padTop=25;let padBottom=25*3;if(isMobile&&(isBar||isTiledPlot)){const maxCategoryHeight=Math.max(0,...Object.values(self.state.categoryHeights));if(maxCategoryHeight){let offsetY=25;if(isTiledPlot){offsetY+=10;}padBottom=offsetY+maxCategoryHeight;}}if(isMobile){c.scale=[(plotDimensions[0]-padX*4)/c.dimX,(plotDimensions[1]-(padTop+padBottom))/c.dimY];}else {c.scale=_.map([c.dimX,c.dimY],function(dim,i){return plotDimensions[i]/dim});}if(isTiledPlot){c.scale[1]=c.picBoxHeight/c.scaleY;}padX/=c.scale[0];padTop/=c.scale[1];padBottom/=c.scale[1];graphie.init({range:[[-3*padX,c.dimX+padX],[-padBottom,c.dimY+padTop]],scale:c.scale,isMobile:this.props.apiOptions.isMobile});graphie.addMouseLayer({allowScratchpad:true,setDrawingAreaAvailable:this.props.apiOptions.setDrawingAreaAvailable});if(!isTiledPlot){const initialY=isMobile?c.scaleY:0;for(let y=initialY;y<=c.dimY;y+=c.scaleY){graphie.label([0,y],KhanMath.roundToApprox(y,2),"left",true);graphie.style({stroke:isMobile?"#e9ebec":"#000",strokeWidth:1,opacity:isMobile?1:.3},function(){graphie.line([0,y],[c.dimX,y]);});}}if((isBar||isLine)&&isMobile&&!this.props.static){self.graphie.dragPrompt=graphie.label([c.dimX/2,c.dimY/2],this.context.strings.dragHandles,"center",false).css("font-weight","bold").css("color",KhanColors.KA_GREEN).css("display","none");}self.setupCategories(config);if(isTiledPlot&&isMobile){self.graphie.dotPrompt=graphie.label([c.dimX/2,c.dimY/2],this.context.strings.tapAddPoints,"center",false).css("font-weight","bold").css("color",KhanColors.KA_GREEN).css("display","none");}if(isTiledPlot){self.drawPicHeights(self.props.userInput,prevValues);}graphie.style({stroke:"#000",strokeWidth:2,opacity:1},function(){if(isTiledPlot){if(isDotplot){graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2},()=>graphie.line([isMobile?0:.5,0],[c.dimX-(isMobile?0:.5),0]));}else {graphie.line([0,0],[c.dimX,0]);if(self.props.labels[1].length!==0||!isMobile){graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2},()=>graphie.line([0,0],[0,c.dimY]));}}}else {graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2},()=>graphie.line([isMobile?-padX*3:0,0],[c.dimX+(isMobile?padX:0),0]));if(!((isBar||isLine)&&isMobile)){graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2},()=>graphie.line([0,0],[0,c.dimY]));}}});graphie.label([c.dimX/2,isMobile?-padBottom:-35/c.scale[1]],self.props.labels[0],isMobile?"above":"below",false).css("font-weight","bold").css("color",isMobile&&KhanColors.GRAY_F);graphie.label([(isMobile?-35:-60)/c.scale[0],c.dimY/2],self.props.labels[1],"center",false).css("font-weight","bold").css("color",isMobile&&KhanColors.GRAY_F).addClass("rotate");if(this.props.apiOptions.isMobile){this.horizHairline=new WrappedLine(this.graphie,[0,0],[0,0],{normalStyle:{strokeWidth:1}});this.horizHairline.attr({stroke:KhanColors.INTERACTIVE});this.horizHairline.hide();this.hairlineRange=[[0,c.dimX],[0,c.dimY]];}}_testInsertUserInput(userInput){this.props.handleUserInput(userInput);}drawPicHeights(values,prevValues){const self=this;const graphie=self.graphie;const pics=graphie.pics;const isMobile=this.props.apiOptions.isMobile;if(isMobile){const shouldDisplay=values.every(v=>v===0);graphie.dotPrompt[0].style.display=shouldDisplay?"inline":"none";}_.each(pics,function(ps,i){_.each(ps,function(pic,j){const y=(j+1)*self.props.scaleY;const show=y<=values[i];if(self.props.type==="dotplot"){const wasShown=y<=prevValues[i];const wasJustShown=show&&!wasShown;if(wasJustShown){pic.animate({"stroke-width":8},75,()=>pic.animate({"stroke-width":2},75));}}$(pic[0]).css({display:show?"inline":"none"});graphie.dotTicks[i][j][0].style.display=show||!isMobile?"none":"inline";});});}getUserInput(){return this.props.userInput}getPromptJSON(){return getPromptJSON$3()}getSerializedState(){const{userInput:_,...rest}=this.props;return {...rest,values:this.props.userInput}}render(){const paddingForBottomLabel=75;const style={marginBottom:this.props.labels[0]?paddingForBottomLabel:0};return jsxRuntimeExports.jsx("div",{className:"perseus-widget-plotter graphie",ref:this.graphieDiv,style:style})}constructor(...args){super(...args),this._isMounted=false,this.graphieDiv=React.createRef(),this.state={categoryHeights:{}},this.DOT_PLOT_POINT_SIZE=()=>{return this.props.apiOptions.isMobile?6:4},this.DOT_PLOT_POINT_PADDING=()=>{return 8},this.DOT_TICK_POINT_SIZE=()=>{return 2},this.showHairlines=point=>{if(this.props.apiOptions.isMobile){this.horizHairline.moveTo([this.hairlineRange[0][0],point[1]],[this.hairlineRange[0][1],point[1]]);this.horizHairline.show();}},this.hideHairlines=()=>{if(this.props.apiOptions.isMobile){this.horizHairline.hide();}},this.labelCategory=(x,category)=>{const isMobile=this.props.apiOptions.isMobile;const graphie=this.graphie;category=category+"";let isTeX=false;const mathyCategory=category.match(/^\$(.*)\$$/);if(mathyCategory){category=mathyCategory[1];isTeX=true;}const translateX=5;const rotationDeg=45;const rotationRad=rotationDeg*(Math.PI/180);const labelRotation=`translateX(-50%) translateX(${translateX}px) `+`translateY(-50%) rotate(-${rotationDeg}deg)`;const shouldRotate=isMobile&&!mathyCategory;return new Promise(resolve=>{graphie.style({color:isMobile?KhanColors.GRAY_G:"inherit",transform:shouldRotate?labelRotation:"none",transformOrigin:"100%"},()=>{const $span=graphie.label([x,isMobile?-0.5:0],category,"below",isTeX);const height=14+(shouldRotate?Math.round($span.height()*Math.cos(rotationRad)+($span.width()+translateX)*Math.sin(rotationRad)):$span.height());resolve({category,height});});})},this.setupCategories=config=>{const self=this;const c=config;const graphie=self.graphie;const isMobile=this.props.apiOptions.isMobile;const categoryHeightPromises=[];if(self.props.type==="histogram"){_.times(self.props.categories.length-1,function(i){self.setupBar({index:i,startHeight:self.props.userInput[i],config:config,isHistogram:true});});_.each(self.props.categories,function(category,i){const x=.5+i*c.barWidth;categoryHeightPromises.push(self.labelCategory(x,category));const tickHeight=6/c.scale[1];graphie.style({stroke:"#000",strokeWidth:isMobile?1:2,opacity:1},function(){graphie.line([x,-tickHeight],[x,0]);});});}else {_.each(self.props.categories,function(category,i){const startHeight=self.props.userInput[i];let x;if(self.props.type==="bar"){x=self.setupBar({index:i,startHeight:startHeight,config:config,isHistogram:false});}else if(self.props.type==="line"){x=self.setupLine(i,startHeight,config);}else if(self.props.type==="pic"){x=self.setupPic(i,config);}else if(self.props.type==="dotplot"){x=self.setupDotplot(i,config);}let tickStart=0;let tickEnd=-6/c.scale[1];if(self.props.type==="dotplot"&&!isMobile){tickStart=-tickEnd;}if(self.props.type==="dotplot"){if(i%self.props.labelInterval===0||i===self.props.categories.length-1){categoryHeightPromises.push(self.labelCategory(x,category));tickStart*=1.5;tickEnd*=1.5;}}else {categoryHeightPromises.push(self.labelCategory(x,category));}graphie.style({stroke:isMobile?KhanColors.GRAY_G:"#000",strokeWidth:isMobile?1:2,opacity:1},function(){graphie.line([x,tickStart],[x,tickEnd]);});});}Promise.all(categoryHeightPromises).then(measurements=>{if(self._isMounted){const categoryHeights={};measurements.forEach(({category,height})=>categoryHeights[category]=height);self.setState({categoryHeights});}});},this._clampValue=(v,min,max)=>{return Math.max(Math.min(v,max),min)},this._maybeShowDragPrompt=()=>{if(this.graphie.dragPrompt!=null){this.graphie.dragPrompt[0].style.display="inline";}},this._maybeHideDragPrompt=()=>{if(this.graphie.dragPrompt!=null){this.graphie.dragPrompt[0].style.display="none";}},this.setupBar=args=>{const isMobile=this.props.apiOptions.isMobile;const i=args.index;const startHeight=args.startHeight;const config=args.config;const isHistogram=args.isHistogram;const self=this;const graphie=self.graphie;const barHalfWidth=config.barWidth/2;let x;if(isHistogram){x=.5+i*config.barWidth+barHalfWidth;}else {x=(isMobile?barHalfWidth:.5+config.barPad)+i;}const scaleBar=function(i,height){const center=graphie.scalePoint(0);config.graph.bars[i].scale(1,Math.max(isMobile?.2:.01,height/config.scaleY),center[0],center[1]);if(isHistogram){const leftDivider=config.graph.dividers[i-1];const rightDivider=config.graph.dividers[i];if(leftDivider){const divHeight=Math.min(self.props.userInput[i-1],height);leftDivider.scale(1,Math.max(.01,divHeight/config.scaleY),center[0],center[1]);}if(rightDivider){const divHeight=Math.min(self.props.userInput[i+1],height);rightDivider.scale(1,Math.max(.01,divHeight/config.scaleY),center[0],center[1]);}}};graphie.style({stroke:"none",fill:isMobile?KhanColors.BLUE_C:KhanColors.LIGHT_BLUE,opacity:1},function(){config.graph.bars[i]=graphie.path([[x-barHalfWidth,0],[x-barHalfWidth,config.scaleY],[x+barHalfWidth,config.scaleY],[x+barHalfWidth,0],[x-barHalfWidth,0]]);});if(isHistogram){if(i>0){graphie.style({stroke:"#000",strokeWidth:1,opacity:.3},function(){config.graph.dividers.push(graphie.path([[x-barHalfWidth,0],[x-barHalfWidth,config.scaleY]]));});}}if(isMobile){const snap=config.scaleY/self.props.snapsPerLine;config.graph.lines[i]=Interactive2.addMaybeMobileMovablePoint(this,{coord:[x,startHeight],constraints:[(coord,prev,options)=>{return [x,this._clampValue(Math.round(coord[1]/snap)*snap,0,config.dimY)]}],onMoveStart:function(){config.graph.bars[i].attr({fill:KhanColors.INTERACTIVE});},onMove:function(){const y=config.graph.lines[i].coord()[1];const values=[...self.props.userInput];values[i]=y;self.changeAndTrack(values);self._maybeHideDragPrompt();scaleBar(i,y);},onMoveEnd:function(){config.graph.bars[i].attr({fill:KhanColors.BLUE_C});}});config.graph.lines[i].state.visibleShape.wrapper.style.zIndex="1";self._maybeShowDragPrompt();}else {config.graph.lines[i]=graphie.addMovableLineSegment({coordA:[x-barHalfWidth,startHeight],coordZ:[x+barHalfWidth,startHeight],snapY:config.scaleY/self.props.snapsPerLine,constraints:{constrainX:true},normalStyle:{stroke:KhanColors.INTERACTIVE,"stroke-width":this.props.static?0:4}});config.graph.lines[i].onMove=function(dx,dy){let y=this.coordA[1];if(y<0||y>config.dimY){y=Math.min(Math.max(y,0),config.dimY);this.coordA[1]=this.coordZ[1]=y;this.transform();}const values=[...self.props.userInput];values[i]=y;self.changeAndTrack(values);scaleBar(i,y);};}scaleBar(i,startHeight);return x},this.setupLine=(i,startHeight,config)=>{const isMobile=this.props.apiOptions.isMobile;const self=this;const c=config;const graphie=self.graphie;const x=i+(isMobile?.4:1);if(isMobile){const snap=config.scaleY/self.props.snapsPerLine;c.graph.points[i]=Interactive2.addMaybeMobileMovablePoint(this,{coord:[x,startHeight],constraints:[(coord,prev,options)=>{return [x,this._clampValue(Math.round(coord[1]/snap)*snap,0,config.dimY)]}],onMove:function(){const y=c.graph.points[i].coord()[1];const values=[...self.props.userInput];values[i]=y;self.changeAndTrack(values);self._maybeHideDragPrompt();}});self._maybeShowDragPrompt();if(i>0){c.graph.lines[i]=Interactive2.addMovableLine(graphie,{points:[c.graph.points[i-1],c.graph.points[i]],constraints:Interactive2.MovablePoint.constraints.fixed(),normalStyle:{stroke:KhanColors.BLUE_C,"stroke-width":2},highlightStyle:{stroke:KhanColors.BLUE_C,"stroke-width":2}});}}else {c.graph.points[i]=graphie.addMovablePoint({coord:[x,startHeight],constraints:{constrainX:true},normalStyle:{fill:KhanColors.INTERACTIVE,stroke:KhanColors.INTERACTIVE},snapY:c.scaleY/self.props.snapsPerLine});c.graph.points[i].onMove=function(x,y){y=Math.min(Math.max(y,0),c.dimY);const values=[...self.props.userInput];values[i]=y;self.changeAndTrack(values);return [x,y]};if(i>0){c.graph.lines[i]=graphie.addMovableLineSegment({pointA:c.graph.points[i-1],pointZ:c.graph.points[i],constraints:{fixed:true},normalStyle:{stroke:"#9ab8ed","stroke-width":2}});}}return x},this.setupDotplot=(i,config)=>{const graphie=this.graphie;const isMobile=this.props.apiOptions.isMobile;return this.setupTiledPlot(i,isMobile?.5:1,config,(x,y)=>{return graphie.ellipse([x,y],[this.DOT_PLOT_POINT_SIZE()/graphie.scale[0],this.DOT_PLOT_POINT_SIZE()/graphie.scale[1]],{fill:KhanColors.INTERACTIVE,stroke:KhanColors.INTERACTIVE})})},this.setupPic=(i,config)=>{const graphie=this.graphie;return this.setupTiledPlot(i,0,config,(x,y)=>{const scaledCenter=graphie.scalePoint([x,y]);const size=this.props.picSize;return graphie.raphael.image(this.props.picUrl,scaledCenter[0]-size/2,scaledCenter[1]-size/2,size,size)})},this.setupTiledPlot=(i,bottomMargin,config,createImage)=>{const self=this;const c=config;const graphie=self.graphie;const pics=graphie.pics;const dotTicks=graphie.dotTicks;const x=i+.5+c.picPad;graphie.mouselayer.canvas.style.touchAction="none";pics[i]=[];dotTicks[i]=[];const n=Math.round(c.dimY/c.scaleY)+1;_(n).times(function(j){j-=1;const midY=(j+.5)*c.scaleY;const leftX=x-c.picBoxWidth/2;const topY=midY+.5*c.scaleY;const coord=graphie.scalePoint([leftX,topY+bottomMargin]);const mouseRect=graphie.mouselayer.rect(coord[0],coord[1],c.picBoxWidthPx,c.picBoxHeight);$(mouseRect[0]).css({fill:"#000",opacity:0,cursor:"pointer"}).on("vmousedown",function(e){e.preventDefault();self.whichPicClicked=i;self.setPicHeight(i,topY);$(document).on("vmouseup.plotTile",function(e){$(document).unbind(".plotTile");});$(document).on("vmousemove.plotTile",function(e){e.preventDefault();const yCoord=graphie.getMouseCoord(e)[1];const adjustedCoord=Math.floor(yCoord-bottomMargin);const newJ=Math.max(-1,Math.floor(adjustedCoord/c.scaleY));const newMidY=(newJ+.5)*c.scaleY;const newTopY=Math.min(newMidY+.5*c.scaleY,c.dimY);self.setPicHeight(self.whichPicClicked,newTopY);});});if(j<0){return}pics[i][j]=createImage(x,midY+bottomMargin);dotTicks[i][j]=graphie.ellipse([x,midY+bottomMargin],[self.DOT_TICK_POINT_SIZE()/graphie.scale[0],self.DOT_TICK_POINT_SIZE()/graphie.scale[1]],{fill:"#dee1e3",stroke:"#dee1e3"});});return x},this.setPicHeight=(i,y)=>{const values=[...this.props.userInput];values[i]=y;this.drawPicHeights(values,this.props.userInput);this.changeAndTrack(values);},this.changeAndTrack=userInput=>{this.props.handleUserInput(userInput);this.props.trackInteraction();};}}Plotter.contextType=PerseusI18nContext;Plotter.defaultProps={type:"bar",labels:["",""],categories:[""],scaleY:1,maxY:10,snapsPerLine:2,picSize:40,picBoxHeight:48,picUrl:"",plotDimensions:[380,300],labelInterval:1};function getStartUserInput$2(options){return options.starting}function getUserInputFromSerializedState$2(serializedState){return serializedState.values}var Plotter$1 = {name:"plotter",displayName:"Plotter",hidden:true,widget:Plotter,staticTransform:_.identity,getStartUserInput: getStartUserInput$2,getUserInputFromSerializedState: getUserInputFromSerializedState$2};
2053
2057
 
2054
2058
  const getPromptJSON$2=()=>{return getUnsupportedPromptJSON("python-program")};
2055
2059
 
@@ -2057,21 +2061,21 @@ function getUrlFromProgramID(programID){const path=`/python-program/${programID}
2057
2061
 
2058
2062
  const getPromptJSON$1=userInput=>{return {type:"sorter",userInput:{values:userInput.options,changed:userInput.changed}}};
2059
2063
 
2060
- class Sorter extends React.Component{componentDidMount(){this._isMounted=true;}componentWillUnmount(){this._isMounted=false;}getUserInput(){const options=this.refs.sortable.getOptions();return {options,changed:this.state.changed}}getPromptJSON(){return getPromptJSON$1(this.getUserInput())}render(){const options=shuffleSorter(this.props);const{apiOptions}=this.props;const marginPx=apiOptions.isMobile?8:5;return jsxRuntimeExports.jsx("div",{className:"perseus-widget-sorter perseus-clearfix",children:jsxRuntimeExports.jsx(Sortable,{options:options,layout:this.props.layout,margin:marginPx,padding:this.props.padding,onChange:this.handleChange,linterContext:this.props.linterContext,ref:"sortable"})})}constructor(...args){super(...args),this._isMounted=false,this.state={changed:false},this.handleChange=e=>{if(!this._isMounted){return}this.setState({changed:true},()=>{setTimeout(()=>{this.props.onChange(e);this.props.trackInteraction();},0);});},this.moveOptionToIndex=(option,index)=>{this.refs.sortable.moveOptionToIndex(option,index);};}}Sorter.defaultProps={correct:[],layout:"horizontal",padding:true,problemNum:0,onChange:function(){},linterContext:linterContextDefault};var Sorter$1 = {name:"sorter",displayName:"Sorter",widget:Sorter,isLintable:true};
2064
+ class Sorter extends React.Component{componentDidMount(){this._isMounted=true;}componentWillUnmount(){this._isMounted=false;}getUserInput(){return this.props.userInput}_getOptionsFromSortable(){const options=this.refs.sortable.getOptions();return options}getPromptJSON(){return getPromptJSON$1(this.getUserInput())}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,changed:userInput.changed,options:userInput.options}}render(){const{apiOptions,userInput}=this.props;const marginPx=apiOptions.isMobile?8:5;return jsxRuntimeExports.jsx("div",{className:"perseus-widget-sorter perseus-clearfix",children:jsxRuntimeExports.jsx(Sortable,{options:userInput.options,layout:this.props.layout,margin:marginPx,padding:this.props.padding,onChange:this.handleChange,linterContext:this.props.linterContext,ref:"sortable"})})}constructor(...args){super(...args),this._isMounted=false,this.handleChange=e=>{if(!this._isMounted){return}this.props.handleUserInput({options:this._getOptionsFromSortable(),changed:true});this.props.trackInteraction();},this.moveOptionToIndex=(option,index)=>{this.refs.sortable.moveOptionToIndex(option,index);};}}Sorter.defaultProps={correct:[],layout:"horizontal",padding:true,problemNum:0,linterContext:linterContextDefault};function getStartUserInput$1(options,problemNum){const shuffled=shuffleSorter(options,problemNum);return {options:shuffled,changed:false}}function getUserInputFromSerializedState$1(serializedState){return {changed:serializedState.changed,options:serializedState.options}}var Sorter$1 = {name:"sorter",displayName:"Sorter",widget:Sorter,isLintable:true,getStartUserInput: getStartUserInput$1,getUserInputFromSerializedState: getUserInputFromSerializedState$1};
2061
2065
 
2062
- const{assert}=InteractiveUtil;function getInputPath(row,column){return [""+row,""+column]}function getDefaultPath(){return getInputPath(0,0)}function getRowFromPath(path){assert(Array.isArray(path)&&path.length===2);return +path[0]}function getColumnFromPath(path){assert(Array.isArray(path)&&path.length===2);return +path[1]}function getRefForPath(path){const row=getRowFromPath(path);const column=getColumnFromPath(path);return "answer"+row+","+column}class Table extends React.Component{_getRows(){return this.props.answers.length}_getColumns(){return this.props.answers[0].length}_getAnswersClone(){return JSON.parse(JSON.stringify(this.props.answers))}getUserInput(){return this._getAnswersClone()}onValueChange(row,column,eventOrValue){const answers=this._getAnswersClone();answers[row][column]=eventOrValue.target?eventOrValue.target.value:eventOrValue;this.props.onChange({answers:answers});this.props.trackInteraction();}onHeaderChange(index,e){const headers=this.props.headers.slice();headers[index]=e.content;this.props.onChange({headers:headers});}_handleFocus(inputPath){this.props.onFocus(inputPath);}_handleBlur(inputPath){this.props.onBlur(inputPath);}focus(){this.focusInputPath(getDefaultPath());return true}focusInputPath(path){const inputID=getRefForPath(path);const inputComponent=this.answerRefs[inputID];if(this.props.apiOptions.customKeypad){inputComponent.focus();}else {ReactDOM__default.findDOMNode(inputComponent).focus();}}blurInputPath(path){const inputID=getRefForPath(path);const inputComponent=this.answerRefs[inputID];if(this.props.apiOptions.customKeypad){inputComponent.blur();}else {ReactDOM__default.findDOMNode(inputComponent).blur();}}getDOMNodeForPath(path){const inputID=getRefForPath(path);const inputRef=this.answerRefs[inputID];return ReactDOM__default.findDOMNode(inputRef)}getInputPaths(){const rows=this._getRows();const columns=this._getColumns();const inputPaths=[];for(let r=0;r<rows;r++){for(let c=0;c<columns;c++){const inputPath=getInputPath(r,c);inputPaths.push(inputPath);}}return inputPaths}setInputValue(path,newValue,cb){const typedPath=path;const row=getRowFromPath(typedPath);const column=getColumnFromPath(typedPath);const answers=this._getAnswersClone();answers[row][column]=newValue;this.props.onChange({answers:answers},cb);}render(){const headers=this.props.headers;let InputComponent;let inputStyle;const extraInputProps={};if(this.props.apiOptions.customKeypad){InputComponent=SimpleKeypadInput;inputStyle={width:80};extraInputProps.keypadElement=this.props.keypadElement;}else {InputComponent="input";inputStyle={};}return jsxRuntimeExports.jsxs("table",{className:"perseus-widget-table-of-values non-markdown",children:[jsxRuntimeExports.jsx("thead",{children:jsxRuntimeExports.jsx("tr",{children:headers.map((header,i)=>{if(this.props.editableHeaders){return jsxRuntimeExports.jsx("th",{children:jsxRuntimeExports.jsx(this.props.Editor,{ref:ref=>{this.headerRefs["columnHeader"+i]=ref;},apiOptions:this.props.apiOptions,content:header,widgetEnabled:false,onChange:e=>this.onHeaderChange(i,e)})},i)}return jsxRuntimeExports.jsx("th",{children:jsxRuntimeExports.jsx(Renderer,{content:header,linterContext:this.props.linterContext,strings:this.context.strings})},i)})})}),jsxRuntimeExports.jsx("tbody",{children:this.props.answers.map((rowArr,r)=>{return jsxRuntimeExports.jsx("tr",{children:rowArr.map((answer,c)=>{return jsxRuntimeExports.jsx("td",{children:jsxRuntimeExports.jsx(InputComponent,{ref:ref=>{this.answerRefs[getRefForPath(getInputPath(r,c))]=ref;},type:"text",value:answer,disabled:this.props.apiOptions.readOnly,onFocus:()=>this._handleFocus(getInputPath(r,c)),onBlur:()=>this._handleBlur(getInputPath(r,c)),onChange:e=>this.onValueChange(r,c,e),style:inputStyle,...extraInputProps})},c)})},r)})})]})}constructor(...args){super(...args),this.headerRefs={},this.answerRefs={};}}Table.contextType=PerseusI18nContext;Table.defaultProps=function(){const defaultRows=4;const defaultColumns=1;const blankAnswers=Util.stringArrayOfSize2D({rows:defaultRows,columns:defaultColumns});return {apiOptions:ApiOptions.defaults,headers:[""],editableHeaders:false,rows:defaultRows,columns:defaultColumns,answers:blankAnswers,linterContext:linterContextDefault}}();function propTransform(options){const rows=options.rows;const columns=options.columns;const blankAnswers=Util.stringArrayOfSize2D({rows,columns});return {...options,answers:blankAnswers}}var Table$1 = {name:"table",displayName:"Table (deprecated - use markdown table instead)",widget:Table,transform:propTransform,hidden:true,isLintable:true};
2066
+ const{assert}=InteractiveUtil;function getInputPath(row,column){return [""+row,""+column]}function getDefaultPath(){return getInputPath(0,0)}function getRowFromPath(path){assert(Array.isArray(path)&&path.length===2);return +path[0]}function getColumnFromPath(path){assert(Array.isArray(path)&&path.length===2);return +path[1]}function getRefForPath(path){const row=getRowFromPath(path);const column=getColumnFromPath(path);return "answer"+row+","+column}class Table extends React.Component{_getRows(){return this.props.userInput.length}_getColumns(){return this.props.userInput[0].length}_getAnswersClone(){return JSON.parse(JSON.stringify(this.props.userInput))}getUserInput(){return this._getAnswersClone()}onValueChange(row,column,eventOrValue){const answers=this._getAnswersClone();answers[row][column]=eventOrValue.target?eventOrValue.target.value:eventOrValue;this.props.handleUserInput(answers);this.props.trackInteraction();}onHeaderChange(index,e){const headers=this.props.headers.slice();headers[index]=e.content;this.props.onChange({headers:headers});}_handleFocus(inputPath){this.props.onFocus(inputPath);}_handleBlur(inputPath){this.props.onBlur(inputPath);}focus(){this.focusInputPath(getDefaultPath());return true}focusInputPath(path){const inputID=getRefForPath(path);const inputComponent=this.answerRefs[inputID];if(this.props.apiOptions.customKeypad){inputComponent.focus();}else {ReactDOM__default.findDOMNode(inputComponent).focus();}}blurInputPath(path){const inputID=getRefForPath(path);const inputComponent=this.answerRefs[inputID];if(this.props.apiOptions.customKeypad){inputComponent.blur();}else {ReactDOM__default.findDOMNode(inputComponent).blur();}}getDOMNodeForPath(path){const inputID=getRefForPath(path);const inputRef=this.answerRefs[inputID];return ReactDOM__default.findDOMNode(inputRef)}getInputPaths(){const rows=this._getRows();const columns=this._getColumns();const inputPaths=[];for(let r=0;r<rows;r++){for(let c=0;c<columns;c++){const inputPath=getInputPath(r,c);inputPaths.push(inputPath);}}return inputPaths}setInputValue(path,newValue,cb){const typedPath=path;const row=getRowFromPath(typedPath);const column=getColumnFromPath(typedPath);const answers=this._getAnswersClone();answers[row][column]=newValue;this.props.handleUserInput(answers,cb);}getSerializedState(){const{userInput,editableHeaders:_,...rest}=this.props;return {...rest,answers:userInput}}render(){const headers=this.props.headers;let InputComponent;let inputStyle;const extraInputProps={};if(this.props.apiOptions.customKeypad){InputComponent=SimpleKeypadInput;inputStyle={width:80};extraInputProps.keypadElement=this.props.keypadElement;}else {InputComponent="input";inputStyle={};}return jsxRuntimeExports.jsxs("table",{className:"perseus-widget-table-of-values non-markdown",children:[jsxRuntimeExports.jsx("thead",{children:jsxRuntimeExports.jsx("tr",{children:headers.map((header,i)=>{if(this.props.editableHeaders){return jsxRuntimeExports.jsx("th",{children:jsxRuntimeExports.jsx(this.props.Editor,{ref:ref=>{this.headerRefs["columnHeader"+i]=ref;},apiOptions:this.props.apiOptions,content:header,widgetEnabled:false,onChange:e=>this.onHeaderChange(i,e)})},i)}return jsxRuntimeExports.jsx("th",{children:jsxRuntimeExports.jsx(Renderer,{content:header,linterContext:this.props.linterContext,strings:this.context.strings})},i)})})}),jsxRuntimeExports.jsx("tbody",{children:this.props.userInput.map((rowArr,r)=>{return jsxRuntimeExports.jsx("tr",{children:rowArr.map((answer,c)=>{return jsxRuntimeExports.jsx("td",{children:jsxRuntimeExports.jsx(InputComponent,{ref:ref=>{this.answerRefs[getRefForPath(getInputPath(r,c))]=ref;},type:"text",value:answer,disabled:this.props.apiOptions.readOnly,onFocus:()=>this._handleFocus(getInputPath(r,c)),onBlur:()=>this._handleBlur(getInputPath(r,c)),onChange:e=>this.onValueChange(r,c,e),style:inputStyle,...extraInputProps})},c)})},r)})})]})}constructor(...args){super(...args),this.headerRefs={},this.answerRefs={};}}Table.contextType=PerseusI18nContext;Table.defaultProps={apiOptions:ApiOptions.defaults,headers:[""],editableHeaders:false,rows:4,columns:1,linterContext:linterContextDefault};function getStartUserInput(options){const rows=options.rows;const columns=options.columns;return Util.stringArrayOfSize2D({rows,columns})}function getUserInputFromSerializedState(serializedState){return serializedState.answers}var Table$1 = {name:"table",displayName:"Table (deprecated - use markdown table instead)",widget:Table,hidden:true,isLintable:true,getStartUserInput,getUserInputFromSerializedState};
2063
2067
 
2064
2068
  const getPromptJSON=()=>{return getUnsupportedPromptJSON("video")};
2065
2069
 
2066
2070
  const IS_URL$1=/^https?:\/\//;const getYoutubeId=url=>{const regExp=/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;const match=url.match(regExp);if(match&&match[7].length===11){return match[7]}return "videoNotFound"};const VideoTranscriptLink=props=>{const{location}=props;const{useVideo}=useDependencies();const[id,kind]=IS_URL$1.test(location)?[getYoutubeId(location),"YOUTUBE_ID"]:[location,"READABLE_ID"];const result=useVideo(id,kind);const{strings}=usePerseusI18n();switch(result.status){case "loading":return jsxRuntimeExports.jsx(View,{children:strings.loading});case "success":{const video=result.data?.video;return jsxRuntimeExports.jsxs(View,{style:styles$3.transcriptLink,children:[jsxRuntimeExports.jsx(Text$1,{children:video?.title}),jsxRuntimeExports.jsx(Strut,{size:10}),jsxRuntimeExports.jsx(Link,{href:"/transcript/"+(video?.contentId||"videoNotFound"),target:"_blank",className:"visited-no-recolor",children:strings.videoTranscript})]})}case "error":return jsxRuntimeExports.jsx(View,{children:strings.somethingWrong});case "aborted":return jsxRuntimeExports.jsx(View,{children:strings.somethingWrong});default:return jsxRuntimeExports.jsx(View,{children:strings.somethingWrong})}};const styles$3=StyleSheet.create({transcriptLink:{flexDirection:"row",width:"100%",justifyContent:"center"}});
2067
2071
 
2068
- const DEFAULT_WIDTH=1280;const DEFAULT_HEIGHT=720;const KA_EMBED="{host}/embed_video?slug={slug}"+"&internal_video_only=1";const IS_URL=/^https?:\/\//;const IS_KA_SITE=/(khanacademy\.org|localhost)/;const IS_VIMEO=/(vimeo\.com)/;class Video extends React.Component{getPromptJSON(){return getPromptJSON()}render(){const{InitialRequestUrl}=getDependencies();const location=this.props.location;if(!location){return jsxRuntimeExports.jsx("div",{})}let url;if(IS_URL.test(location)){url=location;if(IS_VIMEO.test(url)){if(url.indexOf("?")===-1){url+="?dnt=1";}else {url+="&dnt=1";}}}else {url=KA_EMBED.replace("{slug}",location);let embedHostname="https://www.khanacademy.org";if(IS_KA_SITE.test(InitialRequestUrl.host)){embedHostname=InitialRequestUrl.origin;}url=url.replace("{host}",embedHostname);}return jsxRuntimeExports.jsxs(View,{children:[jsxRuntimeExports.jsxs(FixedToResponsive,{width:DEFAULT_WIDTH,height:DEFAULT_HEIGHT,children:[jsxRuntimeExports.jsx(View,{style:a11y.srOnly,children:this.context.strings.videoWrapper}),jsxRuntimeExports.jsx("iframe",{className:"perseus-video-widget",sandbox:"allow-same-origin allow-scripts",width:DEFAULT_WIDTH,height:DEFAULT_HEIGHT,src:url,allowFullScreen:true})]},location+this.props.alignment),jsxRuntimeExports.jsx(VideoTranscriptLink,{location:location})]})}constructor(...args){super(...args),this.isWidget=true,this.change=(...args)=>{return change.apply(this,args)};}}Video.contextType=PerseusI18nContext;var Video$1 = {name:"video",displayName:"Video",widget:Video};
2072
+ const DEFAULT_WIDTH=1280;const DEFAULT_HEIGHT=720;const KA_EMBED="{host}/embed_video?slug={slug}"+"&internal_video_only=1";const IS_URL=/^https?:\/\//;const IS_KA_SITE=/(khanacademy\.org|localhost)/;const IS_VIMEO=/(vimeo\.com)/;class Video extends React.Component{getPromptJSON(){return getPromptJSON()}render(){const{InitialRequestUrl}=getDependencies();const location=this.props.location;if(!location){return jsxRuntimeExports.jsx("div",{})}let url;if(IS_URL.test(location)){url=location;if(IS_VIMEO.test(url)){if(url.indexOf("?")===-1){url+="?dnt=1";}else {url+="&dnt=1";}}}else {url=KA_EMBED.replace("{slug}",location);let embedHostname="https://www.khanacademy.org";if(IS_KA_SITE.test(InitialRequestUrl.host)){embedHostname=InitialRequestUrl.origin;}url=url.replace("{host}",embedHostname);}return jsxRuntimeExports.jsxs(View,{children:[jsxRuntimeExports.jsxs(FixedToResponsive,{width:DEFAULT_WIDTH,height:DEFAULT_HEIGHT,children:[jsxRuntimeExports.jsx(View,{style:a11y.srOnly,children:this.context.strings.videoWrapper}),jsxRuntimeExports.jsx("iframe",{className:"perseus-video-widget",sandbox:"allow-same-origin allow-scripts",width:DEFAULT_WIDTH,height:DEFAULT_HEIGHT,src:url,allowFullScreen:true})]},location+this.props.alignment),jsxRuntimeExports.jsx(VideoTranscriptLink,{location:location})]})}constructor(...args){super(...args),this.isWidget=true;}}Video.contextType=PerseusI18nContext;var Video$1 = {name:"video",displayName:"Video",widget:Video};
2069
2073
 
2070
2074
  var extraWidgets = [CSProgram$1,Categorizer$1,Definition$1,DeprecatedStandin$1,Dropdown$1,Explanation$1,FreeResponse$1,GradedGroup$1,GradedGroupSet$1,Grapher$1,Group$1,Iframe$1,Image$1,Interactive,InteractiveGraph$1,LabelImage$1,Matcher$1,Matrix$1,Measurer$1,Molecule$1,NumberLine$1,Orderer$1,Passage$1,PassageRef$1,PassageRefTarget$1,PhetSimulation$1,Plotter$1,PythonProgram$1,Sorter$1,Table$1,Video$1];
2071
2075
 
2072
2076
  const init=function(){registerWidgets(basicWidgets);registerWidgets(extraWidgets);replaceDeprecatedWidgets();};
2073
2077
 
2074
- const libName="@khanacademy/perseus";const libVersion="65.3.7";addLibraryVersionToPerseusDebug(libName,libVersion);
2078
+ const libName="@khanacademy/perseus";const libVersion="65.5.0";addLibraryVersionToPerseusDebug(libName,libVersion);
2075
2079
 
2076
2080
  const apiVersion={major:12,minor:0};
2077
2081
 
@@ -2097,8 +2101,6 @@ function displaySigFigs(f,sigFigs,sigDecs,scientific){const s=""+f;let order=par
2097
2101
 
2098
2102
  const registerAllWidgetsForTesting=()=>{registerWidgets(allWidgets);replaceDeprecatedWidgets();};
2099
2103
 
2100
- function getWidgetTypeByWidgetId(WidgetId,widgetMap){const widget=widgetMap[WidgetId];return widget?.type??null}function getWidgetSubTypeByWidgetId(widgetId,widgetMap){const widget=widgetMap[widgetId];const widgetType=widget?.type??null;switch(widgetType){case "interactive-graph":const graph=widget.options.graph;return graph?.type??null;default:return null}}function contentHasWidgetType(type,content,widgetMap){return getWidgetIdsFromContentByType(type,content,widgetMap).length>0}function getWidgetsMapFromItemData(itemData){return itemData.question.widgets}function getWidgetFromWidgetMap(widgetId,widgetMap){return widgetMap[widgetId]??null}function getWidgetsFromWidgetMap(widgetIds,widgetMap){const widgets={};widgetIds.forEach(widgetId=>{const widget=getWidgetFromWidgetMap(widgetId,widgetMap);if(widget){widgets[widgetId]=widget;}});return widgets}
2101
-
2102
2104
  function getAnswersFromWidgets(widgets){const answers=[];keys(widgets).forEach(widgetID=>{const widget=widgets[widgetID];if(!widget.options){return}switch(widget.type){case "radio":const radio=widget;const options=radio.options;if(options?.choices?.length){for(const choice of options.choices){if(choice?.correct){answers.push(choice.content);}}}break;case "categorizer":const categorizer=widget;if(categorizer.options?.categories&&categorizer.options?.items&&categorizer.options?.values){const categories=categorizer.options?.categories;const items=categorizer.options?.items;const values=categorizer.options?.values;answers.push(...values.map((value,index)=>`${items[index]}: ${categories[value]}`));}break;case "dropdown":const dropdown=widget;if(dropdown.options?.choices){for(const choice of dropdown.options.choices){if(choice.correct){answers.push(choice.content);}}}break;case "numeric-input":const numericInput=widget;if(numericInput.options?.answers){for(const ans of numericInput.options.answers){if(ans.status==="correct"&&ans.value!=null){answers.push(ans.value.toString());}}}break;case "input-number":const inputNumber=widget;if(inputNumber.options?.value){answers.push(inputNumber.options.value.toString());}break;case "expression":const expression=widget;if(expression.options?.answerForms){answers.push(...expression.options.answerForms.map(answer=>answer.value));}break;case "group":case "graded-group":const gradedGroup=widget;if(gradedGroup.options?.widgets){answers.push(...getAnswersFromWidgets(gradedGroup.options.widgets));}break;case "plotter":const plotter=widget;if(plotter.options?.categories&&plotter.options?.correct&&plotter.options.categories.length===plotter.options.correct.length){const{categories,correct}=plotter.options;answers.push(`{${categories.map((category,index)=>`${category}: ${correct[index]}`).join(", ")}}`);}break;case "interactive-graph":case "grapher":const grapher=widget;if(grapher.options?.correct?.coords){answers.push(`There should be point(s) on [${grapher.options.correct?.coords.join("], [")}]`);}break;case "orderer":const orderer=widget;if(orderer.options?.correctOptions){answers.push(joinOptionContents(orderer.options.correctOptions));}break;case "sorter":const sorter=widget;if(sorter.options?.correct){answers.push(sorter.options.correct.join(", "));}break;case "label-image":const labelImage=widget;if(labelImage.options?.markers){answers.push(...labelImage.options.markers.map(m=>`{label: "${m.label}", position: {${m.x},${m.y}}, answer: "${m.answers.join(", ")}"}`));}break;case "number-line":const numberLine=widget;if(numberLine.options?.correctX!=null){answers.push(numberLine.options.correctX.toString());}break;case "matrix":const matrix=widget;if(matrix.options?.answers){answers.push(`[${matrix.options.answers.join("], [")}]`);}break;case "matcher":const matcher=widget;if(matcher.options?.left&&matcher.options?.right){const{left,right}=matcher.options;const[leftHeader,rightHeader]=matcher.options.labels;const tableHeader=`| ${leftHeader} | ${rightHeader} |
2103
2105
  | --- | --- |`;const tableRows=left.map((leftItem,index)=>{return `| ${leftItem} | ${right[index]} |`});const table=[tableHeader,...tableRows].join("\n");answers.push(table);}break}});return answers}const joinOptionContents=options=>options.map(({content})=>content).join("\n");const toOptionLetter=index=>String.fromCharCode("A".charCodeAt(0)+index);function injectWidgets(content,widgets,widgetProps){if(!content){return ""}if(!widgets){return content}let context=content;keys(widgets).forEach(widgetID=>{const widget=widgets[widgetID];if(!widget.options){return}switch(widget.type){case "radio":const radio=widget;const radioProps=widgetProps?.[widgetID];if(radio.options?.choices?.length){let radioContext=joinOptionContents(radioProps?radioProps.choices.map(({content},i)=>({content:`Option ${toOptionLetter(i)}: ${content}`})):radio.options.choices);if(!radioProps&&radio.options?.randomize){radioContext+="\nThose options are displayed in a different order to the user. If the user says the letter, number, or ordinal number, always ask them clarify which option they are referring to.\n";}context=context.replace(`[[☃ ${widgetID}]]`,radioContext);}break;case "image":const image=widget;if(image.options?.alt){context=context.replace(`[[☃ ${widgetID}]]`,`<img id="${widgetID}" alt="${image.options.alt}">`);}break;case "label-image":const labelImage=widget;if(labelImage.options?.imageAlt){context=context.replace(`[[☃ ${widgetID}]]`,`[An image with dots that user needs to label. Label choices: [${labelImage.options.choices.join(", ")}]. Image alt text: ${labelImage.options?.imageAlt??""}]`);}break;case "explanation":const explanation=widget;if(explanation.options?.explanation){context=context.replace(`[[☃ ${widgetID}]]`,injectWidgets(explanation.options.explanation,explanation.options.widgets));}break;case "passage":const passage=widget;if(passage.options?.passageTitle||passage.options?.passageText){const{passageTitle,passageText}=passage.options;context=context.replace(`[[☃ ${widgetID}]]`,`# ${passageTitle}
2104
2106
 
@@ -2113,9 +2115,9 @@ function generateTestRadioWidget(){return {type:"radio",options:{choices:[]}}}fu
2113
2115
 
2114
2116
  const widgetNameToEnum={categorizer:"CATEGORIZER","cs-program":"CS_PROGRAM",definition:"DEFINITION","deprecated-standin":"DEPRECATED_STANDIN",dropdown:"DROPDOWN",explanation:"EXPLANATION",expression:"EXPRESSION","free-response":"FREE_RESPONSE","graded-group-set":"GRADED_GROUP_SET","graded-group":"GRADED_GROUP",grapher:"GRAPHER",group:"GROUP",iframe:"IFRAME",image:"IMAGE","input-number":"INPUT_NUMBER",interaction:"INTERACTIVE","interactive-graph":"INTERACTIVE_GRAPH","label-image":"LABEL_IMAGE","lights-puzzle":"LIGHTS_PUZZLE",matcher:"MATCHER",matrix:"MATRIX",measurer:"MEASURER","molecule-renderer":"MOLECULE","number-line":"NUMBER_LINE","numeric-input":"NUMERIC_INPUT",orderer:"ORDERER",passage:"PASSAGE","passage-ref":"PASSAGE_REF","passage-ref-target":"PASSAGE_REF_TARGET","phet-simulation":"PHET_SIMULATION",plotter:"PLOTTER","python-program":"PYTHON_PROGRAM","reaction-diagram":"REACTION_DIAGRAM",radio:"RADIO",sequence:"SEQUENCE",simulator:"SIMULATOR",sorter:"SORTER",table:"TABLE",video:"VIDEO"};function convertWidgetNameToEnum(name){const widgetEnum=widgetNameToEnum[name];if(!widgetEnum){throw new Error(`Unknown widget name: ${name}`)}return widgetEnum}
2115
2117
 
2116
- const EditorJsonify={serialize:function(){return _.omit(this.props,denylist)}};
2118
+ const EditorJsonify={serialize:function(){return excludeDenylistKeys(this.props)}};
2117
2119
 
2118
2120
  const GrapherUtil={DEFAULT_GRAPHER_PROPS,chooseType,defaultPlotProps,getEquationString,typeToButton};const ScoringUtil={keScoreFromPerseusScore};
2119
2121
 
2120
- export { ApiOptions, ArticleRenderer, BaseRadio$1 as BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestRadioWidget, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetSubTypeByWidgetId, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, ItemVersion as itemVersion, libVersion, makeSafeUrl, mathOnlyParser, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting, shouldHaveIndividualAnswer, usePerseusI18n, allWidgets as widgets };
2122
+ export { ApiOptions, ArticleRenderer, BaseRadio$1 as BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, context as LoadingContext, Log, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ScoringUtil, ref as ServerItemRenderer, Table$1 as TableWidget, Util, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, excludeDenylistKeys, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestRadioWidget, getAngleCoords, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetSubTypeByWidgetId, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, iconChevronDown, iconTrash, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, ItemVersion as itemVersion, libVersion, makeSafeUrl, mathOnlyParser, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting, shouldHaveIndividualAnswer, usePerseusI18n, allWidgets as widgets };
2121
2123
  //# sourceMappingURL=index.js.map