@khanacademy/perseus 79.1.0 → 79.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1938,7 +1938,7 @@ const LegacyGrid=({box,backgroundImage})=>{const{url,width,height}=backgroundIma
1938
1938
 
1939
1939
  function LabelStrokeFilter(){const color=wonderBlocksTokens.semanticColor.core.border.knockout.default;return jsxRuntimeExports.jsx("svg",{width:"0",height:"0",style:{position:"absolute"},children:jsxRuntimeExports.jsx("defs",{children:jsxRuntimeExports.jsxs("filter",{id:"math-stroke",children:[jsxRuntimeExports.jsx("feMorphology",{operator:"dilate",radius:"2",in:"SourceGraphic",result:"expanded"}),jsxRuntimeExports.jsx("feFlood",{floodColor:color,result:"flood"}),jsxRuntimeExports.jsx("feComposite",{in:"flood",in2:"expanded",operator:"in",result:"outline"}),jsxRuntimeExports.jsxs("feMerge",{children:[jsxRuntimeExports.jsx("feMergeNode",{in:"outline"}),jsxRuntimeExports.jsx("feMergeNode",{in:"SourceGraphic"})]})]})})})}
1940
1940
 
1941
- function LockedLabel(props){const{coord,text,color,size}=props;const[x,y]=pointToPixel(coord,useGraphConfig());const{TeX}=getDependencies();return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(LabelStrokeFilter,{}),jsxRuntimeExports.jsx("span",{className:"locked-label",style:{position:"absolute",left:x,top:y,color:perseusCore.lockedFigureColors[color],fontSize:wonderBlocksTokens.font.size[size],filter:"url(#math-stroke)"},"aria-hidden":true,children:jsxRuntimeExports.jsx(TeX,{children:replaceOutsideTeX(text)})})]})}
1941
+ const labelFontSize={small:wonderBlocksTokens.font.body.size.small,medium:wonderBlocksTokens.font.body.size.medium,large:wonderBlocksTokens.font.heading.size.medium};function LockedLabel(props){const{coord,text,color,size}=props;const[x,y]=pointToPixel(coord,useGraphConfig());const{TeX}=getDependencies();return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(LabelStrokeFilter,{}),jsxRuntimeExports.jsx("span",{className:"locked-label",style:{position:"absolute",left:x,top:y,color:perseusCore.lockedFigureColors[color],fontSize:labelFontSize[size],filter:"url(#math-stroke)"},"aria-hidden":true,children:jsxRuntimeExports.jsx(TeX,{children:replaceOutsideTeX(text)})})]})}
1942
1942
 
1943
1943
  function GraphLockedLabelsLayer(props){const{lockedFigures}=props;return lockedFigures.map((figure,i)=>{if(figure.type==="label"){return jsxRuntimeExports.jsx(LockedLabel,{...figure},`label-${i}`)}return jsxRuntimeExports.jsxs(React__namespace.Fragment,{children:[figure.labels.map((label,j)=>jsxRuntimeExports.jsx(LockedLabel,{...label},`${i}-label-${j}`)),figure.type==="line"&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[figure.points[0].labels.map((label,k)=>jsxRuntimeExports.jsx(LockedLabel,{...label},`locked-figure-${i}-point-1-label-${k}`)),figure.points[1].labels.map((label,k)=>jsxRuntimeExports.jsx(LockedLabel,{...label},`locked-figure-${i}-point-2-label-${k}`))]})]},i)})}
1944
1944
 
@@ -1984,7 +1984,7 @@ function renderCircleGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.
1984
1984
 
