@khanacademy/perseus 84.1.1 → 84.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -9,7 +9,6 @@ import Clickable from '@khanacademy/wonder-blocks-clickable';
9
9
  import { Popover, PopoverContentCore } from '@khanacademy/wonder-blocks-popover';
10
10
  import { semanticColor, sizing, color, tokenValue, border as border$1, spacing, font, boxShadow } from '@khanacademy/wonder-blocks-tokens';
11
11
  import classNames$2 from 'classnames';
12
- import $ from 'jquery';
13
12
  import { v4 } from 'uuid';
14
13
  import PropTypes from 'prop-types';
15
14
  import * as PerseusLinter from '@khanacademy/perseus-linter';
@@ -24,6 +23,7 @@ import _, { debounce as debounce$1 } from 'underscore';
24
23
  import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
25
24
  import { point, KhanMath, number, vector as vector$3, geometry, line, angles, coefficients } from '@khanacademy/kmath';
26
25
  import { entries, UnreachableCaseError, isTruthy, keys } from '@khanacademy/wonder-stuff-core';
26
+ import $ from 'jquery';
27
27
  import { ModalDialog, ModalPanel, ModalLauncher, FlexibleDialog } from '@khanacademy/wonder-blocks-modal';
28
28
  import IconButton from '@khanacademy/wonder-blocks-icon-button';
29
29
  import closeIcon from '@phosphor-icons/core/bold/x-bold.svg';
@@ -1406,7 +1406,7 @@ function requireJsxRuntime () {
1406
1406
 
1407
1407
  var jsxRuntimeExports = requireJsxRuntime();
1408
1408
 
1409
- const mockStrings={characterCount:({used,num})=>num===1?`${used} / ${num} Character`:`${used} / ${num} Characters`,closeKeypad:"close math keypad",openKeypad:"open math keypad",mathInputBox:"Math input box",removeHighlight:"Remove highlight",addHighlight:"Add highlight",hintPos:({pos})=>`Hint #${pos}`,errorRendering:({error})=>`Error rendering: ${error}`,APPROXIMATED_PI_ERROR:"Your answer is close, but you may "+"have approximated pi. Enter your "+"answer as a multiple of pi, like "+"<code>12\\ \\text{pi}</code> or "+"<code>2/3\\ \\text{pi}</code>",EMPTY_RESPONSE_ERROR:"There are still more parts of this question to answer.",EXTRA_SYMBOLS_ERROR:"We could not understand your "+"answer. Please check your answer for extra "+"text or symbols.",NEEDS_TO_BE_SIMPLFIED_ERROR:"Your answer is almost correct, "+"but it needs to be simplified.",MISSING_PERCENT_ERROR:"Your answer is almost correct, "+"but it is missing a "+"<code>\\%</code> at the end.",MULTIPLICATION_SIGN_ERROR:"I'm a computer. I only understand "+"multiplication if you use an asterisk "+"(*) as the multiplication sign.",USER_INPUT_EMPTY:"Your answer is empty.",USER_INPUT_TOO_LONG:"Please shorten your response.",WRONG_CASE_ERROR:"Your answer includes use of a variable with the wrong case.",WRONG_LETTER_ERROR:"Your answer includes a wrong variable letter.",invalidSelection:"Make sure you select something for every row.",INVALID_CHOICE_SELECTION:"Invalid choice selection",ERROR_TITLE:"Oops!",ERROR_MESSAGE:"Sorry, I don't understand that!",hints:"Hints",getAnotherHint:"Get another hint",deprecatedStandin:"Sorry, this part of the question is no longer available. 😅 Don't worry, you won't be graded on this part. Keep going!",keepTrying:"Keep trying",tryAgain:"Try again",check:"Check",correctExcited:"Correct!",nextQuestion:"Next question",skipToTitle:({title})=>`Skip to ${title}`,current:"Current",correct:"Correct",correctSelected:"Correct (selected)",incorrect:"Incorrect",incorrectSelected:"Incorrect (selected)",hideExplanation:"Hide explanation",explain:"Explain",INVALID_MESSAGE_PREFIX:"We couldn't grade your answer.",DEFAULT_INVALID_MESSAGE_1:"It looks like you left something blank or ",DEFAULT_INVALID_MESSAGE_2:"entered in an invalid answer.",integerExample:"an integer, like $6$",properExample:"a *proper* fraction, like $1/2$ or $6/10$",simplifiedProperExample:"a *simplified proper* fraction, like $3/5$",improperExample:"an *improper* fraction, like $10/7$ or $14/8$",simplifiedImproperExample:"a *simplified improper* fraction, like $7/4$",mixedExample:"a mixed number, like $1\\ 3/4$",decimalExample:"an *exact* decimal, like $0.75$",percentExample:"a percent, like $12.34\\%$",piExample:"a multiple of pi, like $12\\ \\text{pi}$ or $2/3\\ \\text{pi}$",yourAnswer:"**Your answer should be** ",yourAnswerLabel:"Your answer:",addPoints:"Click to add points",addVertices:"Click to add vertices",tapMultiple:"Tap each dot on the image to select all answers that apply.",tapSingle:"Tap each dot on the image to select an answer.",clickMultiple:"Click each dot on the image to select all answers that apply.",clickSingle:"Click each dot on the image to select an answer.",choices:"Choices:",answers:({num})=>num===1?`${num} answer`:`${num} answers`,hideAnswersToggleLabel:"Hide answer choices",moves:({num})=>num===1?`Moves: ${num}`:`Moves: ${num}`,clickTiles:"Click on the tiles to change the lights.",turnOffLights:"You must turn on all of the lights to continue.",fillAllCells:"Make sure you fill in all cells in the matrix.",molecularDrawing:({content})=>`A molecular structure drawing. SMILES notation: ${content}`,switchDirection:"Switch direction",circleOpen:"Make circle open",circleFilled:"Make circle filled",numDivisions:"Number of divisions:",divisions:({divRangeString})=>`Please make sure the number of divisions is in the range ${divRangeString}.`,dragHandles:"Drag handles to make graph",tapAddPoints:"Tap to add points",false:"False",true:"True",no:"No",yes:"Yes",chooseCorrectNum:"Please choose the correct number of answers.",notNoneOfTheAbove:"'None of the above' may not be selected when other answers are selected.",noneOfTheAbove:"None of the above",chooseNumAnswers:({numCorrect})=>`Choose ${numCorrect} answers:`,chooseAllAnswers:"Choose all answers that apply:",chooseOneAnswer:"Choose 1 answer:",choiceCheckedCorrect:({letter})=>`(Choice ${letter}, Checked, Correct)`,choiceCorrect:({letter})=>`(Choice ${letter}, Correct)`,choiceCheckedIncorrect:({letter})=>`(Choice ${letter}, Checked, Incorrect)`,choiceIncorrect:({letter})=>`(Choice ${letter}, Incorrect)`,choiceChecked:({letter})=>`(Choice ${letter}, Checked)`,choice:({letter})=>`(Choice ${letter})`,notSelected:"not selected",choicesSelected:({num})=>num===1?`${num} choice selected`:`${num} choices selected`,bringBack:"Bring back",openMenuForChoice:({letter})=>`Open menu for Choice ${letter}`,letters:"A B C D E F G H I J K L M N O P Q R S T U V W X Y Z",scrollAnswers:"Scroll Answers",scrollStart:"Scroll to view start of the content",scrollEnd:"Scroll to view the end of the content",rightArrow:"Reaction arrow pointing to the right.",dontUnderstandUnits:"I couldn't understand those units.",checkSigFigs:"Check your significant figures.",answerNumericallyIncorrect:"That answer is numerically incorrect.",checkUnits:"Check your units.",dontUnderstand:"I don't understand that",loading:"Loading...",videoTranscript:"See video transcript",somethingWrong:"Something went wrong.",videoWrapper:"Khan Academy video wrapper",pythonProgram:"Python program",computerScienceProgram:"Computer science program",embeddedContent:"Embedded content",mathInputTitle:"mathematics keyboard",mathInputDescription:"Use keyboard/mouse to interact with math-based input fields",sin:"sin",cos:"cos",tan:"tan",simulationLoadFail:"Sorry, this simulation cannot load.",simulationLocaleWarning:"Sorry, this simulation isn't available in your language.",openInNewTab:"Open simulation in a new tab",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",selectAnAnswer:"Select an answer",srGraphInstructions:"Enable Forms or Focus mode and use the Tab key to move through the interactive elements in the graph. When an interactive element has focus, use Arrow keys to move it.",srUnlimitedGraphInstructions:"Press Shift + Enter to interact with the graph. Enable Forms or Focus mode and use the Tab key to move through the interactive elements in the graph and access the graph Action Bar. When an interactive element has focus, use Arrow keys to move it or use the Delete key to remove it from the graph. Use the buttons in the Action Bar to add or adjust elements within the graph.",graphKeyboardPrompt:"Press Shift + Enter to interact with the graph",addPoint:"Add Point",removePoint:"Remove Point",closePolygon:"Close shape",openPolygon:"Re-open shape",ungradedInteractiveGraph:"Use this graph to check your thinking, but it does not count as your answer.",srPointAtCoordinates:({pointLabel,x,y})=>`Point ${pointLabel} at ${x} comma ${y}.`,srInteractiveElements:({elements})=>`Interactive elements: ${elements}`,srNoInteractiveElements:"No interactive elements",srCircleGraph:"A circle on a coordinate plane.",srCircleShape:({centerX,centerY})=>`Circle. The center point is at ${centerX} comma ${centerY}.`,srCircleRadiusPointRight:({radiusPointX,radiusPointY})=>`Right radius endpoint at ${radiusPointX} comma ${radiusPointY}.`,srCircleRadiusPointLeft:({radiusPointX,radiusPointY})=>`Left radius endpoint at ${radiusPointX} comma ${radiusPointY}.`,srCircleRadiusPointRightWithLabel:({pointLabel,radiusPointX,radiusPointY})=>`Right radius endpoint ${pointLabel} at ${radiusPointX} comma ${radiusPointY}.`,srCircleRadiusPointLeftWithLabel:({pointLabel,radiusPointX,radiusPointY})=>`Left radius endpoint ${pointLabel} at ${radiusPointX} comma ${radiusPointY}.`,srCircleRadius:({radius})=>`Circle radius is ${radius}.`,srCircleOuterPoints:({point1X,point1Y,point2X,point2Y,point3X,point3Y,point4X,point4Y})=>`Points on the circle at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, ${point3X} comma ${point3Y}, ${point4X} comma ${point4Y}.`,srLinearGraph:"A line on a coordinate plane.",srLinearGraphPoints:({point1X,point1Y,point2X,point2Y})=>`The line has two points, point 1 at ${point1X} comma ${point1Y} and point 2 at ${point2X} comma ${point2Y}.`,srLinearGraphSlopeIncreasing:"Its slope increases from left to right.",srLinearGraphSlopeDecreasing:"Its slope decreases from left to right.",srLinearGraphSlopeHorizontal:"Its slope is zero.",srLinearGraphSlopeVertical:"Its slope is undefined.",srLinearGraphXOnlyIntercept:({xIntercept})=>`The line crosses the X-axis at ${xIntercept} comma 0.`,srLinearGraphYOnlyIntercept:({yIntercept})=>`The line crosses the Y-axis at 0 comma ${yIntercept}.`,srLinearGraphBothIntercepts:({xIntercept,yIntercept})=>`The line crosses the X-axis at ${xIntercept} comma 0 and the Y-axis at 0 comma ${yIntercept}.`,srLinearGraphOriginIntercept:"The line crosses the X and Y axes at the graph's origin.",srLinearGrabHandle:({point1X,point1Y,point2X,point2Y})=>`Line going through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y}.`,srAngleStartingSide:({pointLabel,x,y})=>`Point ${pointLabel}, starting side at ${x} comma ${y}.`,srAngleEndingSide:({pointLabel,x,y})=>`Point ${pointLabel}, ending side at ${x} comma ${y}.`,srAngleVertexWithAngleMeasure:({pointLabel,x,y,angleMeasure})=>`Point ${pointLabel}, vertex at ${x} comma ${y}. Angle ${angleMeasure} degrees.`,srAngleGraphAriaLabel:"An angle on a coordinate plane.",srAngleGraphAriaDescription:({angleMeasure,vertexX,vertexY,startingSideX,startingSideY,endingSideX,endingSideY})=>`The angle measure is ${angleMeasure} degrees with a vertex at ${vertexX} comma ${vertexY}, a point on the starting side at ${startingSideX} comma ${startingSideY} and a point on the ending side at ${endingSideX} comma ${endingSideY}.`,srAngleInteractiveElements:({vertexX,vertexY,startingSideX,startingSideY,endingSideX,endingSideY})=>`An angle formed by 3 points. The vertex is at ${vertexX} comma ${vertexY}. The starting side point is at ${startingSideX} comma ${startingSideY}. The ending side point is at ${endingSideX} comma ${endingSideY}.`,srSingleSegmentGraphAriaLabel:"A line segment on a coordinate plane.",srMultipleSegmentGraphAriaLabel:({countOfSegments})=>`${countOfSegments} segments on a coordinate plane.`,srMultipleSegmentIndividualLabel:({point1X,point1Y,point2X,point2Y,indexOfSegment})=>`Segment ${indexOfSegment}: Endpoint 1 at ${point1X} comma ${point1Y}. Endpoint 2 at ${point2X} comma ${point2Y}.`,srSingleSegmentLabel:({point1X,point1Y,point2X,point2Y})=>`Endpoint 1 at ${point1X} comma ${point1Y}. Endpoint 2 at ${point2X} comma ${point2Y}.`,srSegmentLength:({length})=>`Segment length ${length} units.`,srSingleSegmentGraphEndpointAriaLabel:({pointLabel,x,y})=>`Endpoint ${pointLabel} at ${x} comma ${y}.`,srMultipleSegmentGraphEndpointAriaLabel:({pointLabel,x,y,indexOfSegment})=>`Endpoint ${pointLabel} on segment ${indexOfSegment} at ${x} comma ${y}.`,srSegmentGrabHandle:({point1X,point1Y,point2X,point2Y})=>`Segment from ${point1X} comma ${point1Y} to ${point2X} comma ${point2Y}.`,srLinearSystemGraph:"Two lines on a coordinate plane.",srLinearSystemPoints:({lineNumber,point1X,point1Y,point2X,point2Y})=>`Line ${lineNumber} has two points, point 1 at ${point1X} comma ${point1Y} and point 2 at ${point2X} comma ${point2Y}.`,srLinearSystemPoint:({lineNumber,pointLabel,x,y})=>`Point ${pointLabel} on line ${lineNumber} at ${x} comma ${y}.`,srLinearSystemGrabHandle:({lineNumber,point1X,point1Y,point2X,point2Y})=>`Line ${lineNumber} going through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y}.`,srLinearSystemIntersection:({x,y})=>`Line 1 and line 2 intersect at point ${x} comma ${y}.`,srLinearSystemParallel:"Line 1 and line 2 are parallel.",srRayGraph:"A ray on a coordinate plane.",srRayPoints:({point1X,point1Y,point2X,point2Y})=>`The endpoint is at ${point1X} comma ${point1Y} and the ray goes through point ${point2X} comma ${point2Y}.`,srRayGrabHandle:({point1X,point1Y,point2X,point2Y})=>`Ray with endpoint ${point1X} comma ${point1Y} going through point ${point2X} comma ${point2Y}.`,srRayEndpoint:({x,y})=>`Endpoint at ${x} comma ${y}.`,srRayEndpointWithLabel:({pointLabel,x,y})=>`Endpoint ${pointLabel} at ${x} comma ${y}.`,srRayTerminalPoint:({x,y})=>`Through point at ${x} comma ${y}.`,srRayTerminalPointWithLabel:({pointLabel,x,y})=>`Through point ${pointLabel} at ${x} comma ${y}.`,srVectorGraph:"A vector on a coordinate plane.",srVectorPoints:({tailX,tailY,headX,headY})=>`The tail is at ${tailX} comma ${tailY} and the head is at ${headX} comma ${headY}.`,srVectorHeadPoint:({x,y})=>`Vector head at ${x} comma ${y}.`,srVectorMagnitudeDirection:({magnitude,direction})=>`The vector has a magnitude of ${magnitude} and a direction of ${direction} degrees.`,srVectorGrabHandle:({tailX,tailY,headX,headY})=>`Vector from ${tailX} comma ${tailY} to ${headX} comma ${headY}.`,srQuadraticGraph:"A parabola on a 4-quadrant coordinate plane.",srQuadraticFaceUp:"The parabola opens upward.",srQuadraticFaceDown:"The parabola opens downward.",srQuadraticGraphVertexOrigin:"Vertex is at the origin.",srQuadraticGraphVertexXAxis:"Vertex is on the X-axis.",srQuadraticGraphVertexYAxis:"Vertex is on the Y-axis.",srQuadraticGraphVertexQuadrant:({quadrant})=>`Vertex is in quadrant ${quadrant}.`,srQuadraticTwoXIntercepts:({intercept1,intercept2})=>`The X-intercepts are at ${intercept1} comma 0 and ${intercept2} comma 0.`,srQuadraticOneXIntercept:({intercept})=>`The X-intercept is at ${intercept} comma 0.`,srQuadraticYIntercept:({intercept})=>`The Y-intercept is at 0 comma ${intercept}.`,srQuadraticPointOrigin:({pointLabel})=>`Point ${pointLabel} on parabola at the origin.`,srQuadraticPointAxis:({pointLabel,x,y})=>`Point ${pointLabel} on parabola at ${x} comma ${y}.`,srQuadraticPointQuadrant:({pointLabel,x,y,quadrant})=>`Point ${pointLabel} on parabola in quadrant ${quadrant} at ${x} comma ${y}.`,srQuadraticInteractiveElements:({point1X,point1Y,point2X,point2Y,point3X,point3Y})=>`Parabola with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and ${point3X} comma ${point3Y}.`,srPolygonGraph:"A polygon.",srPolygonGraphCoordinatePlane:"A polygon on a coordinate plane.",srPolygonGraphPointsNum:({num})=>`The polygon has ${num} points.`,srPolygonGraphPointsOne:"The polygon has 1 point.",srPolygonElementsNum:({num})=>`A polygon with ${num} points.`,srPolygonElementsOne:"A polygon with 1 point.",srPolygonPointAngleApprox:({angle})=>`Angle approximately equal to ${angle} degrees.`,srPolygonPointAngle:({angle})=>`Angle equal to ${angle} degrees.`,srPolygonSideLength:({pointNum,length})=>`A line segment, length equal to ${length} units, connects to point ${pointNum}.`,srPolygonSideLengthApprox:({pointNum,length})=>`A line segment, length approximately equal to ${length} units, connects to point ${pointNum}.`,srUnlimitedPolygonEmpty:"An empty coordinate plane.",srSinusoidGraph:"A sinusoid function on a coordinate plane.",srSinusoidRootPoint:({x,y})=>`Midline intersection at ${x} comma ${y}.`,srSinusoidRootPointWithLabel:({pointLabel,x,y})=>`Midline intersection point ${pointLabel} at ${x} comma ${y}.`,srSinusoidMaxPoint:({x,y})=>`Maximum point at ${x} comma ${y}.`,srSinusoidMaxPointWithLabel:({pointLabel,x,y})=>`Maximum point ${pointLabel} at ${x} comma ${y}.`,srSinusoidMinPoint:({x,y})=>`Minimum point at ${x} comma ${y}.`,srSinusoidMinPointWithLabel:({pointLabel,x,y})=>`Minimum point ${pointLabel} at ${x} comma ${y}.`,srSinusoidFlatPoint:({x,y})=>`Line through point at ${x} comma ${y}.`,srSinusoidFlatPointWithLabel:({pointLabel,x,y})=>`Line through point ${pointLabel} at ${x} comma ${y}.`,srSinusoidDescription:({minValue,maxValue,cycleStart,cycleEnd})=>`The graph shows a wave with a minimum value of ${minValue} and a maximum value of ${maxValue}. The wave completes a full cycle from ${cycleStart} to ${cycleEnd}.`,srSinusoidInteractiveElements:({point1X,point1Y,point2X,point2Y})=>`Sinusoid graph with midline intersection point at ${point1X} comma ${point1Y} and extremum point at ${point2X} comma ${point2Y}.`,srExponentialGraph:"An exponential curve on a coordinate plane.",srExponentialPoint:({pointLabel,x,y})=>`Point ${pointLabel} on an exponential curve at ${x} comma ${y}.`,srExponentialDescriptionRightNeg:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches y equals ${asymptoteY} from the right and extends to negative infinity.`,srExponentialDescriptionLeftPos:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches y equals ${asymptoteY} from the left and extends to positive infinity.`,srExponentialAboveAsymptote:"The curve lies above the asymptote.",srExponentialBelowAsymptote:"The curve lies below the asymptote.",srExponentialNoCurve:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`No exponential curve can be drawn through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} with a horizontal asymptote at y equals ${asymptoteY}. Move both points to the same side of the asymptote to draw the curve.`,srExponentialIntercepts:({xIntercept,yIntercept})=>`The x-intercept is at ${xIntercept} comma 0. The y-intercept is at 0 comma ${yIntercept}.`,srExponentialYIntercept:({yIntercept})=>`The y-intercept is at 0 comma ${yIntercept}.`,srExponentialInteractiveElements:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`Exponential graph with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and a horizontal asymptote at y equals ${asymptoteY}.`,srExponentialAsymptote:({asymptoteY})=>`Horizontal asymptote at y equals ${asymptoteY}`,srLogarithmGraph:"A logarithmic curve on a coordinate plane.",srLogarithmPoint:({pointLabel,x,y})=>`Point ${pointLabel} on a logarithmic curve at ${x} comma ${y}.`,srLogarithmDescriptionRightNeg:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches x equals ${asymptoteX} from the right and extends to negative infinity.`,srLogarithmDescriptionRightPos:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches x equals ${asymptoteX} from the right and extends to positive infinity.`,srLogarithmDescriptionLeftNeg:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches x equals ${asymptoteX} from the left and extends to negative infinity.`,srLogarithmDescriptionLeftPos:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches x equals ${asymptoteX} from the left and extends to positive infinity.`,srLogarithmToRightOfAsymptote:"The curve is to the right of the asymptote.",srLogarithmToLeftOfAsymptote:"The curve is to the left of the asymptote.",srLogarithmNoCurve:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`No curve can be drawn through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} with a vertical asymptote at x equals ${asymptoteX}. Move both points to the same side of the asymptote to draw the curve.`,srLogarithmIntercepts:({xIntercept,yIntercept})=>`The x-intercept is at ${xIntercept} comma 0. The y-intercept is at 0 comma ${yIntercept}.`,srLogarithmXIntercept:({xIntercept})=>`The x-intercept is at ${xIntercept} comma 0.`,srLogarithmInteractiveElements:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`Logarithmic graph with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and a vertical asymptote at x equals ${asymptoteX}.`,srLogarithmAsymptote:({asymptoteX})=>`Vertical asymptote at x equals ${asymptoteX}.`,srAbsoluteValueGraph:"An absolute value on a coordinate plane.",srAbsoluteValueVertexPoint:({x,y})=>`Vertex point at ${x} comma ${y}.`,srAbsoluteValueVertexPointWithLabel:({pointLabel,x,y})=>`Vertex point ${pointLabel} at ${x} comma ${y}.`,srAbsoluteValueArmPoint:({x,y})=>`Point on arm at ${x} comma ${y}.`,srAbsoluteValueArmPointWithLabel:({pointLabel,x,y})=>`Point ${pointLabel} on arm at ${x} comma ${y}.`,srAbsoluteValueSlope:({slope})=>`The slope is ${slope}.`,srAbsoluteValueOpensUp:"The graph opens upward.",srAbsoluteValueOpensDown:"The graph opens downward.",srAbsoluteValueVertex:({x,y})=>`Vertex is at ${x} comma ${y}.`,srAbsoluteValueVertexOrigin:"Vertex is at the origin.",srAbsoluteValueTwoXIntercepts:({intercept1,intercept2})=>`The X-intercepts are at ${intercept1} comma 0 and ${intercept2} comma 0.`,srAbsoluteValueOneXIntercept:({intercept})=>`The X-intercept is at ${intercept} comma 0.`,srAbsoluteValueYIntercept:({intercept})=>`The Y-intercept is at 0 comma ${intercept}.`,srAbsoluteValueInteractiveElements:({point1X,point1Y,point2X,point2Y,slope})=>`Absolute value graph with vertex point at ${point1X} comma ${point1Y}, arm point at ${point2X} comma ${point2Y} and slope of ${slope}.`,srTangentGraph:"A tangent curve on a coordinate plane.",srTangentInflectionPoint:({x,y})=>`Inflection point at ${x} comma ${y}.`,srTangentInflectionPointWithLabel:({pointLabel,x,y})=>`Inflection point ${pointLabel} at ${x} comma ${y}.`,srTangentControlPoint:({x,y})=>`Control point at ${x} comma ${y}.`,srTangentControlPointWithLabel:({pointLabel,x,y})=>`Control point ${pointLabel} at ${x} comma ${y}.`,srTangentDescriptionPoints:({inflectionX,inflectionY,controlX,controlY})=>`The curve passes through an inflection point at ${inflectionX} comma ${inflectionY} and a control point at ${controlX} comma ${controlY}.`,srTangentIncreasing:({period})=>`The curve increases through the inflection point, repeating every ${period} units.`,srTangentDecreasing:({period})=>`The curve decreases through the inflection point, repeating every ${period} units.`,srTangentAsymptotes:({leftAsymptote,rightAsymptote})=>`The nearest vertical asymptotes are at x equals ${leftAsymptote} and x equals ${rightAsymptote}.`,srTangentInteractiveElements:({point1X,point1Y,point2X,point2Y})=>`Tangent graph with inflection point at ${point1X} comma ${point1Y} and control point at ${point2X} comma ${point2Y}.`,imageExploreButton:"Explore image",imageExploreButtonAriaLabel:"Explore image and description",imageAlternativeTitle:"Explore image and description",imageDescriptionLabel:"Description",imageZoomAriaLabel:"Make image bigger.",imageResetZoomAriaLabel:"Close image.",gifPlayButtonLabel:"Play Animation",gifPauseButtonLabel:"Pause Animation",definitionIdentifier:({word})=>`Definition of: ${word}`};const errorToString={APPROXIMATED_PI_ERROR:"APPROXIMATED_PI_ERROR",CHOOSE_CORRECT_NUM_ERROR:"chooseCorrectNum",EMPTY_RESPONSE_ERROR:"EMPTY_RESPONSE_ERROR",EXTRA_SYMBOLS_ERROR:"EXTRA_SYMBOLS_ERROR",FILL_ALL_CELLS_ERROR:"fillAllCells",INVALID_CHOICE_SELECTION:"INVALID_CHOICE_SELECTION",INVALID_SELECTION_ERROR:"invalidSelection",MISSING_PERCENT_ERROR:"MISSING_PERCENT_ERROR",MULTIPLICATION_SIGN_ERROR:"MULTIPLICATION_SIGN_ERROR",NEEDS_TO_BE_SIMPLIFIED_ERROR:"NEEDS_TO_BE_SIMPLFIED_ERROR",NOT_NONE_ABOVE_ERROR:"notNoneOfTheAbove",USER_INPUT_EMPTY:"USER_INPUT_EMPTY",USER_INPUT_TOO_LONG:"USER_INPUT_TOO_LONG",WRONG_CASE_ERROR:"WRONG_CASE_ERROR",WRONG_LETTER_ERROR:"WRONG_LETTER_ERROR"};function mapErrorToString(err,translatedStrings){if(!err){return err}return translatedStrings[errorToString[err]]||err}
1409
+ const mockStrings={characterCount:({used,num})=>num===1?`${used} / ${num} Character`:`${used} / ${num} Characters`,closeKeypad:"close math keypad",openKeypad:"open math keypad",mathInputBox:"Math input box",removeHighlight:"Remove highlight",addHighlight:"Add highlight",hintPos:({pos})=>`Hint #${pos}`,errorRendering:({error})=>`Error rendering: ${error}`,APPROXIMATED_PI_ERROR:"Your answer is close, but you may "+"have approximated pi. Enter your "+"answer as a multiple of pi, like "+"<code>12\\ \\text{pi}</code> or "+"<code>2/3\\ \\text{pi}</code>",EMPTY_RESPONSE_ERROR:"There are still more parts of this question to answer.",EXTRA_SYMBOLS_ERROR:"We could not understand your "+"answer. Please check your answer for extra "+"text or symbols.",NEEDS_TO_BE_SIMPLFIED_ERROR:"Your answer is almost correct, "+"but it needs to be simplified.",MISSING_PERCENT_ERROR:"Your answer is almost correct, "+"but it is missing a "+"<code>\\%</code> at the end.",MULTIPLICATION_SIGN_ERROR:"I'm a computer. I only understand "+"multiplication if you use an asterisk "+"(*) as the multiplication sign.",USER_INPUT_EMPTY:"Your answer is empty.",USER_INPUT_TOO_LONG:"Please shorten your response.",WRONG_CASE_ERROR:"Your answer includes use of a variable with the wrong case.",WRONG_LETTER_ERROR:"Your answer includes a wrong variable letter.",invalidSelection:"Make sure you select something for every row.",INVALID_CHOICE_SELECTION:"Invalid choice selection",ERROR_TITLE:"Oops!",ERROR_MESSAGE:"Sorry, I don't understand that!",hints:"Hints",getAnotherHint:"Get another hint",deprecatedStandin:"Sorry, this part of the question is no longer available. 😅 Don't worry, you won't be graded on this part. Keep going!",keepTrying:"Keep trying",tryAgain:"Try again",check:"Check",correctExcited:"Correct!",nextQuestion:"Next question",skipToTitle:({title})=>`Skip to ${title}`,current:"Current",correct:"Correct",correctSelected:"Correct (selected)",incorrect:"Incorrect",incorrectSelected:"Incorrect (selected)",hideExplanation:"Hide explanation",explain:"Explain",INVALID_MESSAGE_PREFIX:"We couldn't grade your answer.",DEFAULT_INVALID_MESSAGE_1:"It looks like you left something blank or ",DEFAULT_INVALID_MESSAGE_2:"entered in an invalid answer.",integerExample:"an integer (like $-3$ or $45$)",properExample:"a proper fraction (like $1/3$ or $12/13$)",simplifiedProperExample:"a simplified proper fraction (like $1/2$ or $3/4$)",improperExample:"an improper fraction (like $3/2$ or $17/15$)",simplifiedImproperExample:"a simplified improper fraction (like $5/4$ or $9/8$)",mixedExample:"a mixed number (like $1\\ 3/4$ or $2\\ 5/6$)",decimalExample:"an exact decimal (like $-0.85$ or $3.14$)",percentExample:"a percent (like $6\\%$ or $12.34\\%$)",piExample:"a multiple of pi (like $2/3\\ \\text{pi}$ or $5\\ \\text{pi}$)",yourAnswer:"**Your answer should be** ",yourAnswerCanBe:"**Your answer can be** ",yourAnswerLabel:"Your answer:",addPoints:"Click to add points",addVertices:"Click to add vertices",tapMultiple:"Tap each dot on the image to select all answers that apply.",tapSingle:"Tap each dot on the image to select an answer.",clickMultiple:"Click each dot on the image to select all answers that apply.",clickSingle:"Click each dot on the image to select an answer.",choices:"Choices:",answers:({num})=>num===1?`${num} answer`:`${num} answers`,hideAnswersToggleLabel:"Hide answer choices",moves:({num})=>num===1?`Moves: ${num}`:`Moves: ${num}`,clickTiles:"Click on the tiles to change the lights.",turnOffLights:"You must turn on all of the lights to continue.",fillAllCells:"Make sure you fill in all cells in the matrix.",molecularDrawing:({content})=>`A molecular structure drawing. SMILES notation: ${content}`,switchDirection:"Switch direction",circleOpen:"Make circle open",circleFilled:"Make circle filled",numDivisions:"Number of divisions:",divisions:({divRangeString})=>`Please make sure the number of divisions is in the range ${divRangeString}.`,dragHandles:"Drag handles to make graph",tapAddPoints:"Tap to add points",false:"False",true:"True",no:"No",yes:"Yes",chooseCorrectNum:"Please choose the correct number of answers.",notNoneOfTheAbove:"'None of the above' may not be selected when other answers are selected.",noneOfTheAbove:"None of the above",chooseNumAnswers:({numCorrect})=>`Choose ${numCorrect} answers:`,chooseAllAnswers:"Choose all answers that apply:",chooseOneAnswer:"Choose 1 answer:",choiceCheckedCorrect:({letter})=>`(Choice ${letter}, Checked, Correct)`,choiceCorrect:({letter})=>`(Choice ${letter}, Correct)`,choiceCheckedIncorrect:({letter})=>`(Choice ${letter}, Checked, Incorrect)`,choiceIncorrect:({letter})=>`(Choice ${letter}, Incorrect)`,choiceChecked:({letter})=>`(Choice ${letter}, Checked)`,choice:({letter})=>`(Choice ${letter})`,notSelected:"not selected",choicesSelected:({num})=>num===1?`${num} choice selected`:`${num} choices selected`,bringBack:"Bring back",openMenuForChoice:({letter})=>`Open menu for Choice ${letter}`,letters:"A B C D E F G H I J K L M N O P Q R S T U V W X Y Z",scrollAnswers:"Scroll Answers",scrollStart:"Scroll to view start of the content",scrollEnd:"Scroll to view the end of the content",rightArrow:"Reaction arrow pointing to the right.",dontUnderstandUnits:"I couldn't understand those units.",checkSigFigs:"Check your significant figures.",answerNumericallyIncorrect:"That answer is numerically incorrect.",checkUnits:"Check your units.",dontUnderstand:"I don't understand that",loading:"Loading...",videoTranscript:"See video transcript",somethingWrong:"Something went wrong.",videoWrapper:"Khan Academy video wrapper",pythonProgram:"Python program",computerScienceProgram:"Computer science program",embeddedContent:"Embedded content",mathInputTitle:"mathematics keyboard",mathInputDescription:"Use keyboard/mouse to interact with math-based input fields",sin:"sin",cos:"cos",tan:"tan",simulationLoadFail:"Sorry, this simulation cannot load.",simulationLocaleWarning:"Sorry, this simulation isn't available in your language.",openInNewTab:"Open simulation in a new tab",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",selectAnAnswer:"Select an answer",srGraphInstructions:"Enable Forms or Focus mode and use the Tab key to move through the interactive elements in the graph. When an interactive element has focus, use Arrow keys to move it.",srUnlimitedGraphInstructions:"Press Shift + Enter to interact with the graph. Enable Forms or Focus mode and use the Tab key to move through the interactive elements in the graph and access the graph Action Bar. When an interactive element has focus, use Arrow keys to move it or use the Delete key to remove it from the graph. Use the buttons in the Action Bar to add or adjust elements within the graph.",graphKeyboardPrompt:"Press Shift + Enter to interact with the graph",addPoint:"Add Point",removePoint:"Remove Point",closePolygon:"Close shape",openPolygon:"Re-open shape",ungradedInteractiveGraph:"Use this graph to check your thinking, but it does not count as your answer.",srPointAtCoordinates:({pointLabel,x,y})=>`Point ${pointLabel} at ${x} comma ${y}.`,srInteractiveElements:({elements})=>`Interactive elements: ${elements}`,srNoInteractiveElements:"No interactive elements",srCircleGraph:"A circle on a coordinate plane.",srCircleShape:({centerX,centerY})=>`Circle. The center point is at ${centerX} comma ${centerY}.`,srCircleRadiusPointRight:({radiusPointX,radiusPointY})=>`Right radius endpoint at ${radiusPointX} comma ${radiusPointY}.`,srCircleRadiusPointLeft:({radiusPointX,radiusPointY})=>`Left radius endpoint at ${radiusPointX} comma ${radiusPointY}.`,srCircleRadiusPointRightWithLabel:({pointLabel,radiusPointX,radiusPointY})=>`Right radius endpoint ${pointLabel} at ${radiusPointX} comma ${radiusPointY}.`,srCircleRadiusPointLeftWithLabel:({pointLabel,radiusPointX,radiusPointY})=>`Left radius endpoint ${pointLabel} at ${radiusPointX} comma ${radiusPointY}.`,srCircleRadius:({radius})=>`Circle radius is ${radius}.`,srCircleOuterPoints:({point1X,point1Y,point2X,point2Y,point3X,point3Y,point4X,point4Y})=>`Points on the circle at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, ${point3X} comma ${point3Y}, ${point4X} comma ${point4Y}.`,srLinearGraph:"A line on a coordinate plane.",srLinearGraphPoints:({point1X,point1Y,point2X,point2Y})=>`The line has two points, point 1 at ${point1X} comma ${point1Y} and point 2 at ${point2X} comma ${point2Y}.`,srLinearGraphSlopeIncreasing:"Its slope increases from left to right.",srLinearGraphSlopeDecreasing:"Its slope decreases from left to right.",srLinearGraphSlopeHorizontal:"Its slope is zero.",srLinearGraphSlopeVertical:"Its slope is undefined.",srLinearGraphXOnlyIntercept:({xIntercept})=>`The line crosses the X-axis at ${xIntercept} comma 0.`,srLinearGraphYOnlyIntercept:({yIntercept})=>`The line crosses the Y-axis at 0 comma ${yIntercept}.`,srLinearGraphBothIntercepts:({xIntercept,yIntercept})=>`The line crosses the X-axis at ${xIntercept} comma 0 and the Y-axis at 0 comma ${yIntercept}.`,srLinearGraphOriginIntercept:"The line crosses the X and Y axes at the graph's origin.",srLinearGrabHandle:({point1X,point1Y,point2X,point2Y})=>`Line going through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y}.`,srAngleStartingSide:({pointLabel,x,y})=>`Point ${pointLabel}, starting side at ${x} comma ${y}.`,srAngleEndingSide:({pointLabel,x,y})=>`Point ${pointLabel}, ending side at ${x} comma ${y}.`,srAngleVertexWithAngleMeasure:({pointLabel,x,y,angleMeasure})=>`Point ${pointLabel}, vertex at ${x} comma ${y}. Angle ${angleMeasure} degrees.`,srAngleGraphAriaLabel:"An angle on a coordinate plane.",srAngleGraphAriaDescription:({angleMeasure,vertexX,vertexY,startingSideX,startingSideY,endingSideX,endingSideY})=>`The angle measure is ${angleMeasure} degrees with a vertex at ${vertexX} comma ${vertexY}, a point on the starting side at ${startingSideX} comma ${startingSideY} and a point on the ending side at ${endingSideX} comma ${endingSideY}.`,srAngleInteractiveElements:({vertexX,vertexY,startingSideX,startingSideY,endingSideX,endingSideY})=>`An angle formed by 3 points. The vertex is at ${vertexX} comma ${vertexY}. The starting side point is at ${startingSideX} comma ${startingSideY}. The ending side point is at ${endingSideX} comma ${endingSideY}.`,srSingleSegmentGraphAriaLabel:"A line segment on a coordinate plane.",srMultipleSegmentGraphAriaLabel:({countOfSegments})=>`${countOfSegments} segments on a coordinate plane.`,srMultipleSegmentIndividualLabel:({point1X,point1Y,point2X,point2Y,indexOfSegment})=>`Segment ${indexOfSegment}: Endpoint 1 at ${point1X} comma ${point1Y}. Endpoint 2 at ${point2X} comma ${point2Y}.`,srSingleSegmentLabel:({point1X,point1Y,point2X,point2Y})=>`Endpoint 1 at ${point1X} comma ${point1Y}. Endpoint 2 at ${point2X} comma ${point2Y}.`,srSegmentLength:({length})=>`Segment length ${length} units.`,srSingleSegmentGraphEndpointAriaLabel:({pointLabel,x,y})=>`Endpoint ${pointLabel} at ${x} comma ${y}.`,srMultipleSegmentGraphEndpointAriaLabel:({pointLabel,x,y,indexOfSegment})=>`Endpoint ${pointLabel} on segment ${indexOfSegment} at ${x} comma ${y}.`,srSegmentGrabHandle:({point1X,point1Y,point2X,point2Y})=>`Segment from ${point1X} comma ${point1Y} to ${point2X} comma ${point2Y}.`,srLinearSystemGraph:"Two lines on a coordinate plane.",srLinearSystemPoints:({lineNumber,point1X,point1Y,point2X,point2Y})=>`Line ${lineNumber} has two points, point 1 at ${point1X} comma ${point1Y} and point 2 at ${point2X} comma ${point2Y}.`,srLinearSystemPoint:({lineNumber,pointLabel,x,y})=>`Point ${pointLabel} on line ${lineNumber} at ${x} comma ${y}.`,srLinearSystemGrabHandle:({lineNumber,point1X,point1Y,point2X,point2Y})=>`Line ${lineNumber} going through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y}.`,srLinearSystemIntersection:({x,y})=>`Line 1 and line 2 intersect at point ${x} comma ${y}.`,srLinearSystemParallel:"Line 1 and line 2 are parallel.",srRayGraph:"A ray on a coordinate plane.",srRayPoints:({point1X,point1Y,point2X,point2Y})=>`The endpoint is at ${point1X} comma ${point1Y} and the ray goes through point ${point2X} comma ${point2Y}.`,srRayGrabHandle:({point1X,point1Y,point2X,point2Y})=>`Ray with endpoint ${point1X} comma ${point1Y} going through point ${point2X} comma ${point2Y}.`,srRayEndpoint:({x,y})=>`Endpoint at ${x} comma ${y}.`,srRayEndpointWithLabel:({pointLabel,x,y})=>`Endpoint ${pointLabel} at ${x} comma ${y}.`,srRayTerminalPoint:({x,y})=>`Through point at ${x} comma ${y}.`,srRayTerminalPointWithLabel:({pointLabel,x,y})=>`Through point ${pointLabel} at ${x} comma ${y}.`,srVectorGraph:"A vector on a coordinate plane.",srVectorPoints:({tailX,tailY,headX,headY})=>`The tail is at ${tailX} comma ${tailY} and the head is at ${headX} comma ${headY}.`,srVectorHeadPoint:({x,y})=>`Vector head at ${x} comma ${y}.`,srVectorMagnitudeDirection:({magnitude,direction})=>`The vector has a magnitude of ${magnitude} and a direction of ${direction} degrees.`,srVectorGrabHandle:({tailX,tailY,headX,headY})=>`Vector from ${tailX} comma ${tailY} to ${headX} comma ${headY}.`,srQuadraticGraph:"A parabola on a 4-quadrant coordinate plane.",srQuadraticFaceUp:"The parabola opens upward.",srQuadraticFaceDown:"The parabola opens downward.",srQuadraticGraphVertexOrigin:"Vertex is at the origin.",srQuadraticGraphVertexXAxis:"Vertex is on the X-axis.",srQuadraticGraphVertexYAxis:"Vertex is on the Y-axis.",srQuadraticGraphVertexQuadrant:({quadrant})=>`Vertex is in quadrant ${quadrant}.`,srQuadraticTwoXIntercepts:({intercept1,intercept2})=>`The X-intercepts are at ${intercept1} comma 0 and ${intercept2} comma 0.`,srQuadraticOneXIntercept:({intercept})=>`The X-intercept is at ${intercept} comma 0.`,srQuadraticYIntercept:({intercept})=>`The Y-intercept is at 0 comma ${intercept}.`,srQuadraticPointOrigin:({pointLabel})=>`Point ${pointLabel} on parabola at the origin.`,srQuadraticPointAxis:({pointLabel,x,y})=>`Point ${pointLabel} on parabola at ${x} comma ${y}.`,srQuadraticPointQuadrant:({pointLabel,x,y,quadrant})=>`Point ${pointLabel} on parabola in quadrant ${quadrant} at ${x} comma ${y}.`,srQuadraticInteractiveElements:({point1X,point1Y,point2X,point2Y,point3X,point3Y})=>`Parabola with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and ${point3X} comma ${point3Y}.`,srPolygonGraph:"A polygon.",srPolygonGraphCoordinatePlane:"A polygon on a coordinate plane.",srPolygonGraphPointsNum:({num})=>`The polygon has ${num} points.`,srPolygonGraphPointsOne:"The polygon has 1 point.",srPolygonElementsNum:({num})=>`A polygon with ${num} points.`,srPolygonElementsOne:"A polygon with 1 point.",srPolygonPointAngleApprox:({angle})=>`Angle approximately equal to ${angle} degrees.`,srPolygonPointAngle:({angle})=>`Angle equal to ${angle} degrees.`,srPolygonSideLength:({pointNum,length})=>`A line segment, length equal to ${length} units, connects to point ${pointNum}.`,srPolygonSideLengthApprox:({pointNum,length})=>`A line segment, length approximately equal to ${length} units, connects to point ${pointNum}.`,srUnlimitedPolygonEmpty:"An empty coordinate plane.",srSinusoidGraph:"A sinusoid function on a coordinate plane.",srSinusoidRootPoint:({x,y})=>`Midline intersection at ${x} comma ${y}.`,srSinusoidRootPointWithLabel:({pointLabel,x,y})=>`Midline intersection point ${pointLabel} at ${x} comma ${y}.`,srSinusoidMaxPoint:({x,y})=>`Maximum point at ${x} comma ${y}.`,srSinusoidMaxPointWithLabel:({pointLabel,x,y})=>`Maximum point ${pointLabel} at ${x} comma ${y}.`,srSinusoidMinPoint:({x,y})=>`Minimum point at ${x} comma ${y}.`,srSinusoidMinPointWithLabel:({pointLabel,x,y})=>`Minimum point ${pointLabel} at ${x} comma ${y}.`,srSinusoidFlatPoint:({x,y})=>`Line through point at ${x} comma ${y}.`,srSinusoidFlatPointWithLabel:({pointLabel,x,y})=>`Line through point ${pointLabel} at ${x} comma ${y}.`,srSinusoidDescription:({minValue,maxValue,cycleStart,cycleEnd})=>`The graph shows a wave with a minimum value of ${minValue} and a maximum value of ${maxValue}. The wave completes a full cycle from ${cycleStart} to ${cycleEnd}.`,srSinusoidInteractiveElements:({point1X,point1Y,point2X,point2Y})=>`Sinusoid graph with midline intersection point at ${point1X} comma ${point1Y} and extremum point at ${point2X} comma ${point2Y}.`,srExponentialGraph:"An exponential curve on a coordinate plane.",srExponentialPoint:({pointLabel,x,y})=>`Point ${pointLabel} on an exponential curve at ${x} comma ${y}.`,srExponentialDescriptionRightNeg:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches y equals ${asymptoteY} from the right and extends to negative infinity.`,srExponentialDescriptionLeftPos:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches y equals ${asymptoteY} from the left and extends to positive infinity.`,srExponentialAboveAsymptote:"The curve lies above the asymptote.",srExponentialBelowAsymptote:"The curve lies below the asymptote.",srExponentialNoCurve:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`No exponential curve can be drawn through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} with a horizontal asymptote at y equals ${asymptoteY}. Move both points to the same side of the asymptote to draw the curve.`,srExponentialIntercepts:({xIntercept,yIntercept})=>`The x-intercept is at ${xIntercept} comma 0. The y-intercept is at 0 comma ${yIntercept}.`,srExponentialYIntercept:({yIntercept})=>`The y-intercept is at 0 comma ${yIntercept}.`,srExponentialInteractiveElements:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`Exponential graph with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and a horizontal asymptote at y equals ${asymptoteY}.`,srExponentialAsymptote:({asymptoteY})=>`Horizontal asymptote at y equals ${asymptoteY}`,srLogarithmGraph:"A logarithmic curve on a coordinate plane.",srLogarithmPoint:({pointLabel,x,y})=>`Point ${pointLabel} on a logarithmic curve at ${x} comma ${y}.`,srLogarithmDescriptionRightNeg:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches x equals ${asymptoteX} from the right and extends to negative infinity.`,srLogarithmDescriptionRightPos:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches x equals ${asymptoteX} from the right and extends to positive infinity.`,srLogarithmDescriptionLeftNeg:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches x equals ${asymptoteX} from the left and extends to negative infinity.`,srLogarithmDescriptionLeftPos:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The curve passes through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} as the curve approaches x equals ${asymptoteX} from the left and extends to positive infinity.`,srLogarithmToRightOfAsymptote:"The curve is to the right of the asymptote.",srLogarithmToLeftOfAsymptote:"The curve is to the left of the asymptote.",srLogarithmNoCurve:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`No curve can be drawn through ${point1X} comma ${point1Y} and ${point2X} comma ${point2Y} with a vertical asymptote at x equals ${asymptoteX}. Move both points to the same side of the asymptote to draw the curve.`,srLogarithmIntercepts:({xIntercept,yIntercept})=>`The x-intercept is at ${xIntercept} comma 0. The y-intercept is at 0 comma ${yIntercept}.`,srLogarithmXIntercept:({xIntercept})=>`The x-intercept is at ${xIntercept} comma 0.`,srLogarithmInteractiveElements:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`Logarithmic graph with points at ${point1X} comma ${point1Y}, ${point2X} comma ${point2Y}, and a vertical asymptote at x equals ${asymptoteX}.`,srLogarithmAsymptote:({asymptoteX})=>`Vertical asymptote at x equals ${asymptoteX}.`,srAbsoluteValueGraph:"An absolute value on a coordinate plane.",srAbsoluteValueVertexPoint:({x,y})=>`Vertex point at ${x} comma ${y}.`,srAbsoluteValueVertexPointWithLabel:({pointLabel,x,y})=>`Vertex point ${pointLabel} at ${x} comma ${y}.`,srAbsoluteValueArmPoint:({x,y})=>`Point on arm at ${x} comma ${y}.`,srAbsoluteValueArmPointWithLabel:({pointLabel,x,y})=>`Point ${pointLabel} on arm at ${x} comma ${y}.`,srAbsoluteValueSlope:({slope})=>`The slope is ${slope}.`,srAbsoluteValueOpensUp:"The graph opens upward.",srAbsoluteValueOpensDown:"The graph opens downward.",srAbsoluteValueVertex:({x,y})=>`Vertex is at ${x} comma ${y}.`,srAbsoluteValueVertexOrigin:"Vertex is at the origin.",srAbsoluteValueTwoXIntercepts:({intercept1,intercept2})=>`The X-intercepts are at ${intercept1} comma 0 and ${intercept2} comma 0.`,srAbsoluteValueOneXIntercept:({intercept})=>`The X-intercept is at ${intercept} comma 0.`,srAbsoluteValueYIntercept:({intercept})=>`The Y-intercept is at 0 comma ${intercept}.`,srAbsoluteValueInteractiveElements:({point1X,point1Y,point2X,point2Y,slope})=>`Absolute value graph with vertex point at ${point1X} comma ${point1Y}, arm point at ${point2X} comma ${point2Y} and slope of ${slope}.`,srTangentGraph:"A tangent curve on a coordinate plane.",srTangentInflectionPoint:({x,y})=>`Inflection point at ${x} comma ${y}.`,srTangentInflectionPointWithLabel:({pointLabel,x,y})=>`Inflection point ${pointLabel} at ${x} comma ${y}.`,srTangentControlPoint:({x,y})=>`Control point at ${x} comma ${y}.`,srTangentControlPointWithLabel:({pointLabel,x,y})=>`Control point ${pointLabel} at ${x} comma ${y}.`,srTangentDescriptionPoints:({inflectionX,inflectionY,controlX,controlY})=>`The curve passes through an inflection point at ${inflectionX} comma ${inflectionY} and a control point at ${controlX} comma ${controlY}.`,srTangentIncreasing:({period})=>`The curve increases through the inflection point, repeating every ${period} units.`,srTangentDecreasing:({period})=>`The curve decreases through the inflection point, repeating every ${period} units.`,srTangentAsymptotes:({leftAsymptote,rightAsymptote})=>`The nearest vertical asymptotes are at x equals ${leftAsymptote} and x equals ${rightAsymptote}.`,srTangentInteractiveElements:({point1X,point1Y,point2X,point2Y})=>`Tangent graph with inflection point at ${point1X} comma ${point1Y} and control point at ${point2X} comma ${point2Y}.`,imageExploreButton:"Explore image",imageExploreButtonAriaLabel:"Explore image and description",imageAlternativeTitle:"Explore image and description",imageDescriptionLabel:"Description",imageZoomAriaLabel:"Make image bigger.",imageResetZoomAriaLabel:"Close image.",gifPlayButtonLabel:"Play Animation",gifPauseButtonLabel:"Pause Animation",definitionIdentifier:({word})=>`Definition of: ${word}`};const errorToString={APPROXIMATED_PI_ERROR:"APPROXIMATED_PI_ERROR",CHOOSE_CORRECT_NUM_ERROR:"chooseCorrectNum",EMPTY_RESPONSE_ERROR:"EMPTY_RESPONSE_ERROR",EXTRA_SYMBOLS_ERROR:"EXTRA_SYMBOLS_ERROR",FILL_ALL_CELLS_ERROR:"fillAllCells",INVALID_CHOICE_SELECTION:"INVALID_CHOICE_SELECTION",INVALID_SELECTION_ERROR:"invalidSelection",MISSING_PERCENT_ERROR:"MISSING_PERCENT_ERROR",MULTIPLICATION_SIGN_ERROR:"MULTIPLICATION_SIGN_ERROR",NEEDS_TO_BE_SIMPLIFIED_ERROR:"NEEDS_TO_BE_SIMPLFIED_ERROR",NOT_NONE_ABOVE_ERROR:"notNoneOfTheAbove",USER_INPUT_EMPTY:"USER_INPUT_EMPTY",USER_INPUT_TOO_LONG:"USER_INPUT_TOO_LONG",WRONG_CASE_ERROR:"WRONG_CASE_ERROR",WRONG_LETTER_ERROR:"WRONG_LETTER_ERROR"};function mapErrorToString(err,translatedStrings){if(!err){return err}return translatedStrings[errorToString[err]]||err}
1410
1410
 
1411
1411
  const mockPerseusI18nContext={strings:mockStrings,locale:"en"};const PerseusI18nContext=React.createContext(process.env.NODE_ENV==="test"||process.env.STORYBOOK?mockPerseusI18nContext:null);function PerseusI18nContextProvider({children,strings,locale}){return jsxRuntimeExports.jsx(PerseusI18nContext.Provider,{value:{strings,locale},children:children})}const usePerseusI18n=()=>useContext(PerseusI18nContext);
1412
1412
 
@@ -1414,7 +1414,7 @@ var a11y = StyleSheet.create({srOnly:{border:0,clip:"rect(0,0,0,0)",height:1,mar
1414
1414
 
1415
1415
  const debounce=(func,delay)=>{let timer=null;return (...args)=>{if(timer){clearTimeout(timer);}timer=window.setTimeout(()=>{func(...args);},delay);}};
1416
1416
 
1417
- class InnerMathInput extends React.Component{componentDidMount(){this.mathField()?.latex(this.props.value);}componentDidUpdate(prevProps){if(prevProps.value!==this.props.value){if(this.state.focused){return}const field=this.mathField();if(!field){return}const current=field.latex();if(this.props.value!==current){field.latex(this.props.value);}}}openKeypad(){if(this.props.buttonsVisible==="never"){return}this.setState({keypadOpen:true});}closeKeypad(){this.setState({keypadOpen:false});}render(){let className=classNames$2({"perseus-math-input":true,"mq-editable-field":true,"mq-math-mode":true});const popoverContentUniqueId=v4().slice(0,8);if(this.props.className){className=className+" "+this.props.className;}return jsxRuntimeExports.jsx(View,{style:[styles$D.outerWrapper,this.state.focused&&styles$D.wrapperFocused],children:jsxRuntimeExports.jsxs("div",{style:{display:"flex",padding:1},onClick:e=>{e.stopPropagation();const mathField=this.mathField();if(!mathField){return}this.setState({cursorContext:getCursorContext(mathField)});},children:[jsxRuntimeExports.jsx("span",{className:className,ref:ref=>this.__mathFieldWrapperRef=ref,onFocus:()=>this.focus(),onBlur:()=>this.blur()}),jsxRuntimeExports.jsx(Popover,{opened:this.state.keypadOpen,dismissEnabled:true,rootBoundary:"document","aria-label":this.context.strings.mathInputTitle,"aria-describedby":`popover-content-${popoverContentUniqueId}`,onClose:()=>this.closeKeypad(),content:()=>jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Heading,{size:"large",id:`popover-content-${popoverContentUniqueId}`,style:a11y.srOnly,children:this.context.strings.mathInputDescription}),jsxRuntimeExports.jsx(PopoverContentCore,{style:styles$D.popoverContent,children:jsxRuntimeExports.jsx(DesktopKeypad,{onAnalyticsEvent:this.props.onAnalyticsEvent,extraKeys:this.props.extraKeys,onClickKey:this.handleKeypadPress,cursorContext:this.state.cursorContext,convertDotToTimes:this.props.convertDotToTimes,...this.props.keypadButtonSets??mapButtonSets(this.props?.buttonSets),showDismiss:true})})]}),children:this.props.buttonsVisible==="never"?jsxRuntimeExports.jsx(MathInputIcon,{hovered:false,focused:false,active:false}):jsxRuntimeExports.jsx(Clickable,{"aria-label":this.state.keypadOpen?this.context.strings.closeKeypad:this.context.strings.openKeypad,role:"button",onClick:()=>this.state.keypadOpen?this.closeKeypad():this.openKeypad(),children:props=>jsxRuntimeExports.jsx(MathInputIcon,{active:this.state.keypadOpen,...props})})})]})})}constructor(...args){super(...args),this.__mathFieldWrapperRef=null,this.__mathField=null,this.state={focused:false,keypadOpen:this.props.buttonsVisible==="always"?true:false,cursorContext:CursorContext.NONE},this.insert=value=>{const input=this.mathField();const{locale}=this.context;const customKeyTranslator={...getKeyTranslator(locale,this.context.strings),FRAC:mathQuill=>{const contents=mathQuill.latex();mathQuill.typedText("/");if(mathQuill.latex()===contents){mathQuill.cmd("\\frac");}}};const inputModifier=customKeyTranslator[value];if(inputModifier){inputModifier(input,value);input?.focus();return}if(typeof value==="function"){value(input);}else if(value[0]==="\\"){input?.cmd(value).focus();}else {input?.write(value).focus();}input?.focus();},this.mathField=()=>{if(!this.__mathField&&this.__mathFieldWrapperRef){const{locale}=this.context;this.__mathField=createMathField(this.__mathFieldWrapperRef,locale,this.props.mathInputStrings,baseConfig=>({...baseConfig,handlers:{edit:debounce(mathField=>{let value=mathField.latex();value=value.replace(/<>/g,"\\ne");if(convertDotToTimesByLocale(locale,this.props.convertDotToTimes)){value=value.replace(/\\cdot/g,"\\times");const left=mathField.cursor()[mathQuillInstance.L];if(left&&left.ctrlSeq==="\\cdot "){mathField.controller().backspace();mathField.cmd("\\times");}}else {value=value.replace(/\\times/g,"\\cdot");}if(this.props.value!==value){this.props.onChange(value);}this.setState({cursorContext:getCursorContext(mathField)});},100),enter:()=>{if(this.__mathFieldWrapperRef){$(this.__mathFieldWrapperRef).submit();}},upOutOf:mathField=>{mathField.typedText("^");}}}));}this.__mathField?.setAriaLabel(this.props.ariaLabel);return this.__mathField},this.focus=()=>{this.mathField()?.focus();this.setState({focused:true});},this.blur=()=>this.setState({focused:false}),this.handleKeypadPress=(key,e)=>{if(key==="DISMISS"){this.closeKeypad();return}const{locale}=this.context;const translator=getKeyTranslator(locale,this.context.strings)[key];const mathField=this.mathField();if(mathField){if(translator){translator(mathField,key);}this.setState({cursorContext:getCursorContext(mathField)});}if(e?.type==="click"){this.focus();}};}}InnerMathInput.contextType=PerseusI18nContext;InnerMathInput.defaultProps={value:"",convertDotToTimes:false};class MathInput extends React.Component{blur(){this.inputRef.current?.blur();}focus(){this.inputRef.current?.focus();}insert(value){this.inputRef.current?.insert(value);}render(){return jsxRuntimeExports.jsx(InnerMathInput,{...this.props,ref:this.inputRef,mathInputStrings:this.context.strings})}constructor(...args){super(...args),this.inputRef=React.createRef();}}MathInput.contextType=MathInputI18nContext;MathInput.defaultProps={ariaLabel:"Math input"};const MathInputIcon=({hovered,focused,active})=>{let fillColor;switch(true){case focused||active:fillColor=semanticColor.action.primary.progressive.default.foreground;break;case hovered:fillColor=semanticColor.action.primary.progressive.hover.background;break;default:fillColor=semanticColor.core.foreground.neutral.strong;break}const dynamicClass=active||focused?styles$D.iconActive:styles$D.iconInactive;return jsxRuntimeExports.jsx(View,{style:[styles$D.iconContainer,dynamicClass],children:jsxRuntimeExports.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",fill:fillColor,viewBox:"0 0 256 256",children:jsxRuntimeExports.jsx("path",{d:"M112,72a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16h64A8,8,0,0,1,112,72Zm-8,104H80V152a8,8,0,0,0-16,0v24H40a8,8,0,0,0,0,16H64v24a8,8,0,0,0,16,0V192h24a8,8,0,0,0,0-16Zm48,0h64a8,8,0,0,0,0-16H152a8,8,0,0,0,0,16Zm64,16H152a8,8,0,0,0,0,16h64a8,8,0,0,0,0-16Zm-61.66-90.34a8,8,0,0,0,11.32,0L184,83.31l18.34,18.35a8,8,0,0,0,11.32-11.32L195.31,72l18.35-18.34a8,8,0,0,0-11.32-11.32L184,60.69,165.66,42.34a8,8,0,0,0-11.32,11.32L172.69,72,154.34,90.34A8,8,0,0,0,154.34,101.66Z"})})})};const mapButtonSets=buttonSets=>{const keypadButtonSets={};if(!buttonSets){return keypadButtonSets}buttonSets.forEach(buttonSet=>{switch(buttonSet){case "advanced relations":keypadButtonSets.advancedRelations=true;break;case "basic relations":keypadButtonSets.basicRelations=true;break;case "basic+div":keypadButtonSets.divisionKey=true;break;case "logarithms":keypadButtonSets.logarithms=true;break;case "prealgebra":keypadButtonSets.preAlgebra=true;break;case "trig":keypadButtonSets.trigonometry=true;break;case "scientific":keypadButtonSets.scientific=true;break;}});return keypadButtonSets};const inputFocused={borderWidth:2,borderColor:semanticColor.core.border.instructive.default,margin:-1};const styles$D=StyleSheet.create({iconContainer:{display:"flex",justifyContent:"center",height:"100%",padding:sizing.size_040,borderRadius:1},iconInactive:{border:"2px solid transparent",backgroundColor:semanticColor.core.background.neutral.subtle},iconActive:{border:`2px solid ${semanticColor.core.border.knockout.default}`,backgroundColor:semanticColor.core.background.neutral.default},outerWrapper:{display:"inline-block",borderStyle:"solid",borderWidth:1,borderColor:semanticColor.core.border.neutral.default,borderRadius:3,background:semanticColor.core.background.base.default,":hover":inputFocused},wrapperFocused:inputFocused,popoverContent:{padding:0,paddingBlockEnd:sizing.size_060,maxInlineSize:"initial"}});
1417
+ class InnerMathInput extends React.Component{componentDidMount(){this.mathField()?.latex(this.props.value);}componentDidUpdate(prevProps){if(prevProps.value!==this.props.value){if(this.state.focused){return}const field=this.mathField();if(!field){return}const current=field.latex();if(this.props.value!==current){field.latex(this.props.value);}}}openKeypad(){if(this.props.buttonsVisible==="never"){return}this.setState({keypadOpen:true});}closeKeypad(){this.setState({keypadOpen:false});}render(){let className=classNames$2({"perseus-math-input":true,"mq-editable-field":true,"mq-math-mode":true});const popoverContentUniqueId=v4().slice(0,8);if(this.props.className){className=className+" "+this.props.className;}return jsxRuntimeExports.jsx(View,{style:[styles$D.outerWrapper,this.state.focused&&styles$D.wrapperFocused],children:jsxRuntimeExports.jsxs("div",{style:{display:"flex",padding:1},onClick:e=>{e.stopPropagation();const mathField=this.mathField();if(!mathField){return}this.setState({cursorContext:getCursorContext(mathField)});},children:[jsxRuntimeExports.jsx("span",{className:className,ref:ref=>this.__mathFieldWrapperRef=ref,onFocus:()=>this.focus(),onBlur:()=>this.blur()}),jsxRuntimeExports.jsx(Popover,{opened:this.state.keypadOpen,dismissEnabled:true,rootBoundary:"document","aria-label":this.context.strings.mathInputTitle,"aria-describedby":`popover-content-${popoverContentUniqueId}`,onClose:()=>this.closeKeypad(),content:()=>jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Heading,{size:"large",id:`popover-content-${popoverContentUniqueId}`,style:a11y.srOnly,children:this.context.strings.mathInputDescription}),jsxRuntimeExports.jsx(PopoverContentCore,{style:styles$D.popoverContent,children:jsxRuntimeExports.jsx(DesktopKeypad,{onAnalyticsEvent:this.props.onAnalyticsEvent,extraKeys:this.props.extraKeys,onClickKey:this.handleKeypadPress,cursorContext:this.state.cursorContext,convertDotToTimes:this.props.convertDotToTimes,...this.props.keypadButtonSets??mapButtonSets(this.props?.buttonSets),showDismiss:true})})]}),children:this.props.buttonsVisible==="never"?jsxRuntimeExports.jsx(MathInputIcon,{hovered:false,focused:false,active:false}):jsxRuntimeExports.jsx(Clickable,{"aria-label":this.state.keypadOpen?this.context.strings.closeKeypad:this.context.strings.openKeypad,role:"button",onClick:()=>this.state.keypadOpen?this.closeKeypad():this.openKeypad(),children:props=>jsxRuntimeExports.jsx(MathInputIcon,{active:this.state.keypadOpen,...props})})})]})})}constructor(...args){super(...args),this.__mathFieldWrapperRef=null,this.__mathField=null,this.state={focused:false,keypadOpen:this.props.buttonsVisible==="always"?true:false,cursorContext:CursorContext.NONE},this.insert=value=>{const input=this.mathField();const{locale}=this.context;const customKeyTranslator={...getKeyTranslator(locale,this.context.strings),FRAC:mathQuill=>{const contents=mathQuill.latex();mathQuill.typedText("/");if(mathQuill.latex()===contents){mathQuill.cmd("\\frac");}}};const inputModifier=customKeyTranslator[value];if(inputModifier){inputModifier(input,value);input?.focus();return}if(typeof value==="function"){value(input);}else if(value[0]==="\\"){input?.cmd(value).focus();}else {input?.write(value).focus();}input?.focus();},this.mathField=()=>{if(!this.__mathField&&this.__mathFieldWrapperRef){const{locale}=this.context;this.__mathField=createMathField(this.__mathFieldWrapperRef,locale,this.props.mathInputStrings,baseConfig=>({...baseConfig,handlers:{edit:debounce(mathField=>{let value=mathField.latex();value=value.replace(/<>/g,"\\ne");if(convertDotToTimesByLocale(locale,this.props.convertDotToTimes)){value=value.replace(/\\cdot/g,"\\times");const left=mathField.cursor()[mathQuillInstance.L];if(left&&left.ctrlSeq==="\\cdot "){mathField.controller().backspace();mathField.cmd("\\times");}}else {value=value.replace(/\\times/g,"\\cdot");}if(this.props.value!==value){this.props.onChange(value);}this.setState({cursorContext:getCursorContext(mathField)});},100),enter:()=>{if(this.__mathFieldWrapperRef){this.__mathFieldWrapperRef.dispatchEvent(new Event("submit",{bubbles:true,cancelable:true}));}},upOutOf:mathField=>{mathField.typedText("^");}}}));}this.__mathField?.setAriaLabel(this.props.ariaLabel);return this.__mathField},this.focus=()=>{this.mathField()?.focus();this.setState({focused:true});},this.blur=()=>this.setState({focused:false}),this.handleKeypadPress=(key,e)=>{if(key==="DISMISS"){this.closeKeypad();return}const{locale}=this.context;const translator=getKeyTranslator(locale,this.context.strings)[key];const mathField=this.mathField();if(mathField){if(translator){translator(mathField,key);}this.setState({cursorContext:getCursorContext(mathField)});}if(e?.type==="click"){this.focus();}};}}InnerMathInput.contextType=PerseusI18nContext;InnerMathInput.defaultProps={value:"",convertDotToTimes:false};class MathInput extends React.Component{blur(){this.inputRef.current?.blur();}focus(){this.inputRef.current?.focus();}insert(value){this.inputRef.current?.insert(value);}render(){return jsxRuntimeExports.jsx(InnerMathInput,{...this.props,ref:this.inputRef,mathInputStrings:this.context.strings})}constructor(...args){super(...args),this.inputRef=React.createRef();}}MathInput.contextType=MathInputI18nContext;MathInput.defaultProps={ariaLabel:"Math input"};const MathInputIcon=({hovered,focused,active})=>{let fillColor;switch(true){case focused||active:fillColor=semanticColor.action.primary.progressive.default.foreground;break;case hovered:fillColor=semanticColor.action.primary.progressive.hover.background;break;default:fillColor=semanticColor.core.foreground.neutral.strong;break}const dynamicClass=active||focused?styles$D.iconActive:styles$D.iconInactive;return jsxRuntimeExports.jsx(View,{style:[styles$D.iconContainer,dynamicClass],children:jsxRuntimeExports.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",fill:fillColor,viewBox:"0 0 256 256",children:jsxRuntimeExports.jsx("path",{d:"M112,72a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16h64A8,8,0,0,1,112,72Zm-8,104H80V152a8,8,0,0,0-16,0v24H40a8,8,0,0,0,0,16H64v24a8,8,0,0,0,16,0V192h24a8,8,0,0,0,0-16Zm48,0h64a8,8,0,0,0,0-16H152a8,8,0,0,0,0,16Zm64,16H152a8,8,0,0,0,0,16h64a8,8,0,0,0,0-16Zm-61.66-90.34a8,8,0,0,0,11.32,0L184,83.31l18.34,18.35a8,8,0,0,0,11.32-11.32L195.31,72l18.35-18.34a8,8,0,0,0-11.32-11.32L184,60.69,165.66,42.34a8,8,0,0,0-11.32,11.32L172.69,72,154.34,90.34A8,8,0,0,0,154.34,101.66Z"})})})};const mapButtonSets=buttonSets=>{const keypadButtonSets={};if(!buttonSets){return keypadButtonSets}buttonSets.forEach(buttonSet=>{switch(buttonSet){case "advanced relations":keypadButtonSets.advancedRelations=true;break;case "basic relations":keypadButtonSets.basicRelations=true;break;case "basic+div":keypadButtonSets.divisionKey=true;break;case "logarithms":keypadButtonSets.logarithms=true;break;case "prealgebra":keypadButtonSets.preAlgebra=true;break;case "trig":keypadButtonSets.trigonometry=true;break;case "scientific":keypadButtonSets.scientific=true;break;}});return keypadButtonSets};const inputFocused={borderWidth:2,borderColor:semanticColor.core.border.instructive.default,margin:-1};const styles$D=StyleSheet.create({iconContainer:{display:"flex",justifyContent:"center",height:"100%",padding:sizing.size_040,borderRadius:1},iconInactive:{border:"2px solid transparent",backgroundColor:semanticColor.core.background.neutral.subtle},iconActive:{border:`2px solid ${semanticColor.core.border.knockout.default}`,backgroundColor:semanticColor.core.background.neutral.default},outerWrapper:{display:"inline-block",borderStyle:"solid",borderWidth:1,borderColor:semanticColor.core.border.neutral.default,borderRadius:3,background:semanticColor.core.background.base.default,":hover":inputFocused},wrapperFocused:inputFocused,popoverContent:{padding:0,paddingBlockEnd:sizing.size_060,maxInlineSize:"initial"}});
1418
1418
 
1419
1419
  let _dependencies=null;const setDependencies=dependencies=>{_dependencies=dependencies;};const getDependencies=()=>{if(_dependencies){return _dependencies}throw new Error(["Perseus has not been provided required dependencies.","setDependencies(dependencies) must be called first.","Make sure Perseus is being imported from javascript/perseus/perseus.js."].join("\n"))};const DependenciesContext=React.createContext({analytics:{onAnalyticsEvent:async()=>{}},generateUrl:()=>{throw new Error("generateUrl dependency not provided in Perseus dependencies")},useVideo:()=>{throw new Error("useVideo dependency not provided in Perseus dependencies")}});const useDependencies=()=>{const deps=React.useContext(DependenciesContext);return deps};
1420
1420
 
@@ -1552,7 +1552,7 @@ var clientExports = requireClient();
1552
1552
 
1553
1553
  const reactRootCache=new WeakMap;function render(element,container){const children=jsxRuntimeExports.jsx(RenderStateRoot,{throwIfNested:false,children:element});let root=reactRootCache.get(container);if(root==null){root=clientExports.createRoot(container);reactRootCache.set(container,root);}root.render(children);}
1554
1554
 
1555
- function findChildOrAdd(elem,className){const $child=$(elem).find("."+className);if($child.length===0){return $("<span>").addClass(className).appendTo($(elem))}return $child}var Tex$1 = {processMath:async function(elem,text,force,callback){const $elem=$(elem);if($elem.attr("data-math-formula")==null||force){const $texHolder=findChildOrAdd($elem,"tex-holder");if(text==null&&$elem.attr("data-math-formula")){text=$elem.attr("data-math-formula");}text=text!=null?text+"":"";text=KhanMath.cleanMath(text);$elem.attr("data-math-formula",text);const{TeX}=await getDependencies();render(React.createElement(TeX,{children:text,onRender:callback}),$texHolder[0]);}}};
1555
+ function findChildOrAdd(elem,className){const child=elem.querySelector("."+className);if(child){return child}const newChild=document.createElement("span");newChild.classList.add(className);elem.appendChild(newChild);return newChild}var Tex$1 = {processMath:async function(elem,text,force,callback){const dataMathFormula=elem.getAttribute("data-math-formula");if(dataMathFormula==null||force){const texHolder=findChildOrAdd(elem,"tex-holder");if(text==null&&dataMathFormula){text=dataMathFormula;}text=text!=null?text+"":"";text=KhanMath.cleanMath(text);elem.setAttribute("data-math-formula",text);const{TeX}=await getDependencies();render(React.createElement(TeX,{children:text,onRender:callback}),texHolder);}}};
1556
1556
 
1557
1557
  const{processMath: processMath$1}=Tex$1;function polar$2(r,th){if(typeof r==="number"){r=[r,r];}th=th*Math.PI/180;return [r[0]*Math.cos(th),r[1]*Math.sin(th)]}var _bounds=/*#__PURE__*/_class_private_field_loose_key("_bounds"),_drawingTransform=/*#__PURE__*/_class_private_field_loose_key("_drawingTransform"),_labelElements=/*#__PURE__*/_class_private_field_loose_key("_labelElements"),_resizeObserver=/*#__PURE__*/_class_private_field_loose_key("_resizeObserver");let Graphie$1 = class Graphie{init(options){let scale=options.scale||[40,40];scale=typeof scale==="number"?[scale,scale]:scale;if(options.range==null){throw new PerseusError("range should be specified in graph init",Errors.Internal)}_class_private_field_loose_base(this,_bounds)[_bounds]=new GraphBounds(...options.range);_class_private_field_loose_base(this,_drawingTransform)[_drawingTransform]=new DrawingTransform(this.raphael,scale,this.bounds());const[w,h]=this.drawingTransform().canvasDimensions();$(this.el).css({width:w,height:h});this.range=options.range;this.scale=scale;this.dimensions=[w,h];this.xpixels=w;this.ypixels=h;this.isMobile=options.isMobile??false;return this}graphInit(options){options=options||{};for(const[prop,val]of entries(options)){if(!prop.match(/.*Opacity$/)&&prop!=="range"&&typeof val==="number"){options[prop]=[val,val];}if(prop==="range"||prop==="gridRange"){options[prop]=normalizeRange(options[prop]);}}const range=normalizeRange(options.range||[[-10,10],[-10,10]]);const gridRange=normalizeRange(options.gridRange||range);const scale=options.scale||[20,20];const grid=options.grid!=null?options.grid:true;const gridOpacity=options.gridOpacity||.1;const gridStep=toPair(options.gridStep||[1,1]);const axes=options.axes!=null?options.axes:true;const axisArrows=options.axisArrows||"";const axisOpacity=options.axisOpacity||1;const axisCenter=options.axisCenter||[Math.min(Math.max(range[0][0],0),range[0][1]),Math.min(Math.max(range[1][0],0),range[1][1])];const axisLabels=options.axisLabels!=null?options.axisLabels:false;const ticks=options.ticks!=null?options.ticks:true;const tickStep=options.tickStep||[2,2];const tickLen=options.tickLen||[5,5];const tickOpacity=options.tickOpacity||1;const labels=options.labels||options.labelStep||false;const labelStep=options.labelStep||[1,1];const labelOpacity=options.labelOpacity||1;let unityLabels=options.unityLabels||false;const labelFormat=options.labelFormat||function(a){return a};let xLabelFormat=options.xLabelFormat||labelFormat;let yLabelFormat=options.yLabelFormat||labelFormat;const realRange=[[range[0][0]-(range[0][0]>0?1:0),range[0][1]+(range[0][1]<0?1:0)],[range[1][0]-(range[1][0]>0?1:0),range[1][1]+(range[1][1]<0?1:0)]];if(!Array.isArray(unityLabels)){unityLabels=[unityLabels,unityLabels];}const minusIgnorer=function(lf){return function(a){return (lf(a)+"").replace(/-(\d)/g,"\\llap{-}$1")}};xLabelFormat=minusIgnorer(xLabelFormat);yLabelFormat=minusIgnorer(yLabelFormat);this.init({range:realRange,scale:scale,isMobile:options.isMobile});if(grid){this.grid(gridRange[0],gridRange[1],{stroke:options.isMobile?KhanColors.GRAY_C:"#000000",opacity:options.isMobile?1:gridOpacity,step:gridStep,strokeWidth:options.isMobile?1:2});}if(axes){if(axisArrows==="<->"||axisArrows===true){const thisGraphie=this;this.style({stroke:options.isMobile?KhanColors.GRAY_G:"#000000",opacity:options.isMobile?1:axisOpacity,strokeWidth:options.isMobile?1:2,arrows:"->"},function(){if(range[1][0]<0&&range[1][1]>0){thisGraphie.path([axisCenter,[gridRange[0][0],axisCenter[1]]]);thisGraphie.path([axisCenter,[gridRange[0][1],axisCenter[1]]]);}if(range[0][0]<0&&range[0][1]>0){thisGraphie.path([axisCenter,[axisCenter[0],gridRange[1][0]]]);thisGraphie.path([axisCenter,[axisCenter[0],gridRange[1][1]]]);}});}else if(axisArrows==="->"||axisArrows===""){const thisGraphie=this;this.style({stroke:"#000000",opacity:axisOpacity,strokeWidth:2,arrows:axisArrows},function(){thisGraphie.path([[gridRange[0][0],axisCenter[1]],[gridRange[0][1],axisCenter[1]]]);thisGraphie.path([[axisCenter[0],gridRange[1][0]],[axisCenter[0],gridRange[1][1]]]);});}if(axisLabels&&axisLabels.length===2){this.label([gridRange[0][1],axisCenter[1]],axisLabels[0],"right");this.label([axisCenter[0],gridRange[1][1]],axisLabels[1],"above");}}if(ticks){const halfWidthTicks=options.isMobile;const thisGraphie=this;this.style({stroke:options.isMobile?KhanColors.GRAY_G:"#000000",opacity:options.isMobile?1:tickOpacity,strokeWidth:1},()=>{let step=gridStep[0]*tickStep[0];let len=tickLen[0]/scale[1];let start=gridRange[0][0];let stop=gridRange[0][1];if(range[1][0]<0&&range[1][1]>0){for(let x=step+axisCenter[0];x<=stop;x+=step){if(x<stop||!axisArrows){thisGraphie.line([x,-len+axisCenter[1]],[x,halfWidthTicks?0:len+axisCenter[1]]);}}for(let x=-step+axisCenter[0];x>=start;x-=step){if(x>start||!axisArrows){thisGraphie.line([x,-len+axisCenter[1]],[x,halfWidthTicks?0:len+axisCenter[1]]);}}}step=gridStep[1]*tickStep[1];len=tickLen[1]/scale[0];start=gridRange[1][0];stop=gridRange[1][1];if(range[0][0]<0&&range[0][1]>0){for(let y=step+axisCenter[1];y<=stop;y+=step){if(y<stop||!axisArrows){this.line([-len+axisCenter[0],y],[halfWidthTicks?0:len+axisCenter[0],y]);}}for(let y=-step+axisCenter[1];y>=start;y-=step){if(y>start||!axisArrows){this.line([-len+axisCenter[0],y],[halfWidthTicks?0:len+axisCenter[0],y]);}}}});}if(labels){const thisGraphie=this;this.style({stroke:options.isMobile?KhanColors.GRAY_G:"#000000",opacity:options.isMobile?1:labelOpacity},function(){let step=gridStep[0]*tickStep[0]*labelStep[0];let start=gridRange[0][0];let stop=gridRange[0][1];const xAxisPosition=axisCenter[0]<0?"above":"below";const yAxisPosition=axisCenter[0]<0?"right":"left";const xShowZero=axisCenter[0]===0&&axisCenter[1]!==0;const yShowZero=axisCenter[0]!==0&&axisCenter[1]===0;const axisOffCenter=axisCenter[0]!==0||axisCenter[1]!==0;const showUnityX=unityLabels[0]||axisOffCenter;const showUnityY=unityLabels[1]||axisOffCenter;for(let x=(xShowZero?0:step)+axisCenter[0];x<=stop;x+=step){if(x<stop||!axisArrows){thisGraphie.label([x,axisCenter[1]],xLabelFormat(x),xAxisPosition);}}for(let x=-step*(showUnityX?1:2)+axisCenter[0];x>=start;x-=step){if(x>start||!axisArrows){thisGraphie.label([x,axisCenter[1]],xLabelFormat(x),xAxisPosition);}}step=gridStep[1]*tickStep[1]*labelStep[1];start=gridRange[1][0];stop=gridRange[1][1];for(let y=(yShowZero?0:step)+axisCenter[1];y<=stop;y+=step){if(y<stop||!axisArrows){thisGraphie.label([axisCenter[0],y],yLabelFormat(y),yAxisPosition);}}for(let y=-step*(showUnityY?1:2)+axisCenter[1];y>=start;y-=step){if(y>start||!axisArrows){thisGraphie.label([axisCenter[0],y],yLabelFormat(y),yAxisPosition);}}});}}drawingTransform(){if(_class_private_field_loose_base(this,_drawingTransform)[_drawingTransform]==null){throw new Error("Can't get drawingTransform of an uninitialized Graphie")}return _class_private_field_loose_base(this,_drawingTransform)[_drawingTransform]}bounds(){if(_class_private_field_loose_base(this,_bounds)[_bounds]==null){throw new Error("Can't get bounds of an uninitialized Graphie")}return _class_private_field_loose_base(this,_bounds)[_bounds]}style(attrs,fn){const processed=this.processAttributes(attrs);if(typeof fn==="function"){const oldStyle=this.currentStyle;this.currentStyle={...this.currentStyle,...processed};const result=fn.call(this);this.currentStyle=oldStyle;return result}Object.assign(this.currentStyle,processed);}grid(xr,yr,style){return this.withStyle(style,()=>{const step=this.currentStyle.step||[1,1];const set=this.raphael.set();let x=step[0]*Math.ceil(xr[0]/step[0]);for(;x<=xr[1];x+=step[0]){set.push(this.line([x,yr[0]],[x,yr[1]]));}let y=step[1]*Math.ceil(yr[0]/step[1]);for(;y<=yr[1];y+=step[1]){set.push(this.line([xr[0],y],[xr[1],y]));}return set})}arc(center,radius,startAngle,endAngle,sector,style){return this.withStyle(style,()=>{startAngle=(startAngle%360+360)%360;endAngle=(endAngle%360+360)%360;const cent=this.scalePoint(center);const radii=this.scaleVector(radius);const startVector=polar$2(radius,startAngle);const endVector=polar$2(radius,endAngle);const round=x=>number.round(x,6);const startPoint=this.scalePoint([round(center[0]+startVector[0]),round(center[1]+startVector[1])]);const endPoint=this.scalePoint([round(center[0]+endVector[0]),round(center[1]+endVector[1])]);const largeAngle=((endAngle-startAngle)%360+360)%360>180;return this.raphael.path("M"+startPoint.join(" ")+"A"+radii.join(" ")+" 0 "+(largeAngle?1:0)+" 0 "+endPoint.join(" ")+(sector?"L"+cent.join(" ")+"z":""))})}circle(center,radius,style){return this.withStyle(style,()=>this.raphael.ellipse(...this.scalePoint(center),...this.scaleVector([radius,radius])))}rect(x,y,width,height,style){return this.withStyle(style,()=>{const corner=this.scalePoint([x,y+height]);const dims=this.scaleVector([width,height]);const elem=this.raphael.rect(...corner.concat(dims));if(this.isMobile){elem.node.style.shapeRendering="crispEdges";}return elem})}ellipse(center,radii,style){return this.withStyle(style,()=>this.raphael.ellipse(...this.scalePoint(center).concat(this.scaleVector(radii))))}fixedEllipse(center,radii,maxScale,padding,style){return this.withStyle(style,()=>{const scaledPoint=this.scalePoint(center);const scaledRadii=this.scaleVector(radii);const width=2*scaledRadii[0]*maxScale+padding;const height=2*scaledRadii[1]*maxScale+padding;const left=scaledPoint[0]-width/2;const top=scaledPoint[1]-height/2;const wrapper=document.createElement("div");$(wrapper).css({position:"absolute",width:width+"px",height:height+"px",left:left+"px",top:top+"px"});const localRaphael=Raphael$1(wrapper,width,height);const visibleShape=localRaphael.ellipse(width/2,height/2,scaledRadii[0],scaledRadii[1]);return {wrapper:wrapper,visibleShape:visibleShape}})}unstyledPath(points){const p=this.raphael.path(this.svgPath(points));p.graphiePath=points;return p}path(points,style){return this.withStyle(style,()=>{return this.unstyledPath(points)})}fixedPath(points,center,createPath){points=points.map(this.scalePoint);center=center?this.scalePoint(center):null;createPath=createPath||this.svgPath;const xs=points.map(p=>p[0]);const ys=points.map(p=>p[1]);const pathLeft=Math.min(...xs);const pathRight=Math.max(...xs);const pathTop=Math.min(...ys);const pathBottom=Math.max(...ys);const padding=[4,4];const topLeftOfBoundingBox=[pathLeft,pathTop];points=points.map(function(point){return vector$3.add(vector$3.subtract(point,topLeftOfBoundingBox),vector$3.scale(padding,.5))});const width=pathRight-pathLeft+padding[0];const height=pathBottom-pathTop+padding[1];const left=topLeftOfBoundingBox[0]-padding[0]/2;const top=topLeftOfBoundingBox[1]-padding[1]/2;const wrapper=document.createElement("div");$(wrapper).css({position:"absolute",width:width+"px",height:height+"px",left:left+"px",top:top+"px",transformOrigin:center?width/2+center[0]+"px "+(height/2+center[1])+"px":null});const localRaphael=Raphael$1(wrapper,width,height);const visibleShape=localRaphael.path(createPath(points));return {wrapper:wrapper,visibleShape:visibleShape}}scaledPath(points,style){return this.withStyle(style,()=>{const p=this.raphael.path(this.svgPath(points,true));p.graphiePath=points;return p})}line(start,end,style){return this.withStyle(style,()=>{const l=this.unstyledPath([start,end]);if(this.isMobile){l.node.style.shapeRendering="crispEdges";}return l})}parabola(a,b,c,style){return this.withStyle(style,()=>this.raphael.path(this.svgParabolaPath(a,b,c)))}fixedLine(start,end,thickness){const padding=[thickness,thickness];start=this.scalePoint(start);end=this.scalePoint(end);const extraOffset=[Math.min(start[0],end[0]),Math.min(start[1],end[1])];start=vector$3.add(vector$3.subtract(start,extraOffset),vector$3.scale(padding,.5));end=vector$3.add(vector$3.subtract(end,extraOffset),vector$3.scale(padding,.5));const left=extraOffset[0]-padding[0]/2;const top=extraOffset[1]-padding[1]/2;const width=Math.abs(start[0]-end[0])+padding[0];const height=Math.abs(start[1]-end[1])+padding[1];const wrapper=document.createElement("div");$(wrapper).css({position:"absolute",width:width+"px",height:height+"px",left:left+"px",top:top+"px",transformOrigin:start[0]+"px "+start[1]+"px"});const localRaphael=Raphael$1(wrapper,width,height);const path="M"+start[0]+" "+start[1]+" "+"L"+end[0]+" "+end[1];const visibleShape=localRaphael.path(path);visibleShape.graphiePath=[start,end];return {wrapper:wrapper,visibleShape:visibleShape}}sinusoid(a,b,c,d,style){return this.withStyle(style,()=>this.raphael.path(this.svgSinusoidPath(a,b,c,d)))}plotParametric(fn,range,style){return this.withStyle(style,()=>{const clip=xy=>{if(Math.abs(xy[1])>5e5){return [xy[0],Math.min(Math.max(xy[1],-5e5),5e5)]}return xy};const clippedFn=x=>clip(fn(x));const min=range[0];const max=range[1];let step=(max-min)/(this.currentStyle["plot-points"]||800);if(step===0){step=1;}const paths=this.raphael.set();let points=[];let lastY=clippedFn(min)[1];for(let t=min;t<=max;t+=step){const point=clippedFn(t);const y=point[1];if(y>0!==lastY>0&&Math.abs(y-lastY)>2*this.drawingTransform().pixelsPerUnitY()||isNaN(y)){paths.push(this.unstyledPath(points));points=[];}else {points.push(point);}lastY=y;}paths.push(this.unstyledPath(points));return paths})}plot(fn,range,style){return this.withStyle(style,()=>{const min=range[0];const max=range[1];if(!this.currentStyle["plot-points"]){this.currentStyle["plot-points"]=2*(max-min)*this.drawingTransform().pixelsPerUnitX();}const parametricFn=x=>[x,fn(x)];return this.plotParametric(parametricFn,range)})}withStyle(style,fn){const oldStyle=this.currentStyle;this.currentStyle={...this.currentStyle,...this.processAttributes(style)};const result=this.postprocessDrawingResult(fn());this.currentStyle=oldStyle;return result}postprocessDrawingResult(result){const type=result.constructor.prototype;if(type===Raphael$1.el||type===Raphael$1.st){result.attr(this.currentStyle);if(this.currentStyle.arrows){result=this.addArrowheads(result);}}else if(result instanceof $){result.css({...this.currentStyle,...SVG_SPECIFIC_STYLE_MASK});}return result}addArrowheads(path){const type=path.constructor.prototype;if(type===Raphael$1.el){if(path.type==="path"&&typeof path.arrowheadsDrawn==="undefined"){const w=path.attr("stroke-width");const s=.6+.4*w;const l=path.getTotalLength();const set=this.raphael.set();const head=this.raphael.path(this.isMobile?"M-4,4 C-4,4 -0.25,0 -0.25,0 C-0.25,0 -4,-4 -4,-4":"M-3 4 C-2.75 2.5 0 0.25 0.75 0C0 -0.25 -2.75 -2.5 -3 -4");const end=path.getPointAtLength(l-.4);const almostTheEnd=path.getPointAtLength(l-.75*s);const angle=Math.atan2(end.y-almostTheEnd.y,end.x-almostTheEnd.x)*180/Math.PI;const attrs=path.attr();delete attrs.path;let subpath=path.getSubpath(0,l-.75*s);subpath=this.raphael.path(subpath).attr(attrs);subpath.arrowheadsDrawn=true;path.remove();head.rotate(angle,this.isMobile?1e-5:.75,0).scale(s,s,.75,0).translate(almostTheEnd.x,almostTheEnd.y).attr(attrs).attr({"stroke-linejoin":"round","stroke-linecap":"round"});head.arrowheadsDrawn=true;set.push(subpath);set.push(head);return set}}else if(type===Raphael$1.st){for(let i=0,l=path.items.length;i<l;i++){this.addArrowheads(path.items[i]);}}return path}processAttributes(attrs){const transformers={scale:scale=>{this.drawingTransform().setScale(scale);},clipRect:pair=>{const point=pair[0];const size=pair[1];point[1]+=size[1];return {"clip-rect":this.scalePoint(point).concat(this.scaleVector(size)).join(" ")}},strokeWidth:function(val){return {"stroke-width":parseFloat(val)}},rx:val=>{return {rx:this.scaleVector([val,0])[0]}},ry:val=>{return {ry:this.scaleVector([0,val])[1]}},r:val=>{const scaled=this.scaleVector([val,val]);return {rx:scaled[0],ry:scaled[1]}}};const processed={};Object.entries(attrs||{}).forEach(function([key,value]){const transformer=transformers[key];if(typeof transformer==="function"){Object.assign(processed,transformer(value));}else {const dasherized=String(key).replace(/([A-Z]+)([A-Z][a-z])/g,"$1-$2").replace(/([a-z\d])([A-Z])/g,"$1-$2").toLowerCase();processed[dasherized]=value;}});return processed}addMouseLayer(options){const localOptions={allowScratchpad:false,...options};const mouselayerZIndex=2;this.mouselayer=Raphael$1(this.el,this.xpixels,this.ypixels);$(this.mouselayer.canvas).css("z-index",mouselayerZIndex);if(localOptions.onClick||localOptions.onMouseDown||localOptions.onMouseMove||localOptions.onMouseOver||localOptions.onMouseOut){const canvasClickTarget=this.mouselayer.rect(0,0,this.xpixels,this.ypixels).attr({fill:"#000",opacity:0});let isClickingCanvas=false;$(this.mouselayer.canvas).on("vmousedown",e=>{if(e.target===canvasClickTarget[0]){if(localOptions.onMouseDown){localOptions.onMouseDown(this.getMouseCoord(e));}isClickingCanvas=true;if(localOptions.onMouseMove){const handler=localOptions.onMouseMove;$(document).on("vmousemove.mouseLayer",e=>{if(isClickingCanvas){e.preventDefault();handler(this.getMouseCoord(e));}});}$(document).on("vmouseup.mouseLayer",e=>{$(document).unbind(".mouseLayer");if(isClickingCanvas&&localOptions.onClick){localOptions.onClick(this.getMouseCoord(e));}isClickingCanvas=false;});}});if(localOptions.onMouseOver){const handler=localOptions.onMouseOver;$(this.mouselayer.canvas).on("vmouseover",e=>{handler(this.getMouseCoord(e));});}if(localOptions.onMouseOut){const handler=localOptions.onMouseOut;$(this.mouselayer.canvas).on("vmouseout",e=>{handler(this.getMouseCoord(e));});}}this._mouselayerWrapper=document.createElement("div");$(this._mouselayerWrapper).css({position:"absolute",left:0,top:0,zIndex:mouselayerZIndex});this._visiblelayerWrapper=document.createElement("div");$(this._visiblelayerWrapper).css({position:"absolute",left:0,top:0});const el=this.el;el.appendChild(this._visiblelayerWrapper);el.appendChild(this._mouselayerWrapper);this.addToMouseLayerWrapper=el=>{this._mouselayerWrapper?.appendChild(el);};this.addToVisibleLayerWrapper=el=>{this._visiblelayerWrapper?.appendChild(el);};}addToMouseLayerWrapper(el){throw new Error("addToMouseLayerWrapper is not ready. Call addMouseLayer() first.")}addToVisibleLayerWrapper(el){throw new Error("addToVisibleLayerWrapper is not ready. Call addMouseLayer() first.")}getMousePx(event){const offset=$(this.el).offset();const mouseX=event.pageX-offset.left;const mouseY=event.pageY-offset.top;return [mouseX,mouseY]}getMouseCoord(event){return this.unscalePoint(this.getMousePx(event))}_ensureResizeObserver(){if(_class_private_field_loose_base(this,_resizeObserver)[_resizeObserver]!=null||typeof ResizeObserver==="undefined"){return}_class_private_field_loose_base(this,_resizeObserver)[_resizeObserver]=new ResizeObserver(()=>{this._recalculateLabels();});_class_private_field_loose_base(this,_resizeObserver)[_resizeObserver].observe(this.el);}_recalculateLabels(){_class_private_field_loose_base(this,_labelElements)[_labelElements].forEach(span=>{const originalSize=$(span).data("originalLabelSize");if(originalSize!=null){setLabelMargins(span,originalSize);}});}cleanup(){_class_private_field_loose_base(this,_resizeObserver)[_resizeObserver]?.disconnect();_class_private_field_loose_base(this,_resizeObserver)[_resizeObserver]=null;_class_private_field_loose_base(this,_labelElements)[_labelElements].clear();}constructor(el){Object.defineProperty(this,_bounds,{writable:true,value:void 0});Object.defineProperty(this,_drawingTransform,{writable:true,value:void 0});Object.defineProperty(this,_labelElements,{writable:true,value:void 0});Object.defineProperty(this,_resizeObserver,{writable:true,value:void 0});_class_private_field_loose_base(this,_labelElements)[_labelElements]=new Set;_class_private_field_loose_base(this,_resizeObserver)[_resizeObserver]=null;this.isMobile=false;this.currentStyle={"stroke-width":2,fill:"none"};this.label=(point,text,direction,arg4,arg5)=>{const style=typeof arg4==="object"?arg4:arg5;const latex=typeof arg4==="boolean"?arg4:true;return this.withStyle(style,()=>{const $span=$("<span>").addClass("graphie-label");const pad=this.currentStyle["label-distance"];$span.css({position:"absolute",padding:(pad!=null?pad:7)+"px",color:"black"}).data("labelDirection",direction).appendTo(this.el);$span.setPosition=point=>{const scaledPoint=this.scalePoint(point);$span.css({left:scaledPoint[0],top:scaledPoint[1]});};$span.setPosition(point);$span.css({"font-size":this.currentStyle["font-size"]});const span=$span[0];_class_private_field_loose_base(this,_labelElements)[_labelElements].add(span);$span.processMath=function(math,force){processMath$1(span,math,force,function(){const width=span.scrollWidth;const height=span.scrollHeight;setLabelMargins(span,[width,height]);});};$span.processText=function(text){$span.html(text);const width=span.scrollWidth;const height=span.scrollHeight;setLabelMargins(span,[width,height]);};if(latex){$span.processMath(text,false);}else {$span.processText(text);}this._ensureResizeObserver();return $span})};this.svgPath=(points,alreadyScaled)=>{return points.map((point,i)=>{if(point===true){return "z"}const scaled=alreadyScaled?point:this.scalePoint(point);return (i===0?"M":"L")+KhanMath.bound(scaled[0])+" "+KhanMath.bound(scaled[1])}).join("")};this.svgParabolaPath=(a,b,c)=>{const computeParabola=function(x){return (a*x+b)*x+c};if(a===0){const points=[[this.bounds().xMin,computeParabola(this.bounds().xMin)],[this.bounds().xMax,computeParabola(this.bounds().xMax)]];return this.svgPath(points)}const xVertex=-b/(2*a);const distToEdge=Math.max(Math.abs(xVertex-this.bounds().xMin),Math.abs(xVertex-this.bounds().xMax));const xPoint=xVertex+distToEdge;const vertex=[xVertex,computeParabola(xVertex)];const point=[xPoint,computeParabola(xPoint)];const control=[vertex[0],vertex[1]-(point[1]-vertex[1])];const dx=Math.abs(vertex[0]-point[0]);const left=[vertex[0]-dx,point[1]];const right=[vertex[0]+dx,point[1]];const points=[left,control,right].map(this.scalePoint);const values=points.flat().map(KhanMath.bound);return "M"+values[0]+","+values[1]+" Q"+values[2]+","+values[3]+" "+values[4]+","+values[5]};this.svgSinusoidPath=(a,b,c,d)=>{const quarterPeriod=Math.abs(Math.PI/(2*b));const computeSine=function(x){return a*Math.sin(b*x-c)+d};const computeDerivative=function(x){return a*b*Math.cos(c-b*x)};const coordsForOffset=(initial,i)=>{const x0=initial+quarterPeriod*i;const x1=x0+quarterPeriod;const xCoords=[x0,x0*2/3+x1*1/3,x0*1/3+x1*2/3,x1];const yCoords=[computeSine(x0),computeSine(x0)+computeDerivative(x0)*(x1-x0)/3,computeSine(x1)-computeDerivative(x1)*(x1-x0)/3,computeSine(x1)];const points=vector$3.zip(xCoords,yCoords);return points.map(this.scalePoint)};const extent=this.bounds().width();const numQuarterPeriods=Math.ceil(extent/quarterPeriod)+1;let initial=c/b;const distToEdge=initial-this.bounds().xMin;initial-=quarterPeriod*Math.ceil(distToEdge/quarterPeriod);let coords=coordsForOffset(initial,0);let path="M"+coords[0][0]+","+coords[0][1]+" C"+coords[1][0]+","+coords[1][1]+" "+coords[2][0]+","+coords[2][1]+" "+coords[3][0]+","+coords[3][1];for(let i=1;i<numQuarterPeriods;i++){coords=coordsForOffset(initial,i);path+=" C"+coords[1][0]+","+coords[1][1]+" "+coords[2][0]+","+coords[2][1]+" "+coords[3][0]+","+coords[3][1];}return path};this.scalePoint=point=>{return this.drawingTransform().scalePoint(point)};this.scaleVector=point=>{return this.drawingTransform().scaleVector(point)};this.unscalePoint=point=>{return this.drawingTransform().unscalePoint(point)};this.unscaleVector=point=>{return this.drawingTransform().unscaleVector(point)};this.el=el;$(el).css("position","relative");this.raphael=Raphael$1(el);$(el).attr("aria-hidden","true");$(el).children("div").css("position","absolute");}};const labelDirections={center:[-0.5,-0.5],above:[-0.5,-1],"above right":[0,-1],right:[0,-0.5],"below right":[0,0],below:[-0.5,0],"below left":[-1,0],left:[-1,-0.5],"above left":[-1,-1]};function normalizeRange(range){function normalizeInterval(magnitude){if(typeof magnitude==="number"){return [-magnitude,magnitude]}else {return magnitude}}function getXAndYRanges(range){if(Array.isArray(range)){return range}else {return [range,range]}}if(range==null){return range}const[xRange,yRange]=getXAndYRanges(range);return [normalizeInterval(xRange),normalizeInterval(yRange)]}function toPair(x){if(Array.isArray(x)){return x}return [x,x]}const SVG_SPECIFIC_STYLE_MASK={"stroke-width":null};const setLabelMargins=function(span,size){if(document.fonts?.status==="loading"){document.fonts.ready.then(()=>applyLabelMargins(span,size));return}applyLabelMargins(span,size);};const applyLabelMargins=function(span,size){const $span=$(span);const direction=$span.data("labelDirection");let[width,height]=size;if(width===0&&height===0){[width,height]=[1,1];Log.log("Label size was 0x0 in graphie.js; using 1x1 instead");}$span.css("visibility","");if(typeof direction==="number"){const x=Math.cos(direction);const y=Math.sin(direction);const scale=Math.min(width/2/Math.abs(x),height/2/Math.abs(y));$span.css({marginLeft:-width/2+x*scale,marginTop:-height/2-y*scale});if($span.data("originalLabelSize")==null){$span.data("originalLabelSize",[width,height]);}}else {const $svgImage=$span.closest(".svg-image");const $graphie=$span.closest(".graphie");const $container=$svgImage.length>0?$svgImage:$graphie;$container.css("line-height","normal");if($span.data("originalLabelSize")==null){height=span.scrollHeight;$span.data("originalLabelSize",[width,height]);}const widthValues=$container.css(["max-width","width"])??{"max-width":"0px"};const expectedWidth=widthValues["max-width"]!=="none"?widthValues["max-width"]:widthValues["width"];let scale=($container.width()??0)/parseInt(expectedWidth.replace(/px$/,""));if(isNaN(scale)){scale=1;}else if(scale===0){scale=1;}let rawPadding;const storedPadding=$span.data("originalPadding");if(storedPadding!=null){rawPadding=storedPadding;}else {const padding=$span.css("padding")??"0px";const currentPadding=padding!=="none"?padding:"0px";rawPadding=parseInt(currentPadding.replace(/px$/,""));$span.data("originalPadding",rawPadding);}let normalizedWidth=width;let normalizedHeight=height;const imageScale=Number($container.attr("data-scale"));const hasValidImageScale=!Number.isNaN(imageScale)&&imageScale>=0;if(hasValidImageScale){scale=scale*imageScale;const totalPadding=2*rawPadding;normalizedWidth=(width-totalPadding)/imageScale+totalPadding;normalizedHeight=(height-totalPadding)/imageScale+totalPadding;}const newPadding=rawPadding*scale;const multipliers=labelDirections[direction||"center"];const styling={marginLeft:normalizedWidth*multipliers[0]*scale,marginTop:normalizedHeight*multipliers[1]*scale,padding:`${newPadding}px`,fontSize:`${scale*100}%`};$span.css(styling);}};const GraphUtils={Graphie: Graphie$1,createGraphie:function(el){return new Graphie$1(el)},unscaledSvgPath:function(points){if(points[0]===true){return ""}return points.map(function(point,i){if(point===true){return "z"}return (i===0?"M":"L")+point[0]+" "+point[1]}).join("")},getDistance:function(point1,point2){return point.distanceToPoint(point1,point2)},findAngleDeprecated:function(point1,point2,vertex){if(vertex===undefined){const x=point1[0]-point2[0];const y=point1[1]-point2[1];if(!x&&!y){return 0}return (180+Math.atan2(-y,-x)*180/Math.PI+360)%360}return GraphUtils.findAngleDeprecated(point1,vertex)-GraphUtils.findAngleDeprecated(point2,vertex)},graphs:{}};
1558
1558
 
@@ -1560,7 +1560,7 @@ const{processMath: processMath$1}=Tex$1;function polar$2(r,th){if(typeof r==="nu
1560
1560
 
1561
1561
  const getClipPoint=function(graph,coord,angle){const xExtent=graph.range[0][1]-graph.range[0][0];const yExtent=graph.range[1][1]-graph.range[1][0];const distance=xExtent+yExtent;const angleVec=graph.unscaleVector(vector$3.cartFromPolarDeg(1,angle));const distVec=vector$3.scale(vector$3.normalize(angleVec),distance);const farCoord=vector$3.add(coord,distVec);const scaledAngle=vector$3.polarDegFromCart(angleVec)[1];const clipPoint=graph.constrainToBoundsOnAngle(farCoord,4,scaledAngle*Math.PI/180);return clipPoint};
1562
1562
 
1563
- class WrappedDrawing{transform(transformation){const prefixedTransform=InteractiveUtil.getPrefixedTransform();this.wrapper.style[prefixedTransform]=transformation;}toFront(){const parentNode=this.wrapper.parentNode;if(parentNode){parentNode.appendChild(this.wrapper);}}toBack(){const parentNode=this.wrapper.parentNode;if(parentNode!=null&&parentNode.firstChild!==this.wrapper){parentNode.insertBefore(this.wrapper,parentNode.firstChild);}}remove(){this.visibleShape.remove();$(this.wrapper).remove();}getMouseTarget(){return this.visibleShape[0]}moveTo(point){const delta=vector$3.subtract(this.graphie.scalePoint(point),this.graphie.scalePoint(this.initialPoint));const do3dTransform=InteractiveUtil.getCanUse3dTransform();const transformation="translateX("+delta[0]+"px) "+"translateY("+delta[1]+"px)"+(do3dTransform?" translateZ(0)":"");this.transform(transformation);}hide(){this.visibleShape.hide();}show(){this.visibleShape.show();}attr(...args){this.visibleShape.attr(...args);}animate(...args){this.visibleShape.animate(...args);}constructor(graphie,initialPoint){this.graphie=graphie;this.initialPoint=initialPoint;}}
1563
+ class WrappedDrawing{transform(transformation){const prefixedTransform=InteractiveUtil.getPrefixedTransform();this.wrapper.style[prefixedTransform]=transformation;}toFront(){const parentNode=this.wrapper.parentNode;if(parentNode){parentNode.appendChild(this.wrapper);}}toBack(){const parentNode=this.wrapper.parentNode;if(parentNode!=null&&parentNode.firstChild!==this.wrapper){parentNode.insertBefore(this.wrapper,parentNode.firstChild);}}remove(){this.visibleShape.remove();this.wrapper.remove();}getMouseTarget(){return this.visibleShape[0]}moveTo(point){const delta=vector$3.subtract(this.graphie.scalePoint(point),this.graphie.scalePoint(this.initialPoint));const do3dTransform=InteractiveUtil.getCanUse3dTransform();const transformation="translateX("+delta[0]+"px) "+"translateY("+delta[1]+"px)"+(do3dTransform?" translateZ(0)":"");this.transform(transformation);}hide(){this.visibleShape.hide();}show(){this.visibleShape.show();}attr(...args){this.visibleShape.attr(...args);}animate(...args){this.visibleShape.animate(...args);}constructor(graphie,initialPoint){this.graphie=graphie;this.initialPoint=initialPoint;}}
1564
1564
 
1565
1565
  const DEFAULT_OPTIONS$2={center:null,createPath:null,mouselayer:false};class WrappedPath extends WrappedDrawing{constructor(graphie,points,options){const initialPoint=graphie.scalePoint(_.head(points));super(graphie,initialPoint);options=_.extend({},DEFAULT_OPTIONS$2,options);const fixedPath=graphie.fixedPath(points,options.center,options.createPath);this.wrapper=fixedPath.wrapper;this.visibleShape=fixedPath.visibleShape;if(options.mouselayer){this.wrapper.style.touchAction="none";this.graphie.addToMouseLayerWrapper(this.wrapper);}else {this.graphie.addToVisibleLayerWrapper(this.wrapper);}}}
1566
1566
 
@@ -1570,7 +1570,7 @@ const DEFAULT_OPTIONS$1={maxScale:1,mouselayer:false,shadow:false,disableMouseEv
1570
1570
 
1571
1571
  const DEFAULT_OPTIONS={thickness:2,mouselayer:false};class WrappedLine extends WrappedDrawing{getMouseTarget(){return this.wrapper}moveTo(start,end){const scaledStart=this.graphie.scalePoint(start);const scaledEnd=this.graphie.scalePoint(end);const polarDiff=vector$3.polarDegFromCart(vector$3.subtract(scaledEnd,scaledStart));const lineLength=polarDiff[0];const angle=KhanMath.bound(polarDiff[1]);const delta=vector$3.subtract(scaledStart,this.initialPoint);const scale=KhanMath.bound(lineLength/this.initialLength);const do3dTransform=InteractiveUtil.getCanUse3dTransform();const transformation="translateX("+delta[0]+"px) "+"translateY("+delta[1]+"px) "+(do3dTransform?" translateZ(0)":"")+"rotate("+angle+"deg) "+"scaleX("+scale+") scaleY(1)";this.transform(transformation);}constructor(graphie,start,end,options){const initialStart=[graphie.range[0][0],0];const initialEnd=[graphie.range[0][1],0];const initialPoint=graphie.scalePoint(initialStart);super(graphie,initialPoint);options=_.extend({},DEFAULT_OPTIONS,options);const fixedLine=graphie.fixedLine(initialStart,initialEnd,options.thickness);this.wrapper=fixedLine.wrapper;this.visibleShape=fixedLine.visibleShape;if(options.interactiveKindForTesting){this.wrapper.setAttribute("data-interactive-kind-for-testing",options.interactiveKindForTesting);}this.visibleShape.attr(options.normalStyle);this.initialPoint=initialPoint;this.initialLength=point.distanceToPoint(graphie.scalePoint(initialStart),graphie.scalePoint(initialEnd));if(options.mouselayer){this.wrapper.style.touchAction="none";this.graphie.addToMouseLayerWrapper(this.wrapper);}else {this.graphie.addToVisibleLayerWrapper(this.wrapper);}this.moveTo(start,end);}}
1572
1572
 
1573
- const{clockwise: clockwise$3,reverseVector: reverseVector$1}=geometry;function scaledDistanceFromAngle(angle){const a=3.51470560176242*20;const b=.5687298702748785*20;const c=-0.037587715462826674;return (a-b)*Math.exp(c*angle)+b}function scaledPolarRad(radius,radians){return [radius*Math.cos(radians),radius*Math.sin(radians)*-1]}function scaledPolarDeg(radius,degrees){const radians=degrees*Math.PI/180;return scaledPolarRad(radius,radians)}let dragging=false;const InteractiveUtils={bogusShape:{animate:function(){},attr:function(){},remove:function(){}}};_.extend(GraphUtils.Graphie.prototype,{labelAngle:function(options){const graphie=this;_.defaults(options,{point1:[0,0],vertex:[0,0],point3:[0,0],label:null,numArcs:1,showRightAngleMarker:true,pushOut:0,clockwise:false,style:{}});let text=options.text===undefined?"":options.text;const vertex=options.vertex;const sVertex=graphie.scalePoint(vertex);let p1;let p3;if(options.clockwise){p1=options.point1;p3=options.point3;}else {p1=options.point3;p3=options.point1;}const startAngle=GraphUtils.findAngleDeprecated(p1,vertex);const endAngle=GraphUtils.findAngleDeprecated(p3,vertex);const angle=(endAngle+360-startAngle)%360;const halfAngle=(startAngle+angle/2)%360;const sPadding=5*options.pushOut;let sRadius=sPadding+scaledDistanceFromAngle(angle);const temp=[];if(Math.abs(angle-90)<1e-9&&options.showRightAngleMarker){const v1=vector$3.add(sVertex,scaledPolarDeg(sRadius,startAngle));const v2=vector$3.add(sVertex,scaledPolarDeg(sRadius,endAngle));sRadius*=Math.SQRT2;const v3=vector$3.add(sVertex,scaledPolarDeg(sRadius,halfAngle));[v1,v2].forEach(function(v){temp.push(graphie.scaledPath([v,v3],options.style));});}else {for(let i=0;i<options.numArcs;i++){temp.push(graphie.arc(vertex,graphie.unscaleVector(sRadius),startAngle,endAngle,options.style));sRadius+=3;}}if(text){const match=text.match(/\$deg(\d)?/);if(match){const precision=match[1]||1;text=text.replace(match[0],KhanMath.toFixedApprox(angle,precision)+"^{\\circ}");}const sOffset=scaledPolarDeg(sRadius+15,halfAngle);const sPosition=vector$3.add(sVertex,sOffset);const position=graphie.unscalePoint(sPosition);if(options.label){options.label.setPosition(position);options.label.processMath(text,true);}else {graphie.label(position,text,"center",options.style);}}return temp},labelSide:function(options){const graphie=this;_.defaults(options,{point1:[0,0],point2:[0,0],label:null,text:"",numTicks:0,numArrows:0,clockwise:false,style:{}});let p1;let p2;if(options.clockwise){p1=options.point1;p2=options.point2;}else {p1=options.point2;p2=options.point1;}const midpoint=[(p1[0]+p2[0])/2,(p1[1]+p2[1])/2];const sMidpoint=graphie.scalePoint(midpoint);const parallelAngle=Math.atan2(p2[1]-p1[1],p2[0]-p1[0]);const perpendicularAngle=parallelAngle+Math.PI/2;const temp=[];let sCumulativeOffset=0;if(options.numTicks){const n=options.numTicks;const sSpacing=5;const sHeight=5;const style={...options.style,strokeWidth:2};for(let i=0;i<n;i++){const sOffset=sSpacing*(i-(n-1)/2);const sOffsetVector=scaledPolarRad(sOffset,parallelAngle);const sHeightVector=scaledPolarRad(sHeight,perpendicularAngle);const sPath=[vector$3.add(sMidpoint,sOffsetVector,sHeightVector),vector$3.add(sMidpoint,sOffsetVector,reverseVector$1(sHeightVector))];temp.push(graphie.scaledPath(sPath,style));}sCumulativeOffset+=sSpacing*(n-1)+15;}if(options.numArrows){const n=options.numArrows;const start=[p1,p2].sort(function(a,b){if(a[1]===b[1]){return a[0]-b[0]}return a[1]-b[1]})[0];const sStart=graphie.scalePoint(start);const style={...options.style,arrows:"->",strokeWidth:2};const sSpacing=5;for(let i=0;i<n;i++){const sOffset=sCumulativeOffset+sSpacing*i;let sOffsetVector=scaledPolarRad(sOffset,parallelAngle);if(start!==p1){sOffsetVector=reverseVector$1(sOffsetVector);}const sEnd=vector$3.add(sMidpoint,sOffsetVector);temp.push(graphie.scaledPath([sStart,sEnd],style));}}let text=options.text;if(text){const match=text.match(/\$len(\d)?/);if(match){const distance=GraphUtils.getDistance(p1,p2);const precision=match[1]||1;text=text.replace(match[0],KhanMath.toFixedApprox(distance,precision));}const sOffset=20;const sOffsetVector=scaledPolarRad(sOffset,perpendicularAngle);const sPosition=vector$3.add(sMidpoint,sOffsetVector);const position=graphie.unscalePoint(sPosition);if(options.label){options.label.setPosition(position);options.label.processMath(text,true);}else {graphie.label(position,text,"center",options.style);}}return temp},labelVertex:function(options){const graphie=this;_.defaults(options,{point1:null,vertex:[0,0],point3:null,label:null,text:"",clockwise:false,style:{}});if(!options.text){return}const vertex=options.vertex;const sVertex=graphie.scalePoint(vertex);let p1;let p3;if(options.clockwise){p1=options.point1;p3=options.point3;}else {p1=options.point3;p3=options.point1;}let angle=135;let halfAngle;if(p1&&p3){const startAngle=GraphUtils.findAngleDeprecated(p1,vertex);const endAngle=GraphUtils.findAngleDeprecated(p3,vertex);angle=(endAngle+360-startAngle)%360;halfAngle=(startAngle+angle/2+180)%360;}else if(p1){const parallelAngle=GraphUtils.findAngleDeprecated(vertex,p1);halfAngle=parallelAngle+90;}else if(p3){const parallelAngle=GraphUtils.findAngleDeprecated(p3,vertex);halfAngle=parallelAngle+90;}else {halfAngle=135;}const sRadius=10+scaledDistanceFromAngle(360-angle);const sOffsetVector=scaledPolarDeg(sRadius,halfAngle);const sPosition=vector$3.add(sVertex,sOffsetVector);const position=graphie.unscalePoint(sPosition);if(options.label){options.label.setPosition(position);options.label.processMath(options.text,true);}else {graphie.label(position,options.text,"center",options.style);}},addMovablePoint:function(options){const movablePoint=$.extend(true,{graph:this,coord:[0,0],snapX:0,snapY:0,pointSize:4,highlight:false,dragging:false,visible:true,bounded:true,constraints:{fixed:false,constrainX:false,constrainY:false,fixedAngle:{},fixedDistance:{}},lineStarts:[],lineEnds:[],polygonVertices:[],normalStyle:{},highlightStyle:{fill:KhanColors.INTERACTING,stroke:KhanColors.INTERACTING},labelStyle:{color:KhanColors.INTERACTIVE},vertexLabel:"",mouseTarget:null},options);const normalColor=movablePoint.constraints.fixed?KhanColors.DYNAMIC:KhanColors.INTERACTIVE;movablePoint.normalStyle={fill:normalColor,stroke:normalColor,...options.normalStyle};if(options.coordX!==undefined){movablePoint.coord[0]=options.coordX;}if(options.coordY!==undefined){movablePoint.coord[1]=options.coordY;}const graph=movablePoint.graph;const applySnapAndConstraints=function(coord){if(movablePoint.visible&&movablePoint.bounded&&!movablePoint.constraints.fixed){coord=graph.constrainToBounds(coord,10);}let coordX=coord[0];let coordY=coord[1];if(movablePoint.snapX!==0){coordX=Math.round(coordX/movablePoint.snapX)*movablePoint.snapX;}if(movablePoint.snapY!==0){coordY=Math.round(coordY/movablePoint.snapY)*movablePoint.snapY;}if(movablePoint.constraints.fixedDistance.snapPoints){const mouse=graph.scalePoint(coord);let mouseX=mouse[0];let mouseY=mouse[1];const snapRadians=2*Math.PI/movablePoint.constraints.fixedDistance.snapPoints;const radius=movablePoint.constraints.fixedDistance.dist;const centerCoord=movablePoint.constraints.fixedDistance.point;const centerX=(centerCoord[0]-graph.range[0][0])*graph.scale[0];const centerY=(-centerCoord[1]+graph.range[1][1])*graph.scale[1];let mouseXrel=mouseX-centerX;let mouseYrel=-mouseY+centerY;let radians=Math.atan(mouseYrel/mouseXrel);const outsideArcTanRange=mouseXrel<0;if(outsideArcTanRange){radians+=Math.PI;}radians=Math.round(radians/snapRadians)*snapRadians;mouseXrel=radius*Math.cos(radians);mouseYrel=radius*Math.sin(radians);mouseX=mouseXrel+centerX;mouseY=-mouseYrel+centerY;coordX=KhanMath.roundTo(5,mouseX/graph.scale[0]+graph.range[0][0]);coordY=KhanMath.roundTo(5,graph.range[1][1]-mouseY/graph.scale[1]);}const result=movablePoint.applyConstraint([coordX,coordY]);return result};movablePoint.applyConstraint=function(coord,extraConstraints,override){let newCoord=coord.slice();const constraints={};if(override){$.extend(constraints,{fixed:false,constrainX:false,constrainY:false,fixedAngle:{},fixedDistance:{}},extraConstraints);}else {$.extend(constraints,this.constraints,extraConstraints);}if(constraints.constrainX){newCoord=[this.coord[0],coord[1]];}else if(constraints.constrainY){newCoord=[coord[0],this.coord[1]];}else if(typeof constraints.fixedAngle.angle==="number"&&typeof constraints.fixedDistance.dist==="number"){const vertex=constraints.fixedAngle.vertex.coord||constraints.fixedAngle.vertex;const ref=constraints.fixedAngle.ref.coord||constraints.fixedAngle.ref;const distPoint=constraints.fixedDistance.point.coord||constraints.fixedDistance.point;const constrainedAngle=(constraints.fixedAngle.angle+GraphUtils.findAngleDeprecated(ref,vertex))*Math.PI/180;const length=constraints.fixedDistance.dist;newCoord[0]=length*Math.cos(constrainedAngle)+distPoint[0];newCoord[1]=length*Math.sin(constrainedAngle)+distPoint[1];}else if(typeof constraints.fixedAngle.angle==="number"){const vertex=constraints.fixedAngle.vertex.coord||constraints.fixedAngle.vertex;const ref=constraints.fixedAngle.ref.coord||constraints.fixedAngle.ref;const constrainedAngle=(constraints.fixedAngle.angle+GraphUtils.findAngleDeprecated(ref,vertex))*Math.PI/180;const angle=GraphUtils.findAngleDeprecated(coord,vertex)*Math.PI/180;const distance=GraphUtils.getDistance(coord,vertex);let length=distance*Math.cos(constrainedAngle-angle);length=length<1?1:length;newCoord[0]=length*Math.cos(constrainedAngle)+vertex[0];newCoord[1]=length*Math.sin(constrainedAngle)+vertex[1];}else if(typeof constraints.fixedDistance.dist==="number"){const distPoint=constraints.fixedDistance.point.coord||constraints.fixedDistance.point;let angle=GraphUtils.findAngleDeprecated(coord,distPoint);const length=constraints.fixedDistance.dist;angle=angle*Math.PI/180;newCoord[0]=length*Math.cos(angle)+distPoint[0];newCoord[1]=length*Math.sin(angle)+distPoint[1];}else if(constraints.fixed){newCoord=movablePoint.coord;}return newCoord};movablePoint.coord=applySnapAndConstraints(movablePoint.coord);const highlightScale=2;if(movablePoint.visible){graph.style(movablePoint.normalStyle,function(){const radii=[movablePoint.pointSize/graph.scale[0],movablePoint.pointSize/graph.scale[1]];const options={maxScale:highlightScale,padding:2};movablePoint.visibleShape=new WrappedEllipse(graph,movablePoint.coord,radii,options);const{scale,...styleWithoutScale}=movablePoint.normalStyle;movablePoint.visibleShape.attr(styleWithoutScale);movablePoint.visibleShape.toFront();});}movablePoint.normalStyle.scale=1;movablePoint.highlightStyle.scale=highlightScale;if(movablePoint.vertexLabel){movablePoint.labeledVertex=this.label([0,0],"","center",movablePoint.labelStyle);}movablePoint.drawLabel=function(){if(movablePoint.vertexLabel){movablePoint.graph.labelVertex({vertex:movablePoint.coord,label:movablePoint.labeledVertex,text:movablePoint.vertexLabel,style:movablePoint.labelStyle});}};movablePoint.drawLabel();movablePoint.grab=function(offset){offset=offset||[0,0];$(document).on("vmousemove.point vmouseup.point",function(event){event.preventDefault();movablePoint.dragging=true;dragging=true;let coord=vector$3.add(graph.getMouseCoord(event),offset);coord=applySnapAndConstraints(coord);let coordX=coord[0];let coordY=coord[1];let mouseX;let mouseY;if(event.type==="vmousemove"){let doMove=true;if(typeof movablePoint.onMove==="function"){const result=movablePoint.onMove(coordX,coordY);if(result===false){doMove=false;}if(Array.isArray(result)){coordX=result[0];coordY=result[1];}}mouseX=(coordX-graph.range[0][0])*graph.scale[0];mouseY=(-coordY+graph.range[1][1])*graph.scale[1];if(doMove){const point=graph.unscalePoint([mouseX,mouseY]);movablePoint.visibleShape.moveTo(point);movablePoint.mouseTarget.moveTo(point);movablePoint.coord=[coordX,coordY];movablePoint.updateLineEnds();$(movablePoint).trigger("move");}movablePoint.drawLabel();}else if(event.type==="vmouseup"){$(document).unbind(".point");movablePoint.dragging=false;dragging=false;if(typeof movablePoint.onMoveEnd==="function"){const result=movablePoint.onMoveEnd(coordX,coordY);if(Array.isArray(result)){coordX=result[0];coordY=result[1];mouseX=(coordX-graph.range[0][0])*graph.scale[0];mouseY=(-coordY+graph.range[1][1])*graph.scale[1];const point=graph.unscalePoint([mouseX,mouseY]);movablePoint.visibleShape.moveTo(point);movablePoint.mouseTarget.moveTo(point);movablePoint.coord=[coordX,coordY];}}if(!movablePoint.highlight){movablePoint.visibleShape.animate(movablePoint.normalStyle,50);if(movablePoint.onUnhighlight){movablePoint.onUnhighlight();}}}});};if(movablePoint.visible&&!movablePoint.constraints.fixed){if(!movablePoint.mouseTarget){const radii=graph.unscaleVector(16);const options={mouselayer:true,padding:0,disableMouseEventsOnWrapper:true,interactiveKindForTesting:"movable-point"};movablePoint.mouseTarget=new WrappedEllipse(graph,movablePoint.coord,radii,options);movablePoint.mouseTarget.attr({fill:"#000",opacity:0});}const $mouseTarget=$(movablePoint.mouseTarget.getMouseTarget());$mouseTarget.css("cursor","move");$mouseTarget.on("vmousedown vmouseover vmouseout",function(event){if(event.type==="vmouseover"){movablePoint.highlight=true;if(!dragging){movablePoint.visibleShape.animate(movablePoint.highlightStyle,50);if(movablePoint.onHighlight){movablePoint.onHighlight();}}}else if(event.type==="vmouseout"){movablePoint.highlight=false;if(!movablePoint.dragging&&!dragging){movablePoint.visibleShape.animate(movablePoint.normalStyle,50);if(movablePoint.onUnhighlight){movablePoint.onUnhighlight();}}}else if(event.type==="vmousedown"&&(event.which===1||event.which===0)){event.preventDefault();const startCoord=movablePoint.coord;const startMouseCoord=graph.getMouseCoord(event);const isMouse=!("ontouchstart"in window);const touchOffset=isMouse?[0,0]:vector$3.subtract(startCoord,startMouseCoord);movablePoint.grab(touchOffset);}});}movablePoint.moveTo=function(coordX,coordY,updateLines){const distance=GraphUtils.getDistance(this.graph.scalePoint([coordX,coordY]),this.graph.scalePoint(this.coord));const time=distance*5;const cb=updateLines&&function(coord){movablePoint.coord=coord;movablePoint.updateLineEnds();};this.visibleShape.animateTo([coordX,coordY],time,cb);this.mouseTarget.animateTo([coordX,coordY],time,cb);this.coord=[coordX,coordY];if(typeof this.onMove==="function"){this.onMove(coordX,coordY);}};movablePoint.updateLineEnds=function(){$(this.lineStarts).each(function(){this.coordA=movablePoint.coord;this.transform();});$(this.lineEnds).each(function(){this.coordZ=movablePoint.coord;this.transform();});$(this.polygonVertices).each(function(){this.transform();});};movablePoint.setCoord=function(coord){if(this.visible){this.visibleShape.moveTo(coord);if(this.mouseTarget!=null){this.mouseTarget.moveTo(coord);}}this.coord=coord.slice();};movablePoint.setCoordConstrained=function(coord){this.setCoord(applySnapAndConstraints(coord));};movablePoint.toBack=function(){if(this.visible){if(this.mouseTarget!=null){this.mouseTarget.toBack();}this.visibleShape.toBack();}};movablePoint.toFront=function(){if(this.visible){if(this.mouseTarget!=null){this.mouseTarget.toFront();}this.visibleShape.toFront();}};movablePoint.remove=function(){if(this.visibleShape){this.visibleShape.remove();}if(this.mouseTarget){this.mouseTarget.remove();}if(this.labeledVertex){this.labeledVertex.remove();}};return movablePoint},addMovableLineSegment:function(options){const lineSegment=$.extend({graph:this,coordA:[0,0],coordZ:[1,1],snapX:0,snapY:0,fixed:false,ticks:0,normalStyle:{},highlightStyle:{stroke:KhanColors.INTERACTING,"stroke-width":6},labelStyle:{stroke:KhanColors.INTERACTIVE,color:KhanColors.INTERACTIVE},highlight:false,dragging:false,tick:[],extendLine:false,extendRay:false,constraints:{fixed:false,constrainX:false,constrainY:false},sideLabel:"",vertexLabels:[],numArrows:0,numTicks:0,movePointsWithLine:false},options);const normalColor=lineSegment.fixed?KhanColors.DYNAMIC:KhanColors.INTERACTIVE;lineSegment.normalStyle={"stroke-width":2,stroke:normalColor,...options.normalStyle};lineSegment.arrowStyle={...lineSegment.normalStyle,color:lineSegment.normalStyle.stroke};if(options.pointA!==undefined){lineSegment.coordA=options.pointA.coord;lineSegment.pointA.lineStarts.push(lineSegment);}else if(options.coordA!==undefined){lineSegment.coordA=options.coordA.slice();}if(options.pointZ!==undefined){lineSegment.coordZ=options.pointZ.coord;lineSegment.pointZ.lineEnds.push(lineSegment);}else if(options.coordA!==undefined){lineSegment.coordA=lineSegment.coordA.slice();}const graph=lineSegment.graph;graph.style(lineSegment.normalStyle);for(let i=0;i<lineSegment.ticks;++i){lineSegment.tick[i]=InteractiveUtils.bogusShape;}let path=GraphUtils.unscaledSvgPath([[0,0],[1,0]]);for(let i=0;i<lineSegment.ticks;++i){const tickoffset=.5-(lineSegment.ticks-1+i*2)/graph.scale[0];path+=GraphUtils.unscaledSvgPath([[tickoffset,-7],[tickoffset,7]]);}options={thickness:Math.max(lineSegment.normalStyle["stroke-width"],lineSegment.highlightStyle["stroke-width"])};lineSegment.visibleLine=new WrappedLine(graph,[0,0],[1,0],options);lineSegment.visibleLine.attr(lineSegment.normalStyle);if(!lineSegment.fixed){const options={thickness:30,mouselayer:true,interactiveKindForTesting:"movable-line"};lineSegment.mouseTarget=new WrappedLine(graph,[0,0],[1,0],options);lineSegment.mouseTarget.attr({fill:"#000",opacity:0});}lineSegment.transform=function(syncToPoints){if(syncToPoints){if(typeof this.pointA==="object"){this.coordA=this.pointA.coord;}if(typeof this.pointZ==="object"){this.coordZ=this.pointZ.coord;}}const getScaledAngle=function(line){const scaledA=line.graph.scalePoint(line.coordA);const scaledZ=line.graph.scalePoint(line.coordZ);return vector$3.polarDegFromCart(vector$3.subtract(scaledZ,scaledA))[1]};const getClipPoint=function(graph,coord,angle){graph=lineSegment.graph;const xExtent=graph.range[0][1]-graph.range[0][0];const yExtent=graph.range[1][1]-graph.range[1][0];const distance=xExtent+yExtent;const angleVec=graph.unscaleVector(vector$3.cartFromPolarDeg(1,angle));const distVec=vector$3.scale(vector$3.normalize(angleVec),distance);const farCoord=vector$3.add(coord,distVec);const scaledAngle=vector$3.polarDegFromCart(angleVec)[1];const clipPoint=graph.constrainToBoundsOnAngle(farCoord,4,scaledAngle*Math.PI/180);return clipPoint};const angle=getScaledAngle(this);let start=this.coordA;let end=this.coordZ;if(this.extendLine){start=getClipPoint(graph,start,360-angle);end=getClipPoint(graph,end,(540-angle)%360);}else if(this.extendRay){end=getClipPoint(graph,start,360-angle);}const elements=[this.visibleLine];if(!this.fixed){elements.push(this.mouseTarget);}elements.forEach(function(element){element.moveTo(start,end);});if(this._arrows==null){this._arrows=[];if(this.extendLine){this._arrows.push(new Arrowhead$1(graph,this.normalStyle));this._arrows.push(new Arrowhead$1(graph,this.normalStyle));}else if(this.extendRay){this._arrows.push(new Arrowhead$1(graph,this.normalStyle));}}const coordForArrow=[this.coordA,this.coordZ];const angleForArrow=[360-angle,(540-angle)%360];this._arrows.forEach(function(arrow,i){arrow.toCoordAtAngle(coordForArrow[i],angleForArrow[i]);});_.invoke(this.temp,"remove");this.temp=[];const isClockwise=this.coordA[0]<this.coordZ[0]||this.coordA[0]===this.coordZ[0]&&this.coordA[1]>this.coordZ[1];if(this.sideLabel){this.temp.push(this.graph.labelSide({point1:this.coordA,point2:this.coordZ,label:this.labeledSide,text:this.sideLabel,numArrows:this.numArrows,numTicks:this.numTicks,clockwise:isClockwise,style:this.labelStyle}));}if(this.vertexLabels.length){this.graph.labelVertex({vertex:this.coordA,point3:this.coordZ,label:this.labeledVertices[0],text:this.vertexLabels[0],clockwise:isClockwise,style:this.labelStyle});this.graph.labelVertex({point1:this.coordA,vertex:this.coordZ,label:this.labeledVertices[1],text:this.vertexLabels[1],clockwise:isClockwise,style:this.labelStyle});}this.temp=this.temp.flat(Infinity);};lineSegment.toBack=function(){if(!lineSegment.fixed){lineSegment.mouseTarget.toBack();}lineSegment.visibleLine.toBack();};lineSegment.toFront=function(){if(!lineSegment.fixed){lineSegment.mouseTarget.toFront();}lineSegment.visibleLine.toFront();};lineSegment.remove=function(){if(!lineSegment.fixed){lineSegment.mouseTarget.remove();}lineSegment.visibleLine.remove();if(lineSegment.labeledSide){lineSegment.labeledSide.remove();}if(lineSegment.labeledVertices){_.invoke(lineSegment.labeledVertices,"remove");}if(lineSegment._arrows){_.invoke(lineSegment._arrows,"remove");}if(lineSegment.temp.length){_.invoke(lineSegment.temp,"remove");}};lineSegment.hide=function(){lineSegment.visibleLine.hide();if(lineSegment.temp.length){_.invoke(lineSegment.temp,"hide");}if(lineSegment._arrows){_.invoke(lineSegment._arrows,"hide");}};lineSegment.show=function(){lineSegment.visibleLine.show();if(lineSegment.temp.length){_.invoke(lineSegment.temp,"show");}if(lineSegment._arrows){_.invoke(lineSegment._arrows,"show");}};if(lineSegment.sideLabel){lineSegment.labeledSide=this.label([0,0],"","center",lineSegment.labelStyle);}if(lineSegment.vertexLabels.length){lineSegment.labeledVertices=lineSegment.vertexLabels.map(label=>{return this.label([0,0],"","center",lineSegment.labelStyle)});}if(!lineSegment.fixed&&!lineSegment.constraints.fixed){const $mouseTarget=$(lineSegment.mouseTarget.getMouseTarget());$mouseTarget.css("cursor","move");$mouseTarget.on("vmousedown vmouseover vmouseout",function(event){if(event.type==="vmouseover"){if(!dragging){lineSegment.highlight=true;lineSegment.visibleLine.animate(lineSegment.highlightStyle,50);lineSegment.arrowStyle={...lineSegment.arrowStyle,color:lineSegment.highlightStyle.stroke,stroke:lineSegment.highlightStyle.stroke};lineSegment.transform();}}else if(event.type==="vmouseout"){lineSegment.highlight=false;if(!lineSegment.dragging){lineSegment.visibleLine.animate(lineSegment.normalStyle,50);lineSegment.arrowStyle={...lineSegment.arrowStyle,color:lineSegment.normalStyle.stroke,stroke:lineSegment.normalStyle.stroke};lineSegment.transform();}}else if(event.type==="vmousedown"&&(event.which===1||event.which===0)){event.preventDefault();let coordX=(event.pageX-$(graph.raphael.canvas.parentNode).offset().left)/graph.scale[0]+graph.range[0][0];let coordY=graph.range[1][1]-(event.pageY-$(graph.raphael.canvas.parentNode).offset().top)/graph.scale[1];if(lineSegment.snapX>0){coordX=Math.round(coordX/lineSegment.snapX)*lineSegment.snapX;}if(lineSegment.snapY>0){coordY=Math.round(coordY/lineSegment.snapY)*lineSegment.snapY;}const mouseOffsetA=[lineSegment.coordA[0]-coordX,lineSegment.coordA[1]-coordY];const mouseOffsetZ=[lineSegment.coordZ[0]-coordX,lineSegment.coordZ[1]-coordY];const offsetLeft=-Math.min(graph.scaleVector(mouseOffsetA)[0],graph.scaleVector(mouseOffsetZ)[0]);const offsetRight=Math.max(graph.scaleVector(mouseOffsetA)[0],graph.scaleVector(mouseOffsetZ)[0]);const offsetTop=Math.max(graph.scaleVector(mouseOffsetA)[1],graph.scaleVector(mouseOffsetZ)[1]);const offsetBottom=-Math.min(graph.scaleVector(mouseOffsetA)[1],graph.scaleVector(mouseOffsetZ)[1]);$(document).on("vmousemove.lineSegment vmouseup.lineSegment",function(event){event.preventDefault();lineSegment.dragging=true;dragging=true;let mouseX=event.pageX-$(graph.raphael.canvas.parentNode).offset().left;let mouseY=event.pageY-$(graph.raphael.canvas.parentNode).offset().top;mouseX=Math.max(offsetLeft+10,Math.min(graph.xpixels-10-offsetRight,mouseX));mouseY=Math.max(offsetTop+10,Math.min(graph.ypixels-10-offsetBottom,mouseY));let coordX=mouseX/graph.scale[0]+graph.range[0][0];let coordY=graph.range[1][1]-mouseY/graph.scale[1];if(lineSegment.snapX>0){coordX=Math.round(coordX/lineSegment.snapX)*lineSegment.snapX;}if(lineSegment.snapY>0){coordY=Math.round(coordY/lineSegment.snapY)*lineSegment.snapY;}if(event.type==="vmousemove"){if(lineSegment.constraints.constrainX){coordX=lineSegment.coordA[0]-mouseOffsetA[0];}if(lineSegment.constraints.constrainY){coordY=lineSegment.coordA[1]-mouseOffsetA[1];}const dX=coordX+mouseOffsetA[0]-lineSegment.coordA[0];const dY=coordY+mouseOffsetA[1]-lineSegment.coordA[1];lineSegment.coordA=[coordX+mouseOffsetA[0],coordY+mouseOffsetA[1]];lineSegment.coordZ=[coordX+mouseOffsetZ[0],coordY+mouseOffsetZ[1]];lineSegment.transform();if(lineSegment.movePointsWithLine){if(typeof lineSegment.pointA==="object"){lineSegment.pointA.setCoord([lineSegment.pointA.coord[0]+dX,lineSegment.pointA.coord[1]+dY]);}if(typeof lineSegment.pointZ==="object"){lineSegment.pointZ.setCoord([lineSegment.pointZ.coord[0]+dX,lineSegment.pointZ.coord[1]+dY]);}}if(typeof lineSegment.onMove==="function"){lineSegment.onMove(dX,dY);}}else if(event.type==="vmouseup"){$(document).unbind(".lineSegment");lineSegment.dragging=false;dragging=false;if(!lineSegment.highlight){lineSegment.visibleLine.animate(lineSegment.normalStyle,50);lineSegment.arrowStyle={...lineSegment.arrowStyle,color:lineSegment.normalStyle.stroke,stroke:lineSegment.normalStyle.stroke};lineSegment.transform();}if(typeof lineSegment.onMoveEnd==="function"){lineSegment.onMoveEnd();}}$(lineSegment).trigger("move");});}});}if(lineSegment.pointA!==undefined){lineSegment.pointA.toFront();}if(lineSegment.pointZ!==undefined){lineSegment.pointZ.toFront();}lineSegment.transform();return lineSegment},addMovablePolygon:function(options){const graphie=this;const{points,...optionsWithoutPoints}=options;const polygon=$.extend({snapX:0,snapY:0,fixed:false,constrainToGraph:true,normalStyle:{},highlightStyle:{stroke:KhanColors.INTERACTING,"stroke-width":2,fill:KhanColors.INTERACTING,"fill-opacity":.05},pointHighlightStyle:{fill:KhanColors.INTERACTING,stroke:KhanColors.INTERACTING},labelStyle:{stroke:KhanColors.DYNAMIC,"stroke-width":1,color:KhanColors.DYNAMIC},angleLabels:[],showRightAngleMarkers:[],sideLabels:[],vertexLabels:[],numArcs:[],numArrows:[],numTicks:[],updateOnPointMove:true,closed:true},optionsWithoutPoints);const normalColor=polygon.fixed?KhanColors.DYNAMIC:KhanColors.INTERACTIVE;polygon.normalStyle={"stroke-width":2,"fill-opacity":0,fill:normalColor,stroke:normalColor,...options.normalStyle};polygon.points=options.points;const isPoint=function(coordOrPoint){return !Array.isArray(coordOrPoint)};polygon.update=function(){const n=polygon.points.length;polygon.coords=polygon.points.map(function(coordOrPoint,i){if(isPoint(coordOrPoint)){return coordOrPoint.coord}return coordOrPoint});polygon.left=Math.min(...polygon.coords.map(coord=>coord[0]));polygon.right=Math.max(...polygon.coords.map(coord=>coord[0]));polygon.top=Math.max(...polygon.coords.map(coord=>coord[1]));polygon.bottom=Math.min(...polygon.coords.map(coord=>coord[1]));let scaledCoords=polygon.coords.map(function(coord){return graphie.scalePoint(coord)});if(polygon.closed){scaledCoords.push(true);}else {scaledCoords=scaledCoords.concat([...scaledCoords].reverse());}polygon.path=GraphUtils.unscaledSvgPath(scaledCoords);_.invoke(polygon.temp,"remove");polygon.temp=[];const isClockwise=clockwise$3(polygon.coords);if(polygon.angleLabels.length||polygon.showRightAngleMarkers.length){polygon.labeledAngles.forEach(function(label,i){polygon.temp.push(graphie.labelAngle({point1:polygon.coords[(i-1+n)%n],vertex:polygon.coords[i],point3:polygon.coords[(i+1)%n],label:label,text:polygon.angleLabels[i],showRightAngleMarker:polygon.showRightAngleMarkers[i],numArcs:polygon.numArcs[i],clockwise:isClockwise,style:polygon.labelStyle}));});}if(polygon.sideLabels.length){polygon.labeledSides.forEach(function(label,i){polygon.temp.push(graphie.labelSide({point1:polygon.coords[i],point2:polygon.coords[(i+1)%n],label:label,text:polygon.sideLabels[i],numArrows:polygon.numArrows[i],numTicks:polygon.numTicks[i],clockwise:isClockwise,style:polygon.labelStyle}));});}if(polygon.vertexLabels.length){polygon.labeledVertices.forEach(function(label,i){graphie.labelVertex({point1:polygon.coords[(i-1+n)%n],vertex:polygon.coords[i],point3:polygon.coords[(i+1)%n],label:label,text:polygon.vertexLabels[i],clockwise:isClockwise,style:polygon.labelStyle});});}polygon.temp=polygon.temp.flat(Infinity);};polygon.transform=function(){polygon.update();polygon.visibleShape.attr({path:polygon.path});if(!polygon.fixed){polygon.mouseTarget.attr({path:polygon.path});}};polygon.remove=function(){polygon.visibleShape.remove();if(!polygon.fixed){polygon.mouseTarget.remove();}if(polygon.labeledAngles){_.invoke(polygon.labeledAngles,"remove");}if(polygon.labeledSides){_.invoke(polygon.labeledSides,"remove");}if(polygon.labeledVertices){_.invoke(polygon.labeledVertices,"remove");}if(polygon.temp.length){_.invoke(polygon.temp,"remove");}};polygon.toBack=function(){if(!polygon.fixed){polygon.mouseTarget.toBack();}polygon.visibleShape.toBack();};polygon.toFront=function(){if(!polygon.fixed){polygon.mouseTarget.toFront();}polygon.visibleShape.toFront();};if(polygon.updateOnPointMove){polygon.points.filter(isPoint).forEach(function(coordOrPoint){coordOrPoint.polygonVertices.push(polygon);});}polygon.coords=new Array(polygon.points.length);if(polygon.angleLabels.length){const numLabels=Math.max(polygon.angleLabels.length,polygon.showRightAngleMarkers.length);polygon.labeledAngles=Array.from({length:numLabels},()=>{return this.label([0,0],"","center",polygon.labelStyle)});}if(polygon.sideLabels.length){polygon.labeledSides=polygon.sideLabels.map(label=>{return this.label([0,0],"","center",polygon.labelStyle)});}if(polygon.vertexLabels.length){polygon.labeledVertices=polygon.vertexLabels.map(label=>{return this.label([0,0],"","center",polygon.labelStyle)});}polygon.update();polygon.visibleShape=graphie.raphael.path(polygon.path);polygon.visibleShape.attr(polygon.normalStyle);if(!polygon.fixed){polygon.mouseTarget=graphie.mouselayer.path(polygon.path);polygon.mouseTarget.attr({fill:"#000",opacity:0,cursor:"move"});$(polygon.mouseTarget[0]).on("vmousedown vmouseover vmouseout",function(event){if(event.type==="vmouseover"){if(!dragging||polygon.dragging){polygon.highlight=true;polygon.visibleShape.animate(polygon.highlightStyle,50);polygon.points.filter(isPoint).forEach(function(point){point.visibleShape.animate(polygon.pointHighlightStyle,50);});}}else if(event.type==="vmouseout"){polygon.highlight=false;if(!polygon.dragging){polygon.visibleShape.animate(polygon.normalStyle,50);const points=polygon.points.filter(isPoint);if(!points.some(point=>point.dragging)){points.forEach(function(point){point.visibleShape.animate(point.normalStyle,50);});}}}else if(event.type==="vmousedown"&&(event.which===1||event.which===0)){event.preventDefault();polygon.points.filter(isPoint).forEach(function(point){point.dragging=true;});let startX=(event.pageX-$(graphie.raphael.canvas.parentNode).offset().left)/graphie.scale[0]+graphie.range[0][0];let startY=graphie.range[1][1]-(event.pageY-$(graphie.raphael.canvas.parentNode).offset().top)/graphie.scale[1];if(polygon.snapX>0){startX=Math.round(startX/polygon.snapX)*polygon.snapX;}if(polygon.snapY>0){startY=Math.round(startY/polygon.snapY)*polygon.snapY;}let lastX=startX;let lastY=startY;const polygonCoords=polygon.coords.slice();const offsetLeft=(startX-polygon.left)*graphie.scale[0];const offsetRight=(polygon.right-startX)*graphie.scale[0];const offsetTop=(polygon.top-startY)*graphie.scale[1];const offsetBottom=(startY-polygon.bottom)*graphie.scale[1];$(document).on("vmousemove.polygon vmouseup.polygon",function(event){event.preventDefault();polygon.dragging=true;dragging=true;let mouseX=event.pageX-$(graphie.raphael.canvas.parentNode).offset().left;let mouseY=event.pageY-$(graphie.raphael.canvas.parentNode).offset().top;if(polygon.constrainToGraph){mouseX=Math.max(offsetLeft+10,Math.min(graphie.xpixels-10-offsetRight,mouseX));mouseY=Math.max(offsetTop+10,Math.min(graphie.ypixels-10-offsetBottom,mouseY));}let currentX=mouseX/graphie.scale[0]+graphie.range[0][0];let currentY=graphie.range[1][1]-mouseY/graphie.scale[1];if(polygon.snapX>0){currentX=Math.round(currentX/polygon.snapX)*polygon.snapX;}if(polygon.snapY>0){currentY=Math.round(currentY/polygon.snapY)*polygon.snapY;}if(event.type==="vmousemove"){let dX=currentX-startX;let dY=currentY-startY;let doMove=true;if(typeof polygon.onMove==="function"){const onMoveResult=polygon.onMove(dX,dY);if(onMoveResult===false){doMove=false;}else if(Array.isArray(onMoveResult)){dX=onMoveResult[0];dY=onMoveResult[1];currentX=startX+dX;currentY=startY+dY;}}const increment=function(i){return [polygonCoords[i][0]+dX,polygonCoords[i][1]+dY]};if(doMove){polygon.points.forEach(function(coordOrPoint,i){if(isPoint(coordOrPoint)){coordOrPoint.setCoord(increment(i));}else {polygon.points[i]=increment(i);}});polygon.transform();$(polygon).trigger("move");lastX=currentX;lastY=currentY;}}else if(event.type==="vmouseup"){$(document).unbind(".polygon");const points=polygon.points.filter(isPoint);points.forEach(function(point){point.dragging=false;});polygon.dragging=false;dragging=false;if(!polygon.highlight){polygon.visibleShape.animate(polygon.normalStyle,50);points.forEach(function(point){point.visibleShape.animate(point.normalStyle,50);});}if(typeof polygon.onMoveEnd==="function"){polygon.onMoveEnd(lastX-startX,lastY-startY);}}});}});}_.invoke(polygon.points.filter(isPoint),"toFront");return polygon},constrainToBounds:function(point,padding){const lower=this.unscalePoint([padding,this.ypixels-padding]);const upper=this.unscalePoint([this.xpixels-padding,padding]);const coordX=Math.max(lower[0],Math.min(upper[0],point[0]));const coordY=Math.max(lower[1],Math.min(upper[1],point[1]));return [coordX,coordY]},constrainToBoundsOnAngle:function(point,padding,angle){const lower=this.unscalePoint([padding,this.ypixels-padding]);const upper=this.unscalePoint([this.xpixels-padding,padding]);let result=point.slice();if(result[0]<lower[0]){result=[lower[0],result[1]+(lower[0]-result[0])*Math.tan(angle)];}else if(result[0]>upper[0]){result=[upper[0],result[1]-(result[0]-upper[0])*Math.tan(angle)];}if(result[1]<lower[1]){result=[result[0]+(lower[1]-result[1])/Math.tan(angle),lower[1]];}else if(result[1]>upper[1]){result=[result[0]-(result[1]-upper[1])/Math.tan(angle),upper[1]];}return result},addMovableAngle:function(options){return new MovableAngle(this,options)},addCircleGraph:function(options){const graphie=this;const circle=$.extend({center:[0,0],radius:2,snapX:.5,snapY:.5,snapRadius:.5,minRadius:1,centerConstraints:{},centerNormalStyle:{},centerHighlightStyle:{stroke:KhanColors.INTERACTING,fill:KhanColors.INTERACTING},circleNormalStyle:{stroke:KhanColors.INTERACTIVE,"fill-opacity":0},circleHighlightStyle:{stroke:KhanColors.INTERACTING,fill:KhanColors.INTERACTING,"fill-opacity":.05}},options);const normalColor=circle.centerConstraints.fixed?KhanColors.DYNAMIC:KhanColors.INTERACTIVE;const centerNormalStyle=options?options.centerNormalStyle:null;circle.centerNormalStyle={fill:normalColor,stroke:normalColor,...centerNormalStyle};circle.centerPoint=graphie.addMovablePoint({graph:graphie,coord:circle.center,normalStyle:circle.centerNormalStyle,snapX:circle.snapX,snapY:circle.snapY,constraints:circle.centerConstraints});circle.circ=graphie.circle(circle.center,circle.radius,circle.circleNormalStyle);circle.perim=graphie.mouselayer.circle(graphie.scalePoint(circle.center)[0],graphie.scalePoint(circle.center)[1],graphie.scaleVector(circle.radius)[0]).attr({"stroke-width":20,opacity:.002});if(!circle.centerConstraints.fixed){$(circle.centerPoint.mouseTarget.getMouseTarget()).on("vmouseover vmouseout",function(event){if(circle.centerPoint.highlight||circle.centerPoint.dragging){circle.circ.animate(circle.circleHighlightStyle,50);}else {circle.circ.animate(circle.circleNormalStyle,50);}});}circle.toFront=function(){circle.circ.toFront();circle.perim.toFront();circle.centerPoint.visibleShape.toFront();if(!circle.centerConstraints.fixed){circle.centerPoint.mouseTarget.toFront();}};circle.centerPoint.onMove=function(x,y){circle.toFront();circle.circ.attr({cx:graphie.scalePoint(x)[0],cy:graphie.scalePoint(y)[1]});circle.perim.attr({cx:graphie.scalePoint(x)[0],cy:graphie.scalePoint(y)[1]});if(circle.onMove){circle.onMove(x,y);}};$(circle.centerPoint).on("move",function(){circle.center=this.coord;$(circle).trigger("move");});circle.setCenter=function(x,y){circle.centerPoint.setCoord([x,y]);circle.centerPoint.onMove(x,y);circle.center=[x,y];};circle.setRadius=function(r){circle.radius=r;circle.perim.attr({r:graphie.scaleVector(r)[0]});circle.circ.attr({rx:graphie.scaleVector(r)[0],ry:graphie.scaleVector(r)[1]});};circle.remove=function(){circle.centerPoint.remove();circle.circ.remove();circle.perim.remove();};const cursorAxes=[[Math.PI*-1,Math.PI*-0.875,"ew-resize"],[Math.PI*-0.875,Math.PI*-0.625,"nesw-resize"],[Math.PI*-0.625,Math.PI*-0.375,"ns-resize"],[Math.PI*-0.375,Math.PI*-0.125,"nwse-resize"],[Math.PI*-0.125,Math.PI*.125,"ew-resize"],[Math.PI*.125,Math.PI*.375,"nesw-resize"],[Math.PI*.375,Math.PI*.625,"ns-resize"],[Math.PI*.625,Math.PI*.875,"nwse-resize"],[Math.PI*.875,Math.PI*1,"ew-resize"]];$(circle.perim.node).on("vmousemove",event=>{let[x,y]=this.getMouseCoord(event);x-=circle.center[0];y-=circle.center[1];const theta=Math.atan2(y,x);cursorAxes.forEach(function(axes){const[min,max,cursorName]=axes;if(theta>=min&&theta<max){$(circle.perim.node).css("cursor",cursorName);}});});$(circle.perim.node).css("cursor","nesw-resize");circle.perim.node.addEventListener("touchstart",function(event){event.preventDefault();},{passive:false});$(circle.perim.node).on("vmouseover vmouseout vmousedown",function(event){if(event.type==="vmouseover"){circle.highlight=true;if(!dragging){circle.circ.animate(circle.circleHighlightStyle,50);circle.centerPoint.visibleShape.animate(circle.centerHighlightStyle,50);}}else if(event.type==="vmouseout"){circle.highlight=false;if(!circle.dragging&&!circle.centerPoint.dragging){circle.circ.animate(circle.circleNormalStyle,50);circle.centerPoint.visibleShape.animate(circle.centerNormalStyle,50);}}else if(event.type==="vmousedown"&&(event.which===1||event.which===0)){event.preventDefault();circle.toFront();const startRadius=circle.radius;$(document).on("vmousemove vmouseup",function(event){event.preventDefault();circle.dragging=true;dragging=true;if(event.type==="vmousemove"){const coord=graphie.constrainToBounds(graphie.getMouseCoord(event),10);let radius=GraphUtils.getDistance(circle.centerPoint.coord,coord);radius=Math.max(circle.minRadius,Math.round(radius/circle.snapRadius)*circle.snapRadius);const oldRadius=circle.radius;let doResize=true;if(circle.onResize){const onResizeResult=circle.onResize(radius,oldRadius);if(typeof onResizeResult==="number"){radius=onResizeResult;}else if(onResizeResult===false){doResize=false;}}if(doResize){circle.setRadius(radius);$(circle).trigger("move");}}else if(event.type==="vmouseup"){$(document).off("vmousemove vmouseup");circle.dragging=false;dragging=false;if(circle.onResizeEnd){circle.onResizeEnd(circle.radius,startRadius);}}});}});return circle},addRotateHandle:function(){const drawRotateHandle=function(graphie,center,radius,halfWidth,lengthAngle,angle,interacting){const getRotateHandlePoint=function(offset,distanceFromArrowMidline){const distFromRotationCenter=radius+distanceFromArrowMidline;const vec=vector$3.cartFromPolarDeg(distFromRotationCenter,angle+offset);const absolute=vector$3.add(center,vec);const pixels=graphie.scalePoint(absolute);return pixels[0]+","+pixels[1]};const innerR=graphie.scaleVector(radius-halfWidth);const outerR=graphie.scaleVector(radius+halfWidth);return graphie.raphael.path(" M"+getRotateHandlePoint(lengthAngle,-halfWidth)+" L"+getRotateHandlePoint(lengthAngle,-3*halfWidth)+" L"+getRotateHandlePoint(2*lengthAngle,0)+" L"+getRotateHandlePoint(lengthAngle,3*halfWidth)+" L"+getRotateHandlePoint(lengthAngle,halfWidth)+" A"+outerR[0]+","+outerR[1]+",0,0,1,"+getRotateHandlePoint(-lengthAngle,halfWidth)+" L"+getRotateHandlePoint(-lengthAngle,3*halfWidth)+" L"+getRotateHandlePoint(-2*lengthAngle,0)+" L"+getRotateHandlePoint(-lengthAngle,-3*halfWidth)+" L"+getRotateHandlePoint(-lengthAngle,-halfWidth)+" A"+innerR[0]+","+innerR[1]+",0,0,0,"+getRotateHandlePoint(lengthAngle,-halfWidth)+" Z").attr({stroke:null,fill:interacting?KhanColors.INTERACTING:KhanColors.INTERACTIVE})};return function(options){const graph=this;let rotatePoint=options.center;const radius=options.radius;const lengthAngle=options.lengthAngle||30;const hideArrow=options.hideArrow||false;const mouseTarget=options.mouseTarget;const id=_.uniqueId("rotateHandle");if(Array.isArray(rotatePoint)){rotatePoint={coord:rotatePoint};}const rotateHandle=graph.addMovablePoint({coord:point.addVector(rotatePoint.coord,vector$3.cartFromPolarDeg(radius,options.angleDeg||0)),constraints:{fixedDistance:{dist:radius,point:rotatePoint}},mouseTarget:mouseTarget});rotatePoint.toFront();let rotatePointPrevCoord=rotatePoint.coord;let rotateHandlePrevCoord=rotateHandle.coord;let rotateHandleStartCoord=rotateHandlePrevCoord;let isRotating=false;let isHovering=false;let drawnRotateHandle;const redrawRotateHandle=function(handleCoord){if(hideArrow){return}const handleVec=vector$3.subtract(handleCoord,rotatePoint.coord);const handlePolar=vector$3.polarDegFromCart(handleVec);const angle=handlePolar[1];if(drawnRotateHandle){drawnRotateHandle.remove();}drawnRotateHandle=drawRotateHandle(graph,rotatePoint.coord,options.radius,isRotating||isHovering?options.hoverWidth/2:options.width/2,lengthAngle,angle,isRotating||isHovering);};$(rotatePoint).on("move."+id,function(){const delta=vector$3.subtract(rotatePoint.coord,rotatePointPrevCoord);rotateHandle.setCoord(vector$3.add(rotateHandle.coord,delta));redrawRotateHandle(rotateHandle.coord);rotatePointPrevCoord=rotatePoint.coord;rotateHandle.constraints.fixedDistance.point=rotatePoint;rotateHandlePrevCoord=rotateHandle.coord;});rotateHandle.onMove=function(x,y){if(!isRotating){rotateHandleStartCoord=rotateHandlePrevCoord;isRotating=true;}let coord=[x,y];if(options.onMove){const oldPolar=vector$3.polarDegFromCart(vector$3.subtract(rotateHandlePrevCoord,rotatePoint.coord));const newPolar=vector$3.polarDegFromCart(vector$3.subtract(coord,rotatePoint.coord));const oldAngle=oldPolar[1];const newAngle=newPolar[1];let result=options.onMove(newAngle,oldAngle);if(result!=null&&result!==true){if(result===false){result=oldAngle;}coord=vector$3.add(rotatePoint.coord,vector$3.cartFromPolarDeg(oldPolar[0],result));}}redrawRotateHandle(coord);rotateHandlePrevCoord=coord;return coord};rotateHandle.onMoveEnd=function(){isRotating=false;redrawRotateHandle(rotateHandle.coord);if(options.onMoveEnd){const oldPolar=vector$3.polarDegFromCart(vector$3.subtract(rotateHandleStartCoord,rotatePoint.coord));const newPolar=vector$3.polarDegFromCart(vector$3.subtract(rotateHandle.coord,rotatePoint.coord));options.onMoveEnd(newPolar[1],oldPolar[1]);}};rotateHandle.visibleShape.remove();if(!mouseTarget){rotateHandle.mouseTarget.attr({scale:2});}const $mouseTarget=$(rotateHandle.mouseTarget.getMouseTarget());$mouseTarget.on("vmouseover",function(e){isHovering=true;redrawRotateHandle(rotateHandle.coord);});$mouseTarget.on("vmouseout",function(e){isHovering=false;redrawRotateHandle(rotateHandle.coord);});redrawRotateHandle(rotateHandle.coord);const oldRemove=rotateHandle.remove;rotateHandle.remove=function(){oldRemove.call(rotateHandle);if(drawnRotateHandle){drawnRotateHandle.remove();}$(rotatePoint).off("move."+id);};rotateHandle.update=function(){redrawRotateHandle(rotateHandle.coord);};return rotateHandle}}(),addReflectButton:function(){const drawButton=function(graphie,buttonCoord,lineCoords,size,distanceFromCenter,leftStyle,rightStyle){if(point.equal(lineCoords[0],lineCoords[1])){lineCoords=[lineCoords[0],point.addVector(lineCoords[0],[1,1])];}const lineDirection=vector$3.normalize(vector$3.subtract(lineCoords[1],lineCoords[0]));const lineVec=vector$3.scale(lineDirection,size/2);const centerVec=vector$3.scale(lineDirection,distanceFromCenter);const leftCenterVec=vector$3.rotateDeg(centerVec,90);const rightCenterVec=vector$3.rotateDeg(centerVec,-90);const negLineVec=vector$3.negate(lineVec);const leftVec=vector$3.rotateDeg(lineVec,90);const rightVec=vector$3.rotateDeg(lineVec,-90);const leftCenter=point.addVectors(buttonCoord,leftCenterVec);const rightCenter=point.addVectors(buttonCoord,rightCenterVec);const leftCoord1=point.addVectors(buttonCoord,leftCenterVec,lineVec,leftVec);const leftCoord2=point.addVectors(buttonCoord,leftCenterVec,negLineVec,leftVec);const rightCoord1=point.addVectors(buttonCoord,rightCenterVec,lineVec,rightVec);const rightCoord2=point.addVectors(buttonCoord,rightCenterVec,negLineVec,rightVec);const leftButton=graphie.path([leftCenter,leftCoord1,leftCoord2,true],leftStyle);const rightButton=graphie.path([rightCenter,rightCoord1,rightCoord2,true],rightStyle);return {remove:function(){leftButton.remove();rightButton.remove();}}};return function(options){const graphie=this;const line$1=options.line;const button=graphie.addMovablePoint({constraints:options.constraints,coord:line.midpoint([line$1.pointA.coord,line$1.pointZ.coord]),snapX:graphie.snap[0],snapY:graphie.snap[1],onMove:function(x,y){return false},onMoveEnd:function(x,y){if(options.onMoveEnd){options.onMoveEnd.call(this,x,y);}}});let isHovering=false;let isFlipped=false;let currentlyDrawnButton;const isHighlight=function(){return isHovering};const styles=[0,1].map(function(isHighlight){const baseStyle=isHighlight?options.highlightStyle:options.normalStyle;return [0,1].map(function(opacity){return _.defaults({"fill-opacity":opacity},baseStyle)})});const getStyle=function(isRight){if(isFlipped){isRight=!isRight;}return styles[+isHighlight()][+isRight]};const redraw=function(coord,lineCoords){if(currentlyDrawnButton){currentlyDrawnButton.remove();}currentlyDrawnButton=drawButton(graphie,coord,lineCoords,isHighlight()?options.size*1.5:options.size,isHighlight()?options.size*.125:.25,getStyle(0),getStyle(1));};const update=function(coordA,coordZ){coordA=coordA||line$1.pointA.coord;coordZ=coordZ||line$1.pointZ.coord;const buttonCoord=line.midpoint([coordA,coordZ]);button.setCoord(buttonCoord);redraw(buttonCoord,[coordA,coordZ]);};$(line$1).on("move",()=>update(null,null));const $mouseTarget=$(button.mouseTarget.getMouseTarget());$mouseTarget.on("vclick",function(){const result=options.onClick();if(result!==false){isFlipped=!isFlipped;redraw(button.coord,[line$1.pointA.coord,line$1.pointZ.coord]);}});line$1.pointA.toFront();line$1.pointZ.toFront();button.visibleShape.remove();const pointScale=graphie.scaleVector(options.size)[0]/20;button.mouseTarget.attr({scale:1.5*pointScale});$mouseTarget.css("cursor","pointer");$mouseTarget.on("vmouseover",function(e){isHovering=true;redraw(button.coord,[line$1.pointA.coord,line$1.pointZ.coord]);});$mouseTarget.on("vmouseout",function(e){isHovering=false;redraw(button.coord,[line$1.pointA.coord,line$1.pointZ.coord]);});const oldButtonRemove=button.remove;button.remove=function(){currentlyDrawnButton.remove();oldButtonRemove.call(button);};button.update=update;button.isFlipped=function(){return isFlipped};update();return button}}(),protractor:function(center){return new Protractor$1(this,center)},ruler:function(options){return new Ruler(this,options||{})},addPoints:vector$3.add});function Protractor$1(graph,center){this.set=graph.raphael.set();this.cx=center[0];this.cy=center[1];const pro=this;const r=graph.unscaleVector(180.5)[0];const imgPos=graph.scalePoint([this.cx-r,this.cy+r-graph.unscaleVector(10.5)[1]]);const image=graph.mouselayer.image("https://ka-perseus-graphie.s3.amazonaws.com/e9d032f2ab8b95979f674fbfa67056442ba1ff6a.png",imgPos[0],imgPos[1],360,180);this.set.push(image);image.node.addEventListener("touchstart",function(event){event.preventDefault();},{passive:false});const arrowHelper=function(angle,pixelsFromEdge){const scaledRadius=graph.scaleVector(r);scaledRadius[0]-=16;scaledRadius[1]-=16;const scaledCenter=graph.scalePoint(center);const x=Math.sin((angle+90)*Math.PI/180)*(scaledRadius[0]+pixelsFromEdge)+scaledCenter[0];const y=Math.cos((angle+90)*Math.PI/180)*(scaledRadius[1]+pixelsFromEdge)+scaledCenter[1];return x+","+y};const arrow=graph.raphael.path(" M"+arrowHelper(180,6)+" L"+arrowHelper(180,2)+" L"+arrowHelper(183,10)+" L"+arrowHelper(180,18)+" L"+arrowHelper(180,14)+" A"+(graph.scaleVector(r)[0]+10)+","+(graph.scaleVector(r)[1]+10)+",0,0,1,"+arrowHelper(170,14)+" L"+arrowHelper(170,18)+" L"+arrowHelper(167,10)+" L"+arrowHelper(170,2)+" L"+arrowHelper(170,6)+" A"+(graph.scaleVector(r)[0]+10)+","+(graph.scaleVector(r)[1]+10)+",0,0,0,"+arrowHelper(180,6)+" Z").attr({stroke:null,fill:KhanColors.INTERACTIVE});this.set.push(arrow);this.centerPoint=graph.addMovablePoint({coord:center,visible:false});this.rotateHandle=graph.addMovablePoint({bounded:false,coord:[Math.sin(275*Math.PI/180)*r+this.cx,Math.cos(275*Math.PI/180)*r+this.cy],onMove:function(x,y){const angle=Math.atan2(pro.centerPoint.coord[1]-y,pro.centerPoint.coord[0]-x)*180/Math.PI;pro.rotate(-angle-5,true);}});this.rotateHandle.constraints.fixedDistance.dist=r;this.rotateHandle.constraints.fixedDistance.point=this.centerPoint;this.rotateHandle.visibleShape.remove();this.rotateHandle.mouseTarget.attr({scale:2});let isDragging=false;let isHovering=false;const isHighlight=function(){return isHovering||isDragging};const self=this;const $mouseTarget=$(self.rotateHandle.mouseTarget.getMouseTarget());$mouseTarget.on("vmousedown",function(event){isDragging=true;$mouseTarget.css("cursor","-webkit-grabbing");$mouseTarget.css("cursor","grabbing");arrow.animate({scale:1.5,fill:KhanColors.INTERACTING},50);$(document).on("vmouseup.rotateHandle",function(event){isDragging=false;$mouseTarget.css("cursor","-webkit-grab");$mouseTarget.css("cursor","grab");if(!isHighlight()){arrow.animate({scale:1,fill:KhanColors.INTERACTIVE},50);}$(document).unbind("vmouseup.rotateHandle");});});$mouseTarget.on("vmouseover",function(event){isHovering=true;arrow.animate({scale:1.5,fill:KhanColors.INTERACTING},50);});$mouseTarget.on("vmouseout",function(event){isHovering=false;if(!isHighlight()){arrow.animate({scale:1,fill:KhanColors.INTERACTIVE},50);}});const setNodes=$.map(this.set,function(el){return el.node});this.makeTranslatable=function makeTranslatable(){$(setNodes).css("cursor","move");$mouseTarget.css("cursor","-webkit-grab");$mouseTarget.css("cursor","grab");$(setNodes).on("vmousedown",function(event){event.preventDefault();let startx=event.pageX-$(graph.raphael.canvas.parentNode).offset().left;let starty=event.pageY-$(graph.raphael.canvas.parentNode).offset().top;$(document).on("vmousemove.protractor",function(event){let mouseX=event.pageX-$(graph.raphael.canvas.parentNode).offset().left;let mouseY=event.pageY-$(graph.raphael.canvas.parentNode).offset().top;mouseX=Math.max(10,Math.min(graph.xpixels-10,mouseX));mouseY=Math.max(10,Math.min(graph.ypixels-10,mouseY));const dx=mouseX-startx;const dy=mouseY-starty;$.each(pro.set.items,function(){this.translate(dx,dy);});pro.centerPoint.setCoord([pro.centerPoint.coord[0]+dx/graph.scale[0],pro.centerPoint.coord[1]-dy/graph.scale[1]]);pro.rotateHandle.setCoord([pro.rotateHandle.coord[0]+dx/graph.scale[0],pro.rotateHandle.coord[1]-dy/graph.scale[1]]);startx=mouseX;starty=mouseY;});$(document).one("vmouseup",function(event){$(document).unbind("vmousemove.protractor");});});};this.rotation=0;this.rotate=function(offset,absolute){const center=graph.scalePoint(this.centerPoint.coord);if(absolute){this.rotation=0;}this.set.rotate(this.rotation+offset,center[0],center[1]);this.rotation=this.rotation+offset;return this};this.moveTo=function moveTo(x,y){const start=graph.scalePoint(pro.centerPoint.coord);const end=graph.scalePoint([x,y]);const time=GraphUtils.getDistance(start,end)*2;$({x:start[0],y:start[1]}).animate({x:end[0],y:end[1]},{duration:time,step:function(now,fx){let dx=0;let dy=0;if(fx.prop==="x"){dx=now-graph.scalePoint(pro.centerPoint.coord)[0];}else if(fx.prop==="y"){dy=now-graph.scalePoint(pro.centerPoint.coord)[1];}$.each(pro.set.items,function(){this.translate(dx,dy);});pro.centerPoint.setCoord([pro.centerPoint.coord[0]+dx/graph.scale[0],pro.centerPoint.coord[1]-dy/graph.scale[1]]);pro.rotateHandle.setCoord([pro.rotateHandle.coord[0]+dx/graph.scale[0],pro.rotateHandle.coord[1]-dy/graph.scale[1]]);}});};this.rotateTo=function rotateTo(angle){if(Math.abs(this.rotation-angle)>180){this.rotation+=360;}const time=Math.abs(this.rotation-angle)*5;$({0:this.rotation}).animate({0:angle},{duration:time,step:function(now,fx){pro.rotate(now,true);pro.rotateHandle.setCoord([Math.sin((now+275)*Math.PI/180)*r+pro.centerPoint.coord[0],Math.cos((now+275)*Math.PI/180)*r+pro.centerPoint.coord[1]]);}});};this.remove=function(){this.set.remove();};this.makeTranslatable();return this}function Ruler(graphie,options){_.defaults(options,{center:[0,0],pixelsPerUnit:40,ticksPerUnit:10,units:10,label:"",style:{fill:null,stroke:KhanColors.GRAY}});const light={...options.style,strokeWidth:1};const bold={...options.style,strokeWidth:2};const width=options.units*options.pixelsPerUnit;const height=50;const leftBottom=graphie.unscalePoint(vector$3.subtract(graphie.scalePoint(options.center),vector$3.scale([width,-50],.5)));const graphieUnitsPerUnit=options.pixelsPerUnit/graphie.scale[0];const graphieUnitsHeight=height/graphie.scale[0];const rightTop=vector$3.add(leftBottom,[options.units*graphieUnitsPerUnit,graphieUnitsHeight]);const tickHeight=1;let tickHeightMap;if(options.ticksPerUnit===10){tickHeightMap={10:tickHeight,5:tickHeight*.55,1:tickHeight*.35};}else {const sizes=[1,.6,.45,.3];tickHeightMap={};for(let i=options.ticksPerUnit;i>=1;i/=2){tickHeightMap[i]=tickHeight*(sizes.shift()||.2);}}const tickFrequencies=Object.keys(tickHeightMap).sort(function(a,b){return b-a});function getTickHeight(i){for(let k=0;k<tickFrequencies.length;k++){const key=tickFrequencies[k];if(i%key===0){return tickHeightMap[key]}}return 0}const left=leftBottom[0];const bottom=leftBottom[1];const right=rightTop[0];const top=rightTop[1];const numTicks=options.units*options.ticksPerUnit+1;const set=graphie.raphael.set();const px=1/graphie.scale[0];set.push(graphie.line([left-px,bottom],[right+px,bottom],bold));set.push(graphie.line([left-px,top],[right+px,top],bold));for(let i=0;i<numTicks;i++){const n=i/options.ticksPerUnit;const x=left+n*graphieUnitsPerUnit;const height=getTickHeight(i)*graphieUnitsHeight;const style=i===0||i===numTicks-1?bold:light;set.push(graphie.line([x,bottom],[x,bottom+height],style));if(n%1===0){const coord=graphie.scalePoint([x,top]);let text;let offset;if(n===0){text=options.label;offset=({mm:13,cm:11,m:8,km:11,in:8,ft:8,yd:10,mi:10})[text]||3*text.toString().length;}else {text=n;offset=-3*(n.toString().length+1);}const label=graphie.raphael.text(coord[0]+offset,coord[1]+10,text);label.attr({"font-family":"KaTeX_Main","font-size":"12px",color:"#444"});set.push(label);}}const mouseTarget=graphie.mouselayer.path(graphie.svgPath([leftBottom,[left,top],rightTop,[right,bottom],true]));mouseTarget.attr({fill:"#000",opacity:0,stroke:"#000","stroke-width":2});set.push(mouseTarget);mouseTarget.node.addEventListener("touchstart",function(event){event.preventDefault();},{passive:false});const setNodes=$.map(set,function(el){return el.node});$(setNodes).css("cursor","move");$(setNodes).on("vmousedown",function(event){event.preventDefault();let startx=event.pageX-$(graphie.raphael.canvas.parentNode).offset().left;let starty=event.pageY-$(graphie.raphael.canvas.parentNode).offset().top;$(document).on("vmousemove.ruler",function(event){let mouseX=event.pageX-$(graphie.raphael.canvas.parentNode).offset().left;let mouseY=event.pageY-$(graphie.raphael.canvas.parentNode).offset().top;mouseX=Math.max(10,Math.min(graphie.xpixels-10,mouseX));mouseY=Math.max(10,Math.min(graphie.ypixels-10,mouseY));const dx=mouseX-startx;const dy=mouseY-starty;set.translate(dx,dy);leftBottomHandle.setCoord([leftBottomHandle.coord[0]+dx/graphie.scale[0],leftBottomHandle.coord[1]-dy/graphie.scale[1]]);rightBottomHandle.setCoord([rightBottomHandle.coord[0]+dx/graphie.scale[0],rightBottomHandle.coord[1]-dy/graphie.scale[1]]);startx=mouseX;starty=mouseY;});$(document).one("vmouseup",function(event){$(document).unbind("vmousemove.ruler");});});const leftBottomHandle=graphie.addMovablePoint({coord:leftBottom,normalStyle:{fill:KhanColors.INTERACTIVE,"fill-opacity":0,stroke:KhanColors.INTERACTIVE},highlightStyle:{fill:KhanColors.INTERACTING,"fill-opacity":.1,stroke:KhanColors.INTERACTING},pointSize:6,onMove:function(x,y){const dy=rightBottomHandle.coord[1]-y;const dx=rightBottomHandle.coord[0]-x;const angle=Math.atan2(dy,dx)*180/Math.PI;const center=vector$3.scale(vector$3.add([x,y],rightBottomHandle.coord),.5);const scaledCenter=graphie.scalePoint(center);const oldCenter=vector$3.scale(vector$3.add(leftBottomHandle.coord,rightBottomHandle.coord),.5);const scaledOldCenter=graphie.scalePoint(oldCenter);const diff=vector$3.subtract(scaledCenter,scaledOldCenter);set.rotate(-angle,scaledOldCenter[0],scaledOldCenter[1]);set.translate(diff[0],diff[1]);}});const rightBottomHandle=graphie.addMovablePoint({coord:[right,bottom],normalStyle:{fill:KhanColors.INTERACTIVE,"fill-opacity":0,stroke:KhanColors.INTERACTIVE},highlightStyle:{fill:KhanColors.INTERACTING,"fill-opacity":.1,stroke:KhanColors.INTERACTING},pointSize:6,onMove:function(x,y){const dy=y-leftBottomHandle.coord[1];const dx=x-leftBottomHandle.coord[0];const angle=Math.atan2(dy,dx)*180/Math.PI;const center=vector$3.scale(vector$3.add([x,y],leftBottomHandle.coord),.5);const scaledCenter=graphie.scalePoint(center);const oldCenter=vector$3.scale(vector$3.add(leftBottomHandle.coord,rightBottomHandle.coord),.5);const scaledOldCenter=graphie.scalePoint(oldCenter);const diff=vector$3.subtract(scaledCenter,scaledOldCenter);set.rotate(-angle,scaledOldCenter[0],scaledOldCenter[1]);set.translate(diff[0],diff[1]);}});leftBottomHandle.constraints.fixedDistance.dist=width/graphie.scale[0];leftBottomHandle.constraints.fixedDistance.point=rightBottomHandle;rightBottomHandle.constraints.fixedDistance.dist=width/graphie.scale[0];rightBottomHandle.constraints.fixedDistance.point=leftBottomHandle;this.remove=function(){set.remove();leftBottomHandle.remove();rightBottomHandle.remove();};return this}function MovableAngle(graphie,options){this.graphie=graphie;Object.assign(this,options);_.defaults(this,{normalStyle:{stroke:KhanColors.INTERACTIVE,"stroke-width":2,fill:KhanColors.INTERACTIVE},highlightStyle:{stroke:KhanColors.INTERACTING,"stroke-width":2,fill:KhanColors.INTERACTING},labelStyle:{stroke:KhanColors.DYNAMIC,"stroke-width":1,color:KhanColors.DYNAMIC},angleStyle:{stroke:KhanColors.DYNAMIC,"stroke-width":1,color:KhanColors.DYNAMIC},allowReflex:true});if(!this.points||this.points.length!==3){throw new PerseusError("MovableAngle requires 3 points",Errors.InvalidInput)}this.points=options.points.map(point=>{if(Array.isArray(point)){return graphie.addMovablePoint({coord:point,visible:false,constraints:{fixed:true},normalStyle:this.normalStyle})}return point});this.coords=this.points.map(point=>point.coord);if(this.reflex==null){if(this.allowReflex){this.reflex=this._getClockwiseAngle(this.coords)>180;}else {this.reflex=false;}}this.rays=[0,2].map(i=>{return graphie.addMovableLineSegment({pointA:this.points[1],pointZ:this.points[i],fixed:true,extendRay:true})});this.temp=[];this.labeledAngle=graphie.label([0,0],"","center",this.labelStyle);if(!this.fixed){this.addMoveHandlers();this.addHighlightHandlers();}this.update();}_.extend(MovableAngle.prototype,{points:[],snapDegrees:0,snapOffsetDeg:0,angleLabel:"",numArcs:1,pushOut:0,fixed:false,addMoveHandlers:function(){const graphie=this.graphie;function tooClose(point1,point2){const safeDistance=30;const distance=GraphUtils.getDistance(graphie.scalePoint(point1),graphie.scalePoint(point2));return distance<safeDistance}const points=this.points;points[1].onMove=function(x,y){const oldVertex=points[1].coord;const newVertex=[x,y];const delta=vector$3.add(newVertex,reverseVector$1(oldVertex));let valid=true;const newPoints={};[0,2].forEach(function(i){const oldPoint=points[i].coord;let newPoint=vector$3.add(oldPoint,delta);let angle=GraphUtils.findAngleDeprecated(newVertex,newPoint);angle*=Math.PI/180;newPoint=graphie.constrainToBoundsOnAngle(newPoint,10,angle);newPoints[i]=newPoint;if(tooClose(newVertex,newPoint)){valid=false;}});if(valid){Object.entries(newPoints).forEach(function([i,newPoint]){points[i].setCoord(newPoint);});}return valid};const snap=this.snapDegrees;const snapOffset=this.snapOffsetDeg;[0,2].forEach(function(i){points[i].onMove=function(x,y){const newPoint=[x,y];const vertex=points[1].coord;if(tooClose(vertex,newPoint)){return false}if(snap){let angle=GraphUtils.findAngleDeprecated(newPoint,vertex);angle=Math.round((angle-snapOffset)/snap)*snap+snapOffset;const distance=GraphUtils.getDistance(newPoint,vertex);return vector$3.add(vertex,polar$2(distance,angle))}return true};});$(points).on("move",()=>{this.update();$(this).trigger("move");});},addHighlightHandlers:function(){const vertex=this.points[1];vertex.onHighlight=()=>{this.points.forEach(point=>{point.visibleShape.animate(this.highlightStyle,50);});this.rays.forEach(ray=>{ray.visibleLine.animate(this.highlightStyle,50);ray.arrowStyle={...ray.arrowStyle,color:this.highlightStyle.stroke,stroke:this.highlightStyle.stroke};});this.angleStyle={...this.angleStyle,color:this.highlightStyle.stroke,stroke:this.highlightStyle.stroke};this.update();};vertex.onUnhighlight=()=>{this.points.forEach(point=>{point.visibleShape.animate(this.normalStyle,50);});this.rays.forEach(ray=>{ray.visibleLine.animate(ray.normalStyle,50);ray.arrowStyle={...ray.arrowStyle,color:ray.normalStyle.stroke,stroke:ray.normalStyle.stroke};});this.angleStyle={...this.angleStyle,color:KhanColors.DYNAMIC,stroke:KhanColors.DYNAMIC};this.update();};},_getClockwiseAngle:function(coords){const rawAngle=GraphUtils.findAngleDeprecated(coords[2],coords[0],coords[1]);const clockwiseAngle=rawAngle+360%360;return clockwiseAngle},isReflex:function(){return this.reflex},isClockwise:function(){const clockwiseReflexive=this._getClockwiseAngle(this.coords)>180;return clockwiseReflexive===this.reflex},getClockwiseCoords:function(){if(this.isClockwise()){return [...this.coords]}return [...this.coords].reverse()},update:function(shouldChangeReflexivity){const prevCoords=this.coords;this.coords=this.points.map(point=>point.coord);_.invoke(this.points,"updateLineEnds");const prevAngle=this._getClockwiseAngle(prevCoords);const angle=this._getClockwiseAngle(this.coords);const prevClockwiseReflexive=prevAngle>180;const clockwiseReflexive=angle>180;if(this.allowReflex){if(shouldChangeReflexivity==null){shouldChangeReflexivity=prevClockwiseReflexive!==clockwiseReflexive&&Math.abs(angle-prevAngle)<180;}if(shouldChangeReflexivity){this.reflex=!this.reflex;}}_.invoke(this.temp,"remove");this.temp=this.graphie.labelAngle({point1:this.coords[0],vertex:this.coords[1],point3:this.coords[2],label:this.labeledAngle,text:this.angleLabel,numArcs:this.numArcs,pushOut:this.pushOut,clockwise:this.reflex===clockwiseReflexive,style:this.angleStyle});},remove:function(){_.invoke(this.rays,"remove");_.invoke(this.temp,"remove");this.labeledAngle.remove();}});
1573
+ const{clockwise: clockwise$3,reverseVector: reverseVector$1}=geometry;function scaledDistanceFromAngle(angle){const a=3.51470560176242*20;const b=.5687298702748785*20;const c=-0.037587715462826674;return (a-b)*Math.exp(c*angle)+b}function scaledPolarRad(radius,radians){return [radius*Math.cos(radians),radius*Math.sin(radians)*-1]}function scaledPolarDeg(radius,degrees){const radians=degrees*Math.PI/180;return scaledPolarRad(radius,radians)}let dragging=false;const InteractiveUtils={bogusShape:{animate:function(){},attr:function(){},remove:function(){}}};_.extend(GraphUtils.Graphie.prototype,{labelAngle:function(options){const graphie=this;_.defaults(options,{point1:[0,0],vertex:[0,0],point3:[0,0],label:null,numArcs:1,showRightAngleMarker:true,pushOut:0,clockwise:false,style:{}});let text=options.text===undefined?"":options.text;const vertex=options.vertex;const sVertex=graphie.scalePoint(vertex);let p1;let p3;if(options.clockwise){p1=options.point1;p3=options.point3;}else {p1=options.point3;p3=options.point1;}const startAngle=GraphUtils.findAngleDeprecated(p1,vertex);const endAngle=GraphUtils.findAngleDeprecated(p3,vertex);const angle=(endAngle+360-startAngle)%360;const halfAngle=(startAngle+angle/2)%360;const sPadding=5*options.pushOut;let sRadius=sPadding+scaledDistanceFromAngle(angle);const temp=[];if(Math.abs(angle-90)<1e-9&&options.showRightAngleMarker){const v1=vector$3.add(sVertex,scaledPolarDeg(sRadius,startAngle));const v2=vector$3.add(sVertex,scaledPolarDeg(sRadius,endAngle));sRadius*=Math.SQRT2;const v3=vector$3.add(sVertex,scaledPolarDeg(sRadius,halfAngle));[v1,v2].forEach(function(v){temp.push(graphie.scaledPath([v,v3],options.style));});}else {for(let i=0;i<options.numArcs;i++){temp.push(graphie.arc(vertex,graphie.unscaleVector(sRadius),startAngle,endAngle,options.style));sRadius+=3;}}if(text){const match=text.match(/\$deg(\d)?/);if(match){const precision=match[1]||1;text=text.replace(match[0],KhanMath.toFixedApprox(angle,precision)+"^{\\circ}");}const sOffset=scaledPolarDeg(sRadius+15,halfAngle);const sPosition=vector$3.add(sVertex,sOffset);const position=graphie.unscalePoint(sPosition);if(options.label){options.label.setPosition(position);options.label.processMath(text,true);}else {graphie.label(position,text,"center",options.style);}}return temp},labelSide:function(options){const graphie=this;_.defaults(options,{point1:[0,0],point2:[0,0],label:null,text:"",numTicks:0,numArrows:0,clockwise:false,style:{}});let p1;let p2;if(options.clockwise){p1=options.point1;p2=options.point2;}else {p1=options.point2;p2=options.point1;}const midpoint=[(p1[0]+p2[0])/2,(p1[1]+p2[1])/2];const sMidpoint=graphie.scalePoint(midpoint);const parallelAngle=Math.atan2(p2[1]-p1[1],p2[0]-p1[0]);const perpendicularAngle=parallelAngle+Math.PI/2;const temp=[];let sCumulativeOffset=0;if(options.numTicks){const n=options.numTicks;const sSpacing=5;const sHeight=5;const style={...options.style,strokeWidth:2};for(let i=0;i<n;i++){const sOffset=sSpacing*(i-(n-1)/2);const sOffsetVector=scaledPolarRad(sOffset,parallelAngle);const sHeightVector=scaledPolarRad(sHeight,perpendicularAngle);const sPath=[vector$3.add(sMidpoint,sOffsetVector,sHeightVector),vector$3.add(sMidpoint,sOffsetVector,reverseVector$1(sHeightVector))];temp.push(graphie.scaledPath(sPath,style));}sCumulativeOffset+=sSpacing*(n-1)+15;}if(options.numArrows){const n=options.numArrows;const start=[p1,p2].sort(function(a,b){if(a[1]===b[1]){return a[0]-b[0]}return a[1]-b[1]})[0];const sStart=graphie.scalePoint(start);const style={...options.style,arrows:"->",strokeWidth:2};const sSpacing=5;for(let i=0;i<n;i++){const sOffset=sCumulativeOffset+sSpacing*i;let sOffsetVector=scaledPolarRad(sOffset,parallelAngle);if(start!==p1){sOffsetVector=reverseVector$1(sOffsetVector);}const sEnd=vector$3.add(sMidpoint,sOffsetVector);temp.push(graphie.scaledPath([sStart,sEnd],style));}}let text=options.text;if(text){const match=text.match(/\$len(\d)?/);if(match){const distance=GraphUtils.getDistance(p1,p2);const precision=match[1]||1;text=text.replace(match[0],KhanMath.toFixedApprox(distance,precision));}const sOffset=20;const sOffsetVector=scaledPolarRad(sOffset,perpendicularAngle);const sPosition=vector$3.add(sMidpoint,sOffsetVector);const position=graphie.unscalePoint(sPosition);if(options.label){options.label.setPosition(position);options.label.processMath(text,true);}else {graphie.label(position,text,"center",options.style);}}return temp},labelVertex:function(options){const graphie=this;_.defaults(options,{point1:null,vertex:[0,0],point3:null,label:null,text:"",clockwise:false,style:{}});if(!options.text){return}const vertex=options.vertex;const sVertex=graphie.scalePoint(vertex);let p1;let p3;if(options.clockwise){p1=options.point1;p3=options.point3;}else {p1=options.point3;p3=options.point1;}let angle=135;let halfAngle;if(p1&&p3){const startAngle=GraphUtils.findAngleDeprecated(p1,vertex);const endAngle=GraphUtils.findAngleDeprecated(p3,vertex);angle=(endAngle+360-startAngle)%360;halfAngle=(startAngle+angle/2+180)%360;}else if(p1){const parallelAngle=GraphUtils.findAngleDeprecated(vertex,p1);halfAngle=parallelAngle+90;}else if(p3){const parallelAngle=GraphUtils.findAngleDeprecated(p3,vertex);halfAngle=parallelAngle+90;}else {halfAngle=135;}const sRadius=10+scaledDistanceFromAngle(360-angle);const sOffsetVector=scaledPolarDeg(sRadius,halfAngle);const sPosition=vector$3.add(sVertex,sOffsetVector);const position=graphie.unscalePoint(sPosition);if(options.label){options.label.setPosition(position);options.label.processMath(options.text,true);}else {graphie.label(position,options.text,"center",options.style);}},addMovablePoint:function(options){const movablePoint=$.extend(true,{graph:this,coord:[0,0],snapX:0,snapY:0,pointSize:4,highlight:false,dragging:false,visible:true,bounded:true,constraints:{fixed:false,constrainX:false,constrainY:false,fixedAngle:{},fixedDistance:{}},lineStarts:[],lineEnds:[],polygonVertices:[],normalStyle:{},highlightStyle:{fill:KhanColors.INTERACTING,stroke:KhanColors.INTERACTING},labelStyle:{color:KhanColors.INTERACTIVE},vertexLabel:"",mouseTarget:null},options);const normalColor=movablePoint.constraints.fixed?KhanColors.DYNAMIC:KhanColors.INTERACTIVE;movablePoint.normalStyle={fill:normalColor,stroke:normalColor,...options.normalStyle};if(options.coordX!==undefined){movablePoint.coord[0]=options.coordX;}if(options.coordY!==undefined){movablePoint.coord[1]=options.coordY;}const graph=movablePoint.graph;const applySnapAndConstraints=function(coord){if(movablePoint.visible&&movablePoint.bounded&&!movablePoint.constraints.fixed){coord=graph.constrainToBounds(coord,10);}let coordX=coord[0];let coordY=coord[1];if(movablePoint.snapX!==0){coordX=Math.round(coordX/movablePoint.snapX)*movablePoint.snapX;}if(movablePoint.snapY!==0){coordY=Math.round(coordY/movablePoint.snapY)*movablePoint.snapY;}if(movablePoint.constraints.fixedDistance.snapPoints){const mouse=graph.scalePoint(coord);let mouseX=mouse[0];let mouseY=mouse[1];const snapRadians=2*Math.PI/movablePoint.constraints.fixedDistance.snapPoints;const radius=movablePoint.constraints.fixedDistance.dist;const centerCoord=movablePoint.constraints.fixedDistance.point;const centerX=(centerCoord[0]-graph.range[0][0])*graph.scale[0];const centerY=(-centerCoord[1]+graph.range[1][1])*graph.scale[1];let mouseXrel=mouseX-centerX;let mouseYrel=-mouseY+centerY;let radians=Math.atan(mouseYrel/mouseXrel);const outsideArcTanRange=mouseXrel<0;if(outsideArcTanRange){radians+=Math.PI;}radians=Math.round(radians/snapRadians)*snapRadians;mouseXrel=radius*Math.cos(radians);mouseYrel=radius*Math.sin(radians);mouseX=mouseXrel+centerX;mouseY=-mouseYrel+centerY;coordX=KhanMath.roundTo(5,mouseX/graph.scale[0]+graph.range[0][0]);coordY=KhanMath.roundTo(5,graph.range[1][1]-mouseY/graph.scale[1]);}const result=movablePoint.applyConstraint([coordX,coordY]);return result};movablePoint.applyConstraint=function(coord,extraConstraints,override){let newCoord=coord.slice();const constraints={};if(override){$.extend(constraints,{fixed:false,constrainX:false,constrainY:false,fixedAngle:{},fixedDistance:{}},extraConstraints);}else {$.extend(constraints,this.constraints,extraConstraints);}if(constraints.constrainX){newCoord=[this.coord[0],coord[1]];}else if(constraints.constrainY){newCoord=[coord[0],this.coord[1]];}else if(typeof constraints.fixedAngle.angle==="number"&&typeof constraints.fixedDistance.dist==="number"){const vertex=constraints.fixedAngle.vertex.coord||constraints.fixedAngle.vertex;const ref=constraints.fixedAngle.ref.coord||constraints.fixedAngle.ref;const distPoint=constraints.fixedDistance.point.coord||constraints.fixedDistance.point;const constrainedAngle=(constraints.fixedAngle.angle+GraphUtils.findAngleDeprecated(ref,vertex))*Math.PI/180;const length=constraints.fixedDistance.dist;newCoord[0]=length*Math.cos(constrainedAngle)+distPoint[0];newCoord[1]=length*Math.sin(constrainedAngle)+distPoint[1];}else if(typeof constraints.fixedAngle.angle==="number"){const vertex=constraints.fixedAngle.vertex.coord||constraints.fixedAngle.vertex;const ref=constraints.fixedAngle.ref.coord||constraints.fixedAngle.ref;const constrainedAngle=(constraints.fixedAngle.angle+GraphUtils.findAngleDeprecated(ref,vertex))*Math.PI/180;const angle=GraphUtils.findAngleDeprecated(coord,vertex)*Math.PI/180;const distance=GraphUtils.getDistance(coord,vertex);let length=distance*Math.cos(constrainedAngle-angle);length=length<1?1:length;newCoord[0]=length*Math.cos(constrainedAngle)+vertex[0];newCoord[1]=length*Math.sin(constrainedAngle)+vertex[1];}else if(typeof constraints.fixedDistance.dist==="number"){const distPoint=constraints.fixedDistance.point.coord||constraints.fixedDistance.point;let angle=GraphUtils.findAngleDeprecated(coord,distPoint);const length=constraints.fixedDistance.dist;angle=angle*Math.PI/180;newCoord[0]=length*Math.cos(angle)+distPoint[0];newCoord[1]=length*Math.sin(angle)+distPoint[1];}else if(constraints.fixed){newCoord=movablePoint.coord;}return newCoord};movablePoint.coord=applySnapAndConstraints(movablePoint.coord);const highlightScale=2;if(movablePoint.visible){graph.style(movablePoint.normalStyle,function(){const radii=[movablePoint.pointSize/graph.scale[0],movablePoint.pointSize/graph.scale[1]];const options={maxScale:highlightScale,padding:2};movablePoint.visibleShape=new WrappedEllipse(graph,movablePoint.coord,radii,options);const{scale,...styleWithoutScale}=movablePoint.normalStyle;movablePoint.visibleShape.attr(styleWithoutScale);movablePoint.visibleShape.toFront();});}movablePoint.normalStyle.scale=1;movablePoint.highlightStyle.scale=highlightScale;if(movablePoint.vertexLabel){movablePoint.labeledVertex=this.label([0,0],"","center",movablePoint.labelStyle);}movablePoint.drawLabel=function(){if(movablePoint.vertexLabel){movablePoint.graph.labelVertex({vertex:movablePoint.coord,label:movablePoint.labeledVertex,text:movablePoint.vertexLabel,style:movablePoint.labelStyle});}};movablePoint.drawLabel();movablePoint.grab=function(offset){offset=offset||[0,0];$(document).on("vmousemove.point vmouseup.point",function(event){event.preventDefault();movablePoint.dragging=true;dragging=true;let coord=vector$3.add(graph.getMouseCoord(event),offset);coord=applySnapAndConstraints(coord);let coordX=coord[0];let coordY=coord[1];let mouseX;let mouseY;if(event.type==="vmousemove"){let doMove=true;if(typeof movablePoint.onMove==="function"){const result=movablePoint.onMove(coordX,coordY);if(result===false){doMove=false;}if(Array.isArray(result)){coordX=result[0];coordY=result[1];}}mouseX=(coordX-graph.range[0][0])*graph.scale[0];mouseY=(-coordY+graph.range[1][1])*graph.scale[1];if(doMove){const point=graph.unscalePoint([mouseX,mouseY]);movablePoint.visibleShape.moveTo(point);movablePoint.mouseTarget.moveTo(point);movablePoint.coord=[coordX,coordY];movablePoint.updateLineEnds();$(movablePoint).trigger("move");}movablePoint.drawLabel();}else if(event.type==="vmouseup"){$(document).unbind(".point");movablePoint.dragging=false;dragging=false;if(typeof movablePoint.onMoveEnd==="function"){const result=movablePoint.onMoveEnd(coordX,coordY);if(Array.isArray(result)){coordX=result[0];coordY=result[1];mouseX=(coordX-graph.range[0][0])*graph.scale[0];mouseY=(-coordY+graph.range[1][1])*graph.scale[1];const point=graph.unscalePoint([mouseX,mouseY]);movablePoint.visibleShape.moveTo(point);movablePoint.mouseTarget.moveTo(point);movablePoint.coord=[coordX,coordY];}}if(!movablePoint.highlight){movablePoint.visibleShape.animate(movablePoint.normalStyle,50);if(movablePoint.onUnhighlight){movablePoint.onUnhighlight();}}}});};if(movablePoint.visible&&!movablePoint.constraints.fixed){if(!movablePoint.mouseTarget){const radii=graph.unscaleVector(16);const options={mouselayer:true,padding:0,disableMouseEventsOnWrapper:true,interactiveKindForTesting:"movable-point"};movablePoint.mouseTarget=new WrappedEllipse(graph,movablePoint.coord,radii,options);movablePoint.mouseTarget.attr({fill:"#000",opacity:0});}const $mouseTarget=$(movablePoint.mouseTarget.getMouseTarget());$mouseTarget.css("cursor","move");$mouseTarget.on("vmousedown vmouseover vmouseout",function(event){if(event.type==="vmouseover"){movablePoint.highlight=true;if(!dragging){movablePoint.visibleShape.animate(movablePoint.highlightStyle,50);if(movablePoint.onHighlight){movablePoint.onHighlight();}}}else if(event.type==="vmouseout"){movablePoint.highlight=false;if(!movablePoint.dragging&&!dragging){movablePoint.visibleShape.animate(movablePoint.normalStyle,50);if(movablePoint.onUnhighlight){movablePoint.onUnhighlight();}}}else if(event.type==="vmousedown"&&(event.which===1||event.which===0)){event.preventDefault();const startCoord=movablePoint.coord;const startMouseCoord=graph.getMouseCoord(event);const isMouse=!("ontouchstart"in window);const touchOffset=isMouse?[0,0]:vector$3.subtract(startCoord,startMouseCoord);movablePoint.grab(touchOffset);}});}movablePoint.moveTo=function(coordX,coordY,updateLines){const distance=GraphUtils.getDistance(this.graph.scalePoint([coordX,coordY]),this.graph.scalePoint(this.coord));const time=distance*5;const cb=updateLines&&function(coord){movablePoint.coord=coord;movablePoint.updateLineEnds();};this.visibleShape.animateTo([coordX,coordY],time,cb);this.mouseTarget.animateTo([coordX,coordY],time,cb);this.coord=[coordX,coordY];if(typeof this.onMove==="function"){this.onMove(coordX,coordY);}};movablePoint.updateLineEnds=function(){$(this.lineStarts).each(function(){this.coordA=movablePoint.coord;this.transform();});$(this.lineEnds).each(function(){this.coordZ=movablePoint.coord;this.transform();});$(this.polygonVertices).each(function(){this.transform();});};movablePoint.setCoord=function(coord){if(this.visible){this.visibleShape.moveTo(coord);if(this.mouseTarget!=null){this.mouseTarget.moveTo(coord);}}this.coord=coord.slice();};movablePoint.setCoordConstrained=function(coord){this.setCoord(applySnapAndConstraints(coord));};movablePoint.toBack=function(){if(this.visible){if(this.mouseTarget!=null){this.mouseTarget.toBack();}this.visibleShape.toBack();}};movablePoint.toFront=function(){if(this.visible){if(this.mouseTarget!=null){this.mouseTarget.toFront();}this.visibleShape.toFront();}};movablePoint.remove=function(){if(this.visibleShape){this.visibleShape.remove();}if(this.mouseTarget){this.mouseTarget.remove();}if(this.labeledVertex){this.labeledVertex.remove();}};return movablePoint},addMovableLineSegment:function(options){const lineSegment=$.extend({graph:this,coordA:[0,0],coordZ:[1,1],snapX:0,snapY:0,fixed:false,ticks:0,normalStyle:{},highlightStyle:{stroke:tokenValue(semanticColor.core.border.instructive.default),"stroke-width":6},labelStyle:{stroke:KhanColors.INTERACTIVE,color:KhanColors.INTERACTIVE},highlight:false,dragging:false,tick:[],extendLine:false,extendRay:false,constraints:{fixed:false,constrainX:false,constrainY:false},sideLabel:"",vertexLabels:[],numArrows:0,numTicks:0,movePointsWithLine:false},options);const normalColor=lineSegment.fixed?KhanColors.DYNAMIC:KhanColors.INTERACTIVE;lineSegment.normalStyle={"stroke-width":2,stroke:normalColor,...options.normalStyle};lineSegment.arrowStyle={...lineSegment.normalStyle,color:lineSegment.normalStyle.stroke};if(options.pointA!==undefined){lineSegment.coordA=options.pointA.coord;lineSegment.pointA.lineStarts.push(lineSegment);}else if(options.coordA!==undefined){lineSegment.coordA=options.coordA.slice();}if(options.pointZ!==undefined){lineSegment.coordZ=options.pointZ.coord;lineSegment.pointZ.lineEnds.push(lineSegment);}else if(options.coordA!==undefined){lineSegment.coordA=lineSegment.coordA.slice();}const graph=lineSegment.graph;graph.style(lineSegment.normalStyle);for(let i=0;i<lineSegment.ticks;++i){lineSegment.tick[i]=InteractiveUtils.bogusShape;}let path=GraphUtils.unscaledSvgPath([[0,0],[1,0]]);for(let i=0;i<lineSegment.ticks;++i){const tickoffset=.5-(lineSegment.ticks-1+i*2)/graph.scale[0];path+=GraphUtils.unscaledSvgPath([[tickoffset,-7],[tickoffset,7]]);}options={thickness:Math.max(lineSegment.normalStyle["stroke-width"],lineSegment.highlightStyle["stroke-width"])};lineSegment.visibleLine=new WrappedLine(graph,[0,0],[1,0],options);lineSegment.visibleLine.attr(lineSegment.normalStyle);if(!lineSegment.fixed){const options={thickness:30,mouselayer:true,interactiveKindForTesting:"movable-line"};lineSegment.mouseTarget=new WrappedLine(graph,[0,0],[1,0],options);lineSegment.mouseTarget.attr({fill:"#000",opacity:0});}lineSegment.transform=function(syncToPoints){if(syncToPoints){if(typeof this.pointA==="object"){this.coordA=this.pointA.coord;}if(typeof this.pointZ==="object"){this.coordZ=this.pointZ.coord;}}const getScaledAngle=function(line){const scaledA=line.graph.scalePoint(line.coordA);const scaledZ=line.graph.scalePoint(line.coordZ);return vector$3.polarDegFromCart(vector$3.subtract(scaledZ,scaledA))[1]};const getClipPoint=function(graph,coord,angle){graph=lineSegment.graph;const xExtent=graph.range[0][1]-graph.range[0][0];const yExtent=graph.range[1][1]-graph.range[1][0];const distance=xExtent+yExtent;const angleVec=graph.unscaleVector(vector$3.cartFromPolarDeg(1,angle));const distVec=vector$3.scale(vector$3.normalize(angleVec),distance);const farCoord=vector$3.add(coord,distVec);const scaledAngle=vector$3.polarDegFromCart(angleVec)[1];const clipPoint=graph.constrainToBoundsOnAngle(farCoord,4,scaledAngle*Math.PI/180);return clipPoint};const angle=getScaledAngle(this);let start=this.coordA;let end=this.coordZ;if(this.extendLine){start=getClipPoint(graph,start,360-angle);end=getClipPoint(graph,end,(540-angle)%360);}else if(this.extendRay){end=getClipPoint(graph,start,360-angle);}const elements=[this.visibleLine];if(!this.fixed){elements.push(this.mouseTarget);}elements.forEach(function(element){element.moveTo(start,end);});if(this._arrows==null){this._arrows=[];if(this.extendLine){this._arrows.push(new Arrowhead$1(graph,this.normalStyle));this._arrows.push(new Arrowhead$1(graph,this.normalStyle));}else if(this.extendRay){this._arrows.push(new Arrowhead$1(graph,this.normalStyle));}}const coordForArrow=[this.coordA,this.coordZ];const angleForArrow=[360-angle,(540-angle)%360];this._arrows.forEach(function(arrow,i){arrow.toCoordAtAngle(coordForArrow[i],angleForArrow[i]);});_.invoke(this.temp,"remove");this.temp=[];const isClockwise=this.coordA[0]<this.coordZ[0]||this.coordA[0]===this.coordZ[0]&&this.coordA[1]>this.coordZ[1];if(this.sideLabel){this.temp.push(this.graph.labelSide({point1:this.coordA,point2:this.coordZ,label:this.labeledSide,text:this.sideLabel,numArrows:this.numArrows,numTicks:this.numTicks,clockwise:isClockwise,style:this.labelStyle}));}if(this.vertexLabels.length){this.graph.labelVertex({vertex:this.coordA,point3:this.coordZ,label:this.labeledVertices[0],text:this.vertexLabels[0],clockwise:isClockwise,style:this.labelStyle});this.graph.labelVertex({point1:this.coordA,vertex:this.coordZ,label:this.labeledVertices[1],text:this.vertexLabels[1],clockwise:isClockwise,style:this.labelStyle});}this.temp=this.temp.flat(Infinity);};lineSegment.toBack=function(){if(!lineSegment.fixed){lineSegment.mouseTarget.toBack();}lineSegment.visibleLine.toBack();};lineSegment.toFront=function(){if(!lineSegment.fixed){lineSegment.mouseTarget.toFront();}lineSegment.visibleLine.toFront();};lineSegment.remove=function(){if(!lineSegment.fixed){lineSegment.mouseTarget.remove();}lineSegment.visibleLine.remove();if(lineSegment.labeledSide){lineSegment.labeledSide.remove();}if(lineSegment.labeledVertices){_.invoke(lineSegment.labeledVertices,"remove");}if(lineSegment._arrows){_.invoke(lineSegment._arrows,"remove");}if(lineSegment.temp.length){_.invoke(lineSegment.temp,"remove");}};lineSegment.hide=function(){lineSegment.visibleLine.hide();if(lineSegment.temp.length){_.invoke(lineSegment.temp,"hide");}if(lineSegment._arrows){_.invoke(lineSegment._arrows,"hide");}};lineSegment.show=function(){lineSegment.visibleLine.show();if(lineSegment.temp.length){_.invoke(lineSegment.temp,"show");}if(lineSegment._arrows){_.invoke(lineSegment._arrows,"show");}};if(lineSegment.sideLabel){lineSegment.labeledSide=this.label([0,0],"","center",lineSegment.labelStyle);}if(lineSegment.vertexLabels.length){lineSegment.labeledVertices=lineSegment.vertexLabels.map(label=>{return this.label([0,0],"","center",lineSegment.labelStyle)});}if(!lineSegment.fixed&&!lineSegment.constraints.fixed){const $mouseTarget=$(lineSegment.mouseTarget.getMouseTarget());$mouseTarget.css("cursor","move");$mouseTarget.on("vmousedown vmouseover vmouseout",function(event){if(event.type==="vmouseover"){if(!dragging){lineSegment.highlight=true;lineSegment.visibleLine.animate(lineSegment.highlightStyle,50);lineSegment.arrowStyle={...lineSegment.arrowStyle,color:lineSegment.highlightStyle.stroke,stroke:lineSegment.highlightStyle.stroke};lineSegment.transform();}}else if(event.type==="vmouseout"){lineSegment.highlight=false;if(!lineSegment.dragging){lineSegment.visibleLine.animate(lineSegment.normalStyle,50);lineSegment.arrowStyle={...lineSegment.arrowStyle,color:lineSegment.normalStyle.stroke,stroke:lineSegment.normalStyle.stroke};lineSegment.transform();}}else if(event.type==="vmousedown"&&(event.which===1||event.which===0)){event.preventDefault();let coordX=(event.pageX-$(graph.raphael.canvas.parentNode).offset().left)/graph.scale[0]+graph.range[0][0];let coordY=graph.range[1][1]-(event.pageY-$(graph.raphael.canvas.parentNode).offset().top)/graph.scale[1];if(lineSegment.snapX>0){coordX=Math.round(coordX/lineSegment.snapX)*lineSegment.snapX;}if(lineSegment.snapY>0){coordY=Math.round(coordY/lineSegment.snapY)*lineSegment.snapY;}const mouseOffsetA=[lineSegment.coordA[0]-coordX,lineSegment.coordA[1]-coordY];const mouseOffsetZ=[lineSegment.coordZ[0]-coordX,lineSegment.coordZ[1]-coordY];const offsetLeft=-Math.min(graph.scaleVector(mouseOffsetA)[0],graph.scaleVector(mouseOffsetZ)[0]);const offsetRight=Math.max(graph.scaleVector(mouseOffsetA)[0],graph.scaleVector(mouseOffsetZ)[0]);const offsetTop=Math.max(graph.scaleVector(mouseOffsetA)[1],graph.scaleVector(mouseOffsetZ)[1]);const offsetBottom=-Math.min(graph.scaleVector(mouseOffsetA)[1],graph.scaleVector(mouseOffsetZ)[1]);$(document).on("vmousemove.lineSegment vmouseup.lineSegment",function(event){event.preventDefault();lineSegment.dragging=true;dragging=true;let mouseX=event.pageX-$(graph.raphael.canvas.parentNode).offset().left;let mouseY=event.pageY-$(graph.raphael.canvas.parentNode).offset().top;mouseX=Math.max(offsetLeft+10,Math.min(graph.xpixels-10-offsetRight,mouseX));mouseY=Math.max(offsetTop+10,Math.min(graph.ypixels-10-offsetBottom,mouseY));let coordX=mouseX/graph.scale[0]+graph.range[0][0];let coordY=graph.range[1][1]-mouseY/graph.scale[1];if(lineSegment.snapX>0){coordX=Math.round(coordX/lineSegment.snapX)*lineSegment.snapX;}if(lineSegment.snapY>0){coordY=Math.round(coordY/lineSegment.snapY)*lineSegment.snapY;}if(event.type==="vmousemove"){if(lineSegment.constraints.constrainX){coordX=lineSegment.coordA[0]-mouseOffsetA[0];}if(lineSegment.constraints.constrainY){coordY=lineSegment.coordA[1]-mouseOffsetA[1];}const dX=coordX+mouseOffsetA[0]-lineSegment.coordA[0];const dY=coordY+mouseOffsetA[1]-lineSegment.coordA[1];lineSegment.coordA=[coordX+mouseOffsetA[0],coordY+mouseOffsetA[1]];lineSegment.coordZ=[coordX+mouseOffsetZ[0],coordY+mouseOffsetZ[1]];lineSegment.transform();if(lineSegment.movePointsWithLine){if(typeof lineSegment.pointA==="object"){lineSegment.pointA.setCoord([lineSegment.pointA.coord[0]+dX,lineSegment.pointA.coord[1]+dY]);}if(typeof lineSegment.pointZ==="object"){lineSegment.pointZ.setCoord([lineSegment.pointZ.coord[0]+dX,lineSegment.pointZ.coord[1]+dY]);}}if(typeof lineSegment.onMove==="function"){lineSegment.onMove(dX,dY);}}else if(event.type==="vmouseup"){$(document).unbind(".lineSegment");lineSegment.dragging=false;dragging=false;if(!lineSegment.highlight){lineSegment.visibleLine.animate(lineSegment.normalStyle,50);lineSegment.arrowStyle={...lineSegment.arrowStyle,color:lineSegment.normalStyle.stroke,stroke:lineSegment.normalStyle.stroke};lineSegment.transform();}if(typeof lineSegment.onMoveEnd==="function"){lineSegment.onMoveEnd();}}$(lineSegment).trigger("move");});}});}if(lineSegment.pointA!==undefined){lineSegment.pointA.toFront();}if(lineSegment.pointZ!==undefined){lineSegment.pointZ.toFront();}lineSegment.transform();return lineSegment},addMovablePolygon:function(options){const graphie=this;const{points,...optionsWithoutPoints}=options;const polygon=$.extend({snapX:0,snapY:0,fixed:false,constrainToGraph:true,normalStyle:{},highlightStyle:{stroke:KhanColors.INTERACTING,"stroke-width":2,fill:KhanColors.INTERACTING,"fill-opacity":.05},pointHighlightStyle:{fill:KhanColors.INTERACTING,stroke:KhanColors.INTERACTING},labelStyle:{stroke:KhanColors.DYNAMIC,"stroke-width":1,color:KhanColors.DYNAMIC},angleLabels:[],showRightAngleMarkers:[],sideLabels:[],vertexLabels:[],numArcs:[],numArrows:[],numTicks:[],updateOnPointMove:true,closed:true},optionsWithoutPoints);const normalColor=polygon.fixed?KhanColors.DYNAMIC:KhanColors.INTERACTIVE;polygon.normalStyle={"stroke-width":2,"fill-opacity":0,fill:normalColor,stroke:normalColor,...options.normalStyle};polygon.points=options.points;const isPoint=function(coordOrPoint){return !Array.isArray(coordOrPoint)};polygon.update=function(){const n=polygon.points.length;polygon.coords=polygon.points.map(function(coordOrPoint,i){if(isPoint(coordOrPoint)){return coordOrPoint.coord}return coordOrPoint});polygon.left=Math.min(...polygon.coords.map(coord=>coord[0]));polygon.right=Math.max(...polygon.coords.map(coord=>coord[0]));polygon.top=Math.max(...polygon.coords.map(coord=>coord[1]));polygon.bottom=Math.min(...polygon.coords.map(coord=>coord[1]));let scaledCoords=polygon.coords.map(function(coord){return graphie.scalePoint(coord)});if(polygon.closed){scaledCoords.push(true);}else {scaledCoords=scaledCoords.concat([...scaledCoords].reverse());}polygon.path=GraphUtils.unscaledSvgPath(scaledCoords);_.invoke(polygon.temp,"remove");polygon.temp=[];const isClockwise=clockwise$3(polygon.coords);if(polygon.angleLabels.length||polygon.showRightAngleMarkers.length){polygon.labeledAngles.forEach(function(label,i){polygon.temp.push(graphie.labelAngle({point1:polygon.coords[(i-1+n)%n],vertex:polygon.coords[i],point3:polygon.coords[(i+1)%n],label:label,text:polygon.angleLabels[i],showRightAngleMarker:polygon.showRightAngleMarkers[i],numArcs:polygon.numArcs[i],clockwise:isClockwise,style:polygon.labelStyle}));});}if(polygon.sideLabels.length){polygon.labeledSides.forEach(function(label,i){polygon.temp.push(graphie.labelSide({point1:polygon.coords[i],point2:polygon.coords[(i+1)%n],label:label,text:polygon.sideLabels[i],numArrows:polygon.numArrows[i],numTicks:polygon.numTicks[i],clockwise:isClockwise,style:polygon.labelStyle}));});}if(polygon.vertexLabels.length){polygon.labeledVertices.forEach(function(label,i){graphie.labelVertex({point1:polygon.coords[(i-1+n)%n],vertex:polygon.coords[i],point3:polygon.coords[(i+1)%n],label:label,text:polygon.vertexLabels[i],clockwise:isClockwise,style:polygon.labelStyle});});}polygon.temp=polygon.temp.flat(Infinity);};polygon.transform=function(){polygon.update();polygon.visibleShape.attr({path:polygon.path});if(!polygon.fixed){polygon.mouseTarget.attr({path:polygon.path});}};polygon.remove=function(){polygon.visibleShape.remove();if(!polygon.fixed){polygon.mouseTarget.remove();}if(polygon.labeledAngles){_.invoke(polygon.labeledAngles,"remove");}if(polygon.labeledSides){_.invoke(polygon.labeledSides,"remove");}if(polygon.labeledVertices){_.invoke(polygon.labeledVertices,"remove");}if(polygon.temp.length){_.invoke(polygon.temp,"remove");}};polygon.toBack=function(){if(!polygon.fixed){polygon.mouseTarget.toBack();}polygon.visibleShape.toBack();};polygon.toFront=function(){if(!polygon.fixed){polygon.mouseTarget.toFront();}polygon.visibleShape.toFront();};if(polygon.updateOnPointMove){polygon.points.filter(isPoint).forEach(function(coordOrPoint){coordOrPoint.polygonVertices.push(polygon);});}polygon.coords=new Array(polygon.points.length);if(polygon.angleLabels.length){const numLabels=Math.max(polygon.angleLabels.length,polygon.showRightAngleMarkers.length);polygon.labeledAngles=Array.from({length:numLabels},()=>{return this.label([0,0],"","center",polygon.labelStyle)});}if(polygon.sideLabels.length){polygon.labeledSides=polygon.sideLabels.map(label=>{return this.label([0,0],"","center",polygon.labelStyle)});}if(polygon.vertexLabels.length){polygon.labeledVertices=polygon.vertexLabels.map(label=>{return this.label([0,0],"","center",polygon.labelStyle)});}polygon.update();polygon.visibleShape=graphie.raphael.path(polygon.path);polygon.visibleShape.attr(polygon.normalStyle);if(!polygon.fixed){polygon.mouseTarget=graphie.mouselayer.path(polygon.path);polygon.mouseTarget.attr({fill:"#000",opacity:0,cursor:"move"});$(polygon.mouseTarget[0]).on("vmousedown vmouseover vmouseout",function(event){if(event.type==="vmouseover"){if(!dragging||polygon.dragging){polygon.highlight=true;polygon.visibleShape.animate(polygon.highlightStyle,50);polygon.points.filter(isPoint).forEach(function(point){point.visibleShape.animate(polygon.pointHighlightStyle,50);});}}else if(event.type==="vmouseout"){polygon.highlight=false;if(!polygon.dragging){polygon.visibleShape.animate(polygon.normalStyle,50);const points=polygon.points.filter(isPoint);if(!points.some(point=>point.dragging)){points.forEach(function(point){point.visibleShape.animate(point.normalStyle,50);});}}}else if(event.type==="vmousedown"&&(event.which===1||event.which===0)){event.preventDefault();polygon.points.filter(isPoint).forEach(function(point){point.dragging=true;});let startX=(event.pageX-$(graphie.raphael.canvas.parentNode).offset().left)/graphie.scale[0]+graphie.range[0][0];let startY=graphie.range[1][1]-(event.pageY-$(graphie.raphael.canvas.parentNode).offset().top)/graphie.scale[1];if(polygon.snapX>0){startX=Math.round(startX/polygon.snapX)*polygon.snapX;}if(polygon.snapY>0){startY=Math.round(startY/polygon.snapY)*polygon.snapY;}let lastX=startX;let lastY=startY;const polygonCoords=polygon.coords.slice();const offsetLeft=(startX-polygon.left)*graphie.scale[0];const offsetRight=(polygon.right-startX)*graphie.scale[0];const offsetTop=(polygon.top-startY)*graphie.scale[1];const offsetBottom=(startY-polygon.bottom)*graphie.scale[1];$(document).on("vmousemove.polygon vmouseup.polygon",function(event){event.preventDefault();polygon.dragging=true;dragging=true;let mouseX=event.pageX-$(graphie.raphael.canvas.parentNode).offset().left;let mouseY=event.pageY-$(graphie.raphael.canvas.parentNode).offset().top;if(polygon.constrainToGraph){mouseX=Math.max(offsetLeft+10,Math.min(graphie.xpixels-10-offsetRight,mouseX));mouseY=Math.max(offsetTop+10,Math.min(graphie.ypixels-10-offsetBottom,mouseY));}let currentX=mouseX/graphie.scale[0]+graphie.range[0][0];let currentY=graphie.range[1][1]-mouseY/graphie.scale[1];if(polygon.snapX>0){currentX=Math.round(currentX/polygon.snapX)*polygon.snapX;}if(polygon.snapY>0){currentY=Math.round(currentY/polygon.snapY)*polygon.snapY;}if(event.type==="vmousemove"){let dX=currentX-startX;let dY=currentY-startY;let doMove=true;if(typeof polygon.onMove==="function"){const onMoveResult=polygon.onMove(dX,dY);if(onMoveResult===false){doMove=false;}else if(Array.isArray(onMoveResult)){dX=onMoveResult[0];dY=onMoveResult[1];currentX=startX+dX;currentY=startY+dY;}}const increment=function(i){return [polygonCoords[i][0]+dX,polygonCoords[i][1]+dY]};if(doMove){polygon.points.forEach(function(coordOrPoint,i){if(isPoint(coordOrPoint)){coordOrPoint.setCoord(increment(i));}else {polygon.points[i]=increment(i);}});polygon.transform();$(polygon).trigger("move");lastX=currentX;lastY=currentY;}}else if(event.type==="vmouseup"){$(document).unbind(".polygon");const points=polygon.points.filter(isPoint);points.forEach(function(point){point.dragging=false;});polygon.dragging=false;dragging=false;if(!polygon.highlight){polygon.visibleShape.animate(polygon.normalStyle,50);points.forEach(function(point){point.visibleShape.animate(point.normalStyle,50);});}if(typeof polygon.onMoveEnd==="function"){polygon.onMoveEnd(lastX-startX,lastY-startY);}}});}});}_.invoke(polygon.points.filter(isPoint),"toFront");return polygon},constrainToBounds:function(point,padding){const lower=this.unscalePoint([padding,this.ypixels-padding]);const upper=this.unscalePoint([this.xpixels-padding,padding]);const coordX=Math.max(lower[0],Math.min(upper[0],point[0]));const coordY=Math.max(lower[1],Math.min(upper[1],point[1]));return [coordX,coordY]},constrainToBoundsOnAngle:function(point,padding,angle){const lower=this.unscalePoint([padding,this.ypixels-padding]);const upper=this.unscalePoint([this.xpixels-padding,padding]);let result=point.slice();if(result[0]<lower[0]){result=[lower[0],result[1]+(lower[0]-result[0])*Math.tan(angle)];}else if(result[0]>upper[0]){result=[upper[0],result[1]-(result[0]-upper[0])*Math.tan(angle)];}if(result[1]<lower[1]){result=[result[0]+(lower[1]-result[1])/Math.tan(angle),lower[1]];}else if(result[1]>upper[1]){result=[result[0]-(result[1]-upper[1])/Math.tan(angle),upper[1]];}return result},addMovableAngle:function(options){return new MovableAngle(this,options)},addCircleGraph:function(options){const graphie=this;const circle=$.extend({center:[0,0],radius:2,snapX:.5,snapY:.5,snapRadius:.5,minRadius:1,centerConstraints:{},centerNormalStyle:{},centerHighlightStyle:{stroke:KhanColors.INTERACTING,fill:KhanColors.INTERACTING},circleNormalStyle:{stroke:KhanColors.INTERACTIVE,"fill-opacity":0},circleHighlightStyle:{stroke:KhanColors.INTERACTING,fill:KhanColors.INTERACTING,"fill-opacity":.05}},options);const normalColor=circle.centerConstraints.fixed?KhanColors.DYNAMIC:KhanColors.INTERACTIVE;const centerNormalStyle=options?options.centerNormalStyle:null;circle.centerNormalStyle={fill:normalColor,stroke:normalColor,...centerNormalStyle};circle.centerPoint=graphie.addMovablePoint({graph:graphie,coord:circle.center,normalStyle:circle.centerNormalStyle,snapX:circle.snapX,snapY:circle.snapY,constraints:circle.centerConstraints});circle.circ=graphie.circle(circle.center,circle.radius,circle.circleNormalStyle);circle.perim=graphie.mouselayer.circle(graphie.scalePoint(circle.center)[0],graphie.scalePoint(circle.center)[1],graphie.scaleVector(circle.radius)[0]).attr({"stroke-width":20,opacity:.002});if(!circle.centerConstraints.fixed){$(circle.centerPoint.mouseTarget.getMouseTarget()).on("vmouseover vmouseout",function(event){if(circle.centerPoint.highlight||circle.centerPoint.dragging){circle.circ.animate(circle.circleHighlightStyle,50);}else {circle.circ.animate(circle.circleNormalStyle,50);}});}circle.toFront=function(){circle.circ.toFront();circle.perim.toFront();circle.centerPoint.visibleShape.toFront();if(!circle.centerConstraints.fixed){circle.centerPoint.mouseTarget.toFront();}};circle.centerPoint.onMove=function(x,y){circle.toFront();circle.circ.attr({cx:graphie.scalePoint(x)[0],cy:graphie.scalePoint(y)[1]});circle.perim.attr({cx:graphie.scalePoint(x)[0],cy:graphie.scalePoint(y)[1]});if(circle.onMove){circle.onMove(x,y);}};$(circle.centerPoint).on("move",function(){circle.center=this.coord;$(circle).trigger("move");});circle.setCenter=function(x,y){circle.centerPoint.setCoord([x,y]);circle.centerPoint.onMove(x,y);circle.center=[x,y];};circle.setRadius=function(r){circle.radius=r;circle.perim.attr({r:graphie.scaleVector(r)[0]});circle.circ.attr({rx:graphie.scaleVector(r)[0],ry:graphie.scaleVector(r)[1]});};circle.remove=function(){circle.centerPoint.remove();circle.circ.remove();circle.perim.remove();};const cursorAxes=[[Math.PI*-1,Math.PI*-0.875,"ew-resize"],[Math.PI*-0.875,Math.PI*-0.625,"nesw-resize"],[Math.PI*-0.625,Math.PI*-0.375,"ns-resize"],[Math.PI*-0.375,Math.PI*-0.125,"nwse-resize"],[Math.PI*-0.125,Math.PI*.125,"ew-resize"],[Math.PI*.125,Math.PI*.375,"nesw-resize"],[Math.PI*.375,Math.PI*.625,"ns-resize"],[Math.PI*.625,Math.PI*.875,"nwse-resize"],[Math.PI*.875,Math.PI*1,"ew-resize"]];$(circle.perim.node).on("vmousemove",event=>{let[x,y]=this.getMouseCoord(event);x-=circle.center[0];y-=circle.center[1];const theta=Math.atan2(y,x);cursorAxes.forEach(function(axes){const[min,max,cursorName]=axes;if(theta>=min&&theta<max){$(circle.perim.node).css("cursor",cursorName);}});});$(circle.perim.node).css("cursor","nesw-resize");circle.perim.node.addEventListener("touchstart",function(event){event.preventDefault();},{passive:false});$(circle.perim.node).on("vmouseover vmouseout vmousedown",function(event){if(event.type==="vmouseover"){circle.highlight=true;if(!dragging){circle.circ.animate(circle.circleHighlightStyle,50);circle.centerPoint.visibleShape.animate(circle.centerHighlightStyle,50);}}else if(event.type==="vmouseout"){circle.highlight=false;if(!circle.dragging&&!circle.centerPoint.dragging){circle.circ.animate(circle.circleNormalStyle,50);circle.centerPoint.visibleShape.animate(circle.centerNormalStyle,50);}}else if(event.type==="vmousedown"&&(event.which===1||event.which===0)){event.preventDefault();circle.toFront();const startRadius=circle.radius;$(document).on("vmousemove vmouseup",function(event){event.preventDefault();circle.dragging=true;dragging=true;if(event.type==="vmousemove"){const coord=graphie.constrainToBounds(graphie.getMouseCoord(event),10);let radius=GraphUtils.getDistance(circle.centerPoint.coord,coord);radius=Math.max(circle.minRadius,Math.round(radius/circle.snapRadius)*circle.snapRadius);const oldRadius=circle.radius;let doResize=true;if(circle.onResize){const onResizeResult=circle.onResize(radius,oldRadius);if(typeof onResizeResult==="number"){radius=onResizeResult;}else if(onResizeResult===false){doResize=false;}}if(doResize){circle.setRadius(radius);$(circle).trigger("move");}}else if(event.type==="vmouseup"){$(document).off("vmousemove vmouseup");circle.dragging=false;dragging=false;if(circle.onResizeEnd){circle.onResizeEnd(circle.radius,startRadius);}}});}});return circle},addRotateHandle:function(){const drawRotateHandle=function(graphie,center,radius,halfWidth,lengthAngle,angle,interacting){const getRotateHandlePoint=function(offset,distanceFromArrowMidline){const distFromRotationCenter=radius+distanceFromArrowMidline;const vec=vector$3.cartFromPolarDeg(distFromRotationCenter,angle+offset);const absolute=vector$3.add(center,vec);const pixels=graphie.scalePoint(absolute);return pixels[0]+","+pixels[1]};const innerR=graphie.scaleVector(radius-halfWidth);const outerR=graphie.scaleVector(radius+halfWidth);return graphie.raphael.path(" M"+getRotateHandlePoint(lengthAngle,-halfWidth)+" L"+getRotateHandlePoint(lengthAngle,-3*halfWidth)+" L"+getRotateHandlePoint(2*lengthAngle,0)+" L"+getRotateHandlePoint(lengthAngle,3*halfWidth)+" L"+getRotateHandlePoint(lengthAngle,halfWidth)+" A"+outerR[0]+","+outerR[1]+",0,0,1,"+getRotateHandlePoint(-lengthAngle,halfWidth)+" L"+getRotateHandlePoint(-lengthAngle,3*halfWidth)+" L"+getRotateHandlePoint(-2*lengthAngle,0)+" L"+getRotateHandlePoint(-lengthAngle,-3*halfWidth)+" L"+getRotateHandlePoint(-lengthAngle,-halfWidth)+" A"+innerR[0]+","+innerR[1]+",0,0,0,"+getRotateHandlePoint(lengthAngle,-halfWidth)+" Z").attr({stroke:null,fill:interacting?KhanColors.INTERACTING:KhanColors.INTERACTIVE})};return function(options){const graph=this;let rotatePoint=options.center;const radius=options.radius;const lengthAngle=options.lengthAngle||30;const hideArrow=options.hideArrow||false;const mouseTarget=options.mouseTarget;const id=_.uniqueId("rotateHandle");if(Array.isArray(rotatePoint)){rotatePoint={coord:rotatePoint};}const rotateHandle=graph.addMovablePoint({coord:point.addVector(rotatePoint.coord,vector$3.cartFromPolarDeg(radius,options.angleDeg||0)),constraints:{fixedDistance:{dist:radius,point:rotatePoint}},mouseTarget:mouseTarget});rotatePoint.toFront();let rotatePointPrevCoord=rotatePoint.coord;let rotateHandlePrevCoord=rotateHandle.coord;let rotateHandleStartCoord=rotateHandlePrevCoord;let isRotating=false;let isHovering=false;let drawnRotateHandle;const redrawRotateHandle=function(handleCoord){if(hideArrow){return}const handleVec=vector$3.subtract(handleCoord,rotatePoint.coord);const handlePolar=vector$3.polarDegFromCart(handleVec);const angle=handlePolar[1];if(drawnRotateHandle){drawnRotateHandle.remove();}drawnRotateHandle=drawRotateHandle(graph,rotatePoint.coord,options.radius,isRotating||isHovering?options.hoverWidth/2:options.width/2,lengthAngle,angle,isRotating||isHovering);};$(rotatePoint).on("move."+id,function(){const delta=vector$3.subtract(rotatePoint.coord,rotatePointPrevCoord);rotateHandle.setCoord(vector$3.add(rotateHandle.coord,delta));redrawRotateHandle(rotateHandle.coord);rotatePointPrevCoord=rotatePoint.coord;rotateHandle.constraints.fixedDistance.point=rotatePoint;rotateHandlePrevCoord=rotateHandle.coord;});rotateHandle.onMove=function(x,y){if(!isRotating){rotateHandleStartCoord=rotateHandlePrevCoord;isRotating=true;}let coord=[x,y];if(options.onMove){const oldPolar=vector$3.polarDegFromCart(vector$3.subtract(rotateHandlePrevCoord,rotatePoint.coord));const newPolar=vector$3.polarDegFromCart(vector$3.subtract(coord,rotatePoint.coord));const oldAngle=oldPolar[1];const newAngle=newPolar[1];let result=options.onMove(newAngle,oldAngle);if(result!=null&&result!==true){if(result===false){result=oldAngle;}coord=vector$3.add(rotatePoint.coord,vector$3.cartFromPolarDeg(oldPolar[0],result));}}redrawRotateHandle(coord);rotateHandlePrevCoord=coord;return coord};rotateHandle.onMoveEnd=function(){isRotating=false;redrawRotateHandle(rotateHandle.coord);if(options.onMoveEnd){const oldPolar=vector$3.polarDegFromCart(vector$3.subtract(rotateHandleStartCoord,rotatePoint.coord));const newPolar=vector$3.polarDegFromCart(vector$3.subtract(rotateHandle.coord,rotatePoint.coord));options.onMoveEnd(newPolar[1],oldPolar[1]);}};rotateHandle.visibleShape.remove();if(!mouseTarget){rotateHandle.mouseTarget.attr({scale:2});}const $mouseTarget=$(rotateHandle.mouseTarget.getMouseTarget());$mouseTarget.on("vmouseover",function(e){isHovering=true;redrawRotateHandle(rotateHandle.coord);});$mouseTarget.on("vmouseout",function(e){isHovering=false;redrawRotateHandle(rotateHandle.coord);});redrawRotateHandle(rotateHandle.coord);const oldRemove=rotateHandle.remove;rotateHandle.remove=function(){oldRemove.call(rotateHandle);if(drawnRotateHandle){drawnRotateHandle.remove();}$(rotatePoint).off("move."+id);};rotateHandle.update=function(){redrawRotateHandle(rotateHandle.coord);};return rotateHandle}}(),addReflectButton:function(){const drawButton=function(graphie,buttonCoord,lineCoords,size,distanceFromCenter,leftStyle,rightStyle){if(point.equal(lineCoords[0],lineCoords[1])){lineCoords=[lineCoords[0],point.addVector(lineCoords[0],[1,1])];}const lineDirection=vector$3.normalize(vector$3.subtract(lineCoords[1],lineCoords[0]));const lineVec=vector$3.scale(lineDirection,size/2);const centerVec=vector$3.scale(lineDirection,distanceFromCenter);const leftCenterVec=vector$3.rotateDeg(centerVec,90);const rightCenterVec=vector$3.rotateDeg(centerVec,-90);const negLineVec=vector$3.negate(lineVec);const leftVec=vector$3.rotateDeg(lineVec,90);const rightVec=vector$3.rotateDeg(lineVec,-90);const leftCenter=point.addVectors(buttonCoord,leftCenterVec);const rightCenter=point.addVectors(buttonCoord,rightCenterVec);const leftCoord1=point.addVectors(buttonCoord,leftCenterVec,lineVec,leftVec);const leftCoord2=point.addVectors(buttonCoord,leftCenterVec,negLineVec,leftVec);const rightCoord1=point.addVectors(buttonCoord,rightCenterVec,lineVec,rightVec);const rightCoord2=point.addVectors(buttonCoord,rightCenterVec,negLineVec,rightVec);const leftButton=graphie.path([leftCenter,leftCoord1,leftCoord2,true],leftStyle);const rightButton=graphie.path([rightCenter,rightCoord1,rightCoord2,true],rightStyle);return {remove:function(){leftButton.remove();rightButton.remove();}}};return function(options){const graphie=this;const line$1=options.line;const button=graphie.addMovablePoint({constraints:options.constraints,coord:line.midpoint([line$1.pointA.coord,line$1.pointZ.coord]),snapX:graphie.snap[0],snapY:graphie.snap[1],onMove:function(x,y){return false},onMoveEnd:function(x,y){if(options.onMoveEnd){options.onMoveEnd.call(this,x,y);}}});let isHovering=false;let isFlipped=false;let currentlyDrawnButton;const isHighlight=function(){return isHovering};const styles=[0,1].map(function(isHighlight){const baseStyle=isHighlight?options.highlightStyle:options.normalStyle;return [0,1].map(function(opacity){return _.defaults({"fill-opacity":opacity},baseStyle)})});const getStyle=function(isRight){if(isFlipped){isRight=!isRight;}return styles[+isHighlight()][+isRight]};const redraw=function(coord,lineCoords){if(currentlyDrawnButton){currentlyDrawnButton.remove();}currentlyDrawnButton=drawButton(graphie,coord,lineCoords,isHighlight()?options.size*1.5:options.size,isHighlight()?options.size*.125:.25,getStyle(0),getStyle(1));};const update=function(coordA,coordZ){coordA=coordA||line$1.pointA.coord;coordZ=coordZ||line$1.pointZ.coord;const buttonCoord=line.midpoint([coordA,coordZ]);button.setCoord(buttonCoord);redraw(buttonCoord,[coordA,coordZ]);};$(line$1).on("move",()=>update(null,null));const $mouseTarget=$(button.mouseTarget.getMouseTarget());$mouseTarget.on("vclick",function(){const result=options.onClick();if(result!==false){isFlipped=!isFlipped;redraw(button.coord,[line$1.pointA.coord,line$1.pointZ.coord]);}});line$1.pointA.toFront();line$1.pointZ.toFront();button.visibleShape.remove();const pointScale=graphie.scaleVector(options.size)[0]/20;button.mouseTarget.attr({scale:1.5*pointScale});$mouseTarget.css("cursor","pointer");$mouseTarget.on("vmouseover",function(e){isHovering=true;redraw(button.coord,[line$1.pointA.coord,line$1.pointZ.coord]);});$mouseTarget.on("vmouseout",function(e){isHovering=false;redraw(button.coord,[line$1.pointA.coord,line$1.pointZ.coord]);});const oldButtonRemove=button.remove;button.remove=function(){currentlyDrawnButton.remove();oldButtonRemove.call(button);};button.update=update;button.isFlipped=function(){return isFlipped};update();return button}}(),protractor:function(center){return new Protractor$1(this,center)},ruler:function(options){return new Ruler(this,options||{})},addPoints:vector$3.add});function Protractor$1(graph,center){this.set=graph.raphael.set();this.cx=center[0];this.cy=center[1];const pro=this;const r=graph.unscaleVector(180.5)[0];const imgPos=graph.scalePoint([this.cx-r,this.cy+r-graph.unscaleVector(10.5)[1]]);const image=graph.mouselayer.image("https://ka-perseus-graphie.s3.amazonaws.com/e9d032f2ab8b95979f674fbfa67056442ba1ff6a.png",imgPos[0],imgPos[1],360,180);this.set.push(image);image.node.addEventListener("touchstart",function(event){event.preventDefault();},{passive:false});const arrowHelper=function(angle,pixelsFromEdge){const scaledRadius=graph.scaleVector(r);scaledRadius[0]-=16;scaledRadius[1]-=16;const scaledCenter=graph.scalePoint(center);const x=Math.sin((angle+90)*Math.PI/180)*(scaledRadius[0]+pixelsFromEdge)+scaledCenter[0];const y=Math.cos((angle+90)*Math.PI/180)*(scaledRadius[1]+pixelsFromEdge)+scaledCenter[1];return x+","+y};const arrow=graph.raphael.path(" M"+arrowHelper(180,6)+" L"+arrowHelper(180,2)+" L"+arrowHelper(183,10)+" L"+arrowHelper(180,18)+" L"+arrowHelper(180,14)+" A"+(graph.scaleVector(r)[0]+10)+","+(graph.scaleVector(r)[1]+10)+",0,0,1,"+arrowHelper(170,14)+" L"+arrowHelper(170,18)+" L"+arrowHelper(167,10)+" L"+arrowHelper(170,2)+" L"+arrowHelper(170,6)+" A"+(graph.scaleVector(r)[0]+10)+","+(graph.scaleVector(r)[1]+10)+",0,0,0,"+arrowHelper(180,6)+" Z").attr({stroke:null,fill:KhanColors.INTERACTIVE});this.set.push(arrow);this.centerPoint=graph.addMovablePoint({coord:center,visible:false});this.rotateHandle=graph.addMovablePoint({bounded:false,coord:[Math.sin(275*Math.PI/180)*r+this.cx,Math.cos(275*Math.PI/180)*r+this.cy],onMove:function(x,y){const angle=Math.atan2(pro.centerPoint.coord[1]-y,pro.centerPoint.coord[0]-x)*180/Math.PI;pro.rotate(-angle-5,true);}});this.rotateHandle.constraints.fixedDistance.dist=r;this.rotateHandle.constraints.fixedDistance.point=this.centerPoint;this.rotateHandle.visibleShape.remove();this.rotateHandle.mouseTarget.attr({scale:2});let isDragging=false;let isHovering=false;const isHighlight=function(){return isHovering||isDragging};const self=this;const $mouseTarget=$(self.rotateHandle.mouseTarget.getMouseTarget());$mouseTarget.on("vmousedown",function(event){isDragging=true;$mouseTarget.css("cursor","-webkit-grabbing");$mouseTarget.css("cursor","grabbing");arrow.animate({scale:1.5,fill:KhanColors.INTERACTING},50);$(document).on("vmouseup.rotateHandle",function(event){isDragging=false;$mouseTarget.css("cursor","-webkit-grab");$mouseTarget.css("cursor","grab");if(!isHighlight()){arrow.animate({scale:1,fill:KhanColors.INTERACTIVE},50);}$(document).unbind("vmouseup.rotateHandle");});});$mouseTarget.on("vmouseover",function(event){isHovering=true;arrow.animate({scale:1.5,fill:KhanColors.INTERACTING},50);});$mouseTarget.on("vmouseout",function(event){isHovering=false;if(!isHighlight()){arrow.animate({scale:1,fill:KhanColors.INTERACTIVE},50);}});const setNodes=$.map(this.set,function(el){return el.node});this.makeTranslatable=function makeTranslatable(){$(setNodes).css("cursor","move");$mouseTarget.css("cursor","-webkit-grab");$mouseTarget.css("cursor","grab");$(setNodes).on("vmousedown",function(event){event.preventDefault();let startx=event.pageX-$(graph.raphael.canvas.parentNode).offset().left;let starty=event.pageY-$(graph.raphael.canvas.parentNode).offset().top;$(document).on("vmousemove.protractor",function(event){let mouseX=event.pageX-$(graph.raphael.canvas.parentNode).offset().left;let mouseY=event.pageY-$(graph.raphael.canvas.parentNode).offset().top;mouseX=Math.max(10,Math.min(graph.xpixels-10,mouseX));mouseY=Math.max(10,Math.min(graph.ypixels-10,mouseY));const dx=mouseX-startx;const dy=mouseY-starty;$.each(pro.set.items,function(){this.translate(dx,dy);});pro.centerPoint.setCoord([pro.centerPoint.coord[0]+dx/graph.scale[0],pro.centerPoint.coord[1]-dy/graph.scale[1]]);pro.rotateHandle.setCoord([pro.rotateHandle.coord[0]+dx/graph.scale[0],pro.rotateHandle.coord[1]-dy/graph.scale[1]]);startx=mouseX;starty=mouseY;});$(document).one("vmouseup",function(event){$(document).unbind("vmousemove.protractor");});});};this.rotation=0;this.rotate=function(offset,absolute){const center=graph.scalePoint(this.centerPoint.coord);if(absolute){this.rotation=0;}this.set.rotate(this.rotation+offset,center[0],center[1]);this.rotation=this.rotation+offset;return this};this.moveTo=function moveTo(x,y){const start=graph.scalePoint(pro.centerPoint.coord);const end=graph.scalePoint([x,y]);const time=GraphUtils.getDistance(start,end)*2;$({x:start[0],y:start[1]}).animate({x:end[0],y:end[1]},{duration:time,step:function(now,fx){let dx=0;let dy=0;if(fx.prop==="x"){dx=now-graph.scalePoint(pro.centerPoint.coord)[0];}else if(fx.prop==="y"){dy=now-graph.scalePoint(pro.centerPoint.coord)[1];}$.each(pro.set.items,function(){this.translate(dx,dy);});pro.centerPoint.setCoord([pro.centerPoint.coord[0]+dx/graph.scale[0],pro.centerPoint.coord[1]-dy/graph.scale[1]]);pro.rotateHandle.setCoord([pro.rotateHandle.coord[0]+dx/graph.scale[0],pro.rotateHandle.coord[1]-dy/graph.scale[1]]);}});};this.rotateTo=function rotateTo(angle){if(Math.abs(this.rotation-angle)>180){this.rotation+=360;}const time=Math.abs(this.rotation-angle)*5;$({0:this.rotation}).animate({0:angle},{duration:time,step:function(now,fx){pro.rotate(now,true);pro.rotateHandle.setCoord([Math.sin((now+275)*Math.PI/180)*r+pro.centerPoint.coord[0],Math.cos((now+275)*Math.PI/180)*r+pro.centerPoint.coord[1]]);}});};this.remove=function(){this.set.remove();};this.makeTranslatable();return this}function Ruler(graphie,options){_.defaults(options,{center:[0,0],pixelsPerUnit:40,ticksPerUnit:10,units:10,label:"",style:{fill:null,stroke:KhanColors.GRAY}});const light={...options.style,strokeWidth:1};const bold={...options.style,strokeWidth:2};const width=options.units*options.pixelsPerUnit;const height=50;const leftBottom=graphie.unscalePoint(vector$3.subtract(graphie.scalePoint(options.center),vector$3.scale([width,-50],.5)));const graphieUnitsPerUnit=options.pixelsPerUnit/graphie.scale[0];const graphieUnitsHeight=height/graphie.scale[0];const rightTop=vector$3.add(leftBottom,[options.units*graphieUnitsPerUnit,graphieUnitsHeight]);const tickHeight=1;let tickHeightMap;if(options.ticksPerUnit===10){tickHeightMap={10:tickHeight,5:tickHeight*.55,1:tickHeight*.35};}else {const sizes=[1,.6,.45,.3];tickHeightMap={};for(let i=options.ticksPerUnit;i>=1;i/=2){tickHeightMap[i]=tickHeight*(sizes.shift()||.2);}}const tickFrequencies=Object.keys(tickHeightMap).sort(function(a,b){return b-a});function getTickHeight(i){for(let k=0;k<tickFrequencies.length;k++){const key=tickFrequencies[k];if(i%key===0){return tickHeightMap[key]}}return 0}const left=leftBottom[0];const bottom=leftBottom[1];const right=rightTop[0];const top=rightTop[1];const numTicks=options.units*options.ticksPerUnit+1;const set=graphie.raphael.set();const px=1/graphie.scale[0];set.push(graphie.line([left-px,bottom],[right+px,bottom],bold));set.push(graphie.line([left-px,top],[right+px,top],bold));for(let i=0;i<numTicks;i++){const n=i/options.ticksPerUnit;const x=left+n*graphieUnitsPerUnit;const height=getTickHeight(i)*graphieUnitsHeight;const style=i===0||i===numTicks-1?bold:light;set.push(graphie.line([x,bottom],[x,bottom+height],style));if(n%1===0){const coord=graphie.scalePoint([x,top]);let text;let offset;if(n===0){text=options.label;offset=({mm:13,cm:11,m:8,km:11,in:8,ft:8,yd:10,mi:10})[text]||3*text.toString().length;}else {text=n;offset=-3*(n.toString().length+1);}const label=graphie.raphael.text(coord[0]+offset,coord[1]+10,text);label.attr({"font-family":"KaTeX_Main","font-size":"12px",color:"#444"});set.push(label);}}const mouseTarget=graphie.mouselayer.path(graphie.svgPath([leftBottom,[left,top],rightTop,[right,bottom],true]));mouseTarget.attr({fill:"#000",opacity:0,stroke:"#000","stroke-width":2});set.push(mouseTarget);mouseTarget.node.addEventListener("touchstart",function(event){event.preventDefault();},{passive:false});const setNodes=$.map(set,function(el){return el.node});$(setNodes).css("cursor","move");$(setNodes).on("vmousedown",function(event){event.preventDefault();let startx=event.pageX-$(graphie.raphael.canvas.parentNode).offset().left;let starty=event.pageY-$(graphie.raphael.canvas.parentNode).offset().top;$(document).on("vmousemove.ruler",function(event){let mouseX=event.pageX-$(graphie.raphael.canvas.parentNode).offset().left;let mouseY=event.pageY-$(graphie.raphael.canvas.parentNode).offset().top;mouseX=Math.max(10,Math.min(graphie.xpixels-10,mouseX));mouseY=Math.max(10,Math.min(graphie.ypixels-10,mouseY));const dx=mouseX-startx;const dy=mouseY-starty;set.translate(dx,dy);leftBottomHandle.setCoord([leftBottomHandle.coord[0]+dx/graphie.scale[0],leftBottomHandle.coord[1]-dy/graphie.scale[1]]);rightBottomHandle.setCoord([rightBottomHandle.coord[0]+dx/graphie.scale[0],rightBottomHandle.coord[1]-dy/graphie.scale[1]]);startx=mouseX;starty=mouseY;});$(document).one("vmouseup",function(event){$(document).unbind("vmousemove.ruler");});});const leftBottomHandle=graphie.addMovablePoint({coord:leftBottom,normalStyle:{fill:KhanColors.INTERACTIVE,"fill-opacity":0,stroke:KhanColors.INTERACTIVE},highlightStyle:{fill:KhanColors.INTERACTING,"fill-opacity":.1,stroke:KhanColors.INTERACTING},pointSize:6,onMove:function(x,y){const dy=rightBottomHandle.coord[1]-y;const dx=rightBottomHandle.coord[0]-x;const angle=Math.atan2(dy,dx)*180/Math.PI;const center=vector$3.scale(vector$3.add([x,y],rightBottomHandle.coord),.5);const scaledCenter=graphie.scalePoint(center);const oldCenter=vector$3.scale(vector$3.add(leftBottomHandle.coord,rightBottomHandle.coord),.5);const scaledOldCenter=graphie.scalePoint(oldCenter);const diff=vector$3.subtract(scaledCenter,scaledOldCenter);set.rotate(-angle,scaledOldCenter[0],scaledOldCenter[1]);set.translate(diff[0],diff[1]);}});const rightBottomHandle=graphie.addMovablePoint({coord:[right,bottom],normalStyle:{fill:KhanColors.INTERACTIVE,"fill-opacity":0,stroke:KhanColors.INTERACTIVE},highlightStyle:{fill:KhanColors.INTERACTING,"fill-opacity":.1,stroke:KhanColors.INTERACTING},pointSize:6,onMove:function(x,y){const dy=y-leftBottomHandle.coord[1];const dx=x-leftBottomHandle.coord[0];const angle=Math.atan2(dy,dx)*180/Math.PI;const center=vector$3.scale(vector$3.add([x,y],leftBottomHandle.coord),.5);const scaledCenter=graphie.scalePoint(center);const oldCenter=vector$3.scale(vector$3.add(leftBottomHandle.coord,rightBottomHandle.coord),.5);const scaledOldCenter=graphie.scalePoint(oldCenter);const diff=vector$3.subtract(scaledCenter,scaledOldCenter);set.rotate(-angle,scaledOldCenter[0],scaledOldCenter[1]);set.translate(diff[0],diff[1]);}});leftBottomHandle.constraints.fixedDistance.dist=width/graphie.scale[0];leftBottomHandle.constraints.fixedDistance.point=rightBottomHandle;rightBottomHandle.constraints.fixedDistance.dist=width/graphie.scale[0];rightBottomHandle.constraints.fixedDistance.point=leftBottomHandle;this.remove=function(){set.remove();leftBottomHandle.remove();rightBottomHandle.remove();};return this}function MovableAngle(graphie,options){this.graphie=graphie;Object.assign(this,options);_.defaults(this,{normalStyle:{stroke:KhanColors.INTERACTIVE,"stroke-width":2,fill:KhanColors.INTERACTIVE},highlightStyle:{stroke:KhanColors.INTERACTING,"stroke-width":2,fill:KhanColors.INTERACTING},labelStyle:{stroke:KhanColors.DYNAMIC,"stroke-width":1,color:KhanColors.DYNAMIC},angleStyle:{stroke:KhanColors.DYNAMIC,"stroke-width":1,color:KhanColors.DYNAMIC},allowReflex:true});if(!this.points||this.points.length!==3){throw new PerseusError("MovableAngle requires 3 points",Errors.InvalidInput)}this.points=options.points.map(point=>{if(Array.isArray(point)){return graphie.addMovablePoint({coord:point,visible:false,constraints:{fixed:true},normalStyle:this.normalStyle})}return point});this.coords=this.points.map(point=>point.coord);if(this.reflex==null){if(this.allowReflex){this.reflex=this._getClockwiseAngle(this.coords)>180;}else {this.reflex=false;}}this.rays=[0,2].map(i=>{return graphie.addMovableLineSegment({pointA:this.points[1],pointZ:this.points[i],fixed:true,extendRay:true})});this.temp=[];this.labeledAngle=graphie.label([0,0],"","center",this.labelStyle);if(!this.fixed){this.addMoveHandlers();this.addHighlightHandlers();}this.update();}_.extend(MovableAngle.prototype,{points:[],snapDegrees:0,snapOffsetDeg:0,angleLabel:"",numArcs:1,pushOut:0,fixed:false,addMoveHandlers:function(){const graphie=this.graphie;function tooClose(point1,point2){const safeDistance=30;const distance=GraphUtils.getDistance(graphie.scalePoint(point1),graphie.scalePoint(point2));return distance<safeDistance}const points=this.points;points[1].onMove=function(x,y){const oldVertex=points[1].coord;const newVertex=[x,y];const delta=vector$3.add(newVertex,reverseVector$1(oldVertex));let valid=true;const newPoints={};[0,2].forEach(function(i){const oldPoint=points[i].coord;let newPoint=vector$3.add(oldPoint,delta);let angle=GraphUtils.findAngleDeprecated(newVertex,newPoint);angle*=Math.PI/180;newPoint=graphie.constrainToBoundsOnAngle(newPoint,10,angle);newPoints[i]=newPoint;if(tooClose(newVertex,newPoint)){valid=false;}});if(valid){Object.entries(newPoints).forEach(function([i,newPoint]){points[i].setCoord(newPoint);});}return valid};const snap=this.snapDegrees;const snapOffset=this.snapOffsetDeg;[0,2].forEach(function(i){points[i].onMove=function(x,y){const newPoint=[x,y];const vertex=points[1].coord;if(tooClose(vertex,newPoint)){return false}if(snap){let angle=GraphUtils.findAngleDeprecated(newPoint,vertex);angle=Math.round((angle-snapOffset)/snap)*snap+snapOffset;const distance=GraphUtils.getDistance(newPoint,vertex);return vector$3.add(vertex,polar$2(distance,angle))}return true};});$(points).on("move",()=>{this.update();$(this).trigger("move");});},addHighlightHandlers:function(){const vertex=this.points[1];vertex.onHighlight=()=>{this.points.forEach(point=>{point.visibleShape.animate(this.highlightStyle,50);});this.rays.forEach(ray=>{ray.visibleLine.animate(this.highlightStyle,50);ray.arrowStyle={...ray.arrowStyle,color:this.highlightStyle.stroke,stroke:this.highlightStyle.stroke};});this.angleStyle={...this.angleStyle,color:this.highlightStyle.stroke,stroke:this.highlightStyle.stroke};this.update();};vertex.onUnhighlight=()=>{this.points.forEach(point=>{point.visibleShape.animate(this.normalStyle,50);});this.rays.forEach(ray=>{ray.visibleLine.animate(ray.normalStyle,50);ray.arrowStyle={...ray.arrowStyle,color:ray.normalStyle.stroke,stroke:ray.normalStyle.stroke};});this.angleStyle={...this.angleStyle,color:KhanColors.DYNAMIC,stroke:KhanColors.DYNAMIC};this.update();};},_getClockwiseAngle:function(coords){const rawAngle=GraphUtils.findAngleDeprecated(coords[2],coords[0],coords[1]);const clockwiseAngle=rawAngle+360%360;return clockwiseAngle},isReflex:function(){return this.reflex},isClockwise:function(){const clockwiseReflexive=this._getClockwiseAngle(this.coords)>180;return clockwiseReflexive===this.reflex},getClockwiseCoords:function(){if(this.isClockwise()){return [...this.coords]}return [...this.coords].reverse()},update:function(shouldChangeReflexivity){const prevCoords=this.coords;this.coords=this.points.map(point=>point.coord);_.invoke(this.points,"updateLineEnds");const prevAngle=this._getClockwiseAngle(prevCoords);const angle=this._getClockwiseAngle(this.coords);const prevClockwiseReflexive=prevAngle>180;const clockwiseReflexive=angle>180;if(this.allowReflex){if(shouldChangeReflexivity==null){shouldChangeReflexivity=prevClockwiseReflexive!==clockwiseReflexive&&Math.abs(angle-prevAngle)<180;}if(shouldChangeReflexivity){this.reflex=!this.reflex;}}_.invoke(this.temp,"remove");this.temp=this.graphie.labelAngle({point1:this.coords[0],vertex:this.coords[1],point3:this.coords[2],label:this.labeledAngle,text:this.angleLabel,numArcs:this.numArcs,pushOut:this.pushOut,clockwise:this.reflex===clockwiseReflexive,style:this.angleStyle});},remove:function(){_.invoke(this.rays,"remove");_.invoke(this.temp,"remove");this.labeledAngle.remove();}});
1574
1574
 
1575
1575
  const nestedMap$1=Util.nestedMap;function GraphieMovable$1(descriptor){_.extend(this,descriptor);}const abstractMethod=function(){throw new PerseusError("Abstract method! Must be implemented by Graphie Movable"+this.constructor.displayName,Errors.NotAllowed)};_.extend(GraphieMovable$1.prototype,{movableProps:[],add:abstractMethod,modify:abstractMethod,remove:abstractMethod,toFront:function(){},componentDidMount:function(){}});const rewriteProps=function(props,childrenArray){return _.extend({},props,{children:_.filter(_.flatten(childrenArray),_.identity)})};const createClass=function(spec){const GraphieClass=function(props){if(!(this instanceof GraphieClass)){throw new PerseusError("Use createElement or JSX with graphie movables",Errors.NotAllowed)}this.props=rewriteProps(props,props.children||[]);return this};spec.displayName=spec.displayName||_.uniqueId("GraphieClass");GraphieClass.displayName=spec.displayName;GraphieClass.prototype=new GraphieMovable$1(spec);GraphieClass.prototype.constructor=GraphieClass;return GraphieClass};const createSimpleClass=function(addFunction){return createClass({displayName:addFunction.name||_.uniqueId("GraphieSimpleClass"),movableProps:["children"],add:function(graphie){this._elements=addFunction(graphie,this.props);this._prevProps=this.props;},modify:function(graphie){if(!approximateDeepEqual(this.props,this._prevProps)){this.remove();this.add(graphie);this._prevProps=this.props;return "reordered"}},remove:function(){nestedMap$1(this._elements,elem=>{if(elem){elem.remove();}});this._elements=null;this._prevProps=null;},toFront:function(){nestedMap$1(this._elements,elem=>{if(elem&&typeof elem.toFront==="function"){elem.toFront();}});}})};var GraphieClasses = {GraphieMovable:GraphieMovable$1,createClass:createClass,createSimpleClass:createSimpleClass};
1576
1576
 
@@ -1592,11 +1592,11 @@ function sum(array){return _.reduce(array,function(memo,arg){return memo+arg},0)
1592
1592
 
1593
1593
  const assert$3=InteractiveUtil.assert;const normalizeOptions=InteractiveUtil.normalizeOptions;const DEFAULT_PROPS={points:null,angleLabels:[],showRightAngleMarkers:[],sideLabels:[],vertexLabels:[],numArcs:[],numArrows:[],numTicks:[],closed:true,static:false,cursor:"move",normalStyle:null,highlightStyle:null,labelStyle:null};const DEFAULT_STATE={added:false,hasMoved:false,visibleShape:null,mouseTarget:null};const MovablePolygon=function(graphie,movable,options){assert$3(graphie!=null);assert$3(options!=null);_.extend(this,{graphie:graphie,movable:movable,state:{id:_.uniqueId("movablePolygon")}});this.modify(_.extend({},DEFAULT_STATE,options));};_.extend(MovablePolygon,MovablePolygonOptions);InteractiveUtil.createGettersFor(MovablePolygon,_.extend({},DEFAULT_PROPS,DEFAULT_STATE));InteractiveUtil.addMovableHelperMethodsTo(MovablePolygon);_.extend(MovablePolygon.prototype,{cloneState:function(){return _.extend(this.movable.cloneState(),this.state)},_createDefaultState:function(){return _.extend({id:this.state.id},normalizeOptions(pluck(MovablePolygonOptions,"standard")),DEFAULT_PROPS)},modify:function(options){this.update(_.extend(this._createDefaultState(),options));},update:function(options){const self=this;const graphie=self.graphie;const state=_.extend(self.state,normalizeOptions(options));const normalColor=state.static?KhanColors.DYNAMIC:KhanColors.INTERACTIVE;state.normalStyle=_.extend({},state.normalStyle,{"stroke-width":2,"fill-opacity":0,fill:normalColor,stroke:normalColor},options.normalStyle);state.highlightStyle=_.extend({},{stroke:KhanColors.INTERACTING,"stroke-width":2,fill:KhanColors.INTERACTING,"fill-opacity":.05},state.highlightStyle);state.labelStyle=_.extend({},{stroke:KhanColors.DYNAMIC,"stroke-width":1,color:KhanColors.DYNAMIC},state.labelStyle);if(!state.static){if(!state.mouseTarget){state.mouseTarget=graphie.mouselayer.path(this.path());state.mouseTarget.attr({fill:"#000",opacity:0,cursor:"move"});}}self.movable.modify(_.extend({},state,{modify:null,draw:self.draw.bind(self),remove:null,onMoveStart:function(){self._initialRefCoord=self.coord(0);self._prevRefCoord=self._initialRefCoord;self._totalDelta=[0,0];self._fireEvent(self.state.onMoveStart,self.coord(0),self.coord(0));},onMove:function(mouseCoord,prevMouseCoord){const delta=vector$3.subtract(mouseCoord,prevMouseCoord);self._totalDelta=vector$3.add(self._totalDelta,delta);let refCoord=vector$3.add(self._initialRefCoord,self._totalDelta);refCoord=self._applyConstraints(refCoord,self._prevRefCoord);if(refCoord===false){return}self._fireEvent(self.state.onMove,refCoord,self._prevRefCoord);self._prevRefCoord=refCoord;},onMoveEnd:function(){self._fireEvent(self.state.onMoveEnd,self._prevRefCoord,self._initialRefCoord);}}));_.invoke(state.points,"listen","onMove",state.id,self.draw.bind(self));if(!state.added){self.prevState={};self._fireEvent(state.add,self.cloneState(),self.prevState);state.added=true;self.prevState=self.cloneState();}self._fireEvent(state.modify,self.cloneState(),self.prevState);},path:function(state){const graphie=this.graphie;state=state||this.state;let coords=state.points.map(function(point){return graphie.scalePoint(point.coord())});if(state.closed){coords.push(true);}else {coords=coords.concat(_.clone(coords).reverse());}return GraphUtils.unscaledSvgPath(coords)},coords:function(){return _.invoke(this.state.points,"coord")},point:function(index){return this.state.points[index]},coord:function(index){return this.point(index).coord()},remove:function(){this.state.added=false;this._fireEvent(this.state.remove);if(this.state.points){_.invoke(this.state.points,"unlisten","onMove",this.state.id);}if(this.movable){this.movable.remove();}this.state.mouseTarget=null;},constrain:function(){if(this.points==null||this.points.length===0){return}const prevRefCoord=this.coord(0);const refCoord=this._applyConstraints(prevRefCoord,prevRefCoord);if(refCoord!==false){this._fireEvent(this.state.onMove,refCoord,prevRefCoord);}},normalStyle:function(){return _.clone(this.state.normalStyle)},highlightStyle:function(){return _.clone(this.state.highlightStyle)},toBack:function(){this.movable.toBack();if(this.state.visibleShape){this.state.visibleShape.toBack();}},toFront:function(){if(this.state.visibleShape){this.state.visibleShape.toFront();}this.movable.toFront();},isHovering:function(){return this.movable.isHovering()},isDragging:function(){return this.movable.isDragging()},mouseTarget:function(){return this.movable.mouseTarget()}});
1594
1594
 
1595
- const Interactive2={MovablePoint:MovablePoint$5,addMovablePoint:function(graphie,options){const movable=new Movable(graphie,{});return new MovablePoint$5(graphie,movable,options)},MovableLine:MovableLine$4,addMovableLine:function(graphie,options){const movable=new Movable(graphie,{});return new MovableLine$4(graphie,movable,options)},MovablePolygon:MovablePolygon,addMovablePolygon:function(graphie,options){const movable=new Movable(graphie,{});return new MovablePolygon(graphie,movable,options)},addMaybeMobileMovablePoint:function(widget,extraProps){const isMobile=widget.props.apiOptions.isMobile;const commonStyle=isMobile?{stroke:"#ffffff","stroke-width":3,fill:KhanColors.INTERACTIVE}:{stroke:KhanColors.INTERACTIVE,fill:KhanColors.INTERACTIVE};const normalStyle=Object.assign(commonStyle,extraProps.normalStyle);const highlightStyle=Object.assign(isMobile?{...commonStyle,"stroke-width":0,scale:.75}:{},extraProps.highlightStyle);const props=Object.assign({normalStyle:normalStyle,highlightStyle:highlightStyle,shadow:isMobile,tooltip:isMobile&&widget.props.showTooltips,showHairlines:widget.showHairlines,hideHairlines:widget.hideHairlines},isMobile?{pointSize:7}:{});return Interactive2.addMovablePoint(widget.graphie,Object.assign(extraProps,props))}};
1595
+ const Interactive2={MovablePoint:MovablePoint$5,addMovablePoint:function(graphie,options){const movable=new Movable(graphie,{});return new MovablePoint$5(graphie,movable,options)},MovableLine:MovableLine$4,addMovableLine:function(graphie,options){const movable=new Movable(graphie,{});return new MovableLine$4(graphie,movable,options)},MovablePolygon:MovablePolygon,addMovablePolygon:function(graphie,options){const movable=new Movable(graphie,{});return new MovablePolygon(graphie,movable,options)},addMovablePointV2:function(widget,extraProps){const commonStyle={stroke:tokenValue(semanticColor.core.border.knockout.default),"stroke-width":3,fill:tokenValue(semanticColor.core.foreground.instructive.default)};const normalStyle=Object.assign(commonStyle,extraProps.normalStyle);const highlightStyle=Object.assign({...commonStyle,"stroke-width":2,scale:.75},extraProps.highlightStyle);const props=Object.assign({normalStyle:normalStyle,highlightStyle:highlightStyle,shadow:true,tooltip:widget.props.showTooltips,showHairlines:widget.showHairlines,hideHairlines:widget.hideHairlines},{pointSize:7});return Interactive2.addMovablePoint(widget.graphie,Object.assign(extraProps,props))}};
1596
1596
 
1597
- const MovablePoint$4=GraphieClasses.createClass({displayName:"MovablePoint",movableProps:["children"],_getProps:function(){if(!this.props.isMobile){return this.props}const interactiveColor=tokenValue(semanticColor.core.foreground.instructive.default);const commonStyle={stroke:tokenValue(semanticColor.core.foreground.knockout.default),"stroke-width":3,fill:interactiveColor};return Object.assign(this.props,{normalStyle:{...commonStyle,...this.props.mobileStyleOverride||{}},highlightStyle:{...commonStyle,"stroke-width":0,scale:.75},shadow:true,tooltip:this.props.showTooltips,pointSize:7})},add:function(graphie){this.point=Interactive2.addMovablePoint(graphie,this._getProps());},modify:function(){this.point.modify(this._getProps());},remove:function(){this.point.remove();},toFront:function(){this.point.toFront();},grab:function(coord){this.point.grab(coord);}});_.extend(MovablePoint$4,Interactive2.MovablePoint);const MovableLine$3=GraphieClasses.createClass({displayName:"MovableLine",movableProps:["children"],add:function(graphie){const points=_.pluck(this.props.children,"point");const props=_.extend({},this.props,{points:points});this.line=Interactive2.addMovableLine(graphie,props);},modify:function(){const points=_.pluck(this.props.children,"point");const props=_.extend({},this.props,{points:points});this.line.modify(props);},remove:function(){this.line.remove();},toFront:function(){this.line.toFront();}});_.extend(MovableLine$3,Interactive2.MovableLine);const Label$1=GraphieClasses.createSimpleClass((graphie,props)=>{let coord=props.coord;if(props.unscaled){coord=graphie.unscalePoint(coord);}let elem;if(getDependencies().JIPT.useJIPT){elem=graphie.label(coord,props.text,props.direction,false,props.style);getDependencies().graphieMovablesJiptLabels.addLabel(elem,props.tex);}else {elem=graphie.label(coord,props.text,props.direction,props.tex,props.style);}return elem});const Line$3=GraphieClasses.createClass({displayName:"Line",movableProps:["children"],add:function(graphie){const props=this.props;this.graphie=graphie;this.line=this.graphie.line(props.start,props.end,props.style);},modify:function(){const props=this.props;const path=this.graphie.svgPath([props.start,props.end]);this.line.attr(_.extend({},props.style,{path:path}));},remove:function(){this.line.remove();},toFront:function(){this.line.toFront();}});const Parabola=GraphieClasses.createClass({displayName:"Parabola",movableProps:["children"],add:function(graphie){const props=this.props;this.graphie=graphie;this.parabola=this.graphie.parabola(props.a,props.b,props.c,props.style);},modify:function(){const props=this.props;const path=this.graphie.svgParabolaPath(props.a,props.b,props.c);this.parabola.attr(_.extend({},props.style,{path:path}));},remove:function(){this.parabola.remove();},toFront:function(){this.parabola.toFront();}});const Sinusoid=GraphieClasses.createClass({displayName:"Sinusoid",movableProps:["children"],add:function(graphie){const props=this.props;this.graphie=graphie;this.sinusoid=this.graphie.sinusoid(props.a,props.b,props.c,props.d,props.style);},modify:function(){const props=this.props;const path=this.graphie.svgSinusoidPath(props.a,props.b,props.c,props.d);this.sinusoid.attr(_.extend({},props.style,{path:path}));},remove:function(){this.sinusoid.remove();},toFront:function(){this.sinusoid.toFront();}});const Plot$1=GraphieClasses.createSimpleClass((graphie,props)=>{return graphie.plot(props.fn,props.range,props.style)});const PlotParametric$1=GraphieClasses.createSimpleClass((graphie,props)=>{return graphie.plotParametric(props.fn,props.range,props.style)});const Point$1=GraphieClasses.createSimpleClass((graphie,props)=>{const neutralColor=tokenValue(semanticColor.core.foreground.neutral.strong);return graphie.ellipse(props.coord,graphie.unscaleVector([4,4]),{fill:props.color||neutralColor,stroke:props.color||neutralColor})});const Path=GraphieClasses.createClass({displayName:"Path",movableProps:["children"],add:function(graphie){const props=this.props;this.graphie=graphie;this.path=this.graphie.path(props.coords,props.style);},modify:function(){const props=this.props;const path=this.graphie.svgPath(props.coords);this.path.attr({path:path});},remove:function(){this.path.remove();},toFront:function(){this.path.toFront();}});const Arc$1=GraphieClasses.createSimpleClass((graphie,props)=>{let center=props.center;let radius=props.radius;if(props.unscaled){center=graphie.unscalePoint(center);radius=graphie.unscaleVector(radius);}return graphie.arc(center,radius,props.startAngle,props.endAngle,props.sector,props.style)});const Circle=GraphieClasses.createSimpleClass((graphie,props)=>{return graphie.circle(props.center,props.radius,props.style)});const Rect$1=GraphieClasses.createSimpleClass((graphie,props)=>{return graphie.rect(props.x,props.y,props.width,props.height,props.style)});var Movables = {Arc:Arc$1,Circle:Circle,Label:Label$1,Line:Line$3,MovableLine:MovableLine$3,MovablePoint:MovablePoint$4,Parabola:Parabola,Path:Path,Plot:Plot$1,PlotParametric:PlotParametric$1,Point:Point$1,Sinusoid:Sinusoid,Rect:Rect$1};
1597
+ const MovablePoint$4=GraphieClasses.createClass({displayName:"MovablePoint",movableProps:["children"],_getProps:function(){if(!this.props.isMobile||this.props.static){return this.props}const interactiveColor=tokenValue(semanticColor.core.foreground.instructive.default);const commonStyle={stroke:tokenValue(semanticColor.core.foreground.knockout.default),"stroke-width":3,fill:interactiveColor};return Object.assign(this.props,{normalStyle:{...commonStyle,...this.props.mobileStyleOverride||{}},highlightStyle:{...commonStyle,"stroke-width":0,scale:.75,...this.props.mobileStyleOverride||{}},shadow:true,tooltip:this.props.showTooltips,pointSize:7})},add:function(graphie){this.point=Interactive2.addMovablePoint(graphie,this._getProps());},modify:function(){this.point.modify(this._getProps());},remove:function(){this.point.remove();},toFront:function(){this.point.toFront();},grab:function(coord){this.point.grab(coord);}});_.extend(MovablePoint$4,Interactive2.MovablePoint);const MovableLine$3=GraphieClasses.createClass({displayName:"MovableLine",movableProps:["children"],add:function(graphie){const points=_.pluck(this.props.children,"point");const props=_.extend({},this.props,{points:points});this.line=Interactive2.addMovableLine(graphie,props);},modify:function(){const points=_.pluck(this.props.children,"point");const props=_.extend({},this.props,{points:points});this.line.modify(props);},remove:function(){this.line.remove();},toFront:function(){this.line.toFront();}});_.extend(MovableLine$3,Interactive2.MovableLine);const Label$1=GraphieClasses.createSimpleClass((graphie,props)=>{let coord=props.coord;if(props.unscaled){coord=graphie.unscalePoint(coord);}let elem;if(getDependencies().JIPT.useJIPT){elem=graphie.label(coord,props.text,props.direction,false,props.style);getDependencies().graphieMovablesJiptLabels.addLabel(elem,props.tex);}else {elem=graphie.label(coord,props.text,props.direction,props.tex,props.style);}return elem});const Line$3=GraphieClasses.createClass({displayName:"Line",movableProps:["children"],add:function(graphie){const props=this.props;this.graphie=graphie;this.line=this.graphie.line(props.start,props.end,props.style);},modify:function(){const props=this.props;this.line.remove();this.line=this.graphie.line(props.start,props.end,props.style);},remove:function(){this.line.remove();},toFront:function(){this.line.toFront();}});const Parabola=GraphieClasses.createClass({displayName:"Parabola",movableProps:["children"],add:function(graphie){const props=this.props;this.graphie=graphie;this.parabola=this.graphie.parabola(props.a,props.b,props.c,props.style);},modify:function(){const props=this.props;const path=this.graphie.svgParabolaPath(props.a,props.b,props.c);this.parabola.attr(_.extend({},props.style,{path:path}));},remove:function(){this.parabola.remove();},toFront:function(){this.parabola.toFront();}});const Sinusoid=GraphieClasses.createClass({displayName:"Sinusoid",movableProps:["children"],add:function(graphie){const props=this.props;this.graphie=graphie;this.sinusoid=this.graphie.sinusoid(props.a,props.b,props.c,props.d,props.style);},modify:function(){const props=this.props;const path=this.graphie.svgSinusoidPath(props.a,props.b,props.c,props.d);this.sinusoid.attr(_.extend({},props.style,{path:path}));},remove:function(){this.sinusoid.remove();},toFront:function(){this.sinusoid.toFront();}});const Plot$1=GraphieClasses.createSimpleClass((graphie,props)=>{return graphie.plot(props.fn,props.range,props.style)});const PlotParametric$1=GraphieClasses.createSimpleClass((graphie,props)=>{return graphie.plotParametric(props.fn,props.range,props.style)});const Point$1=GraphieClasses.createSimpleClass((graphie,props)=>{const neutralColor=tokenValue(semanticColor.core.foreground.neutral.strong);return graphie.ellipse(props.coord,graphie.unscaleVector([4,4]),{fill:props.color||neutralColor,stroke:props.color||neutralColor})});const Path=GraphieClasses.createClass({displayName:"Path",movableProps:["children"],add:function(graphie){const props=this.props;this.graphie=graphie;this.path=this.graphie.path(props.coords,props.style);},modify:function(){const props=this.props;const path=this.graphie.svgPath(props.coords);this.path.attr({path:path});},remove:function(){this.path.remove();},toFront:function(){this.path.toFront();}});const Arc$1=GraphieClasses.createSimpleClass((graphie,props)=>{let center=props.center;let radius=props.radius;if(props.unscaled){center=graphie.unscalePoint(center);radius=graphie.unscaleVector(radius);}return graphie.arc(center,radius,props.startAngle,props.endAngle,props.sector,props.style)});const Circle=GraphieClasses.createSimpleClass((graphie,props)=>{return graphie.circle(props.center,props.radius,props.style)});const Rect$1=GraphieClasses.createSimpleClass((graphie,props)=>{return graphie.rect(props.x,props.y,props.width,props.height,props.style)});var Movables = {Arc:Arc$1,Circle:Circle,Label:Label$1,Line:Line$3,MovableLine:MovableLine$3,MovablePoint:MovablePoint$4,Parabola:Parabola,Path:Path,Plot:Plot$1,PlotParametric:PlotParametric$1,Point:Point$1,Sinusoid:Sinusoid,Rect:Rect$1};
1598
1598
 
1599
- const GraphieMovable=GraphieClasses.GraphieMovable;const createGraphie=GraphUtils.createGraphie;const{nestedMap}=Util;const{assert: assert$2}=InteractiveUtil;class Graphie extends React.Component{componentDidMount(){this._setupGraphie();this._updateMovables();}shouldComponentUpdate(nextProps){return !_.isEqual(this.props,nextProps)}componentDidUpdate(prevProps){if(this.props.setup!==prevProps.setup){Log.error("<Graphie> was given a new setup function. "+"This is a bad idea; please refactor your code to give "+"the same setup function reference to <Graphie> on "+"every render.",Errors.Internal);}if(!approximateDeepEqual(this.props.options,prevProps.options)||!approximateDeepEqual(this.props.box,prevProps.box)||!approximateDeepEqual(this.props.range,prevProps.range)){this._setupGraphie();}this._updateMovables();}componentWillUnmount(){this._removeMovables();this._graphie.cleanup();}render(){return jsxRuntimeExports.jsx("div",{className:"graphie-container",children:jsxRuntimeExports.jsx("div",{className:"graphie",ref:this.graphieDivRef})})}constructor(...args){super(...args),this.graphieDivRef=React.createRef(),this._graphie=new Graphie$1(document.createElement("div")),this._movables={},this.movables={},this.getGraphie=()=>{return this._graphie},this._range=()=>{const boundsCheckRange=range=>{if(range[0]>=range[1]){return [-10,10]}return range};return [boundsCheckRange(this.props.range[0]),boundsCheckRange(this.props.range[1])]},this._box=()=>{const ensureMinSize=pixelDim=>{return pixelDim>0?pixelDim:340};return [ensureMinSize(this.props.box[0]),ensureMinSize(this.props.box[1])]},this._scale=()=>{const box=this._box();const range=this._range();return box.map((pixelDim,i)=>{const unitDim=range[i][1]-range[i][0];return pixelDim/unitDim})},this._setupGraphie=()=>{this._graphie.cleanup();this._removeMovables();const graphieDiv=this.graphieDivRef.current;if(graphieDiv==null||graphieDiv instanceof Text){throw new Error("No graphie container div found")}graphieDiv.innerHTML="";const graphie=this._graphie=createGraphie(graphieDiv);graphie.init({range:this._range(),scale:this._scale(),isMobile:this.props.isMobile});if(this.props.addMouseLayer){graphie.addMouseLayer({onClick:this.props.onClick,onMouseDown:this.props.onMouseDown,onMouseMove:this.props.onMouseMove});}graphie.snap=this.props.options.snapStep||[1,1];if(this.props.responsive){$(graphieDiv).css({width:"100%",height:"100%"});graphie.raphael.setSize("100%","100%");}this.props.setup(graphie,_.extend({range:this._range(),scale:this._scale()},this.props.options));},this._removeMovables=()=>{_.invoke(this._movables,"remove");this._movables={};},this._renderMovables=(children,options)=>{const graphie=options.graphie;const oldMovables=options.oldMovables;const newMovables=options.newMovables;const renderChildren=elem=>{_.each(elem.movableProps,prop=>{elem.props[prop]=this._renderMovables(elem.props[prop],options);});};let areMovablesOutOfOrder=false;return nestedMap(children,childDescriptor=>{if(!childDescriptor){options.nextKey++;return childDescriptor}const child=new childDescriptor.type(childDescriptor.props);assert$2(child instanceof GraphieMovable,"All children of a Graphie component must be Graphie "+"movables");const keyProp=childDescriptor.key;const key=keyProp==null?"_no_id_"+options.nextKey:keyProp;options.nextKey++;const ref=childDescriptor.ref;renderChildren(child);const prevMovable=oldMovables[key];if(!prevMovable){child.add(graphie);areMovablesOutOfOrder=true;newMovables[key]=child;}else if(child.constructor===prevMovable.constructor){prevMovable.props=child.props;const modifyResult=prevMovable.modify(graphie);if(modifyResult==="reordered"){areMovablesOutOfOrder=true;}newMovables[key]=prevMovable;}else {if(keyProp==null){Log.error("Replacing a <Graphie> child with a "+"child of a different type. Please add keys "+"to your <Graphie> children",Errors.Internal);}prevMovable.remove();child.add(graphie);areMovablesOutOfOrder=true;newMovables[key]=child;}if(areMovablesOutOfOrder){newMovables[key].toFront();}if(ref){this.movables[ref]=newMovables[key];}return newMovables[key]})},this._updateMovables=()=>{const graphie=this._graphie;const oldMovables=this._movables;const newMovables={};this._movables=newMovables;this.movables={};this._renderMovables(this.props.children,{nextKey:1,graphie:graphie,oldMovables:oldMovables,newMovables:newMovables});_.each(oldMovables,(oldMovable,key)=>{if(!newMovables[key]){oldMovable.remove();}});};}}Graphie.defaultProps={range:[[-10,10],[-10,10]],options:{},responsive:false,addMouseLayer:true};_.extend(Graphie,GraphieClasses);_.extend(Graphie,Movables);
1599
+ const GraphieMovable=GraphieClasses.GraphieMovable;const createGraphie=GraphUtils.createGraphie;const{nestedMap}=Util;const{assert: assert$2}=InteractiveUtil;class Graphie extends React.Component{componentDidMount(){this._setupGraphie();this._updateMovables();}shouldComponentUpdate(nextProps){return !_.isEqual(this.props,nextProps)}componentDidUpdate(prevProps){if(this.props.setup!==prevProps.setup){Log.error("<Graphie> was given a new setup function. "+"This is a bad idea; please refactor your code to give "+"the same setup function reference to <Graphie> on "+"every render.",Errors.Internal);}if(!approximateDeepEqual(this.props.options,prevProps.options)||!approximateDeepEqual(this.props.box,prevProps.box)||!approximateDeepEqual(this.props.range,prevProps.range)){this._setupGraphie();}this._updateMovables();}componentWillUnmount(){this._removeMovables();this._graphie.cleanup();}render(){return jsxRuntimeExports.jsx("div",{className:"graphie-container",children:jsxRuntimeExports.jsx("div",{className:"graphie",ref:this.graphieDivRef})})}constructor(...args){super(...args),this.graphieDivRef=React.createRef(),this._graphie=new Graphie$1(document.createElement("div")),this._movables={},this.movables={},this.getGraphie=()=>{return this._graphie},this._range=()=>{const boundsCheckRange=range=>{if(range[0]>=range[1]){return [-10,10]}return range};return [boundsCheckRange(this.props.range[0]),boundsCheckRange(this.props.range[1])]},this._box=()=>{const ensureMinSize=pixelDim=>{return pixelDim>0?pixelDim:340};return [ensureMinSize(this.props.box[0]),ensureMinSize(this.props.box[1])]},this._scale=()=>{const box=this._box();const range=this._range();return box.map((pixelDim,i)=>{const unitDim=range[i][1]-range[i][0];return pixelDim/unitDim})},this._setupGraphie=()=>{this._graphie.cleanup();this._removeMovables();const graphieDiv=this.graphieDivRef.current;if(graphieDiv==null||graphieDiv instanceof Text){throw new Error("No graphie container div found")}graphieDiv.innerHTML="";const graphie=this._graphie=createGraphie(graphieDiv);graphie.init({range:this._range(),scale:this._scale(),isMobile:this.props.isMobile});if(this.props.addMouseLayer){graphie.addMouseLayer({onClick:this.props.onClick,onMouseDown:this.props.onMouseDown,onMouseMove:this.props.onMouseMove});}graphie.snap=this.props.options.snapStep||[1,1];if(this.props.responsive){graphieDiv.style.width="100%";graphieDiv.style.height="100%";graphie.raphael.setSize("100%","100%");}this.props.setup(graphie,_.extend({range:this._range(),scale:this._scale()},this.props.options));},this._removeMovables=()=>{_.invoke(this._movables,"remove");this._movables={};},this._renderMovables=(children,options)=>{const graphie=options.graphie;const oldMovables=options.oldMovables;const newMovables=options.newMovables;const renderChildren=elem=>{_.each(elem.movableProps,prop=>{elem.props[prop]=this._renderMovables(elem.props[prop],options);});};let areMovablesOutOfOrder=false;return nestedMap(children,childDescriptor=>{if(!childDescriptor){options.nextKey++;return childDescriptor}const child=new childDescriptor.type(childDescriptor.props);assert$2(child instanceof GraphieMovable,"All children of a Graphie component must be Graphie "+"movables");const keyProp=childDescriptor.key;const key=keyProp==null?"_no_id_"+options.nextKey:keyProp;options.nextKey++;const ref=childDescriptor.ref;renderChildren(child);const prevMovable=oldMovables[key];if(!prevMovable){child.add(graphie);areMovablesOutOfOrder=true;newMovables[key]=child;}else if(child.constructor===prevMovable.constructor){prevMovable.props=child.props;const modifyResult=prevMovable.modify(graphie);if(modifyResult==="reordered"){areMovablesOutOfOrder=true;}newMovables[key]=prevMovable;}else {if(keyProp==null){Log.error("Replacing a <Graphie> child with a "+"child of a different type. Please add keys "+"to your <Graphie> children",Errors.Internal);}prevMovable.remove();child.add(graphie);areMovablesOutOfOrder=true;newMovables[key]=child;}if(areMovablesOutOfOrder){newMovables[key].toFront();}if(ref){this.movables[ref]=newMovables[key];}return newMovables[key]})},this._updateMovables=()=>{const graphie=this._graphie;const oldMovables=this._movables;const newMovables={};this._movables=newMovables;this.movables={};this._renderMovables(this.props.children,{nextKey:1,graphie:graphie,oldMovables:oldMovables,newMovables:newMovables});_.each(oldMovables,(oldMovable,key)=>{if(!newMovables[key]){oldMovable.remove();}});};}}Graphie.defaultProps={range:[[-10,10],[-10,10]],options:{},responsive:false,addMouseLayer:true};_.extend(Graphie,GraphieClasses);_.extend(Graphie,Movables);
1600
1600
 
1601
1601
  function withDependencies(WrappedComponent){const WithDependenciesComponent=React__default.forwardRef((props,ref)=>{const dependencies=useDependencies();return jsxRuntimeExports.jsx(WrappedComponent,{...props,dependencies:dependencies,ref:ref})});WithDependenciesComponent.displayName=`withDependencies(${WrappedComponent.displayName||WrappedComponent.name||"Component"})`;return WithDependenciesComponent}
1602
1602
 
@@ -1668,7 +1668,7 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
1668
1668
 
1669
1669
  class WidgetContainer extends React.Component{componentDidMount(){if(this.props.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.")}}render(){let className=classNames$2({"perseus-widget-container":true,"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})}const subType=getWidgetSubType(type,this.props.widgetProps.options)??"null";let alignment=this.props.widgetProps.alignment;if(alignment==null||alignment==="default"){alignment=CoreWidgetRegistry.getDefaultAlignment(type);}className+=CoreWidgetRegistry.getAlignmentClassName(type,alignment);const Container=alignment.startsWith("inline")?"span":"div";const apiOptions=this.props.widgetProps.apiOptions;const isStatic=this.props.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.widgetProps.linterContext:{...this.props.widgetProps.linterContext,highlightLint:false};return jsxRuntimeExports.jsx(Container,{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,stack:error.stack??"No stack trace available",userAgent:userAgent}});},children:[jsxRuntimeExports.jsx(WidgetType,{...this.props.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},this.getWidget=()=>{return this.widgetRef.current};}}
1670
1670
 
1671
- const rContainsNonWhitespace$1=/\S/;const rImageURL$1=/(web\+graphie|https):\/\/[^\s]*/;const noopOnRender$1=()=>{};const makeContainerId$1=id=>"container:"+id;const isIdPathPrefix$1=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$1(propsA,propsB){function makeItem(props){return splitPerseusItem({question:{content:props.content,widgets:props.widgets,images:{}},hints:[],answerArea:getDefaultAnswerArea()})}return propsA.problemNum!==propsB.problemNum||!_.isEqual(makeItem(propsA),makeItem(propsB))}let Renderer$2 = class Renderer extends React.Component{componentDidMount(){this._isMounted=true;this.handleRender({});this._currentFocus=null;this.props.initializeUserInput?.(this.props.widgets,this.props.problemNum??0);if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}if(this.props.userInput){this.props.apiOptions?.answerableCallback?.(this._isAnswerable());}}UNSAFE_componentWillReceiveProps(nextProps){if(isDifferentQuestion$1(this.props,nextProps)){this.props.initializeUserInput?.(nextProps.widgets,nextProps.problemNum??0);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}componentDidUpdate(prevProps,prevState){this.handleRender(prevProps);if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}if(this.props.userInput&&!_.isEqual(this.props.userInput,prevProps.userInput)){this.props.apiOptions?.answerableCallback?.(this._isAnswerable());}}componentWillUnmount(){this.widgetIds=[];if(this.translationIndex!=null){getDependencies().rendererTranslationComponents.removeComponentAtIndex(this.translationIndex);}this._isMounted=false;}_isAnswerable(){if(this.props.userInput){return this.emptyWidgets().length===0}return false}_getWidgetIndexById(id){const widgetIndex=this.widgetIds.indexOf(id);if(widgetIndex<0){Log.error("Unable to get widget index in _getWidgetIndexById",Errors.Internal,{loggedMetadata:{widgets:JSON.stringify(this.props.widgets),widgetId:JSON.stringify(id)}});return 0}return widgetIndex}getWidgetProps(widgetId){const apiOptions=this.getApiOptions();const widgetOptions=this.props.widgets[widgetId].options;const widgetInfo=this.state.widgetInfo[widgetId];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 {options:widgetOptions,userInput:this.props.userInput?.[widgetId],widgetId:widgetId,widgetIndex:this._getWidgetIndexById(widgetId),alignment:widgetInfo&&widgetInfo.alignment,static:widgetInfo?.static,graded:widgetInfo?.graded,problemNum:this.props.problemNum,apiOptions:this.getApiOptions(),keypadElement:this.props.keypadElement,showSolutions:this.props.showSolutions,onFocus:_.partial(this._onWidgetFocus,widgetId),onBlur:_.partial(this._onWidgetBlur,widgetId),findWidgets:this.findWidgets,reviewMode:this.props.reviewMode??false,containerSizeClass:containerSizeClass.MEDIUM,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"widget"),handleUserInput:newUserInput=>{const updatedUserInput={...this.props.userInput,[widgetId]:newUserInput};const emptyWidgetIds=emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,updatedUserInput,this.context.locale);const widgetsEmpty=emptyWidgetIds.length>0;this.props.handleUserInput?.(widgetId,newUserInput,widgetsEmpty);this.props.apiOptions?.interactionCallback?.(updatedUserInput);},trackInteraction:interactionTracker.track}}getSerializedState(){return mapObject(this.props.widgets,(widgetData,widgetId)=>{const widget=this.getWidgetInstance(widgetId);if(widget&&widget.getSerializedState){return excludeDenylistKeys(widget.getSerializedState())}return widgetData.options})}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)});}}emptyWidgets(){if(!this.props.userInput){throw new Error(`emptyWidgets called without providing userInput to Renderer`)}return emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.props.userInput,this.context.locale)}getUserInputMap(){const userInput=this.props.userInput;if(!userInput){throw new Error(`getUserInputMap called without providing userInput to Renderer`)}return userInput}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(){if(!this.props.userInput){throw new Error(`score called without providing userInput to Renderer`)}const scores=scoreWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.props.userInput,this.context.locale);const combinedScore=flattenScores(scores);return combinedScore}render(){const apiOptions=this.getApiOptions();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;let parsedMarkdown=this.props.inline?PerseusMarkdown$1.parseInline(content,{isJipt:this.translationIndex!=null}):PerseusMarkdown$1.parse(content,{isJipt:this.translationIndex!=null});parsedMarkdown=Util.splitBlockWidgetsFromParagraphs(parsedMarkdown);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$2({[ClassNames.RENDERER]:true,[ClassNames.RESPONSIVE_RENDERER]:true,[ClassNames.TWO_COLUMN_RENDERER]:this._isTwoColumn});return jsxRuntimeExports.jsx(DefinitionProvider,{children:jsxRuntimeExports.jsx("div",{className:className,children:markdownContents})})}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}},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;return jsxRuntimeExports.jsx(WidgetContainer,{id:id,ref:node=>{const containerId=makeContainerId$1(id);if(node!=null){this._widgetContainers.set(containerId,node);}else {this._widgetContainers.delete(containerId);}},type:type,widgetProps:this.getWidgetProps(id)},makeContainerId$1(id))}return null},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$1(id));if(!ref){return null}return ref.getWidget()},this._onWidgetFocus=(id,focusPath=[])=>{if(!Array.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.outputMarkdown=(ast,state)=>{if(Array.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$2({"perseus-paragraph-centered":!this._foundTextNodes,"perseus-paragraph-full-width":state.foundFullWidth&&ast.content?.length===1});}return jsxRuntimeExports.jsx(QuestionParagraph$1,{className:className,translationIndex:this.translationIndex,paragraphIndex:state.paragraphIndex,children:jsxRuntimeExports.jsx(ErrorBoundary,{children:output})},state.key)},this.outputNested=(ast,state)=>{if(Array.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$z.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,{allowZoom:true,setAssetStatus:setAssetStatus,src:PerseusMarkdown$1.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$1.ruleOutput(node,nestedOutput,state)},state.key)}if(node.type==="text"){if(rContainsNonWhitespace$1.test(node.content)){this._foundTextNodes=true;}if(imagePlaceholder&&rImageURL$1.test(node.content)){return imagePlaceholder}return node.content}if(node.type==="table"||node.type==="titledTable"){const output=PerseusMarkdown$1.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$z.mobileZoomableParentFix};const wrappedOutput=jsxRuntimeExports.jsx("div",{style:{...mobileInnerStyle,overflowX:"auto"},children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(Zoomable$1,{animateHeight:true,children:output})})});return jsxRuntimeExports.jsx("div",{style:outerStyle,children:wrappedOutput})}return jsxRuntimeExports.jsx(ErrorBoundary,{children:PerseusMarkdown$1.ruleOutput(node,nestedOutput,state)},state.key)},this.handleRender=prevProps=>{const onRender=this.props.onRender;const oldOnRender=prevProps.onRender;if(onRender!==noopOnRender$1||oldOnRender!==noopOnRender$1){const $images=$(ReactDOM__default.findDOMNode(this)).find("img");if(oldOnRender!==noopOnRender$1){$images.off("load",oldOnRender);}if(onRender!==noopOnRender$1){$images.on("load",onRender);}}onRender();},this._setCurrentFocus=path=>{const apiOptions=this.getApiOptions();if(!isIdPathPrefix$1(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)}});}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){const container=this._widgetContainers.get(makeContainerId$1(widgetId));if(container){return ReactDOM__default.findDOMNode(container)}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.getWidgetIds=()=>{return this.widgetIds},this.handletranslationLintErrors=lintErrors=>{if(!this._isMounted){return}this.setState({translationLintErrors:lintErrors});};this._translationLinter=new TranslationLinter;this.state={jiptContent:null,translationLintErrors:[],...this._getInitialWidgetState(props)};}};Renderer$2.contextType=PerseusI18nContext;Renderer$2.defaultProps={content:"",widgets:{},images:{},showSolutions:"none",onRender:noopOnRender$1,findExternalWidgets:()=>[],alwaysUpdate:false,reviewMode:false,linterContext:PerseusLinter.linterContextDefault};const styles$z={mobileZoomableParentFix:{transform:"translate3d(0,0,0)"}};
1671
+ const rContainsNonWhitespace$1=/\S/;const rImageURL$1=/(web\+graphie|https):\/\/[^\s]*/;const noopOnRender$1=()=>{};const makeContainerId$1=id=>"container:"+id;const isIdPathPrefix$1=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$1(propsA,propsB){function makeItem(props){return splitPerseusItem({question:{content:props.content,widgets:props.widgets,images:{}},hints:[],answerArea:getDefaultAnswerArea()})}return propsA.problemNum!==propsB.problemNum||!_.isEqual(makeItem(propsA),makeItem(propsB))}let Renderer$2 = class Renderer extends React.Component{componentDidMount(){this._isMounted=true;this.handleRender({});this._currentFocus=null;this.props.initializeUserInput?.(this.props.widgets,this.props.problemNum??0);if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}if(this.props.userInput){this.props.apiOptions?.answerableCallback?.(this._isAnswerable());}}UNSAFE_componentWillReceiveProps(nextProps){if(isDifferentQuestion$1(this.props,nextProps)){this.props.initializeUserInput?.(nextProps.widgets,nextProps.problemNum??0);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}componentDidUpdate(prevProps,prevState){this.handleRender(prevProps);if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}if(this.props.userInput&&!_.isEqual(this.props.userInput,prevProps.userInput)){this.props.apiOptions?.answerableCallback?.(this._isAnswerable());}}componentWillUnmount(){this.widgetIds=[];if(this.translationIndex!=null){getDependencies().rendererTranslationComponents.removeComponentAtIndex(this.translationIndex);}this._isMounted=false;}_isAnswerable(){if(this.props.userInput){return this.emptyWidgets().length===0}return false}_getWidgetIndexById(id){const widgetIndex=this.widgetIds.indexOf(id);if(widgetIndex<0){Log.error("Unable to get widget index in _getWidgetIndexById",Errors.Internal,{loggedMetadata:{widgets:JSON.stringify(this.props.widgets),widgetId:JSON.stringify(id)}});return 0}return widgetIndex}getWidgetProps(widgetId){const apiOptions=this.getApiOptions();const widgetOptions=this.props.widgets[widgetId].options;const widgetInfo=this.state.widgetInfo[widgetId];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 {options:widgetOptions,userInput:this.props.userInput?.[widgetId],widgetId:widgetId,widgetIndex:this._getWidgetIndexById(widgetId),alignment:widgetInfo&&widgetInfo.alignment,static:widgetInfo?.static,graded:widgetInfo?.graded,problemNum:this.props.problemNum,apiOptions:this.getApiOptions(),keypadElement:this.props.keypadElement,showSolutions:this.props.showSolutions,onFocus:_.partial(this._onWidgetFocus,widgetId),onBlur:_.partial(this._onWidgetBlur,widgetId),findWidgets:this.findWidgets,reviewMode:this.props.reviewMode??false,containerSizeClass:containerSizeClass.MEDIUM,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"widget"),handleUserInput:newUserInput=>{const updatedUserInput={...this.props.userInput,[widgetId]:newUserInput};const emptyWidgetIds=emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,updatedUserInput,this.context.locale);const widgetsEmpty=emptyWidgetIds.length>0;this.props.handleUserInput?.(widgetId,newUserInput,widgetsEmpty);this.props.apiOptions?.interactionCallback?.(updatedUserInput);},trackInteraction:interactionTracker.track}}getSerializedState(){return mapObject(this.props.widgets,(widgetData,widgetId)=>{const widget=this.getWidgetInstance(widgetId);if(widget&&widget.getSerializedState){return excludeDenylistKeys(widget.getSerializedState())}return widgetData.options})}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)});}}emptyWidgets(){if(!this.props.userInput){throw new Error(`emptyWidgets called without providing userInput to Renderer`)}return emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.props.userInput,this.context.locale)}getUserInputMap(){const userInput=this.props.userInput;if(!userInput){throw new Error(`getUserInputMap called without providing userInput to Renderer`)}return userInput}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(){if(!this.props.userInput){throw new Error(`score called without providing userInput to Renderer`)}const scores=scoreWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.props.userInput,this.context.locale);const combinedScore=flattenScores(scores);return combinedScore}render(){const apiOptions=this.getApiOptions();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;let parsedMarkdown=this.props.inline?PerseusMarkdown$1.parseInline(content,{isJipt:this.translationIndex!=null}):PerseusMarkdown$1.parse(content,{isJipt:this.translationIndex!=null});parsedMarkdown=Util.splitBlockWidgetsFromParagraphs(parsedMarkdown);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$2({[ClassNames.RENDERER]:true,[ClassNames.RESPONSIVE_RENDERER]:true,[ClassNames.TWO_COLUMN_RENDERER]:this._isTwoColumn});return jsxRuntimeExports.jsx(DefinitionProvider,{children:jsxRuntimeExports.jsx("div",{className:className,children:markdownContents})})}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}},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;return jsxRuntimeExports.jsx(WidgetContainer,{id:id,ref:node=>{const containerId=makeContainerId$1(id);if(node!=null){this._widgetContainers.set(containerId,node);}else {this._widgetContainers.delete(containerId);}},type:type,widgetProps:this.getWidgetProps(id)},makeContainerId$1(id))}return null},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$1(id));if(!ref){return null}return ref.getWidget()},this._onWidgetFocus=(id,focusPath=[])=>{if(!Array.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.outputMarkdown=(ast,state)=>{if(Array.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$2({"perseus-paragraph-centered":!this._foundTextNodes,"perseus-paragraph-full-width":state.foundFullWidth&&ast.content?.length===1});}return jsxRuntimeExports.jsx(QuestionParagraph$1,{className:className,translationIndex:this.translationIndex,paragraphIndex:state.paragraphIndex,children:jsxRuntimeExports.jsx(ErrorBoundary,{children:output})},state.key)},this.outputNested=(ast,state)=>{if(Array.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$z.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,{allowZoom:true,setAssetStatus:setAssetStatus,src:PerseusMarkdown$1.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$1.ruleOutput(node,nestedOutput,state)},state.key)}if(node.type==="text"){if(rContainsNonWhitespace$1.test(node.content)){this._foundTextNodes=true;}if(imagePlaceholder&&rImageURL$1.test(node.content)){return imagePlaceholder}return node.content}if(node.type==="table"||node.type==="titledTable"){const output=PerseusMarkdown$1.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$z.mobileZoomableParentFix};const wrappedOutput=jsxRuntimeExports.jsx("div",{style:{...mobileInnerStyle,overflowX:"auto"},children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(Zoomable$1,{animateHeight:true,children:output})})});return jsxRuntimeExports.jsx("div",{style:outerStyle,children:wrappedOutput})}return jsxRuntimeExports.jsx(ErrorBoundary,{children:PerseusMarkdown$1.ruleOutput(node,nestedOutput,state)},state.key)},this.handleRender=prevProps=>{const onRender=this.props.onRender;const oldOnRender=prevProps.onRender;if(onRender!==noopOnRender$1||oldOnRender!==noopOnRender$1){const node=ReactDOM__default.findDOMNode(this);if(node instanceof Element){node.querySelectorAll("img").forEach(image=>{if(oldOnRender!==noopOnRender$1){image.removeEventListener("load",oldOnRender);}if(onRender!==noopOnRender$1){image.addEventListener("load",onRender);}});}}onRender();},this._setCurrentFocus=path=>{const apiOptions=this.getApiOptions();if(!isIdPathPrefix$1(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)}});}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){const container=this._widgetContainers.get(makeContainerId$1(widgetId));if(container){return ReactDOM__default.findDOMNode(container)}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.getWidgetIds=()=>{return this.widgetIds},this.handletranslationLintErrors=lintErrors=>{if(!this._isMounted){return}this.setState({translationLintErrors:lintErrors});};this._translationLinter=new TranslationLinter;this.state={jiptContent:null,translationLintErrors:[],...this._getInitialWidgetState(props)};}};Renderer$2.contextType=PerseusI18nContext;Renderer$2.defaultProps={content:"",widgets:{},images:{},showSolutions:"none",onRender:noopOnRender$1,findExternalWidgets:()=>[],alwaysUpdate:false,reviewMode:false,linterContext:PerseusLinter.linterContextDefault};const styles$z={mobileZoomableParentFix:{transform:"translate3d(0,0,0)"}};
1672
1672
 
1673
1673
  const rules={...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.lint.react({...node.table,content:{...node,table:node.table.content}},output,{...state,unwrapTitledTableContents:true});}else {contents=rules.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=SimpleMarkdown.parserFor(rules);const parse=(source,state)=>{const paragraphedSource=source+"\n\n";return builtParser(paragraphedSource,{...state,inline:false})};const inlineParser=(source,state)=>{return builtParser(source,{...state,inline:true})};const getContent=ast=>{if(Array.isArray(ast)){return _.flatten(ast.map(getContent))}if(ast.content&&typeof ast.content==="string"){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(source);const content=getContent(ast).join("");return content.length};var PerseusMarkdown = {characterCount:characterCount,traverseContent:traverseContent,parse:parse,parseInline:inlineParser,reactFor:SimpleMarkdown.reactFor,ruleOutput:SimpleMarkdown.ruleOutput(rules,"react"),basicOutput:SimpleMarkdown.reactFor(SimpleMarkdown.ruleOutput(rules,"react")),sanitizeUrl:SimpleMarkdown.sanitizeUrl};
1674
1674
 
@@ -1676,7 +1676,7 @@ class QuestionParagraph extends React.Component{render(){const className=this.pr
1676
1676
 
1677
1677
  class WidgetContainerOld extends React.Component{componentDidMount(){if(this.props.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.")}}render(){let className=classNames$2({"perseus-widget-container":true,"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})}const subType=getWidgetSubType(type,this.props.widgetProps.options)??"null";let alignment=this.props.widgetProps.alignment;if(alignment==null||alignment==="default"){alignment=CoreWidgetRegistry.getDefaultAlignment(type);}className+=CoreWidgetRegistry.getAlignmentClassName(type,alignment);const apiOptions=this.props.widgetProps.apiOptions;const isStatic=this.props.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.widgetProps.linterContext:{...this.props.widgetProps.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,stack:error.stack??"No stack trace available",userAgent:userAgent}});},children:[jsxRuntimeExports.jsx(WidgetType,{...this.props.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},this.getWidget=()=>{return this.widgetRef.current};}}
1678
1678
 
1679
- 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(propsA,propsB){function makeItem(props){return splitPerseusItem({question:{content:props.content,widgets:props.widgets,images:{}},hints:[],answerArea:getDefaultAnswerArea()})}return propsA.problemNum!==propsB.problemNum||!_.isEqual(makeItem(propsA),makeItem(propsB))}let Renderer$1 = class Renderer extends React.Component{componentDidMount(){this._isMounted=true;this.handleRender({});this._currentFocus=null;this.props.initializeUserInput?.(this.props.widgets,this.props.problemNum??0);if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}if(this.props.userInput){this.props.apiOptions?.answerableCallback?.(this._isAnswerable());}}UNSAFE_componentWillReceiveProps(nextProps){if(isDifferentQuestion(this.props,nextProps)){this.props.initializeUserInput?.(nextProps.widgets,nextProps.problemNum??0);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}componentDidUpdate(prevProps,prevState){this.handleRender(prevProps);if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}if(this.props.userInput&&!_.isEqual(this.props.userInput,prevProps.userInput)){this.props.apiOptions?.answerableCallback?.(this._isAnswerable());}}componentWillUnmount(){this.widgetIds=[];if(this.translationIndex!=null){getDependencies().rendererTranslationComponents.removeComponentAtIndex(this.translationIndex);}this._isMounted=false;}_isAnswerable(){if(this.props.userInput){return this.emptyWidgets().length===0}return false}_getWidgetIndexById(id){const widgetIndex=this.widgetIds.indexOf(id);if(widgetIndex<0){Log.error("Unable to get widget index in _getWidgetIndexById",Errors.Internal,{loggedMetadata:{widgets:JSON.stringify(this.props.widgets),widgetId:JSON.stringify(id)}});return 0}return widgetIndex}getWidgetProps(widgetId){const apiOptions=this.getApiOptions();const widgetOptions=this.props.widgets[widgetId].options;const widgetInfo=this.state.widgetInfo[widgetId];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 {options:widgetOptions,userInput:this.props.userInput?.[widgetId],widgetId:widgetId,widgetIndex:this._getWidgetIndexById(widgetId),alignment:widgetInfo&&widgetInfo.alignment,static:widgetInfo?.static,graded:widgetInfo?.graded,problemNum:this.props.problemNum,apiOptions:this.getApiOptions(),keypadElement:this.props.keypadElement,showSolutions:this.props.showSolutions,onFocus:_.partial(this._onWidgetFocus,widgetId),onBlur:_.partial(this._onWidgetBlur,widgetId),findWidgets:this.findWidgets,reviewMode:this.props.reviewMode??false,containerSizeClass:containerSizeClass.MEDIUM,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"widget"),handleUserInput:newUserInput=>{const updatedUserInput={...this.props.userInput,[widgetId]:newUserInput};const emptyWidgetIds=emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,updatedUserInput,this.context.locale);const widgetsEmpty=emptyWidgetIds.length>0;this.props.handleUserInput?.(widgetId,newUserInput,widgetsEmpty);this.props.apiOptions?.interactionCallback?.(updatedUserInput);},trackInteraction:interactionTracker.track}}getSerializedState(){return mapObject(this.props.widgets,(widgetData,widgetId)=>{const widget=this.getWidgetInstance(widgetId);if(widget&&widget.getSerializedState){return excludeDenylistKeys(widget.getSerializedState())}return widgetData.options})}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)});}}emptyWidgets(){if(!this.props.userInput){throw new Error(`emptyWidgets called without providing userInput to Renderer`)}return emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.props.userInput,this.context.locale)}getUserInputMap(){const userInput=this.props.userInput;if(!userInput){throw new Error(`getUserInputMap called without providing userInput to Renderer`)}return userInput}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(){if(!this.props.userInput){throw new Error(`score called without providing userInput to Renderer`)}const scores=scoreWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.props.userInput,this.context.locale);const combinedScore=flattenScores(scores);return combinedScore}render(){const apiOptions=this.getApiOptions();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$2({[ClassNames.RENDERER]:true,[ClassNames.RESPONSIVE_RENDERER]:true,[ClassNames.TWO_COLUMN_RENDERER]:this._isTwoColumn});return jsxRuntimeExports.jsx(DefinitionProvider,{children:jsxRuntimeExports.jsx("div",{className:className,children:markdownContents})})}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}},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;return jsxRuntimeExports.jsx(WidgetContainerOld,{id:id,ref:node=>{const containerId=makeContainerId(id);if(node!=null){this._widgetContainers.set(containerId,node);}else {this._widgetContainers.delete(containerId);}},type:type,widgetProps:this.getWidgetProps(id)},makeContainerId(id))}return null},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(!Array.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.outputMarkdown=(ast,state)=>{if(Array.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$2({"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(Array.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$y.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,{allowZoom:true,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$y.mobileZoomableParentFix};const wrappedOutput=jsxRuntimeExports.jsx("div",{style:{...mobileInnerStyle,overflowX:"auto"},children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(Zoomable$1,{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)}});}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){const container=this._widgetContainers.get(makeContainerId(widgetId));if(container){return ReactDOM__default.findDOMNode(container)}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.getWidgetIds=()=>{return this.widgetIds},this.handletranslationLintErrors=lintErrors=>{if(!this._isMounted){return}this.setState({translationLintErrors:lintErrors});};this._translationLinter=new TranslationLinter;this.state={jiptContent:null,translationLintErrors:[],...this._getInitialWidgetState(props)};}};Renderer$1.contextType=PerseusI18nContext;Renderer$1.defaultProps={content:"",widgets:{},images:{},showSolutions:"none",onRender:noopOnRender,findExternalWidgets:()=>[],alwaysUpdate:false,reviewMode:false,linterContext:PerseusLinter.linterContextDefault};const styles$y={mobileZoomableParentFix:{transform:"translate3d(0,0,0)"}};
1679
+ 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(propsA,propsB){function makeItem(props){return splitPerseusItem({question:{content:props.content,widgets:props.widgets,images:{}},hints:[],answerArea:getDefaultAnswerArea()})}return propsA.problemNum!==propsB.problemNum||!_.isEqual(makeItem(propsA),makeItem(propsB))}let Renderer$1 = class Renderer extends React.Component{componentDidMount(){this._isMounted=true;this.handleRender({});this._currentFocus=null;this.props.initializeUserInput?.(this.props.widgets,this.props.problemNum??0);if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}if(this.props.userInput){this.props.apiOptions?.answerableCallback?.(this._isAnswerable());}}UNSAFE_componentWillReceiveProps(nextProps){if(isDifferentQuestion(this.props,nextProps)){this.props.initializeUserInput?.(nextProps.widgets,nextProps.problemNum??0);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}componentDidUpdate(prevProps,prevState){this.handleRender(prevProps);if(this.props.linterContext.highlightLint){this._translationLinter.runLinter(this.props.content,this.handletranslationLintErrors);}if(this.props.userInput&&!_.isEqual(this.props.userInput,prevProps.userInput)){this.props.apiOptions?.answerableCallback?.(this._isAnswerable());}}componentWillUnmount(){this.widgetIds=[];if(this.translationIndex!=null){getDependencies().rendererTranslationComponents.removeComponentAtIndex(this.translationIndex);}this._isMounted=false;}_isAnswerable(){if(this.props.userInput){return this.emptyWidgets().length===0}return false}_getWidgetIndexById(id){const widgetIndex=this.widgetIds.indexOf(id);if(widgetIndex<0){Log.error("Unable to get widget index in _getWidgetIndexById",Errors.Internal,{loggedMetadata:{widgets:JSON.stringify(this.props.widgets),widgetId:JSON.stringify(id)}});return 0}return widgetIndex}getWidgetProps(widgetId){const apiOptions=this.getApiOptions();const widgetOptions=this.props.widgets[widgetId].options;const widgetInfo=this.state.widgetInfo[widgetId];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 {options:widgetOptions,userInput:this.props.userInput?.[widgetId],widgetId:widgetId,widgetIndex:this._getWidgetIndexById(widgetId),alignment:widgetInfo&&widgetInfo.alignment,static:widgetInfo?.static,graded:widgetInfo?.graded,problemNum:this.props.problemNum,apiOptions:this.getApiOptions(),keypadElement:this.props.keypadElement,showSolutions:this.props.showSolutions,onFocus:_.partial(this._onWidgetFocus,widgetId),onBlur:_.partial(this._onWidgetBlur,widgetId),findWidgets:this.findWidgets,reviewMode:this.props.reviewMode??false,containerSizeClass:containerSizeClass.MEDIUM,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"widget"),handleUserInput:newUserInput=>{const updatedUserInput={...this.props.userInput,[widgetId]:newUserInput};const emptyWidgetIds=emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,updatedUserInput,this.context.locale);const widgetsEmpty=emptyWidgetIds.length>0;this.props.handleUserInput?.(widgetId,newUserInput,widgetsEmpty);this.props.apiOptions?.interactionCallback?.(updatedUserInput);},trackInteraction:interactionTracker.track}}getSerializedState(){return mapObject(this.props.widgets,(widgetData,widgetId)=>{const widget=this.getWidgetInstance(widgetId);if(widget&&widget.getSerializedState){return excludeDenylistKeys(widget.getSerializedState())}return widgetData.options})}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)});}}emptyWidgets(){if(!this.props.userInput){throw new Error(`emptyWidgets called without providing userInput to Renderer`)}return emptyWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.props.userInput,this.context.locale)}getUserInputMap(){const userInput=this.props.userInput;if(!userInput){throw new Error(`getUserInputMap called without providing userInput to Renderer`)}return userInput}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(){if(!this.props.userInput){throw new Error(`score called without providing userInput to Renderer`)}const scores=scoreWidgetsFunctional(this.state.widgetInfo,this.widgetIds,this.props.userInput,this.context.locale);const combinedScore=flattenScores(scores);return combinedScore}render(){const apiOptions=this.getApiOptions();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$2({[ClassNames.RENDERER]:true,[ClassNames.RESPONSIVE_RENDERER]:true,[ClassNames.TWO_COLUMN_RENDERER]:this._isTwoColumn});return jsxRuntimeExports.jsx(DefinitionProvider,{children:jsxRuntimeExports.jsx("div",{className:className,children:markdownContents})})}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}},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;return jsxRuntimeExports.jsx(WidgetContainerOld,{id:id,ref:node=>{const containerId=makeContainerId(id);if(node!=null){this._widgetContainers.set(containerId,node);}else {this._widgetContainers.delete(containerId);}},type:type,widgetProps:this.getWidgetProps(id)},makeContainerId(id))}return null},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(!Array.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.outputMarkdown=(ast,state)=>{if(Array.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$2({"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(Array.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$y.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,{allowZoom:true,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$y.mobileZoomableParentFix};const wrappedOutput=jsxRuntimeExports.jsx("div",{style:{...mobileInnerStyle,overflowX:"auto"},children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(Zoomable$1,{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 node=ReactDOM__default.findDOMNode(this);if(node instanceof Element){node.querySelectorAll("img").forEach(image=>{if(oldOnRender!==noopOnRender){image.removeEventListener("load",oldOnRender);}if(onRender!==noopOnRender){image.addEventListener("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)}});}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){const container=this._widgetContainers.get(makeContainerId(widgetId));if(container){return ReactDOM__default.findDOMNode(container)}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.getWidgetIds=()=>{return this.widgetIds},this.handletranslationLintErrors=lintErrors=>{if(!this._isMounted){return}this.setState({translationLintErrors:lintErrors});};this._translationLinter=new TranslationLinter;this.state={jiptContent:null,translationLintErrors:[],...this._getInitialWidgetState(props)};}};Renderer$1.contextType=PerseusI18nContext;Renderer$1.defaultProps={content:"",widgets:{},images:{},showSolutions:"none",onRender:noopOnRender,findExternalWidgets:()=>[],alwaysUpdate:false,reviewMode:false,linterContext:PerseusLinter.linterContextDefault};const styles$y={mobileZoomableParentFix:{transform:"translate3d(0,0,0)"}};
1680
1680
 
1681
1681
  const Renderer=React.forwardRef(function Renderer(props,ref){const rendererFF=isFeatureOn({apiOptions:props.apiOptions},"perseus-renderer-upgrade");return rendererFF?jsxRuntimeExports.jsx(Renderer$2,{ref:ref,...props}):jsxRuntimeExports.jsx(Renderer$1,{ref:ref,...props})});
1682
1682
 
@@ -1688,7 +1688,7 @@ var styles$x = {"inputWithExamples":"perseus_QUlWSKE-","isFocused":"perseus_8i8M
1688
1688
 
1689
1689
  const styles$w={inputWithExamples:{borderRadius:"0.3rem",borderWidth:"var(--wb-border-width-thin)",direction:"ltr",display:"inline-block",fontFamily:'Symbola, "Times New Roman", serif',fontSize:"1.8rem",height:"3.2rem",lineHeight:"1.8rem",padding:"0.4rem 0.5rem",textAlign:"left",width:"8rem"},isFocused:{borderWidth:"var(--wb-border-width-medium)",padding:"0.4rem"},rightAlign:{textAlign:"right"},centerAlign:{textAlign:"center"},sizeSmall:{width:"4rem"}};
1690
1690
 
1691
- 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,percent:(form,strings)=>strings.percentExample,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)})};function normalizeCorrectAnswerForms(answers){if(answers==null){return []}return unionAnswerForms(answers.filter(answer=>answer.status==="correct").map(answer=>{return (answer.answerForms||[]).map(form=>{return {simplify:answer.simplify,name:form}})}))}function getStartUserInput$h(){return {currentValue:""}}function getUserInputFromSerializedState$h(serializedState){return {currentValue:serializedState.currentValue}}function findPrecision(value){for(let i=0;i<10;i++){if(value===+value.toFixed(i)){return i}}return 10}function findCommonFractions(value){const whole=Math.floor(value);if(value===whole){return}const decimal=value-whole;const precision=findPrecision(decimal);for(let num=1;num<100;num++){for(let denom=2;denom<100;denom++){if(+(num/denom).toFixed(precision)===decimal){return {num:num+whole*denom,denom}}}}}function getCorrectUserInput$8(options){for(const answer of options.answers){if(answer.status==="correct"&&answer.value!=null){if(answer.answerForms?.includes("decimal")){return {currentValue:answer.value.toString()}}if(answer.answerForms?.includes("improper")){const frac=findCommonFractions(answer.value);if(frac){return {currentValue:`${frac.num}/${frac.denom}`}}}if(answer.answerForms?.includes("proper")){const frac=findCommonFractions(answer.value);if(frac){const{num,denom}=frac;if(num>denom){const whole=Math.floor(num/denom);const remainder=num-whole*denom;return {currentValue:`${whole} ${remainder}/${denom}`}}else {return {currentValue:`${num}/${denom}`}}}}return {currentValue:answer.value.toString()}}}return {currentValue:""}}function 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]}
1691
+ 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,percent:(form,strings)=>strings.percentExample,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)});const yourAnswerText=uniqueForms.length>1?strings.yourAnswerCanBe:strings.yourAnswer;return [yourAnswerText].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)})};function normalizeCorrectAnswerForms(answers){if(answers==null){return []}return unionAnswerForms(answers.filter(answer=>answer.status==="correct").map(answer=>{return (answer.answerForms||[]).map(form=>{return {simplify:answer.simplify,name:form}})}))}function getStartUserInput$h(){return {currentValue:""}}function getUserInputFromSerializedState$h(serializedState){return {currentValue:serializedState.currentValue}}function findPrecision(value){for(let i=0;i<10;i++){if(value===+value.toFixed(i)){return i}}return 10}function findCommonFractions(value){const whole=Math.floor(value);if(value===whole){return}const decimal=value-whole;const precision=findPrecision(decimal);for(let num=1;num<100;num++){for(let denom=2;denom<100;denom++){if(+(num/denom).toFixed(precision)===decimal){return {num:num+whole*denom,denom}}}}}function getCorrectUserInput$8(options){for(const answer of options.answers){if(answer.status==="correct"&&answer.value!=null){if(answer.answerForms?.includes("decimal")){return {currentValue:answer.value.toString()}}if(answer.answerForms?.includes("improper")){const frac=findCommonFractions(answer.value);if(frac){return {currentValue:`${frac.num}/${frac.denom}`}}}if(answer.answerForms?.includes("proper")){const frac=findCommonFractions(answer.value);if(frac){const{num,denom}=frac;if(num>denom){const whole=Math.floor(num/denom);const remainder=num-whole*denom;return {currentValue:`${whole} ${remainder}/${denom}`}}else {return {currentValue:`${num}/${denom}`}}}}return {currentValue:answer.value.toString()}}}return {currentValue:""}}function 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]}
1692
1692
 
1693
1693
  const NumericInput=forwardRef(function NumericInput(props,ref){const{analytics}=useDependencies();const context=useContext(PerseusI18nContext);const inputRef=useRef(null);const[isFocused,setIsFocused]=useState(false);useOnMountEffect(()=>{analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:"null",widgetType:"numeric-input",widgetId:props.widgetId}});});useImperativeHandle(ref,()=>({focus(){if(inputRef.current){inputRef.current.focus();setIsFocused(true);}return true},focusInputPath(){if(inputRef.current){inputRef.current.focus();setIsFocused(true);}},blurInputPath(){if(inputRef.current){inputRef.current.blur();setIsFocused(false);}},getInputPaths:()=>[[]],getPromptJSON:()=>getPromptJSON$p(props),getSerializedState(){const{userInput,options,...otherProps}=props;const{labelText,answers,...otherOptions}=options;return {...otherOptions,...otherProps,answerForms:[],labelText:labelText??"",currentValue:userInput.currentValue}}}));const{answers,labelText,size,textAlign}=props.options;const answerForms=normalizeCorrectAnswerForms(answers);const handleChange=newValue=>{props.handleUserInput({currentValue:newValue});props.trackInteraction();};const handleFocus=()=>{props.onFocus([]);setIsFocused(true);};const handleBlur=()=>{props.onBlur([]);setIsFocused(false);};const alignmentStyles=textAlign==="center"?styles$w.centerAlign:textAlign==="right"?styles$w.rightAlign:{};const legacyStylesToUse={...styles$w.inputWithExamples,...isFocused?styles$w.isFocused:{},...alignmentStyles,...size==="small"?styles$w.sizeSmall:{}};const classesToUse=[styles$x.inputWithExamples];if(isFocused){classesToUse.push(styles$x.isFocused);}if(textAlign!=="left"){const alignmentClass=textAlign==="center"?styles$x.centerAlign:styles$x.rightAlign;classesToUse.push(alignmentClass);}if(size==="small"){classesToUse.push(styles$x.sizeSmall);}if(props.apiOptions.customKeypad){const alignmentClass=textAlign==="center"?"perseus-input-center-align":textAlign==="right"?"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:labelText||context.strings.yourAnswerLabel,examples:generateExamples(answerForms,context.strings),shouldShowExamples:shouldShowExamples(answerForms),onFocus:handleFocus,onBlur:handleBlur,id:props.widgetId,disabled:props.apiOptions.readOnly,style:legacyStylesToUse,className:classesToUse.join(" ")})});var NumericInput$1 = {name:"numeric-input",displayName:"Numeric input",widget:NumericInput,isLintable:true,getCorrectUserInput: getCorrectUserInput$8,getOneCorrectAnswerFromRubric,getStartUserInput: getStartUserInput$h,getUserInputFromSerializedState: getUserInputFromSerializedState$h};
1694
1694
 
@@ -1935,7 +1935,7 @@ function Hairlines(props){const{point}=props;const{range}=useGraphConfig();const
1935
1935
 
1936
1936
  const MovablePointView=forwardRef(function MovablePointViewWithRef(props,hitboxRef){const{markings,showTooltips,interactiveColor,snapStep}=useGraphConfig();const{point,dragging,focused,hovered,cursor,showFocusRing,onClick=()=>{}}=props;const pointClasses=classNames$1("movable-point",dragging&&"movable-point--dragging",showFocusRing&&"movable-point--focus",hovered&&"movable-point--hover");const[[x,y]]=useTransformVectorsToPixels(point);const showHairlines=(dragging||focused)&&markings!=="none";const xSigFigs=countSignificantDecimals(snapStep[X]);const ySigFigs=countSignificantDecimals(snapStep[Y]);const xTickLabel=point[X].toFixed(xSigFigs);const yTickLabel=point[Y].toFixed(ySigFigs);const pointTooltipContent=`(${xTickLabel}, ${yTickLabel})`;const svgForPoint=jsxRuntimeExports.jsxs("g",{"aria-hidden":true,ref:hitboxRef,className:pointClasses,style:{"--movable-point-color":interactiveColor,cursor},"data-testid":"movable-point",onClick:onClick,children:[jsxRuntimeExports.jsx("circle",{className:"movable-point-halo",cx:x,cy:y}),jsxRuntimeExports.jsx("circle",{className:"movable-point-ring",cx:x,cy:y}),jsxRuntimeExports.jsx("circle",{className:"movable-point-focus-outline",cx:x,cy:y}),jsxRuntimeExports.jsx("circle",{className:"movable-point-center",cx:x,cy:y,style:{fill:interactiveColor},"data-testid":"movable-point__center"})]});return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[showHairlines&&jsxRuntimeExports.jsx(Hairlines,{point:point}),showTooltips?jsxRuntimeExports.jsx(Tooltip,{autoUpdate:true,opened:true,variant:"strong",content:pointTooltipContent,children:svgForPoint}):svgForPoint]})});function classNames$1(...names){return names.filter(Boolean).join(" ")}
1937
1937
 
1938
- function useControlPoint(params){const{snapStep,disableKeyboardInteraction}=useGraphConfig();const{point,ariaDescribedBy,ariaLabel,constrain=p=>snap(snapStep,p),cursor,forwardedRef=noop$1,sequenceNumber=1,onMove=noop$1,onDragStart=noop$1,onDragEnd=noop$1,onClick=noop$1,onFocus=noop$1,onBlur=noop$1}=params;const{strings,locale}=usePerseusI18n();const[focused,setFocused]=useState(false);const[hovered,setHovered]=useState(false);const focusableHandleRef=useRef(null);useDraggable({gestureTarget:focusableHandleRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const visiblePointRef=useRef(null);const hitboxDivRef=useRef(null);const{dragging}=useDraggable({gestureTarget:hitboxDivRef,point,onMove,onDragStart,onDragEnd,constrainKeyboardMovement:constrain});const focusPoint=()=>{onClick();focusableHandleRef.current?.focus();};const hitbox=useHitbox({shape:{kind:"box",center:point,sizePx:HANDLE_HITBOX_SIZE_PX},hitboxRef:hitboxDivRef,cursor,dragging,onClick:focusPoint,onHoverChange:setHovered,testId:"movable-point__hitbox"});const pointAriaLabel=ariaLabel||strings.srPointAtCoordinates({pointLabel:`${sequenceNumber}`,x:srFormatNumber(point[X],locale),y:srFormatNumber(point[Y],locale)});useLayoutEffect(()=>{setForwardedRef(forwardedRef,focusableHandleRef.current);},[forwardedRef]);useLayoutEffect(()=>{if(dragging&&!focused){focusableHandleRef.current?.focus({preventScroll:true});}},[dragging,focused]);const focusableHandle=jsxRuntimeExports.jsx("g",{"data-testid":"movable-point__focusable-handle",className:"movable-point__focusable-handle",tabIndex:disableKeyboardInteraction?-1:0,ref:focusableHandleRef,role:"button","aria-describedby":ariaDescribedBy,"aria-label":pointAriaLabel,"aria-disabled":disableKeyboardInteraction,onFocus:event=>{onFocus(event);setFocused(true);},onBlur:event=>{onBlur(event);setFocused(false);}});const visiblePoint=jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[hitbox,jsxRuntimeExports.jsx(MovablePointView,{cursor:cursor,onClick:focusPoint,point:point,dragging:dragging,focused:focused,hovered:hovered,ref:visiblePointRef,showFocusRing:focused})]});return {focusableHandle,visiblePoint,focusableHandleRef,visiblePointRef}}function setForwardedRef(ref,value){if(typeof ref==="function"){ref(value);}else if(ref!==null){ref.current=value;}}const noop$1=()=>{};
1938
+ function useControlPoint(params){const{snapStep,disableKeyboardInteraction}=useGraphConfig();const{point,ariaDescribedBy,ariaLabel,constrain=p=>snap(snapStep,p),cursor,forwardedRef=noop$2,sequenceNumber=1,onMove=noop$2,onDragStart=noop$2,onDragEnd=noop$2,onClick=noop$2,onFocus=noop$2,onBlur=noop$2}=params;const{strings,locale}=usePerseusI18n();const[focused,setFocused]=useState(false);const[hovered,setHovered]=useState(false);const focusableHandleRef=useRef(null);useDraggable({gestureTarget:focusableHandleRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const visiblePointRef=useRef(null);const hitboxDivRef=useRef(null);const{dragging}=useDraggable({gestureTarget:hitboxDivRef,point,onMove,onDragStart,onDragEnd,constrainKeyboardMovement:constrain});const focusPoint=()=>{onClick();focusableHandleRef.current?.focus();};const hitbox=useHitbox({shape:{kind:"box",center:point,sizePx:HANDLE_HITBOX_SIZE_PX},hitboxRef:hitboxDivRef,cursor,dragging,onClick:focusPoint,onHoverChange:setHovered,testId:"movable-point__hitbox"});const pointAriaLabel=ariaLabel||strings.srPointAtCoordinates({pointLabel:`${sequenceNumber}`,x:srFormatNumber(point[X],locale),y:srFormatNumber(point[Y],locale)});useLayoutEffect(()=>{setForwardedRef(forwardedRef,focusableHandleRef.current);},[forwardedRef]);useLayoutEffect(()=>{if(dragging&&!focused){focusableHandleRef.current?.focus({preventScroll:true});}},[dragging,focused]);const focusableHandle=jsxRuntimeExports.jsx("g",{"data-testid":"movable-point__focusable-handle",className:"movable-point__focusable-handle",tabIndex:disableKeyboardInteraction?-1:0,ref:focusableHandleRef,role:"button","aria-describedby":ariaDescribedBy,"aria-label":pointAriaLabel,"aria-disabled":disableKeyboardInteraction,onFocus:event=>{onFocus(event);setFocused(true);},onBlur:event=>{onBlur(event);setFocused(false);}});const visiblePoint=jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[hitbox,jsxRuntimeExports.jsx(MovablePointView,{cursor:cursor,onClick:focusPoint,point:point,dragging:dragging,focused:focused,hovered:hovered,ref:visiblePointRef,showFocusRing:focused})]});return {focusableHandle,visiblePoint,focusableHandleRef,visiblePointRef}}function setForwardedRef(ref,value){if(typeof ref==="function"){ref(value);}else if(ref!==null){ref.current=value;}}const noop$2=()=>{};
1939
1939
 
1940
1940
  const MovablePoint$3=React.forwardRef(function MovablePointWithRef(props,pointRef){const{visiblePoint,focusableHandle}=useControlPoint({...props,forwardedRef:pointRef});return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[focusableHandle,visiblePoint]})});
1941
1941
 
@@ -2001,7 +2001,7 @@ function renderTangentGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports
2001
2001
 
2002
2002
  const ARROW_SCALE=1.5;const arrowPath=pathBuilder().move(-5,5).line(0,0).line(-5,-5).scale(ARROW_SCALE).build();function buildRoundedTriangle(tipX,tipY,armX,armY,backX,backY,backR,tipR){return pathBuilder().move(tipX,tipY).line(armX,armY).circularArc(backR,backX,backY,{sweep:true}).line(backX,-5).circularArc(backR,armX,-7.8,{sweep:true}).line(tipX,-2.8).circularArc(tipR,tipX,tipY,{sweep:true}).scale(ARROW_SCALE).build()}const arrowPathHalo=buildRoundedTriangle(2.8,2.8,-2.2,7.8,-9,5,4,4);const chevronPathAttrs={d:arrowPath,fill:"none",strokeLinejoin:"round",strokeLinecap:"round"};const MovableArrowheadView=forwardRef(function MovableArrowheadViewWithRef(props,hitboxRef){const{showTooltips,interactiveColor,snapStep}=useGraphConfig();const{point,angle,dragging,hovered,cursor,showFocusRing,onClick=()=>{}}=props;const classes=classNames("movable-arrowhead",dragging&&"movable-arrowhead--dragging",showFocusRing&&"movable-arrowhead--focus",hovered&&"movable-arrowhead--hover");const[[x,y]]=useTransformVectorsToPixels(point);const xSigFigs=countSignificantDecimals(snapStep[X]);const ySigFigs=countSignificantDecimals(snapStep[Y]);const xTickLabel=point[X].toFixed(xSigFigs);const yTickLabel=point[Y].toFixed(ySigFigs);const tooltipContent=`(${xTickLabel}, ${yTickLabel})`;const svgForArrowhead=jsxRuntimeExports.jsx("g",{"aria-hidden":true,ref:hitboxRef,className:classes,style:{"--movable-arrowhead-color":interactiveColor,cursor},"data-testid":"movable-arrowhead",onClick:onClick,children:jsxRuntimeExports.jsxs("g",{transform:`translate(${x} ${y}) rotate(${angle})`,children:[jsxRuntimeExports.jsx("path",{d:arrowPathHalo,className:"movable-arrowhead-halo"}),jsxRuntimeExports.jsx("path",{...chevronPathAttrs,className:"movable-arrowhead-ring"}),jsxRuntimeExports.jsx("path",{...chevronPathAttrs,className:"movable-arrowhead-center"})]})});return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:showTooltips?jsxRuntimeExports.jsx(Tooltip,{autoUpdate:true,opened:true,content:tooltipContent,variant:"strong",children:svgForArrowhead}):svgForArrowhead})});function classNames(...names){return names.filter(Boolean).join(" ")}
2003
2003
 
2004
- function useControlArrowhead(params){const{snapStep,disableKeyboardInteraction}=useGraphConfig();const{point,angle,ariaDescribedBy,ariaLabel,constrain=p=>snap(snapStep,p),sequenceNumber=1,onMove=noop,onDragEnd=noop}=params;const{strings,locale}=usePerseusI18n();const[focused,setFocused]=useState(false);const[hovered,setHovered]=useState(false);const focusableHandleRef=useRef(null);useDraggable({gestureTarget:focusableHandleRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const visibleRef=useRef(null);const arrowheadHitboxRef=useRef(null);const{dragging}=useDraggable({gestureTarget:arrowheadHitboxRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const hitbox=useHitbox({shape:{kind:"box",center:point,sizePx:HANDLE_HITBOX_SIZE_PX},hitboxRef:arrowheadHitboxRef,dragging,onClick:()=>focusableHandleRef.current?.focus(),onHoverChange:setHovered,testId:"movable-arrowhead__hitbox"});const pointAriaLabel=ariaLabel||strings.srPointAtCoordinates({pointLabel:`${sequenceNumber}`,x:srFormatNumber(point[X],locale),y:srFormatNumber(point[Y],locale)});useLayoutEffect(()=>{if(dragging&&!focused){focusableHandleRef.current?.focus();}},[dragging,focused]);const focusableHandle=jsxRuntimeExports.jsx("g",{"data-testid":"movable-arrowhead__focusable-handle",className:"movable-point__focusable-handle",tabIndex:disableKeyboardInteraction?-1:0,ref:focusableHandleRef,role:"button","aria-describedby":ariaDescribedBy,"aria-label":pointAriaLabel,"aria-disabled":disableKeyboardInteraction,onFocus:()=>setFocused(true),onBlur:()=>setFocused(false)});const visibleArrowhead=jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[hitbox,jsxRuntimeExports.jsx(MovableArrowheadView,{point:point,angle:angle,dragging:dragging,focused:focused,hovered:hovered,ref:visibleRef,showFocusRing:focused,onClick:()=>{focusableHandleRef.current?.focus();}})]});return {focusableHandle,visibleArrowhead,dragging,focused}}const noop=()=>{};
2004
+ function useControlArrowhead(params){const{snapStep,disableKeyboardInteraction}=useGraphConfig();const{point,angle,ariaDescribedBy,ariaLabel,constrain=p=>snap(snapStep,p),sequenceNumber=1,onMove=noop$1,onDragEnd=noop$1}=params;const{strings,locale}=usePerseusI18n();const[focused,setFocused]=useState(false);const[hovered,setHovered]=useState(false);const focusableHandleRef=useRef(null);useDraggable({gestureTarget:focusableHandleRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const visibleRef=useRef(null);const arrowheadHitboxRef=useRef(null);const{dragging}=useDraggable({gestureTarget:arrowheadHitboxRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const hitbox=useHitbox({shape:{kind:"box",center:point,sizePx:HANDLE_HITBOX_SIZE_PX},hitboxRef:arrowheadHitboxRef,dragging,onClick:()=>focusableHandleRef.current?.focus(),onHoverChange:setHovered,testId:"movable-arrowhead__hitbox"});const pointAriaLabel=ariaLabel||strings.srPointAtCoordinates({pointLabel:`${sequenceNumber}`,x:srFormatNumber(point[X],locale),y:srFormatNumber(point[Y],locale)});useLayoutEffect(()=>{if(dragging&&!focused){focusableHandleRef.current?.focus();}},[dragging,focused]);const focusableHandle=jsxRuntimeExports.jsx("g",{"data-testid":"movable-arrowhead__focusable-handle",className:"movable-point__focusable-handle",tabIndex:disableKeyboardInteraction?-1:0,ref:focusableHandleRef,role:"button","aria-describedby":ariaDescribedBy,"aria-label":pointAriaLabel,"aria-disabled":disableKeyboardInteraction,onFocus:()=>setFocused(true),onBlur:()=>setFocused(false)});const visibleArrowhead=jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[hitbox,jsxRuntimeExports.jsx(MovableArrowheadView,{point:point,angle:angle,dragging:dragging,focused:focused,hovered:hovered,ref:visibleRef,showFocusRing:focused,onClick:()=>{focusableHandleRef.current?.focus();}})]});return {focusableHandle,visibleArrowhead,dragging,focused}}const noop$1=()=>{};
2005
2005
 
2006
2006
  const{calculateAngleInDegrees: calculateAngleInDegrees$1}=angles;const LINE_PULLBACK_PX=4;function renderVectorGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(VectorGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:describeVectorGraph(state,i18n).srVectorInteractiveElement}}const VectorGraph=props=>{const{dispatch}=props;const{coords}=props.graphState;const[tail,tip]=coords;const{strings,locale}=usePerseusI18n();const{markings}=useGraphConfig();const id=React.useId();const pointsDescriptionId=id+"-points";const{srVectorGraph,srVectorDescription,srVectorHeadPoint,srVectorGrabHandle}=describeVectorGraph(props.graphState,{strings,locale});const tipArrowhead=useTipArrowhead({tail,tip,ariaLabel:srVectorHeadPoint,ariaDescribedBy:pointsDescriptionId,onMove:destination=>dispatch(actions.vector.moveTip(destination))});const showHairlines=(tipArrowhead.dragging||tipArrowhead.focused)&&markings!=="none";return jsxRuntimeExports.jsxs("g",{"aria-label":srVectorGraph,"aria-describedby":pointsDescriptionId,children:[showHairlines&&jsxRuntimeExports.jsx(Hairlines,{point:tip}),jsxRuntimeExports.jsx(VectorBody,{tail:tail,tip:tip,ariaLabel:srVectorGrabHandle,ariaDescribedBy:pointsDescriptionId,onMove:newStart=>dispatch(actions.vector.moveVector(newStart))}),tipArrowhead.focusableHandle,tipArrowhead.visibleArrowhead,jsxRuntimeExports.jsx(SRDescInSVG,{id:pointsDescriptionId,children:srVectorDescription})]})};const VectorBody=props=>{const{tail,tip,ariaLabel,ariaDescribedBy,onMove}=props;const{snapStep,disableKeyboardInteraction,interactiveColor}=useGraphConfig();const[hovered,setHovered]=useState(false);const[focused,setFocused]=useState(false);const[tailPx,tipPx]=useTransformVectorsToPixels(tail,tip);const bodyRef=useRef(null);const vectorHitboxRef=useRef(null);useDraggable({gestureTarget:bodyRef,point:tail,onMove,constrainKeyboardMovement:p=>snap(snapStep,p)});const{dragging}=useDraggable({gestureTarget:vectorHitboxRef,point:tail,onMove,onDragEnd:()=>{bodyRef.current?.blur();},constrainKeyboardMovement:p=>snap(snapStep,p)});const vectorHitbox=useHitbox({shape:{kind:"line",start:tail,end:tip,thicknessPx:TARGET_SIZE},hitboxRef:vectorHitboxRef,layer:"body",dragging,onHoverChange:setHovered,testId:"movable-vector__hitbox"});const direction=vec.sub(tipPx,tailPx);const dirMag=vec.mag(direction);const angleDeg=calculateAngleInDegrees$1(direction);const lineEndPx=dirMag>0?[tipPx[X]-direction[X]/dirMag*LINE_PULLBACK_PX,tipPx[Y]-direction[Y]/dirMag*LINE_PULLBACK_PX]:tipPx;const handleT=1/2;const handlePx=[tailPx[X]+(tipPx[X]-tailPx[X])*handleT,tailPx[Y]+(tipPx[Y]-tailPx[Y])*handleT];const active=hovered||dragging||focused;return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[vectorHitbox,jsxRuntimeExports.jsxs("g",{ref:bodyRef,tabIndex:disableKeyboardInteraction?-1:0,"aria-label":ariaLabel,"aria-describedby":ariaDescribedBy,"aria-disabled":disableKeyboardInteraction,className:hovered?"movable-line movable-line--hover":"movable-line","data-testid":"movable-vector",style:{cursor:dragging?"grabbing":"grab"},role:"button",onFocus:()=>setFocused(true),onBlur:()=>setFocused(false),children:[jsxRuntimeExports.jsx(SVGLine,{start:tailPx,end:lineEndPx,className:`movable-vector-line ${active?"movable-dragging":""}`,style:{stroke:interactiveColor,strokeWidth:sizing.size_020},testId:"movable-vector__line"}),active&&jsxRuntimeExports.jsx(MovablePillHandle,{center:handlePx,rotation:angleDeg,active:active,focused:focused})]})]})};function useTipArrowhead(params){const{tail,tip,ariaLabel,ariaDescribedBy,onMove}=params;const{snapStep}=useGraphConfig();const[tailPx,tipPx]=useTransformVectorsToPixels(tail,tip);const direction=vec.sub(tipPx,tailPx);const angleDeg=calculateAngleInDegrees$1(direction);return useControlArrowhead({ariaLabel,ariaDescribedBy,point:tip,angle:angleDeg,sequenceNumber:1,onMove,constrain:getVectorTipKeyboardConstraint(tail,tip,snapStep)})}const getVectorTipKeyboardConstraint=(tail,tip,snapStep)=>{const moveWithConstraint=moveFunc=>{let moved=moveFunc(tip);if(vec.dist(moved,tail)===0){moved=moveFunc(moved);}return moved};return {up:moveWithConstraint(coord=>vec.add(coord,[0,snapStep[Y]])),down:moveWithConstraint(coord=>vec.sub(coord,[0,snapStep[Y]])),left:moveWithConstraint(coord=>vec.sub(coord,[snapStep[X],0])),right:moveWithConstraint(coord=>vec.add(coord,[snapStep[X],0]))}};
2007
2007
 
@@ -2021,7 +2021,7 @@ class InteractiveGraph extends React.Component{static getEquationString(props){r
2021
2021
 
2022
2022
  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})}};
2023
2023
 
2024
- 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.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||!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,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 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=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$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.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,options,...rest}=this.props;const{correct,...optionsRest}=options;return {...optionsRest,...rest,plot:this.props.userInput}}getAvailableTypes(){const{correct,availableTypes}=this.props.options;if(this.props.static){invariant(correct,"static widgets must have a correct answer");return [correct.type]}return 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{graph}=this.props.options;const options={...graph,...getGridAndSnapSteps(graph,box[0]),gridConfig:this._getGridConfig({...graph,box:box,...getGridAndSnapSteps(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,isMobile:this.props.apiOptions.isMobile,showTooltips:graph.showTooltips,showHairlines:this.showHairlines,hideHairlines:this.hideHairlines};return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx(FunctionGrapher,{...grapherProps}),availableTypes.length>1&&typeSelector]})}render(){const{options,...universalProps}=this.props;const interactiveGraphOptions=convertGrapherOptionsToInteractiveGraph(options);if(interactiveGraphOptions==null){return this.renderLegacyGrapher()}const interactiveGraphUserInput=convertGrapherUserInputToInteractiveGraph(this.props.userInput);return jsxRuntimeExports.jsx(InteractiveGraph$1.widget,{...universalProps,options:interactiveGraphOptions,userInput:interactiveGraphUserInput,handleUserInput:interactiveGraphUserInput=>this.props.handleUserInput(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.options;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=>{const{graph}=this.props.options;if(this.props.apiOptions.isMobile){this.horizHairline.moveTo([graph.range[0][0],point[1]],[graph.range[0][1],point[1]]);this.horizHairline.show();this.vertHairline.moveTo([point[0],graph.range[1][0]],[point[0],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(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};
2024
+ 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.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||!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,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:tokenValue(this.props.static?semanticColor.core.foreground.disabled.strong:semanticColor.core.foreground.instructive.default),...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={"stroke-dasharray":"- "};return showAsymptote&&jsxRuntimeExports.jsx(MovableLine$1,{static:this.props.static,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$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.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,options,...rest}=this.props;const{correct,...optionsRest}=options;return {...optionsRest,...rest,plot:this.props.userInput}}getAvailableTypes(){const{correct,availableTypes}=this.props.options;if(this.props.static){invariant(correct,"static widgets must have a correct answer");return [correct.type]}return 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{graph}=this.props.options;const options={...graph,...getGridAndSnapSteps(graph,box[0]),gridConfig:this._getGridConfig({...graph,box:box,...getGridAndSnapSteps(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,isMobile:this.props.apiOptions.isMobile,showTooltips:graph.showTooltips,showHairlines:this.showHairlines,hideHairlines:this.hideHairlines};return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx(FunctionGrapher,{...grapherProps}),availableTypes.length>1&&typeSelector]})}render(){const{options,...universalProps}=this.props;const interactiveGraphOptions=convertGrapherOptionsToInteractiveGraph(options);if(interactiveGraphOptions==null){return this.renderLegacyGrapher()}const interactiveGraphUserInput=convertGrapherUserInputToInteractiveGraph(this.props.userInput);return jsxRuntimeExports.jsx(InteractiveGraph$1.widget,{...universalProps,options:interactiveGraphOptions,userInput:interactiveGraphUserInput,handleUserInput:interactiveGraphUserInput=>this.props.handleUserInput(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.options;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)});}const hairlineStyle={normalStyle:{strokeWidth:1}};this.horizHairline=new WrappedLine(graphie,[0,0],[0,0],hairlineStyle);this.horizHairline.attr({stroke:tokenValue(semanticColor.core.border.instructive.default)});this.horizHairline.hide();this.vertHairline=new WrappedLine(graphie,[0,0],[0,0],hairlineStyle);this.vertHairline.attr({stroke:tokenValue(semanticColor.core.border.instructive.default)});this.vertHairline.hide();},this.showHairlines=point=>{const{graph}=this.props.options;this.horizHairline.moveTo([graph.range[0][0],point[1]],[graph.range[0][1],point[1]]);this.horizHairline.show();this.vertHairline.moveTo([point[0],graph.range[1][0]],[point[0],graph.range[1][1]]);this.vertHairline.show();},this.hideHairlines=()=>{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(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};
2025
2025
 
2026
2026
  const getPromptJSON$e=rendererJSON=>{if(!rendererJSON){return {type:"group",content:"",widgets:{}}}return {...rendererJSON,type:"group"}};
2027
2027
 
@@ -2029,7 +2029,7 @@ class Group extends React.Component{componentDidMount(){this.forceUpdate();}getP
2029
2029
 
2030
2030
  const getPromptJSON$d=()=>{return getUnsupportedPromptJSON("iframe")};
2031
2031
 
2032
- const{updateQueryString}=Util;class Iframe extends React.Component{componentDidMount(){$(window).on("message",this.handleMessageEvent);}componentWillUnmount(){$(window).off("message",this.handleMessageEvent);}getPromptJSON(){return getPromptJSON$d()}getSerializedState(){const{userInput,alignment,options,...rest}=this.props;const defaults={allowTopNavigation:false};return {...defaults,...options,...rest}}render(){const{width,height,allowFullScreen}=this.props.options;const style={width:String(width),height:String(height)};const{InitialRequestUrl}=getDependencies();Object.entries(style).forEach(([key,value])=>{if(!value.endsWith("%")&&!value.endsWith("px")){style[key]=value+"px";}});let url=this.props.options.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",`${width}`);url=updateQueryString(url,"height",`${height}`);url=updateQueryString(url,"origin",InitialRequestUrl.origin);}if(this.context?.locale&&url?.includes("khanacademy.org")){url=updateQueryString(url,"lang",this.context.locale);}if(this.props.options.settings){const settings={};this.props.options.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",{title:this.context.strings.embeddedContent,sandbox:sandboxProperties,style:style,src:url,allowFullScreen: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.contextType=PerseusI18nContext;Iframe.defaultProps={userInput:{status:"incomplete",message:null}};function getUserInputFromSerializedState$8(serializedState){return {status:serializedState.status,message:serializedState.message}}function getStartUserInput$8(){return {status:"incomplete",message:null}}var Iframe$1 = {name:"iframe",displayName:"Iframe (deprecated)",widget:Iframe,hidden:true,getStartUserInput: getStartUserInput$8,getUserInputFromSerializedState: getUserInputFromSerializedState$8};
2032
+ const{updateQueryString}=Util;class Iframe extends React.Component{componentDidMount(){window.addEventListener("message",this.handleMessageEvent);}componentWillUnmount(){window.removeEventListener("message",this.handleMessageEvent);}getPromptJSON(){return getPromptJSON$d()}getSerializedState(){const{userInput,alignment,options,...rest}=this.props;const defaults={allowTopNavigation:false};return {...defaults,...options,...rest}}render(){const{width,height,allowFullScreen}=this.props.options;const style={width:String(width),height:String(height)};const{InitialRequestUrl}=getDependencies();Object.entries(style).forEach(([key,value])=>{if(!value.endsWith("%")&&!value.endsWith("px")){style[key]=value+"px";}});let url=this.props.options.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",`${width}`);url=updateQueryString(url,"height",`${height}`);url=updateQueryString(url,"origin",InitialRequestUrl.origin);}if(this.context?.locale&&url?.includes("khanacademy.org")){url=updateQueryString(url,"lang",this.context.locale);}if(this.props.options.settings){const settings={};this.props.options.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",{title:this.context.strings.embeddedContent,sandbox:sandboxProperties,style:style,src:url,allowFullScreen:allowFullScreen})}constructor(...args){super(...args),this.handleMessageEvent=e=>{let data={};try{data=JSON.parse(e.data);}catch{return}if(data.testsPassed===undefined){return}const status=data.testsPassed?"correct":"incorrect";this.props.handleUserInput({status:status,message:data.message});};}}Iframe.contextType=PerseusI18nContext;Iframe.defaultProps={userInput:{status:"incomplete",message:null}};function getUserInputFromSerializedState$8(serializedState){return {status:serializedState.status,message:serializedState.message}}function getStartUserInput$8(){return {status:"incomplete",message:null}}var Iframe$1 = {name:"iframe",displayName:"Iframe (deprecated)",widget:Iframe,hidden:true,getStartUserInput: getStartUserInput$8,getUserInputFromSerializedState: getUserInputFromSerializedState$8};
2033
2033
 
2034
2034
  const getPromptJSON$c=widgetData=>{return {type:"image",options:{altText:widgetData.options.alt,title:widgetData.options.title,caption:widgetData.options.caption,imageUrl:widgetData.options.backgroundImage.url}}};
2035
2035
 
@@ -2079,15 +2079,15 @@ const{assert: assert$1}=InteractiveUtil;const NORMAL_DIMENSIONS={INPUT_MARGIN:3,
2079
2079
 
2080
2080
  const getPromptJSON$7=()=>{return getUnsupportedPromptJSON("measurer")};
2081
2081
 
2082
- class Measurer extends React.Component{componentDidMount(){this.setupGraphie();}componentDidUpdate(prevProps){const propsAffectingGraphie=["box","showProtractor","showRuler","rulerLabel","rulerTicks","rulerPixels","rulerLength"];const shouldSetupGraphie=propsAffectingGraphie.some(prop=>prevProps.options[prop]!==this.props.options[prop]);if(shouldSetupGraphie){this.setupGraphie();}}setupGraphie(){const graphieDiv=ReactDOM__default.findDOMNode(this.refs.graphieDiv);$(graphieDiv).empty();const graphie=this.graphie=GraphUtils.createGraphie(graphieDiv);const scale=[40,40];const range=[[0,this.props.options.box[0]/scale[0]],[0,this.props.options.box[1]/scale[1]]];graphie.init({range:range,scale:scale});graphie.addMouseLayer({allowScratchpad:true});if(this.protractor){this.protractor.remove();}if(this.props.options.showProtractor){this.protractor=graphie.protractor([7.5,.5]);}if(this.ruler){this.ruler.remove();}if(this.props.options.showRuler){this.ruler=graphie.ruler({center:[(range[0][0]+range[0][1])/2,(range[1][0]+range[1][1])/2],label:this.props.options.rulerLabel,pixelsPerUnit:this.props.options.rulerPixels,ticksPerUnit:this.props.options.rulerTicks,units:this.props.options.rulerLength});}}getPromptJSON(){return getPromptJSON$7()}render(){const{image}=this.props.options;return jsxRuntimeExports.jsxs("div",{className:"perseus-widget perseus-widget-measurer "+"graphie-container blank-background",style:{width:this.props.options.box[0],height:this.props.options.box[1]},children:[image.url&&jsxRuntimeExports.jsx("div",{style:{position:"relative",top:image.top??0,left:image.left??0},children:jsxRuntimeExports.jsx(SvgImage,{src:image.url,allowZoom:false})}),jsxRuntimeExports.jsx("div",{className:"graphie",ref:"graphieDiv"})]})}constructor(...args){super(...args),this.isWidget=true;}}var Measurer$1 = {name:"measurer",displayName:"Measurer",hidden:true,widget:Measurer};
2082
+ class Measurer extends React.Component{componentDidMount(){this.setupGraphie();}componentDidUpdate(prevProps){const propsAffectingGraphie=["box","showProtractor","showRuler","rulerLabel","rulerTicks","rulerPixels","rulerLength"];const shouldSetupGraphie=propsAffectingGraphie.some(prop=>prevProps.options[prop]!==this.props.options[prop]);if(shouldSetupGraphie){this.setupGraphie();}}setupGraphie(){const graphieDiv=this.graphieDivRef.current;if(graphieDiv==null){throw new Error("No graphie container div found")}graphieDiv.innerHTML="";const graphie=GraphUtils.createGraphie(graphieDiv);const scale=[40,40];const range=[[0,this.props.options.box[0]/scale[0]],[0,this.props.options.box[1]/scale[1]]];graphie.init({range:range,scale:scale});graphie.addMouseLayer({allowScratchpad:true});if(this.protractor){this.protractor.remove();}if(this.props.options.showProtractor){this.protractor=graphie.protractor([7.5,.5]);}if(this.ruler){this.ruler.remove();}if(this.props.options.showRuler){this.ruler=graphie.ruler({center:[(range[0][0]+range[0][1])/2,(range[1][0]+range[1][1])/2],label:this.props.options.rulerLabel,pixelsPerUnit:this.props.options.rulerPixels,ticksPerUnit:this.props.options.rulerTicks,units:this.props.options.rulerLength});}}getPromptJSON(){return getPromptJSON$7()}render(){const{image}=this.props.options;return jsxRuntimeExports.jsxs("div",{className:"perseus-widget perseus-widget-measurer "+"graphie-container blank-background",style:{width:this.props.options.box[0],height:this.props.options.box[1]},children:[image.url&&jsxRuntimeExports.jsx("div",{style:{position:"relative",top:image.top??0,left:image.left??0},children:jsxRuntimeExports.jsx(SvgImage,{src:image.url,allowZoom:false})}),jsxRuntimeExports.jsx("div",{className:"graphie",ref:this.graphieDivRef})]})}constructor(...args){super(...args),this.isWidget=true,this.graphieDivRef=React.createRef();}}var Measurer$1 = {name:"measurer",displayName:"Measurer",hidden:true,widget:Measurer};
2083
2083
 
2084
2084
  const getPromptJSON$6=widgetData=>{const{userInput}=widgetData;return {type:"number-line",options:{range:widgetData.options.range,snapDivisions:widgetData.options.snapDivisions},userInput:{numLinePosition:userInput.numLinePosition,numDivisions:userInput.numDivisions,rel:userInput.rel}}};
2085
2085
 
2086
- const MovablePoint=Graphie.MovablePoint;const Line=Graphie.Line;const{assert}=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"){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(!Number.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=fractions.map(getDenom);base=denoms.reduce((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){graphie.style(textStyle,()=>{results.push(_label(graphie,labelStyle,tick,tick,base));});}});return results});function isValid(props){const{range,divisionRange,snapDivisions}=props.options;let initialX=props.userInput?.numLinePosition;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<props.userInput?.numDivisions&&0<snapDivisions}const NumberLine=forwardRef(function NumberLine(props,ref){const{strings}=usePerseusI18n();const propsRef=useLatestRef(props);const[numDivisionsEmpty,setNumDivisionsEmpty]=useState(false);const tickStep=getTickStep(props.options.range,props.userInput.numDivisions);const graphieRef=useRef(null);const tickCtrlRef=useRef(null);useOnMountEffect(()=>{props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:"null",widgetType:"number-line",widgetId:props.widgetId}});});useImperativeHandle(ref,()=>({focus:()=>{if(props.options.isTickCtrl){tickCtrlRef.current?.focus();return true}return false},focusInputPath:path=>{if(path?.length===1){tickCtrlRef.current?.focus();}},blurInputPath:path=>{if(path?.length===1){tickCtrlRef.current?.blur();}},getInputPaths:()=>{if(props.options.isTickCtrl){return [["tick-ctrl"]]}return []},getDOMNodeForPath:inputPath=>{if(inputPath?.length===1){return ReactDOM__default.findDOMNode(tickCtrlRef.current)}return null},getSerializedState:()=>({alignment:props.alignment,static:props.static,range:props.options.range,labelRange:props.options.labelRange,labelStyle:props.options.labelStyle,labelTicks:props.options.labelTicks,divisionRange:props.options.divisionRange,snapDivisions:props.options.snapDivisions,isInequality:props.options.isInequality,showTooltips:props.options.showTooltips,isTickCtrl:props.options.isTickCtrl,numDivisions:props.userInput.numDivisions,numLinePosition:props.userInput.numLinePosition,rel:"ge"}),getPromptJSON(){return getPromptJSON$6(props)},movePosition}));const setupGraphie=useCallback((graphie,options)=>{const latestProps=propsRef.current;if(!isValid(latestProps)){return}const widthInPixels=latestProps.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{labelStyle}=latestProps.options;const hasFractionalLabels=labelStyle==="improper"||labelStyle==="mixed"||labelStyle==="non-reduced";const bottom=hasFractionalLabels?-1.5:-1;const top=1;graphie.init({range:[[left,right],[bottom,top]],scale:[1/scale,40],isMobile:latestProps.apiOptions.isMobile});const center=(range[0]+range[1])/2;graphie.line([center,0],[right,0],{arrows:"->"});graphie.line([center,0],[left,0],{arrows:"->"});},[propsRef]);function snapNumLinePosition(tickStep,numLinePosition){const{range,snapDivisions}=props.options;const left=range[0];const right=range[1];const snapX=tickStep/snapDivisions;let x=bound(numLinePosition,left,right);x=left+number.roundTo(x-left,snapX);assert(Number.isFinite(x));return x}function onNumDivisionsChange(numDivisions,cb){numDivisions=Math.round(numDivisions);numDivisions=numDivisions<0?numDivisions*-1:numDivisions;if(numDivisions){const newTickStep=getTickStep(props.options.range,numDivisions);const newNumLinePosition=snapNumLinePosition(newTickStep,props.userInput.numLinePosition);setNumDivisionsEmpty(false);props.handleUserInput({...props.userInput,numDivisions:numDivisions,numLinePosition:newNumLinePosition},cb);}else {setNumDivisionsEmpty(true);cb?.();}}function handleTickCtrlFocus(){props.onFocus(["tick-ctrl"]);}function handleTickCtrlBlur(){props.onBlur(["tick-ctrl"]);}function movePosition(targetPosition){props.handleUserInput({...props.userInput,numLinePosition:targetPosition});props.trackInteraction();}function handleReverse(){const newRel=reverseRel[props.userInput.rel];props.handleUserInput({...props.userInput,rel:newRel});}function handleToggleStrict(){const newRel=toggleStrictRel[props.userInput.rel];props.handleUserInput({...props.userInput,rel:newRel});}function renderNumberLinePoint(){const isOpen=["lt","gt"].includes(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.options.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=snapNumLinePosition(tickStep,coord[0]);return [x,coord[1]]}],normalStyle:normalStyle,highlightStyle:highlightStyle,onMove:coord=>{movePosition(coord[0]);},isMobile:props.apiOptions.isMobile,mobileStyleOverride:mobileDotStyle,showTooltips:props.options.showTooltips??false,xOnlyTooltip:true})}function getInequalityEndpoint(){const isGreater=["ge","gt"].includes(props.userInput.rel);const widthInPixels=400;const range=props.options.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}function renderInequality(){if(props.options.isInequality){const end=getInequalityEndpoint();const style={arrows:"->",stroke: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}function renderGraphie(){const range=props.options.range;return jsxRuntimeExports.jsxs(Graphie,{ref:graphieRef,box:[props.apiOptions.isMobile?288:460,80],options:{range,isTickCtrl:props.options.isTickCtrl},onMouseDown:coord=>{const point=graphieRef.current?.movables.numberLinePoint;point?.grab(coord);},setup:setupGraphie,isMobile:props.apiOptions.isMobile,children:[jsxRuntimeExports.jsx(TickMarks,{range:props.options.range,labelTicks:props.options.labelTicks,labelStyle:props.options.labelStyle,labelRange:props.options.labelRange,tickStep:tickStep,numDivisions:props.userInput.numDivisions,isMobile:props.apiOptions.isMobile}),renderInequality(),renderNumberLinePoint()]},props.options.labelStyle)}const{divisionRange,isTickCtrl,isInequality}=props.options;const divRangeString=divisionRange[0]+EN_DASH+divisionRange[1];const invalidNumDivisions=props.userInput?.numDivisions<divisionRange[0]||props.userInput?.numDivisions>divisionRange[1];const inequalityControls=jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx("input",{type:"button",className:"simple-button",value:strings.switchDirection,onClick:handleReverse}),jsxRuntimeExports.jsx("input",{type:"button",className:"simple-button",value:["le","ge"].includes(props.userInput?.rel)?strings.circleOpen:strings.circleFilled,onClick:handleToggleStrict})]});let tickCtrl;if(isTickCtrl){const Input=props.apiOptions.customKeypad?SimpleKeypadInput:NumberInput;tickCtrl=jsxRuntimeExports.jsxs("label",{children:[strings.numDivisions," ",jsxRuntimeExports.jsx(Input,{ref:tickCtrlRef,value:numDivisionsEmpty?null:props.userInput?.numDivisions||divisionRange[0],checkValidity:val=>val>=divisionRange[0]&&val<=divisionRange[1],onChange:onNumDivisionsChange,onFocus:handleTickCtrlFocus,onBlur:handleTickCtrlBlur,useArrowKeys:true,keypadElement:props.keypadElement})]});}return jsxRuntimeExports.jsxs("div",{className:"perseus-widget "+"perseus-widget-interactive-number-line",children:[tickCtrl,!isValid(props)?jsxRuntimeExports.jsx("div",{className:"perseus-error",children:"Invalid number line configuration."}):isTickCtrl&&invalidNumDivisions?jsxRuntimeExports.jsx("div",{className:"perseus-error",children:strings.divisions({divRangeString:divRangeString})}):renderGraphie(),!props.static&&isInequality&&inequalityControls]})});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 getTickStep(range,numDivisions){const[min,max]=range;const width=max-min;return width/numDivisions}function getCorrectUserInput$1(options){const numLinePosition=options.correctX!=null?options.correctX:options.range[0];return {numDivisions:getStartNumDivisions(options),numLinePosition,rel:options.isInequality&&options.correctRel||"eq"}}function getStartUserInput$4(options){const numLinePosition=options.initialX!=null?options.initialX:options.range[0];return {numDivisions:getStartNumDivisions(options),numLinePosition,rel:options.isInequality?"ge":"eq"}}const WrappedNumberLine=withDependencies(NumberLine);var NumberLine$1 = {name:"number-line",displayName:"Number line",widget:WrappedNumberLine,getCorrectUserInput: getCorrectUserInput$1,getStartUserInput: getStartUserInput$4,getUserInputFromSerializedState: getUserInputFromSerializedState$4};
2086
+ const MovablePoint=Graphie.MovablePoint;const Line=Graphie.Line;const{assert}=InteractiveUtil;const bound=(x,gt,lt)=>Math.min(Math.max(x,gt),lt);const EN_DASH="–";const horizontalPadding=30;const MOBILE_BOX_WIDTH=288;const DESKTOP_BOX_WIDTH=460;function getNumberLineWidthPx(isMobile){return (isMobile?MOBILE_BOX_WIDTH:DESKTOP_BOX_WIDTH)-horizontalPadding*2}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"){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(!Number.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=fractions.map(getDenom);base=denoms.reduce((x,y)=>KhanMath.getLCM(x,y));}else {base=undefined;}const highlightedLineStyle={stroke:tokenValue(semanticColor.core.foreground.instructive.default),strokeWidth:3.5};const highlightedTextStyle={color:tokenValue(semanticColor.core.foreground.instructive.default)};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){graphie.style(textStyle,()=>{results.push(_label(graphie,labelStyle,tick,tick,base));});}});return results});function isValid(props){const{range,divisionRange,snapDivisions}=props.options;let initialX=props.userInput?.numLinePosition;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<props.userInput?.numDivisions&&0<snapDivisions}const NumberLine=forwardRef(function NumberLine(props,ref){const{strings}=usePerseusI18n();const propsRef=useLatestRef(props);const[numDivisionsEmpty,setNumDivisionsEmpty]=useState(false);const tickStep=getTickStep(props.options.range,props.userInput.numDivisions);const graphieRef=useRef(null);const tickCtrlRef=useRef(null);useOnMountEffect(()=>{props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:"null",widgetType:"number-line",widgetId:props.widgetId}});});useImperativeHandle(ref,()=>({focus:()=>{if(props.options.isTickCtrl){tickCtrlRef.current?.focus();return true}return false},focusInputPath:path=>{if(path?.length===1){tickCtrlRef.current?.focus();}},blurInputPath:path=>{if(path?.length===1){tickCtrlRef.current?.blur();}},getInputPaths:()=>{if(props.options.isTickCtrl){return [["tick-ctrl"]]}return []},getDOMNodeForPath:inputPath=>{if(inputPath?.length===1){return ReactDOM__default.findDOMNode(tickCtrlRef.current)}return null},getSerializedState:()=>({alignment:props.alignment,static:props.static,range:props.options.range,labelRange:props.options.labelRange,labelStyle:props.options.labelStyle,labelTicks:props.options.labelTicks,divisionRange:props.options.divisionRange,snapDivisions:props.options.snapDivisions,isInequality:props.options.isInequality,showTooltips:props.options.showTooltips,isTickCtrl:props.options.isTickCtrl,numDivisions:props.userInput.numDivisions,numLinePosition:props.userInput.numLinePosition,rel:"ge"}),getPromptJSON(){return getPromptJSON$6(props)},movePosition}));const setupGraphie=useCallback((graphie,options)=>{const latestProps=propsRef.current;if(!isValid(latestProps)){return}const widthInPixels=getNumberLineWidthPx(latestProps.apiOptions.isMobile);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{labelStyle}=latestProps.options;const hasFractionalLabels=labelStyle==="improper"||labelStyle==="mixed"||labelStyle==="non-reduced";const bottom=hasFractionalLabels?-1.5:-1;const top=1;graphie.init({range:[[left,right],[bottom,top]],scale:[1/scale,40],isMobile:latestProps.apiOptions.isMobile});const center=(range[0]+range[1])/2;graphie.line([center,0],[right,0],{arrows:"->"});graphie.line([center,0],[left,0],{arrows:"->"});},[propsRef]);function snapNumLinePosition(tickStep,numLinePosition){const{range,snapDivisions}=props.options;const left=range[0];const right=range[1];const snapX=tickStep/snapDivisions;let x=bound(numLinePosition,left,right);x=left+number.roundTo(x-left,snapX);assert(Number.isFinite(x));return x}function onNumDivisionsChange(numDivisions,cb){numDivisions=Math.round(numDivisions);numDivisions=numDivisions<0?numDivisions*-1:numDivisions;if(numDivisions){const newTickStep=getTickStep(props.options.range,numDivisions);const newNumLinePosition=snapNumLinePosition(newTickStep,props.userInput.numLinePosition);setNumDivisionsEmpty(false);props.handleUserInput({...props.userInput,numDivisions:numDivisions,numLinePosition:newNumLinePosition},cb);}else {setNumDivisionsEmpty(true);cb?.();}}function handleTickCtrlFocus(){props.onFocus(["tick-ctrl"]);}function handleTickCtrlBlur(){props.onBlur(["tick-ctrl"]);}function movePosition(targetPosition){props.handleUserInput({...props.userInput,numLinePosition:targetPosition});props.trackInteraction();}function handleReverse(){const newRel=reverseRel[props.userInput.rel];props.handleUserInput({...props.userInput,rel:newRel});}function handleToggleStrict(){const newRel=toggleStrictRel[props.userInput.rel];props.handleUserInput({...props.userInput,rel:newRel});}function renderNumberLinePoint(){const isOpen=["lt","gt"].includes(props.userInput.rel);const interactiveColor=tokenValue(semanticColor.core.foreground.instructive.default);const staticColor=tokenValue(semanticColor.core.foreground.disabled.strong);const knockoutColor=tokenValue(semanticColor.core.border.knockout.default);const hollowFill=tokenValue(semanticColor.core.foreground.knockout.default);const pointColor=props.static?staticColor:interactiveColor;const normalStyle={fill:isOpen?hollowFill:pointColor,stroke:isOpen?pointColor:knockoutColor,"stroke-width":isOpen?3:1};const highlightStyle={fill:isOpen?hollowFill:interactiveColor,"stroke-width":isOpen?3:1};const mobileDotStyle=props.options.isInequality&&isOpen?{fill:hollowFill,stroke:pointColor,"stroke-width":3}:{};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=snapNumLinePosition(tickStep,coord[0]);return [x,coord[1]]}],normalStyle:normalStyle,highlightStyle:highlightStyle,onMove:coord=>{movePosition(coord[0]);},isMobile:props.apiOptions.isMobile,mobileStyleOverride:mobileDotStyle,showTooltips:props.options.showTooltips??false,xOnlyTooltip:true})}function getInequalityEndpoint(){const isGreater=["ge","gt"].includes(props.userInput.rel);const widthInPixels=getNumberLineWidthPx(props.apiOptions.isMobile);const range=props.options.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}function renderInequality(){if(props.options.isInequality){const end=getInequalityEndpoint();const style={arrows:"->",stroke:tokenValue(semanticColor.core.foreground.instructive.default),strokeWidth:3.5};return jsxRuntimeExports.jsx(Line,{start:[props.userInput.numLinePosition,0],end:end,style:style})}return null}function renderGraphie(){const range=props.options.range;return jsxRuntimeExports.jsxs(Graphie,{ref:graphieRef,box:[props.apiOptions.isMobile?MOBILE_BOX_WIDTH:DESKTOP_BOX_WIDTH,80],options:{range,isTickCtrl:props.options.isTickCtrl},onMouseDown:coord=>{const point=graphieRef.current?.movables.numberLinePoint;point?.grab(coord);},setup:setupGraphie,isMobile:props.apiOptions.isMobile,children:[jsxRuntimeExports.jsx(TickMarks,{range:props.options.range,labelTicks:props.options.labelTicks,labelStyle:props.options.labelStyle,labelRange:props.options.labelRange,tickStep:tickStep,numDivisions:props.userInput.numDivisions,isMobile:props.apiOptions.isMobile}),renderInequality(),renderNumberLinePoint()]},props.options.labelStyle)}const{divisionRange,isTickCtrl,isInequality}=props.options;const divRangeString=divisionRange[0]+EN_DASH+divisionRange[1];const invalidNumDivisions=props.userInput?.numDivisions<divisionRange[0]||props.userInput?.numDivisions>divisionRange[1];const inequalityControls=jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx("input",{type:"button",className:"simple-button",value:strings.switchDirection,onClick:handleReverse}),jsxRuntimeExports.jsx("input",{type:"button",className:"simple-button",value:["le","ge"].includes(props.userInput?.rel)?strings.circleOpen:strings.circleFilled,onClick:handleToggleStrict})]});let tickCtrl;if(isTickCtrl){const Input=props.apiOptions.customKeypad?SimpleKeypadInput:NumberInput;tickCtrl=jsxRuntimeExports.jsxs("label",{children:[strings.numDivisions," ",jsxRuntimeExports.jsx(Input,{ref:tickCtrlRef,value:numDivisionsEmpty?null:props.userInput?.numDivisions||divisionRange[0],checkValidity:val=>val>=divisionRange[0]&&val<=divisionRange[1],onChange:onNumDivisionsChange,onFocus:handleTickCtrlFocus,onBlur:handleTickCtrlBlur,useArrowKeys:true,keypadElement:props.keypadElement})]});}return jsxRuntimeExports.jsxs("div",{className:"perseus-widget "+"perseus-widget-interactive-number-line",children:[tickCtrl,!isValid(props)?jsxRuntimeExports.jsx("div",{className:"perseus-error",children:"Invalid number line configuration."}):isTickCtrl&&invalidNumDivisions?jsxRuntimeExports.jsx("div",{className:"perseus-error",children:strings.divisions({divRangeString:divRangeString})}):renderGraphie(),!props.static&&isInequality&&inequalityControls]})});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 getTickStep(range,numDivisions){const[min,max]=range;const width=max-min;return width/numDivisions}function getCorrectUserInput$1(options){const numLinePosition=options.correctX!=null?options.correctX:options.range[0];return {numDivisions:getStartNumDivisions(options),numLinePosition,rel:options.isInequality&&options.correctRel||"eq"}}function getStartUserInput$4(options){const numLinePosition=options.initialX!=null?options.initialX:options.range[0];return {numDivisions:getStartNumDivisions(options),numLinePosition,rel:options.isInequality?"ge":"eq"}}const WrappedNumberLine=withDependencies(NumberLine);var NumberLine$1 = {name:"number-line",displayName:"Number line",widget:WrappedNumberLine,getCorrectUserInput: getCorrectUserInput$1,getStartUserInput: getStartUserInput$4,getUserInputFromSerializedState: getUserInputFromSerializedState$4};
2087
2087
 
2088
2088
  const getPromptJSON$5=widgetData=>{return {type:"orderer",options:{options:widgetData.options.options.map(option=>option.content)},userInput:{values:widgetData.userInput.current}}};
2089
2089
 
2090
- 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){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 hasNotMoved=this.props.mouse.left===this.props.startMouse.left&&this.props.mouse.top===this.props.startMouse.top;if(this.props.removing&&hasNotMoved){className.push("removing");}}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,removing:false,animating:false};class Orderer extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"orderer",widgetSubType:"null",widgetId:this.props.widgetId}});}getPromptJSON(){return getPromptJSON$5(this.props)}getSerializedState(){const{userInput,options,...rest}=this.props;return {...options,...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,removing:this.state.dragSource==="current",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.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.options.height+" "+"layout-"+this.props.options.layout+" "+"blank-background "+"perseus-clearfix ",ref:"orderer",children:[bank,sortable]})}constructor(...args){super(...args),this.state={dragging:false,dragSource:null,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 content;let placeholderIndex=null;if(type==="current"){list.splice(index,1);content=this.props.userInput.current[index];placeholderIndex=index;}else {content=this.props.options.options[index].content;}this.props.handleUserInput({current:list});this.setState({dragging:true,dragSource:type,placeholderIndex:placeholderIndex,dragContent: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 cardContent=this.state.dragContent;if(typeof cardContent==="string"){const newCard={content:cardContent,key:_.uniqueId("perseus_draggable_card_"),width:this.state.dragWidth};list.splice(index,0,newCard.content);}}this.props.handleUserInput({current:list});this.setState({dragging:false,dragSource:null,placeholderIndex:null,animating:false});this.props.trackInteraction();};const offset=$(ReactDOM__default.findDOMNode(draggable)).position();let finalOffset=null;if(inCardBank){this.props.options.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.options.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.options.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});};}}function getUserInputFromSerializedState$3(serializedState){return {current:serializedState.current.map(e=>e.content)}}function getStartUserInput$3(){return {current:[]}}const WrappedOrderer=withDependencies(Orderer);var Orderer$1 = {name:"orderer",displayName:"Orderer",hidden:true,widget:WrappedOrderer,isLintable:true,getStartUserInput: getStartUserInput$3,getUserInputFromSerializedState: getUserInputFromSerializedState$3};
2090
+ const noop=()=>{};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){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 hasNotMoved=this.props.mouse.left===this.props.startMouse.left&&this.props.mouse.top===this.props.startMouse.top;if(this.props.removing&&hasNotMoved){className.push("removing");}}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,removing:false,animating:false};class Orderer extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"orderer",widgetSubType:"null",widgetId:this.props.widgetId}});}getPromptJSON(){return getPromptJSON$5(this.props)}getSerializedState(){const{userInput,options,...rest}=this.props;return {...options,...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,removing:this.state.dragSource==="current",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.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.options.height+" "+"layout-"+this.props.options.layout+" "+"blank-background "+"perseus-clearfix ",ref:"orderer",children:[bank,sortable]})}constructor(...args){super(...args),this.state={dragging:false,dragSource:null,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 content;let placeholderIndex=null;if(type==="current"){list.splice(index,1);content=this.props.userInput.current[index];placeholderIndex=index;}else {content=this.props.options.options[index].content;}this.props.handleUserInput({current:list});this.setState({dragging:true,dragSource:type,placeholderIndex:placeholderIndex,dragContent: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 cardContent=this.state.dragContent;if(typeof cardContent==="string"){const newCard={content:cardContent,key:_.uniqueId("perseus_draggable_card_"),width:this.state.dragWidth};list.splice(index,0,newCard.content);}}this.props.handleUserInput({current:list});this.setState({dragging:false,dragSource:null,placeholderIndex:null,animating:false});this.props.trackInteraction();};const offset=$(ReactDOM__default.findDOMNode(draggable)).position();let finalOffset=null;if(inCardBank){this.props.options.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.options.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.options.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});};}}function getUserInputFromSerializedState$3(serializedState){return {current:serializedState.current.map(e=>e.content)}}function getStartUserInput$3(){return {current:[]}}const WrappedOrderer=withDependencies(Orderer);var Orderer$1 = {name:"orderer",displayName:"Orderer",hidden:true,widget:WrappedOrderer,isLintable:true,getStartUserInput: getStartUserInput$3,getUserInputFromSerializedState: getUserInputFromSerializedState$3};
2091
2091
 
2092
2092
  const getPromptJSON$4=()=>{return getUnsupportedPromptJSON("phet-simulation")};
2093
2093
 
@@ -2095,7 +2095,7 @@ const isFullscreenApiSupported=()=>{if(typeof document==="undefined"){return fal
2095
2095
 
2096
2096
  const getPromptJSON$3=()=>{return getUnsupportedPromptJSON("plotter")};
2097
2097
 
2098
- class Plotter extends React.Component{componentDidMount(){this._isMounted=true;this.setupGraphie(this.props.userInput);this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"plotter",widgetSubType:"null",widgetId:this.props.widgetId}});}UNSAFE_componentWillReceiveProps(nextProps){const optionsKeys=["type","labels","categories","scaleY","maxY","snapsPerLine","picUrl","labelInterval"];const options=this.props.options;const nextOptions=nextProps.options;this.shouldSetupGraphie=this.props.static!==nextProps.static||optionsKeys.some(key=>!_.isEqual(options[key],nextOptions[key]));if(!_.isEqual(options.starting,nextOptions.starting)&&!_.isEqual(this.props.userInput,nextOptions.starting)){this.shouldSetupGraphie=true;this.props.handleUserInput(nextOptions.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.options.type==="bar";const isLine=self.props.options.type==="line";const isPic=self.props.options.type==="pic";const isHistogram=self.props.options.type==="histogram";const isDotplot=self.props.options.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.options.scaleY;c.dimX=self.props.options.categories.length;const plotDimensions=isMobile?[288,336]:self.props.options.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.options.picBoxHeight;c.picBoxWidthPx=plotDimensions[0]/self.props.options.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.options.maxY/c.scaleY)*c.scaleY;let padX=25;if((isBar||isLine)&&isMobile){padX=self.props.options.labels[1].length!==0?17:11;}if(isDotplot){padX/=2;}if(isMobile&&isTiledPlot&&self.props.options.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=[c.dimX,c.dimY].map(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});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.options.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]));}}});const xAxisLabel=graphie.label([c.dimX/2,isMobile?-padBottom:-35/c.scale[1]],self.props.options.labels[0],isMobile?"above":"below",false).css("font-weight","bold");if(isMobile){xAxisLabel.css("color",KhanColors.GRAY_F);}const yAxisLabel=graphie.label([(isMobile?-35:-60)/c.scale[0],c.dimY/2],self.props.options.labels[1],"center",false).css("font-weight","bold").addClass("rotate");if(isMobile){yAxisLabel.css("color",KhanColors.GRAY_F);}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.options.scaleY;const show=y<=values[i];if(self.props.options.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";});});}getPromptJSON(){return getPromptJSON$3()}getSerializedState(){const{userInput,options,...rest}=this.props;return {...options,...rest,values:this.props.userInput}}render(){const paddingForBottomLabel=75;const style={marginBottom:this.props.options.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.options.type==="histogram"){_.times(self.props.options.categories.length-1,function(i){self.setupBar({index:i,startHeight:self.props.userInput[i],config:config,isHistogram:true});});_.each(self.props.options.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.options.categories,function(category,i){const startHeight=self.props.userInput[i];let x;if(self.props.options.type==="bar"){x=self.setupBar({index:i,startHeight:startHeight,config:config,isHistogram:false});}else if(self.props.options.type==="line"){x=self.setupLine(i,startHeight,config);}else if(self.props.options.type==="pic"){x=self.setupPic(i,config);}else if(self.props.options.type==="dotplot"){x=self.setupDotplot(i,config);}let tickStart=0;let tickEnd=-6/c.scale[1];if(self.props.options.type==="dotplot"&&!isMobile){tickStart=-tickEnd;}if(self.props.options.type==="dotplot"){if(i%self.props.options.labelInterval===0||i===self.props.options.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.options.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.options.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.options.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.options.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.options.picSize;return graphie.raphael.image(this.props.options.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;function getStartUserInput$2(options){return options.starting}function getCorrectUserInput(options){return options.correct}function getUserInputFromSerializedState$2(serializedState){return serializedState.values}const WrappedPlotter=withDependencies(Plotter);var Plotter$1 = {name:"plotter",displayName:"Plotter",hidden:true,widget:WrappedPlotter,getCorrectUserInput,getStartUserInput: getStartUserInput$2,getUserInputFromSerializedState: getUserInputFromSerializedState$2};
2098
+ class Plotter extends React.Component{componentDidMount(){this._isMounted=true;this.setupGraphie(this.props.userInput);this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"plotter",widgetSubType:"null",widgetId:this.props.widgetId}});}UNSAFE_componentWillReceiveProps(nextProps){const optionsKeys=["type","labels","categories","scaleY","maxY","snapsPerLine","picUrl","labelInterval"];const options=this.props.options;const nextOptions=nextProps.options;this.shouldSetupGraphie=this.props.static!==nextProps.static||optionsKeys.some(key=>!_.isEqual(options[key],nextOptions[key]));if(!_.isEqual(options.starting,nextOptions.starting)&&!_.isEqual(this.props.userInput,nextOptions.starting)){this.shouldSetupGraphie=true;this.props.handleUserInput(nextOptions.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.innerHTML="";const graphie=GraphUtils.createGraphie(this.graphieDiv.current);self.graphie=graphie;self.graphie.pics=[];self.graphie.dotTicks=[];const isBar=self.props.options.type==="bar";const isLine=self.props.options.type==="line";const isPic=self.props.options.type==="pic";const isHistogram=self.props.options.type==="histogram";const isDotplot=self.props.options.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.options.scaleY;c.dimX=self.props.options.categories.length;const plotDimensions=isMobile?[288,336]:self.props.options.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.options.picBoxHeight;c.picBoxWidthPx=plotDimensions[0]/self.props.options.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.options.maxY/c.scaleY)*c.scaleY;let padX=25;if((isBar||isLine)&&isMobile){padX=self.props.options.labels[1].length!==0?17:11;}if(isDotplot){padX/=2;}if(isMobile&&isTiledPlot&&self.props.options.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=[c.dimX,c.dimY].map(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});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:tokenValue(semanticColor.core.border.neutral.default),strokeWidth:1,opacity:1},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",font.weight.bold).css("color",tokenValue(semanticColor.core.foreground.neutral.strong)).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",font.weight.bold).css("color",semanticColor.core.foreground.neutral.strong).css("display","none");}if(isTiledPlot){self.drawPicHeights(self.props.userInput,prevValues);}graphie.style({stroke:tokenValue(semanticColor.core.border.neutral.strong),strokeWidth:2,opacity:1},function(){if(isTiledPlot){if(isDotplot){graphie.line([isMobile?0:.5,0],[c.dimX-(isMobile?0:.5),0]);}else {graphie.line([0,0],[c.dimX,0]);if(self.props.options.labels[1].length!==0||!isMobile){graphie.line([0,0],[0,c.dimY]);}}}else {graphie.line([isMobile?-padX*3:0,0],[c.dimX+(isMobile?padX:0),0]);if(!((isBar||isLine)&&isMobile)){graphie.line([0,0],[0,c.dimY]);}}});graphie.label([c.dimX/2,isMobile?-padBottom:-35/c.scale[1]],self.props.options.labels[0],isMobile?"above":"below",false).css("color",tokenValue(semanticColor.core.foreground.neutral.strong)).css("font-weight",font.weight.bold);graphie.label([(isMobile?-35:-60)/c.scale[0],c.dimY/2],self.props.options.labels[1],"center",false).css("color",tokenValue(semanticColor.core.foreground.neutral.strong)).css("font-weight",font.weight.bold).addClass("rotate");if(this.props.apiOptions.isMobile){this.horizHairline=new WrappedLine(this.graphie,[0,0],[0,0],{normalStyle:{strokeWidth:1}});this.horizHairline.attr({stroke:tokenValue(semanticColor.core.border.instructive.default)});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.options.scaleY;const show=y<=values[i];if(self.props.options.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].style.display=show?"inline":"none";graphie.dotTicks[i][j][0].style.display=show||!isMobile?"none":"inline";});});}getPromptJSON(){return getPromptJSON$3()}getSerializedState(){const{userInput,options,...rest}=this.props;return {...options,...rest,values:this.props.userInput}}render(){const paddingForBottomLabel=75;const style={marginBottom:this.props.options.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:tokenValue(semanticColor.core.foreground.neutral.strong),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.options.type==="histogram"){_.times(self.props.options.categories.length-1,function(i){self.setupBar({index:i,startHeight:self.props.userInput[i],config:config,isHistogram:true});});_.each(self.props.options.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:tokenValue(semanticColor.core.border.neutral.strong),strokeWidth:2,opacity:1},function(){graphie.line([x,-tickHeight],[x,0]);});});}else {_.each(self.props.options.categories,function(category,i){const startHeight=self.props.userInput[i];let x;if(self.props.options.type==="bar"){x=self.setupBar({index:i,startHeight:startHeight,config:config,isHistogram:false});}else if(self.props.options.type==="line"){x=self.setupLine(i,startHeight,config);}else if(self.props.options.type==="pic"){x=self.setupPic(i,config);}else if(self.props.options.type==="dotplot"){x=self.setupDotplot(i,config);}let tickStart=0;let tickEnd=-6/c.scale[1];if(self.props.options.type==="dotplot"&&!isMobile){tickStart=-tickEnd;}if(self.props.options.type==="dotplot"){if(i%self.props.options.labelInterval===0||i===self.props.options.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:tokenValue(semanticColor.core.border.neutral.strong),strokeWidth: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:tokenValue(semanticColor.core.foreground.instructive.subtle),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:tokenValue(semanticColor.core.border.knockout.default),strokeWidth:1},function(){config.graph.dividers.push(graphie.path([[x-barHalfWidth,0],[x-barHalfWidth,config.scaleY]]));});}}if(isMobile){const snap=config.scaleY/self.props.options.snapsPerLine;config.graph.lines[i]=Interactive2.addMovablePointV2(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=config.graph.lines[i].coord()[1];const values=[...self.props.userInput];values[i]=y;self.changeAndTrack(values);self._maybeHideDragPrompt();scaleBar(i,y);}});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.options.snapsPerLine,constraints:{constrainX:true},normalStyle:{stroke:tokenValue(semanticColor.core.foreground.instructive.default),"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);const snap=config.scaleY/self.props.options.snapsPerLine;c.graph.points[i]=Interactive2.addMovablePointV2(this,{coord:[x,startHeight],constraints:[coord=>{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:tokenValue(semanticColor.core.border.instructive.default),"stroke-width":2},highlightStyle:{stroke:tokenValue(semanticColor.core.border.instructive.default),"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:tokenValue(semanticColor.core.foreground.instructive.default),stroke:tokenValue(semanticColor.core.foreground.instructive.default)})})},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.options.picSize;return graphie.raphael.image(this.props.options.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:tokenValue(semanticColor.core.border.neutral.subtle),stroke:tokenValue(semanticColor.core.border.neutral.subtle)});});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;function getStartUserInput$2(options){return options.starting}function getCorrectUserInput(options){return options.correct}function getUserInputFromSerializedState$2(serializedState){return serializedState.values}const WrappedPlotter=withDependencies(Plotter);var Plotter$1 = {name:"plotter",displayName:"Plotter",hidden:true,widget:WrappedPlotter,getCorrectUserInput,getStartUserInput: getStartUserInput$2,getUserInputFromSerializedState: getUserInputFromSerializedState$2};
2099
2099
 
2100
2100
  const getPromptJSON$2=()=>{return getUnsupportedPromptJSON("python-program")};
2101
2101
 
@@ -2119,7 +2119,7 @@ var extraWidgets = [Blank,CSProgram$1,Categorizer$1,Definition$1,DeprecatedStand
2119
2119
 
2120
2120
  const init=function(){registerWidgets(basicWidgets);registerWidgets(extraWidgets);replaceDeprecatedWidgets();};
2121
2121
 
2122
- const libName="@khanacademy/perseus";const libVersion="84.1.1";addLibraryVersionToPerseusDebug(libName,libVersion);
2122
+ const libName="@khanacademy/perseus";const libVersion="84.3.0";addLibraryVersionToPerseusDebug(libName,libVersion);
2123
2123
 
2124
2124
  const apiVersion={major:12,minor:0};
2125
2125