1985
1985
  function getMovablePointLabels(state){if(!state.showPointLabels){return []}const{pointLabels}=state;switch(state.type){case "point":case "polygon":case "sinusoid":case "linear":case "ray":case "absolute-value":case "exponential":case "logarithm":case "tangent":case "quadratic":return collect(state.coords,pointLabels);case "angle":return collect(state.coords,pointLabels);case "circle":{const text=pointLabels?.[0];if(text==null||text===""){return []}return [{key:"0",coord:state.radiusPoint,text}]}case "linear-system":case "segment":{const flatCoords=state.coords.flatMap(pair=>[pair[0],pair[1]]);return collect(flatCoords,pointLabels)}case "vector":case "none":return []}}function collect(coords,labels){const out=[];for(let i=0;i<coords.length;i++){const text=labels?.[i];if(text==null||text===""){continue}out.push({key:String(i),coord:coords[i],text});}return out}const ATTACH_BY_QUADRANT={n:{e:"ne",w:"nw"},s:{e:"se",w:"sw"}};const EDGE_FLIP_FRACTION=.15;function getLabelAttach(coord,range){const[xMin,xMax]=range[0];const[yMin,yMax]=range[1];const xMid=(xMin+xMax)/2;const yMid=(yMin+yMax)/2;const xSpan=xMax-xMin;const ySpan=yMax-yMin;let vertical=coord[1]>=yMid?"n":"s";let horizontal=coord[0]>=xMid?"e":"w";if(vertical==="n"&&(yMax-coord[1])/ySpan<EDGE_FLIP_FRACTION){vertical="s";}else if(vertical==="s"&&(coord[1]-yMin)/ySpan<EDGE_FLIP_FRACTION){vertical="n";}if(horizontal==="e"&&(xMax-coord[0])/xSpan<EDGE_FLIP_FRACTION){horizontal="w";}else if(horizontal==="w"&&(coord[0]-xMin)/xSpan<EDGE_FLIP_FRACTION){horizontal="e";}return ATTACH_BY_QUADRANT[vertical][horizontal]}
1986
1986
 
1987
- function MovablePointLabelsLayer({state}){const labeledPoints=getMovablePointLabels(state);if(labeledPoints.length===0){return null}return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(LabelStrokeFilter,{}),labeledPoints.map(label=>jsxRuntimeExports.jsx(MovablePointLabelView,{label:label,range:state.range},label.key))]})}function MovablePointLabelView({label,range}){const graphConfig=useGraphConfig();const[x,y]=pointToPixel(label.coord,graphConfig);const attach=getLabelAttach(label.coord,range);return jsxRuntimeExports.jsx("span",{className:"movable-point-label","data-testid":"movable-point__visible-label","aria-hidden":true,style:{position:"absolute",left:x,top:y,transform:translateOutward(attach),color:wonderBlocksTokens.semanticColor.core.foreground.neutral.default,fontFamily:'Symbola, "Times New Roman", serif',fontSize:wonderBlocksTokens.font.size.large,fontWeight:wonderBlocksTokens.font.weight.black,whiteSpace:"nowrap",pointerEvents:"none",filter:"url(#math-stroke)"},children:label.text})}const LABEL_PADDING_PX=12;function translateOutward(attach){const p=LABEL_PADDING_PX;switch(attach){case "n":return `translate(-50%, calc(-100% - ${p}px))`;case "ne":return `translate(${p}px, calc(-100% - ${p}px))`;case "e":return `translate(${p}px, -50%)`;case "se":return `translate(${p}px, ${p}px)`;case "s":return `translate(-50%, ${p}px)`;case "sw":return `translate(calc(-100% - ${p}px), ${p}px)`;case "w":return `translate(calc(-100% - ${p}px), -50%)`;case "nw":return `translate(calc(-100% - ${p}px), calc(-100% - ${p}px))`}}
1987
+ function MovablePointLabelsLayer({state}){const labeledPoints=getMovablePointLabels(state);if(labeledPoints.length===0){return null}return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(LabelStrokeFilter,{}),labeledPoints.map(label=>jsxRuntimeExports.jsx(MovablePointLabelView,{label:label,range:state.range},label.key))]})}function MovablePointLabelView({label,range}){const graphConfig=useGraphConfig();const[x,y]=pointToPixel(label.coord,graphConfig);const attach=getLabelAttach(label.coord,range);return jsxRuntimeExports.jsx("span",{className:"movable-point-label","data-testid":"movable-point__visible-label","aria-hidden":true,style:{position:"absolute",left:x,top:y,transform:translateOutward(attach),color:wonderBlocksTokens.semanticColor.core.foreground.neutral.default,fontFamily:'Symbola, "Times New Roman", serif',fontSize:wonderBlocksTokens.font.heading.size.medium,fontWeight:wonderBlocksTokens.font.weight.black,whiteSpace:"nowrap",pointerEvents:"none",filter:"url(#math-stroke)"},children:label.text})}const LABEL_PADDING_PX=12;function translateOutward(attach){const p=LABEL_PADDING_PX;switch(attach){case "n":return `translate(-50%, calc(-100% - ${p}px))`;case "ne":return `translate(${p}px, calc(-100% - ${p}px))`;case "e":return `translate(${p}px, -50%)`;case "se":return `translate(${p}px, ${p}px)`;case "s":return `translate(-50%, ${p}px)`;case "sw":return `translate(calc(-100% - ${p}px), ${p}px)`;case "w":return `translate(calc(-100% - ${p}px), -50%)`;case "nw":return `translate(calc(-100% - ${p}px), calc(-100% - ${p}px))`}}
1988
1988
 
1989
1989
  const ACTIVE_MAJOR=22;const ACTIVE_MINOR=12;const INACTIVE_MAJOR=16;const INACTIVE_MINOR=6;const RING_PAD=2;const HALO_PAD=3;const FOCUS_RING_PAD=2;const GRIP_DOT_MAJOR_OFFSETS=[-3,0,3];const GRIP_DOT_MINOR_OFFSETS=[-2,2];function MovablePillHandle(props){const{center,active,focused,rotation=0}=props;const[cx,cy]=center;const{interactiveColor}=useGraphConfig();const majorSize=active?ACTIVE_MAJOR:INACTIVE_MAJOR;const minorSize=active?ACTIVE_MINOR:INACTIVE_MINOR;const pillW=majorSize;const pillH=minorSize;const pillRadius=pillH/2;const haloW=pillW+(RING_PAD+HALO_PAD)*2;const haloH=pillH+(RING_PAD+HALO_PAD)*2;const haloRadius=haloH/2;const ringW=pillW+RING_PAD*2;const ringH=pillH+RING_PAD*2;const ringRadius=ringH/2;const focusRingW=haloW+FOCUS_RING_PAD*2;const focusRingH=haloH+FOCUS_RING_PAD*2;const focusRingRadius=focusRingH/2;return jsxRuntimeExports.jsxs("g",{"aria-hidden":true,style:{pointerEvents:"none"},transform:`translate(${cx} ${cy}) rotate(${rotation})`,"data-testid":"movable-pill-handle",children:[focused&&jsxRuntimeExports.jsx("rect",{className:"movable-pill-handle-focus-ring","data-testid":"movable-pill-handle-focus-ring",x:-focusRingW/2,y:-focusRingH/2,width:focusRingW,height:focusRingH,rx:focusRingRadius,ry:focusRingRadius,stroke:interactiveColor}),jsxRuntimeExports.jsx("rect",{className:"movable-pill-handle-halo",x:-haloW/2,y:-haloH/2,width:haloW,height:haloH,rx:haloRadius,ry:haloRadius,fill:interactiveColor}),jsxRuntimeExports.jsx("rect",{className:"movable-pill-handle-ring",x:-ringW/2,y:-ringH/2,width:ringW,height:ringH,rx:ringRadius,ry:ringRadius}),jsxRuntimeExports.jsx("rect",{className:"movable-pill-handle","data-testid":"movable-pill-handle-pill",x:-pillW/2,y:-pillH/2,width:pillW,height:pillH,rx:pillRadius,ry:pillRadius,fill:interactiveColor}),active&&GRIP_DOT_MINOR_OFFSETS.map(dy=>GRIP_DOT_MAJOR_OFFSETS.map(dx=>jsxRuntimeExports.jsx("circle",{className:"movable-pill-handle-dot","data-testid":"movable-pill-handle-dot",cx:dx,cy:dy},`${dx},${dy}`)))]})}
1990
1990
 
@@ -2036,7 +2036,7 @@ const defaultBackgroundImage$1={url:null};class InteractiveGraph extends React__
2036
2036
 
2037
2037
  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=perseusCore.GrapherUtil.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 kmath.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=perseusCore.GrapherUtil.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=___default.default.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(perseusCore.GrapherUtil.functionForType(type).url),alt:capitalized})}};
2038
2038
 
2039
- const MovablePoint$2=Graphie.MovablePoint;const MovableLine$1=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__namespace.Component{render(){const pointForCoord=(coord,i)=>{return jsxRuntimeExports.jsx(MovablePoint$2,{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||!kmath.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=perseusCore.deepClone(this._coords());const oldCoord=perseusCore.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 kmath.point.reflectOverLine(coord,asymptote)});}else {coords=perseusCore.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,allowZoom:false,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,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 React.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$1,{onMove:(newCoord,oldCoord)=>{const delta=kmath.vector.subtract(newCoord,oldCoord);const newAsymptote=this._asymptote().map(coord=>kmath.vector.add(coord,delta));this.props.onChange({asymptote:newAsymptote});},constraints:[Interactive2.MovableLine.constraints.bound(),Interactive2.MovableLine.constraints.snap(),(newCoord,oldCoord)=>{const delta=kmath.vector.subtract(newCoord,oldCoord);const proposedAsymptote=this._asymptote().map(coord=>kmath.vector.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$2,{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__namespace.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"grapher",widgetSubType:"null",widgetId:this.props.widgetId}});}_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}getPromptJSON(){return getPromptJSON$g(this.props)}getSerializedState(){const{userInput:_,correct:__,...rest}=this.props;return {...rest,plot:this.props.userInput}}getAvailableTypes(){if(this.props.static){invariant__default.default(this.props.correct,"static widgets must have a correct answer");return [this.props.correct.type]}return this.props.availableTypes}renderLegacyGrapher(){const availableTypes=this.getAvailableTypes();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: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&&perseusCore.GrapherUtil.functionForType(type),coords:coords,asymptote:asymptote,static:this.props.static,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}),availableTypes.length>1&&typeSelector]})}render(){if(!this.props.apiOptions.flags?.["grapher-to-interactive-graph"]){return this.renderLegacyGrapher()}const interactiveGraphOptions=perseusCore.convertGrapherOptionsToInteractiveGraph(this.props);if(interactiveGraphOptions==null){return this.renderLegacyGrapher()}const interactiveGraphUserInput=perseusCore.convertGrapherUserInputToInteractiveGraph(this.props.userInput);return jsxRuntimeExports.jsx(InteractiveGraph$1.widget,{...this.props,...interactiveGraphOptions,userInput:interactiveGraphUserInput,handleUserInput:interactiveGraphUserInput=>this.props.handleUserInput(perseusCore.convertInteractiveGraphUserInputToGrapher(interactiveGraphUserInput))})}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 getUserInputFromSerializedState$a(serializedState){return serializedState.plot}function getStartUserInput$a(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}function getCorrectUserInput$4(options){invariant__default.default(options.correct,"getCorrectUserInput should only be called for static widgets");return options.correct}const WrappedGrapher=withDependencies(Grapher);var Grapher$1 = {name:"grapher",displayName:"Grapher",hidden:true,widget:WrappedGrapher,getUserInputFromSerializedState: getUserInputFromSerializedState$a,getStartUserInput: getStartUserInput$a,getCorrectUserInput: getCorrectUserInput$4};
2039
+ const MovablePoint$2=Graphie.MovablePoint;const MovableLine$1=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__namespace.Component{render(){const pointForCoord=(coord,i)=>{return jsxRuntimeExports.jsx(MovablePoint$2,{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||!kmath.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=perseusCore.deepClone(this._coords());const oldCoord=perseusCore.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 kmath.point.reflectOverLine(coord,asymptote)});}else {coords=perseusCore.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,allowZoom:false,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,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 React.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$1,{onMove:(newCoord,oldCoord)=>{const delta=kmath.vector.subtract(newCoord,oldCoord);const newAsymptote=this._asymptote().map(coord=>kmath.vector.add(coord,delta));this.props.onChange({asymptote:newAsymptote});},constraints:[Interactive2.MovableLine.constraints.bound(),Interactive2.MovableLine.constraints.snap(),(newCoord,oldCoord)=>{const delta=kmath.vector.subtract(newCoord,oldCoord);const proposedAsymptote=this._asymptote().map(coord=>kmath.vector.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$2,{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__namespace.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"grapher",widgetSubType:"null",widgetId:this.props.widgetId}});}_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}getPromptJSON(){return getPromptJSON$g(this.props)}getSerializedState(){const{userInput:_,correct:__,...rest}=this.props;return {...rest,plot:this.props.userInput}}getAvailableTypes(){if(this.props.static){invariant__default.default(this.props.correct,"static widgets must have a correct answer");return [this.props.correct.type]}return this.props.availableTypes}renderLegacyGrapher(){const availableTypes=this.getAvailableTypes();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: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&&perseusCore.GrapherUtil.functionForType(type),coords:coords,asymptote:asymptote,static:this.props.static,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}),availableTypes.length>1&&typeSelector]})}render(){const interactiveGraphOptions=perseusCore.convertGrapherOptionsToInteractiveGraph(this.props);if(interactiveGraphOptions==null){return this.renderLegacyGrapher()}const interactiveGraphUserInput=perseusCore.convertGrapherUserInputToInteractiveGraph(this.props.userInput);return jsxRuntimeExports.jsx(InteractiveGraph$1.widget,{...this.props,...interactiveGraphOptions,userInput:interactiveGraphUserInput,handleUserInput:interactiveGraphUserInput=>this.props.handleUserInput(perseusCore.convertInteractiveGraphUserInputToGrapher(interactiveGraphUserInput))})}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 getUserInputFromSerializedState$a(serializedState){return serializedState.plot}function getStartUserInput$a(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}function getCorrectUserInput$4(options){invariant__default.default(options.correct,"getCorrectUserInput should only be called for static widgets");return options.correct}const WrappedGrapher=withDependencies(Grapher);var Grapher$1 = {name:"grapher",displayName:"Grapher",hidden:true,widget:WrappedGrapher,getUserInputFromSerializedState: getUserInputFromSerializedState$a,getStartUserInput: getStartUserInput$a,getCorrectUserInput: getCorrectUserInput$4};
2040
2040
 
2041
2041
  const getPromptJSON$e=rendererJSON=>{if(!rendererJSON){return {type:"group",content:"",widgets:{}}}return {...rendererJSON,type:"group"}};
2042
2042
 
@@ -2144,7 +2144,7 @@ var extraWidgets = [CSProgram$1,Categorizer$1,Definition$1,DeprecatedStandin$1,D
2144
2144
 
2145
2145
  const init=function(){registerWidgets(basicWidgets);registerWidgets(extraWidgets);replaceDeprecatedWidgets();};
2146
2146
 
2147
- const libName="@khanacademy/perseus";const libVersion="79.1.0";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
2147
+ const libName="@khanacademy/perseus";const libVersion="79.1.2";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
2148
2148
 
2149
2149
  const apiVersion={major:12,minor:0};
2150
2150