@khanacademy/perseus 77.2.2 → 77.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/es/index.css +5 -1
  2. package/dist/es/index.css.map +1 -1
  3. package/dist/es/index.js +63 -57
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/es/strings.js +1 -1
  6. package/dist/es/strings.js.map +1 -1
  7. package/dist/index.css +5 -1
  8. package/dist/index.css.map +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.js +47 -40
  11. package/dist/index.js.map +1 -1
  12. package/dist/strings.d.ts +35 -0
  13. package/dist/strings.js +1 -1
  14. package/dist/strings.js.map +1 -1
  15. package/dist/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.d.ts +9 -2
  16. package/dist/widgets/free-response/free-response.d.ts +1 -1
  17. package/dist/widgets/image/components/explore-image-modal-content.d.ts +5 -2
  18. package/dist/widgets/interactive-graphs/backgrounds/utils.d.ts +8 -0
  19. package/dist/widgets/interactive-graphs/graphs/components/movable-arrowhead-view.d.ts +14 -0
  20. package/dist/widgets/interactive-graphs/graphs/components/movable-asymptote.d.ts +6 -0
  21. package/dist/widgets/interactive-graphs/graphs/components/movable-line.d.ts +1 -1
  22. package/dist/widgets/interactive-graphs/graphs/components/movable-pill-handle.d.ts +22 -0
  23. package/dist/widgets/interactive-graphs/graphs/components/use-control-arrowhead.d.ts +23 -0
  24. package/dist/widgets/interactive-graphs/graphs/exponential.d.ts +0 -1
  25. package/dist/widgets/interactive-graphs/graphs/logarithm.d.ts +0 -1
  26. package/dist/widgets/interactive-graphs/graphs/utils.d.ts +15 -7
  27. package/dist/widgets/interactive-graphs/graphs/vector.d.ts +11 -0
  28. package/dist/widgets/interactive-graphs/interactive-graph.d.ts +1 -0
  29. package/dist/widgets/interactive-graphs/reducer/initialize-graph-state.d.ts +2 -1
  30. package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +8 -4
  31. package/dist/widgets/interactive-graphs/types.d.ts +5 -1
  32. package/package.json +45 -45
  33. package/dist/widgets/interactive-graphs/graphs/components/asymptote-drag-handle.d.ts +0 -15
  34. package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +0 -192
package/dist/es/index.js CHANGED
@@ -3,13 +3,13 @@ import React__default, { useContext, forwardRef, useId, useRef, useEffect, useCa
3
3
  import { KeypadContext } from '@khanacademy/keypad-context';
4
4
  import { MathInputI18nContext, DesktopKeypad, getCursorContext, CursorContext, getKeyTranslator, createMathField, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, keypadElementPropType } from '@khanacademy/math-input';
5
5
  import { expressionLogic, PerseusError, Errors, isLabeledSVG, getDataUrl, getSvgUrl, getBaseUrl, getRealImageUrl, getImageSizeModern, approximateDeepEqual, pluck, isFeatureOn, getWidgetIdsFromContentByType, Registry, CoreWidgetRegistry, mapObject, applyDefaultsToWidgets, splitPerseusItem, getDefaultAnswerArea, shuffle, usesNumCorrect, radioLogic, GrapherUtil as GrapherUtil$1, deepClone, lockedFigureColors, lockedFigureFillStyles, approximateEqual, shuffleMatcher, getMatrixSize, measurerLogic, makeSafeUrl, shuffleSorter } from '@khanacademy/perseus-core';
6
- import { View, RenderStateRoot, useOnMountEffect, Id, Text as Text$1, useLatestRef } from '@khanacademy/wonder-blocks-core';
6
+ import { View, RenderStateRoot, useOnMountEffect, Id, useLatestRef, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
7
7
  import { Heading, BodyText } from '@khanacademy/wonder-blocks-typography';
8
8
  import { StyleSheet, css } from 'aphrodite';
9
9
  import Clickable from '@khanacademy/wonder-blocks-clickable';
10
10
  import { Popover, PopoverContentCore } from '@khanacademy/wonder-blocks-popover';
11
11
  import { semanticColor, sizing, color, border as border$1, spacing, font, boxShadow } from '@khanacademy/wonder-blocks-tokens';
12
- import classNames$1 from 'classnames';
12
+ import classNames$2 from 'classnames';
13
13
  import $ from 'jquery';
14
14
  import _, { debounce as debounce$1 } from 'underscore';
15
15
  import { v4 } from 'uuid';
@@ -49,7 +49,7 @@ import questionIcon from '@phosphor-icons/core/regular/question.svg';
49
49
  import pauseIcon from '@phosphor-icons/core/fill/pause-fill.svg';
50
50
  import playIcon from '@phosphor-icons/core/fill/play-fill.svg';
51
51
  import * as KAS from '@khanacademy/kas';
52
- import { vec, useTransformContext, Plot as Plot$2, Coordinates, Ellipse, Line as Line$4, Point as Point$2, Polygon, Text as Text$2, Polyline, Mafs } from 'mafs';
52
+ import { vec, useTransformContext, Plot as Plot$2, Coordinates, Ellipse, Line as Line$4, Point as Point$2, Polygon, Text as Text$1, Polyline, Mafs } from 'mafs';
53
53
  import { useDrag } from '@use-gesture/react';
54
54
  import invariant from 'tiny-invariant';
55
55
  import Switch from '@khanacademy/wonder-blocks-switch';
@@ -1405,7 +1405,7 @@ function requireJsxRuntime () {
1405
1405
 
1406
1406
  var jsxRuntimeExports = requireJsxRuntime();
1407
1407
 
1408
- 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",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.",selectAnAnswer:"Select an answer",srGraphInstructions:"Use the Tab key to move through the interactive elements in the graph. When an interactive element has focus, use Control + Shift + Arrows to move it.",srUnlimitedGraphInstructions:"Press Shift + Enter to interact with the graph. 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 Control + Shift + Arrows 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",srPointAtCoordinates:({num,x,y})=>`Point ${num} 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}.`,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:({x,y})=>`Point 3, starting side at ${x} comma ${y}.`,srAngleEndingSide:({x,y})=>`Point 2, ending side at ${x} comma ${y}.`,srAngleVertexWithAngleMeasure:({x,y,angleMeasure})=>`Point 1, 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:({endpointNumber,x,y})=>`Endpoint ${endpointNumber} at ${x} comma ${y}.`,srMultipleSegmentGraphEndpointAriaLabel:({endpointNumber,x,y,indexOfSegment})=>`Endpoint ${endpointNumber} 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,pointSequence,x,y})=>`Point ${pointSequence} 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}.`,srRayTerminalPoint:({x,y})=>`Through point at ${x} comma ${y}.`,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:({pointNumber})=>`Point ${pointNumber} on parabola at the origin.`,srQuadraticPointAxis:({pointNumber,x,y})=>`Point ${pointNumber} on parabola at ${x} comma ${y}.`,srQuadraticPointQuadrant:({pointNumber,x,y,quadrant})=>`Point ${pointNumber} 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}.`,srSinusoidMaxPoint:({x,y})=>`Maximum point at ${x} comma ${y}.`,srSinusoidMinPoint:({x,y})=>`Minimum point at ${x} comma ${y}.`,srSinusoidFlatPoint:({x,y})=>`Line through point 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.",srExponentialPoint1:({x,y})=>`Point 1 at ${x} comma ${y}.`,srExponentialPoint2:({x,y})=>`Point 2 at ${x} comma ${y}.`,srExponentialDescription:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`The graph shows an exponential curve passing through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y} with a horizontal asymptote at y equals ${asymptoteY}.`,srExponentialInteractiveElements:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`Exponential graph with point 1 at ${point1X} comma ${point1Y}, point 2 at ${point2X} comma ${point2Y}, and horizontal asymptote at y equals ${asymptoteY}.`,srExponentialAsymptote:({asymptoteY})=>`Horizontal asymptote at y equals ${asymptoteY}. Use up and down arrow keys to move.`,srLogarithmGraph:"A logarithm function on a coordinate plane.",srLogarithmPoint1:({x,y})=>`Point 1 at ${x} comma ${y}.`,srLogarithmPoint2:({x,y})=>`Point 2 at ${x} comma ${y}.`,srLogarithmDescription:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The graph shows a logarithm curve passing through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y} with a vertical asymptote at x equals ${asymptoteX}.`,srLogarithmInteractiveElements:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`Logarithm graph with point 1 at ${point1X} comma ${point1Y}, point 2 at ${point2X} comma ${point2Y}, and vertical asymptote at x equals ${asymptoteX}.`,srLogarithmAsymptote:({asymptoteX})=>`Vertical asymptote at x equals ${asymptoteX}. Use left and right arrow keys to move.`,srAbsoluteValueGraph:"An absolute value function on a coordinate plane.",srAbsoluteValueVertexPoint:({x,y})=>`Vertex point at ${x} comma ${y}.`,srAbsoluteValueSecondPoint:({x,y})=>`Point on arm at ${x} comma ${y}.`,srAbsoluteValueDescription:({x,y,slope})=>`The graph shows an absolute value function with vertex at ${x} comma ${y} and slope ${slope}.`,srAbsoluteValueInteractiveElements:({point1X,point1Y,point2X,point2Y})=>`Absolute value graph with vertex point at ${point1X} comma ${point1Y} and arm point at ${point2X} comma ${point2Y}.`,srTangentGraph:"A tangent function on a coordinate plane.",srTangentInflectionPoint:({x,y})=>`Inflection point at ${x} comma ${y}.`,srTangentSecondPoint:({x,y})=>`Control point at ${x} comma ${y}.`,srTangentDescription:({inflectionX,inflectionY})=>`The graph shows a tangent function with an inflection point at ${inflectionX} comma ${inflectionY}.`,srTangentInteractiveElements:({point1X,point1Y,point2X,point2Y})=>`Tangent graph with inflection point at ${point1X} comma ${point1Y} and control point at ${point2X} comma ${point2Y}.`,imageExploreButton:"Explore image",imageAlternativeTitle:"Explore image and description",imageDescriptionLabel:"Description",imageZoomAriaLabel:"Make image bigger.",imageResetZoomAriaLabel:"Close image.",gifPlayButtonLabel:"Play Animation",gifPauseButtonLabel:"Pause Animation"};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}
1408
+ 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",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.",selectAnAnswer:"Select an answer",srGraphInstructions:"Use the Tab key to move through the interactive elements in the graph. When an interactive element has focus, use Control + Shift + Arrows to move it.",srUnlimitedGraphInstructions:"Press Shift + Enter to interact with the graph. 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 Control + Shift + Arrows 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",srPointAtCoordinates:({num,x,y})=>`Point ${num} 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}.`,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:({x,y})=>`Point 3, starting side at ${x} comma ${y}.`,srAngleEndingSide:({x,y})=>`Point 2, ending side at ${x} comma ${y}.`,srAngleVertexWithAngleMeasure:({x,y,angleMeasure})=>`Point 1, 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:({endpointNumber,x,y})=>`Endpoint ${endpointNumber} at ${x} comma ${y}.`,srMultipleSegmentGraphEndpointAriaLabel:({endpointNumber,x,y,indexOfSegment})=>`Endpoint ${endpointNumber} 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,pointSequence,x,y})=>`Point ${pointSequence} 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}.`,srRayTerminalPoint:({x,y})=>`Through point at ${x} comma ${y}.`,srVectorGraph:"A vector on a coordinate plane.",srVectorPoints:({tailX,tailY,tipX,tipY})=>`The tail is at ${tailX} comma ${tailY} and the tip is at ${tipX} comma ${tipY}.`,srVectorTipPoint:({x,y})=>`Tip point at ${x} comma ${y}.`,srVectorGrabHandle:({tailX,tailY,tipX,tipY})=>`Vector from ${tailX} comma ${tailY} to ${tipX} comma ${tipY}.`,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:({pointNumber})=>`Point ${pointNumber} on parabola at the origin.`,srQuadraticPointAxis:({pointNumber,x,y})=>`Point ${pointNumber} on parabola at ${x} comma ${y}.`,srQuadraticPointQuadrant:({pointNumber,x,y,quadrant})=>`Point ${pointNumber} 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}.`,srSinusoidMaxPoint:({x,y})=>`Maximum point at ${x} comma ${y}.`,srSinusoidMinPoint:({x,y})=>`Minimum point at ${x} comma ${y}.`,srSinusoidFlatPoint:({x,y})=>`Line through point 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.",srExponentialPoint1:({x,y})=>`Point 1 at ${x} comma ${y}.`,srExponentialPoint2:({x,y})=>`Point 2 at ${x} comma ${y}.`,srExponentialDescription:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`The graph shows an exponential curve passing through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y} with a horizontal asymptote at y equals ${asymptoteY}.`,srExponentialInteractiveElements:({point1X,point1Y,point2X,point2Y,asymptoteY})=>`Exponential graph with point 1 at ${point1X} comma ${point1Y}, point 2 at ${point2X} comma ${point2Y}, and horizontal asymptote at y equals ${asymptoteY}.`,srExponentialAsymptote:({asymptoteY})=>`Horizontal asymptote at y equals ${asymptoteY}. Use up and down arrow keys to move.`,srLogarithmGraph:"A logarithm function on a coordinate plane.",srLogarithmPoint1:({x,y})=>`Point 1 at ${x} comma ${y}.`,srLogarithmPoint2:({x,y})=>`Point 2 at ${x} comma ${y}.`,srLogarithmDescription:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`The graph shows a logarithm curve passing through point ${point1X} comma ${point1Y} and point ${point2X} comma ${point2Y} with a vertical asymptote at x equals ${asymptoteX}.`,srLogarithmInteractiveElements:({point1X,point1Y,point2X,point2Y,asymptoteX})=>`Logarithm graph with point 1 at ${point1X} comma ${point1Y}, point 2 at ${point2X} comma ${point2Y}, and vertical asymptote at x equals ${asymptoteX}.`,srLogarithmAsymptote:({asymptoteX})=>`Vertical asymptote at x equals ${asymptoteX}. Use left and right arrow keys to move.`,srAbsoluteValueGraph:"An absolute value function on a coordinate plane.",srAbsoluteValueVertexPoint:({x,y})=>`Vertex point at ${x} comma ${y}.`,srAbsoluteValueSecondPoint:({x,y})=>`Point on arm at ${x} comma ${y}.`,srAbsoluteValueDescription:({x,y,slope})=>`The graph shows an absolute value function with vertex at ${x} comma ${y} and slope ${slope}.`,srAbsoluteValueInteractiveElements:({point1X,point1Y,point2X,point2Y})=>`Absolute value graph with vertex point at ${point1X} comma ${point1Y} and arm point at ${point2X} comma ${point2Y}.`,srTangentGraph:"A tangent function on a coordinate plane.",srTangentInflectionPoint:({x,y})=>`Inflection point at ${x} comma ${y}.`,srTangentSecondPoint:({x,y})=>`Control point at ${x} comma ${y}.`,srTangentDescription:({inflectionX,inflectionY})=>`The graph shows a tangent function with an inflection point at ${inflectionX} comma ${inflectionY}.`,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"};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
1409
 
1410
1410
  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);
1411
1411
 
@@ -1413,7 +1413,7 @@ var a11y = StyleSheet.create({srOnly:{border:0,clip:"rect(0,0,0,0)",height:1,mar
1413
1413
 
1414
1414
  const debounce=(func,delay)=>{let timer=null;return (...args)=>{if(timer){clearTimeout(timer);}timer=window.setTimeout(()=>{func(...args);},delay);}};
1415
1415
 
1416
- 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$1({"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(_(value).isFunction()){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,paddingBottom:sizing.size_060,maxWidth:"initial"}});
1416
+ 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(_(value).isFunction()){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,paddingBottom:sizing.size_060,maxWidth:"initial"}});
1417
1417
 
1418
1418
  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};
1419
1419
 
@@ -1486,7 +1486,7 @@ var constants = /*#__PURE__*/Object.freeze({
1486
1486
  white: white
1487
1487
  });
1488
1488
 
1489
- const MIN_VIEWPORT_HEIGHT=480;class FixedToResponsive extends React.Component{componentDidMount(){this._isMounted=true;if(window.innerHeight<MIN_VIEWPORT_HEIGHT){setTimeout(this._cacheViewportSize,800);}else {this._cacheViewportSize();}}componentWillUnmount(){this._isMounted=false;}render(){const aspectRatio=this.props.width/this.props.height;let{width,height}=this.props;if(this.props.constrainHeight&&this.state.viewportHeight){const maxHeight=2/3*this.state.viewportHeight;if(this.props.height>=maxHeight){height=maxHeight;width=maxHeight*aspectRatio;}}const style={maxWidth:width,maxHeight:height,aspectRatio:aspectRatio.toFixed(4)};const className=classNames$1("fixed-to-responsive",this.props.className);const container=jsxRuntimeExports.jsx("div",{className:className,style:style,"data-scale":this.props.scale,children:this.props.children});const shouldFullBleed=this.props.allowFullBleed&&this.state.viewportWidth&&width>=this.state.viewportWidth;if(shouldFullBleed){return jsxRuntimeExports.jsx("div",{style:{marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin},children:container})}return container}constructor(...args){super(...args),this._isMounted=false,this.state={viewportHeight:null,viewportWidth:null},this._cacheViewportSize=()=>{if(this._isMounted){this.setState({viewportHeight:Math.max(MIN_VIEWPORT_HEIGHT,window.innerHeight),viewportWidth:window.innerWidth});}};}}FixedToResponsive.defaultProps={className:"",constrainHeight:false,allowFullBleed:false};
1489
+ const MIN_VIEWPORT_HEIGHT=480;class FixedToResponsive extends React.Component{componentDidMount(){this._isMounted=true;if(window.innerHeight<MIN_VIEWPORT_HEIGHT){setTimeout(this._cacheViewportSize,800);}else {this._cacheViewportSize();}}componentWillUnmount(){this._isMounted=false;}render(){const aspectRatio=this.props.width/this.props.height;let{width,height}=this.props;if(this.props.constrainHeight&&this.state.viewportHeight){const maxHeight=2/3*this.state.viewportHeight;if(this.props.height>=maxHeight){height=maxHeight;width=maxHeight*aspectRatio;}}const style={maxWidth:width,maxHeight:height,aspectRatio:aspectRatio.toFixed(4)};const className=classNames$2("fixed-to-responsive",this.props.className);const container=jsxRuntimeExports.jsx("div",{className:className,style:style,"data-scale":this.props.scale,children:this.props.children});const shouldFullBleed=this.props.allowFullBleed&&this.state.viewportWidth&&width>=this.state.viewportWidth;if(shouldFullBleed){return jsxRuntimeExports.jsx("div",{style:{marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin},children:container})}return container}constructor(...args){super(...args),this._isMounted=false,this.state={viewportHeight:null,viewportWidth:null},this._cacheViewportSize=()=>{if(this._isMounted){this.setState({viewportHeight:Math.max(MIN_VIEWPORT_HEIGHT,window.innerHeight),viewportWidth:window.innerWidth});}};}}FixedToResponsive.defaultProps={className:"",constrainHeight:false,allowFullBleed:false};
1490
1490
 
1491
1491
  async function decodeGifFrames(src){const res=await fetch(src);if(!res.ok){return []}const buffer=await res.arrayBuffer();const gif=parseGIF(buffer);return decompressFrames(gif,true)}const GifImage=props=>{const{src,alt,width,height,scale,isPlaying,onLoop,onLoad}=props;const framesRef=React.useRef([]);const canvasRef=React.useRef(null);const hiddenCanvasRef=React.useRef(null);const animationIdRef=React.useRef(null);const currentFrameIndexRef=React.useRef(0);const lastFrameTimeRef=React.useRef(null);const latestPropsRef=React.useRef({isPlaying,onLoop,onLoad});latestPropsRef.current={isPlaying,onLoop,onLoad};const drawPatch=React.useCallback(index=>{const frames=framesRef.current;const hiddenCtx=hiddenCanvasRef.current?.getContext("2d");const displayCtx=canvasRef.current?.getContext("2d");if(!hiddenCtx||!hiddenCanvasRef.current||!displayCtx||!canvasRef.current||frames.length===0){return}const frame=frames[index];const{dims}=frame;if(index>0){const prev=frames[index-1];if(prev.disposalType===2){displayCtx.clearRect(prev.dims.left,prev.dims.top,prev.dims.width,prev.dims.height);}}hiddenCanvasRef.current.width=dims.width;hiddenCanvasRef.current.height=dims.height;const imageData=new ImageData(new Uint8ClampedArray(frame.patch),dims.width,dims.height);hiddenCtx.putImageData(imageData,0,0);displayCtx.drawImage(hiddenCanvasRef.current,dims.left,dims.top);},[]);const drawFrame=React.useCallback((index=0)=>{const frames=framesRef.current;if(frames.length===0||!canvasRef.current||!hiddenCanvasRef.current){return}const targetIndex=Math.min(index,frames.length-1);currentFrameIndexRef.current=targetIndex;const{width:nativeWidth,height:nativeHeight}=frames[0].dims;canvasRef.current.width=nativeWidth;canvasRef.current.height=nativeHeight;for(let i=0;i<=targetIndex;i++){drawPatch(i);}},[drawPatch]);const pause=React.useCallback(()=>{if(animationIdRef.current!==null){cancelAnimationFrame(animationIdRef.current);animationIdRef.current=null;}},[]);const animate=React.useCallback(timestamp=>{const frames=framesRef.current;if(frames.length===0){return}if(lastFrameTimeRef.current===null){lastFrameTimeRef.current=timestamp;drawPatch(currentFrameIndexRef.current);}const frame=frames[currentFrameIndexRef.current];const delay=frame.delay<=0?10:frame.delay;if(timestamp-lastFrameTimeRef.current>=delay){currentFrameIndexRef.current++;if(currentFrameIndexRef.current>=frames.length){currentFrameIndexRef.current=0;animationIdRef.current=null;latestPropsRef.current.onLoop();return}drawPatch(currentFrameIndexRef.current);lastFrameTimeRef.current=lastFrameTimeRef.current+delay;}animationIdRef.current=requestAnimationFrame(animate);},[drawPatch]);const play=React.useCallback(()=>{if(framesRef.current.length===0){return}lastFrameTimeRef.current=null;animationIdRef.current=requestAnimationFrame(animate);},[animate]);const restart=React.useCallback(()=>{pause();drawFrame(0);},[pause,drawFrame]);React.useEffect(()=>{let mounted=true;decodeGifFrames(src).then(frames=>{if(!mounted){return}framesRef.current=frames;drawFrame(0);latestPropsRef.current.onLoad?.();if(latestPropsRef.current.isPlaying){play();}});return ()=>{mounted=false;pause();framesRef.current=[];}},[src]);const prevIsPlayingRef=React.useRef(isPlaying);React.useEffect(()=>{const wasPlaying=prevIsPlayingRef.current;prevIsPlayingRef.current=isPlaying;if(isPlaying&&!wasPlaying){play();}if(!isPlaying&&wasPlaying){if(currentFrameIndexRef.current===0){restart();}else {pause();}}},[isPlaying,restart,play,pause,drawFrame]);const setCanvasRef=React.useCallback(canvas=>{canvasRef.current=canvas;if(canvas&&framesRef.current.length>0){drawFrame(0);}},[drawFrame]);return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx("canvas",{"aria-label":alt,role:"img",ref:setCanvasRef,"data-testid":"gif-canvas",style:{width:width?width*scale:undefined,height:height?height*scale:undefined}}),jsxRuntimeExports.jsx("canvas",{"aria-hidden":true,tabIndex:-1,ref:hiddenCanvasRef,"data-testid":"gif-hidden-canvas",style:{display:"none"}})]})};
1492
1492
 
@@ -1561,7 +1561,7 @@ const reactRootCache=new WeakMap;function render(element,container){const childr
1561
1561
 
1562
1562
  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]);}}};
1563
1563
 
1564
- 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,setDrawingAreaAvailable:function(){},...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).bind("vmousemove.mouseLayer",e=>{if(isClickingCanvas){e.preventDefault();handler(this.getMouseCoord(e));}});}$(document).bind("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));});}}if(!localOptions.allowScratchpad){localOptions.setDrawingAreaAvailable?.(false);}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){const $span=$(span);const direction=$span.data("labelDirection");let[width,height]=size;if($span.data("originalLabelSize")==null){$span.data("originalLabelSize",[width,height]);}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});}else {const currentHeightMatchesProps=span.scrollHeight===height;const $svgImage=$span.closest(".svg-image");const $graphie=$span.closest(".graphie");const $container=$svgImage.length>0?$svgImage:$graphie;$container.css("line-height","normal");if(currentHeightMatchesProps&&span.scrollHeight!==height){height=span.scrollHeight;}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:{}};
1564
+ 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,setDrawingAreaAvailable:function(){},...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).bind("vmousemove.mouseLayer",e=>{if(isClickingCanvas){e.preventDefault();handler(this.getMouseCoord(e));}});}$(document).bind("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));});}}if(!localOptions.allowScratchpad){localOptions.setDrawingAreaAvailable?.(false);}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){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:{}};
1565
1565
 
1566
1566
  (function($,window1,document1,undefined$1){if(typeof $==="undefined"){return}var dataPropertyName="virtualMouseBindings",touchTargetPropertyName="virtualTouchID",virtualEventNames="vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),touchEventProps="clientX clientY pageX pageY screenX screenY".split(" "),mouseHookProps=$.event.mouseHooks?$.event.mouseHooks.props:[],mouseEventProps=$.event.props.concat(mouseHookProps),activeDocHandlers={},resetTimerID=0,startX=0,startY=0,didScroll=false,clickBlockList=[],blockMouseTriggers=false,blockTouchTriggers=false,eventCaptureSupported="addEventListener"in document1,$document=$(document1),nextTouchID=1,lastTouchID=0;$.vmouse={moveDistanceThreshold:10,clickDistanceThreshold:10,resetTimerDuration:1500};function getNativeEvent(event){while(event&&typeof event.originalEvent!=="undefined"){event=event.originalEvent;}return event}function createVirtualEvent(event,eventType){var t=event.type,oe,props,ne,prop,ct,touch,i,j,len;event=$.Event(event);event.type=eventType;oe=event.originalEvent;props=$.event.props;if(t.search(/mouse/)>-1){props=mouseEventProps;}if(oe){for(i=props.length,prop;i;){prop=props[--i];event[prop]=oe[prop];}}if(t.search(/mouse(down|up)|click/)>-1&&!event.which){event.which=1;}if(t.search(/^touch/)!==-1){ne=getNativeEvent(oe);t=ne.touches;ct=ne.changedTouches;touch=t&&t.length?t[0]:ct&&ct.length?ct[0]:undefined$1;if(touch){for(j=0,len=touchEventProps.length;j<len;j++){prop=touchEventProps[j];event[prop]=touch[prop];}}}return event}function getVirtualBindingFlags(element){var flags={},b,k;while(element){b=$.data(element,dataPropertyName);for(k in b){if(b[k]){flags[k]=flags.hasVirtualBinding=true;}}element=element.parentNode;}return flags}function getClosestElementWithVirtualBinding(element,eventType){var b;while(element){b=$.data(element,dataPropertyName);if(b&&(!eventType||b[eventType])){return element}element=element.parentNode;}return null}function enableTouchBindings(){blockTouchTriggers=false;}function disableTouchBindings(){blockTouchTriggers=true;}function enableMouseBindings(){lastTouchID=0;clickBlockList.length=0;blockMouseTriggers=false;disableTouchBindings();}function disableMouseBindings(){enableTouchBindings();}function startResetTimer(){clearResetTimer();resetTimerID=setTimeout(function(){resetTimerID=0;enableMouseBindings();},$.vmouse.resetTimerDuration);}function clearResetTimer(){if(resetTimerID){clearTimeout(resetTimerID);resetTimerID=0;}}function triggerVirtualEvent(eventType,event,flags){var ve;if(flags&&flags[eventType]||!flags&&getClosestElementWithVirtualBinding(event.target,eventType)){ve=createVirtualEvent(event,eventType);$(event.target).trigger(ve);}return ve}function mouseEventCallback(event){var touchID=$.data(event.target,touchTargetPropertyName);if(!blockMouseTriggers&&(!lastTouchID||lastTouchID!==touchID)){var ve=triggerVirtualEvent("v"+event.type,event);if(ve){if(ve.isDefaultPrevented()){event.preventDefault();}if(ve.isPropagationStopped()){event.stopPropagation();}if(ve.isImmediatePropagationStopped()){event.stopImmediatePropagation();}}}}function handleTouchStart(event){var touches=getNativeEvent(event).touches,target,flags;if(touches&&touches.length===1){target=event.target;flags=getVirtualBindingFlags(target);if(flags.hasVirtualBinding){lastTouchID=nextTouchID++;$.data(target,touchTargetPropertyName,lastTouchID);clearResetTimer();disableMouseBindings();didScroll=false;var t=getNativeEvent(event).touches[0];startX=t.pageX;startY=t.pageY;triggerVirtualEvent("vmouseover",event,flags);triggerVirtualEvent("vmousedown",event,flags);}}}function handleScroll(event){if(blockTouchTriggers){return}if(!didScroll){triggerVirtualEvent("vmousecancel",event,getVirtualBindingFlags(event.target));}didScroll=true;startResetTimer();}function handleTouchMove(event){if(blockTouchTriggers){return}var t=getNativeEvent(event).touches[0],didCancel=didScroll,moveThreshold=$.vmouse.moveDistanceThreshold,didScroll=didScroll||Math.abs(t.pageX-startX)>moveThreshold||Math.abs(t.pageY-startY)>moveThreshold,flags=getVirtualBindingFlags(event.target);if(didScroll&&!didCancel){triggerVirtualEvent("vmousecancel",event,flags);}triggerVirtualEvent("vmousemove",event,flags);startResetTimer();}function handleTouchEnd(event){if(blockTouchTriggers){return}disableTouchBindings();var flags=getVirtualBindingFlags(event.target),t;triggerVirtualEvent("vmouseup",event,flags);if(!didScroll){var ve=triggerVirtualEvent("vclick",event,flags);if(ve&&ve.isDefaultPrevented()){t=getNativeEvent(event).changedTouches[0];clickBlockList.push({touchID:lastTouchID,x:t.clientX,y:t.clientY});blockMouseTriggers=true;}}triggerVirtualEvent("vmouseout",event,flags);didScroll=false;startResetTimer();}function hasVirtualBindings(ele){var bindings=$.data(ele,dataPropertyName),k;if(bindings){for(k in bindings){if(bindings[k]){return true}}}return false}function dummyMouseHandler(){}function getSpecialEventObject(eventType){var realType=eventType.substr(1);return {setup:function(data,namespace){if(!hasVirtualBindings(this)){$.data(this,dataPropertyName,{});}var bindings=$.data(this,dataPropertyName);bindings[eventType]=true;activeDocHandlers[eventType]=(activeDocHandlers[eventType]||0)+1;if(activeDocHandlers[eventType]===1){$document.bind(realType,mouseEventCallback);}$(this).bind(realType,dummyMouseHandler);if(eventCaptureSupported){activeDocHandlers["touchstart"]=(activeDocHandlers["touchstart"]||0)+1;if(activeDocHandlers["touchstart"]===1){$document.bind("touchstart",handleTouchStart).bind("touchend",handleTouchEnd).bind("touchmove",handleTouchMove).bind("scroll",handleScroll);}}},teardown:function(data,namespace){--activeDocHandlers[eventType];if(!activeDocHandlers[eventType]){$document.unbind(realType,mouseEventCallback);}if(eventCaptureSupported){--activeDocHandlers["touchstart"];if(!activeDocHandlers["touchstart"]){$document.unbind("touchstart",handleTouchStart).unbind("touchmove",handleTouchMove).unbind("touchend",handleTouchEnd).unbind("scroll",handleScroll);}}var $this=$(this),bindings=$.data(this,dataPropertyName);if(bindings){bindings[eventType]=false;}$this.unbind(realType,dummyMouseHandler);if(!hasVirtualBindings(this)){$this.removeData(dataPropertyName);}}}}for(var i=0;i<virtualEventNames.length;i++){$.event.special[virtualEventNames[i]]=getSpecialEventObject(virtualEventNames[i]);}if(eventCaptureSupported){document1.addEventListener("click",function(e){var cnt=clickBlockList.length,target=e.target,x,y,ele,i,o;if(cnt){x=e.clientX;y=e.clientY;threshold=$.vmouse.clickDistanceThreshold;ele=target;while(ele){for(i=0;i<cnt;i++){o=clickBlockList[i];if(ele===target&&Math.abs(o.x-x)<threshold&&Math.abs(o.y-y)<threshold||$.data(ele,touchTargetPropertyName)===o.touchID){e.preventDefault();e.stopPropagation();return}}ele=ele.parentNode;}}},true);}})($,window,document);
1567
1567
 
@@ -1670,9 +1670,9 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
1670
1670
  supportsStaticMode: supportsStaticMode
1671
1671
  });
1672
1672
 
1673
- 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$1({"perseus-widget-container":true,"widget-highlight":this.props.shouldHighlight,"widget-nohighlight":!this.props.shouldHighlight,"perseus-widget__definition":this.props.type==="definition"});const type=this.props.type;const userAgent=navigator.userAgent;const WidgetType=getWidget(type);if(WidgetType==null){console.warn(`Widget type '${type}' not found!`);return jsxRuntimeExports.jsx("div",{className:className})}const subType=getWidgetSubType(type,this.props.widgetProps)??"null";let alignment=this.props.widgetProps.alignment;if(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.linterContext:{...this.props.linterContext,highlightLint:false};return jsxRuntimeExports.jsx("div",{className:className,style:isStatic?staticContainerStyles:{},children:jsxRuntimeExports.jsx(DependenciesContext.Consumer,{children:({analytics})=>jsxRuntimeExports.jsxs(ErrorBoundary,{metadata:{widget_type:type,widget_id:this.props.id},onError:error=>{analytics.onAnalyticsEvent({type:"perseus:widget-rendering-error:ti",payload:{widgetSubType:subType,widgetType:type,widgetId:this.props.id,message:error.message,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};}}WidgetContainer.defaultProps={linterContext:linterContextDefault};
1673
+ 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,"widget-highlight":this.props.shouldHighlight,"widget-nohighlight":!this.props.shouldHighlight,"perseus-widget__definition":this.props.type==="definition"});const type=this.props.type;const userAgent=navigator.userAgent;const WidgetType=getWidget(type);if(WidgetType==null){console.warn(`Widget type '${type}' not found!`);return jsxRuntimeExports.jsx("div",{className:className})}const subType=getWidgetSubType(type,this.props.widgetProps)??"null";let alignment=this.props.widgetProps.alignment;if(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.linterContext:{...this.props.linterContext,highlightLint:false};return jsxRuntimeExports.jsx("div",{className:className,style:isStatic?staticContainerStyles:{},children:jsxRuntimeExports.jsx(DependenciesContext.Consumer,{children:({analytics})=>jsxRuntimeExports.jsxs(ErrorBoundary,{metadata:{widget_type:type,widget_id:this.props.id},onError:error=>{analytics.onAnalyticsEvent({type:"perseus:widget-rendering-error:ti",payload:{widgetSubType:subType,widgetType:type,widgetId:this.props.id,message:error.message,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};}}WidgetContainer.defaultProps={linterContext:linterContextDefault};
1674
1674
 
1675
- 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))}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);}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 widgetProps=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 {...widgetProps,userInput:this.props.userInput?.[widgetId],widgetId:widgetId,widgetIndex:this._getWidgetIndexById(widgetId),alignment:widgetInfo&&widgetInfo.alignment,static:widgetInfo?.static,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,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})}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$1({[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;let shouldHighlight=false;if(this.props.highlightEmptyWidgets&&this.props.userInput){shouldHighlight=this.emptyWidgets().includes(id);}return jsxRuntimeExports.jsx(WidgetContainer,{id:id,ref:node=>{const containerId=makeContainerId(id);if(node!=null){this._widgetContainers.set(containerId,node);}else {this._widgetContainers.delete(containerId);}},type:type,widgetProps:this.getWidgetProps(id),shouldHighlight:shouldHighlight,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"widget")},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(!_.isArray(focusPath)){throw new PerseusError("widget props.onFocus focusPath must be an Array, "+"but was"+JSON.stringify(focusPath),Errors.Internal)}this._setCurrentFocus([id].concat(focusPath));},this._onWidgetBlur=(id,blurPath)=>{const blurringFocusPath=this._currentFocus;const fullPath=[id].concat(blurPath);if(!_.isEqual(fullPath,blurringFocusPath)){return}_.defer(()=>{if(_.isEqual(this._currentFocus,blurringFocusPath)){this._setCurrentFocus(null);}});},this.getContent=(props,state)=>{return state.jiptContent||props.content},this.shouldRenderJiptPlaceholder=(props,state)=>{return getDependencies().JIPT.useJIPT&&state.jiptContent==null&&props.content.indexOf("crwdns")!==-1},this.replaceJiptContent=(content,paragraphIndex)=>{if(paragraphIndex==null){this.setState({jiptContent:content});}else {const codeFenceRegex=/^\s*(`{3,}|~{3,})\s*(\S+)?\s*\n([\s\S]+?)\s*\1\s*$/;if(codeFenceRegex.test(content));else if(/\S\n\s*\n\S/.test(content)){content="$\\large{\\red{\\text{Please translate each "+"paragraph to a single paragraph.}}}$";}else if(/^\s*$/.test(content)){content="$\\large{\\red{\\text{Translated paragraph is "+"currently empty}}}$";}const allContent=this.getContent(this.props,this.state);const paragraphs=JiptParagraphs.parseToArray(allContent);paragraphs[paragraphIndex]=content;this.setState({jiptContent:JiptParagraphs.joinFromArray(paragraphs)});}},this.outputMarkdown=(ast,state)=>{if(_.isArray(ast)){const oldKey=state.key;const result=[];let lastWasString=false;for(let i=0;i<ast.length;i++){state.key=i;state.paragraphIndex=i;const nodeOut=this.outputMarkdown(ast[i],state);const isString=typeof nodeOut==="string";if(typeof nodeOut==="string"&&lastWasString){result[result.length-1]+=nodeOut;}else {result.push(nodeOut);}lastWasString=isString;}state.key=oldKey;return result}this._foundTextNodes=false;state.foundFullWidth=false;const output=this.outputNested(ast,state);let className;if(this.translationIndex!=null){className=null;}else {className=classNames$1({"perseus-paragraph-centered":!this._foundTextNodes,"perseus-paragraph-full-width":state.foundFullWidth&&ast.content.length===1});}return jsxRuntimeExports.jsx(QuestionParagraph,{className:className,translationIndex:this.translationIndex,paragraphIndex:state.paragraphIndex,inline:this.props.inline,children:jsxRuntimeExports.jsx(ErrorBoundary,{children:output})},state.key)},this.outputNested=(ast,state)=>{if(_.isArray(ast)){const oldKey=state.key;const result=[];let lastWasString=false;for(let i=0;i<ast.length;i++){state.key=i;const nodeOut=this.outputNested(ast[i],state);const isString=typeof nodeOut==="string";if(typeof nodeOut==="string"&&lastWasString){result[result.length-1]+=nodeOut;}else {result.push(nodeOut);}lastWasString=isString;}state.key=oldKey;return result}return this.outputNode(ast,this.outputNested,state)},this.outputNode=(node,nestedOutput,state)=>{const apiOptions=this.getApiOptions();const imagePlaceholder=apiOptions.imagePlaceholder;if(node.type==="widget"){const widgetPlaceholder=apiOptions.widgetPlaceholder;if(widgetPlaceholder){return widgetPlaceholder}this._foundTextNodes=true;if(this.widgetIds.includes(node.id)){return jsxRuntimeExports.jsx("span",{className:"renderer-widget-error",children:["Widget [[","☃"," ",node.id,"]] already exists."].join("")},state.key)}this.widgetIds.push(node.id);return this.renderWidget(node.widgetType,node.id,state)}if(node.type==="blockMath"){const content=preprocessTex(node.content);const innerStyle={overflowX:"auto",overflowY:"hidden",paddingTop:10,paddingBottom:10,marginTop:-10,marginBottom:-10};if(apiOptions.isMobile){const margin=16;const outerStyle={marginLeft:-16,marginRight:-16};const horizontalPadding={paddingLeft:margin,paddingRight:margin};const mobileInnerStyle={...innerStyle,...styles$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.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$z.mobileZoomableParentFix};const wrappedOutput=jsxRuntimeExports.jsx("div",{style:{...mobileInnerStyle,overflowX:"auto"},children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(Zoomable,{animateHeight:true,children:output})})});return jsxRuntimeExports.jsx("div",{style:outerStyle,children:wrappedOutput})}return jsxRuntimeExports.jsx(ErrorBoundary,{children:PerseusMarkdown.ruleOutput(node,nestedOutput,state)},state.key)},this.handleRender=prevProps=>{const onRender=this.props.onRender;const oldOnRender=prevProps.onRender;if(onRender!==noopOnRender||oldOnRender!==noopOnRender){const $images=$(ReactDOM__default.findDOMNode(this)).find("img");if(oldOnRender!==noopOnRender){$images.off("load",oldOnRender);}if(onRender!==noopOnRender){$images.on("load",onRender);}}onRender();},this._setCurrentFocus=path=>{const apiOptions=this.getApiOptions();if(!isIdPathPrefix(path,this._currentFocus)){const prevFocus=this._currentFocus;if(prevFocus){this.blurPath(prevFocus);}this._currentFocus=path;apiOptions.onFocusChange(this._currentFocus,prevFocus);}},this.focus=()=>{let id;let focusResult;for(let i=0;i<this.widgetIds.length;i++){const widgetId=this.widgetIds[i];const widget=this.getWidgetInstance(widgetId);const widgetFocusResult=widget?.focus?.();if(widgetFocusResult){id=widgetId;focusResult=widgetFocusResult;break}}if(id){let path;if(typeof focusResult==="object"){path=[id].concat(focusResult.path||[]);Log.error("Renderer received a focus result of type 'object' "+"instead of the expected type 'boolean'",Errors.Internal,{loggedMetadata:{focusResult:JSON.stringify(focusResult)}});}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.contextType=PerseusI18nContext;Renderer.defaultProps={content:"",widgets:{},images:{},showSolutions:"none",onRender:noopOnRender,findExternalWidgets:()=>[],alwaysUpdate:false,reviewMode:false,linterContext:PerseusLinter.linterContextDefault};const styles$z={mobileZoomableParentFix:{transform:"translate3d(0,0,0)"}};
1675
+ 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))}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);}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 widgetProps=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 {...widgetProps,userInput:this.props.userInput?.[widgetId],widgetId:widgetId,widgetIndex:this._getWidgetIndexById(widgetId),alignment:widgetInfo&&widgetInfo.alignment,static:widgetInfo?.static,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,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})}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;let shouldHighlight=false;if(this.props.highlightEmptyWidgets&&this.props.userInput){shouldHighlight=this.emptyWidgets().includes(id);}return jsxRuntimeExports.jsx(WidgetContainer,{id:id,ref:node=>{const containerId=makeContainerId(id);if(node!=null){this._widgetContainers.set(containerId,node);}else {this._widgetContainers.delete(containerId);}},type:type,widgetProps:this.getWidgetProps(id),shouldHighlight:shouldHighlight,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"widget")},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(!_.isArray(focusPath)){throw new PerseusError("widget props.onFocus focusPath must be an Array, "+"but was"+JSON.stringify(focusPath),Errors.Internal)}this._setCurrentFocus([id].concat(focusPath));},this._onWidgetBlur=(id,blurPath)=>{const blurringFocusPath=this._currentFocus;const fullPath=[id].concat(blurPath);if(!_.isEqual(fullPath,blurringFocusPath)){return}_.defer(()=>{if(_.isEqual(this._currentFocus,blurringFocusPath)){this._setCurrentFocus(null);}});},this.getContent=(props,state)=>{return state.jiptContent||props.content},this.shouldRenderJiptPlaceholder=(props,state)=>{return getDependencies().JIPT.useJIPT&&state.jiptContent==null&&props.content.indexOf("crwdns")!==-1},this.replaceJiptContent=(content,paragraphIndex)=>{if(paragraphIndex==null){this.setState({jiptContent:content});}else {const codeFenceRegex=/^\s*(`{3,}|~{3,})\s*(\S+)?\s*\n([\s\S]+?)\s*\1\s*$/;if(codeFenceRegex.test(content));else if(/\S\n\s*\n\S/.test(content)){content="$\\large{\\red{\\text{Please translate each "+"paragraph to a single paragraph.}}}$";}else if(/^\s*$/.test(content)){content="$\\large{\\red{\\text{Translated paragraph is "+"currently empty}}}$";}const allContent=this.getContent(this.props,this.state);const paragraphs=JiptParagraphs.parseToArray(allContent);paragraphs[paragraphIndex]=content;this.setState({jiptContent:JiptParagraphs.joinFromArray(paragraphs)});}},this.outputMarkdown=(ast,state)=>{if(_.isArray(ast)){const oldKey=state.key;const result=[];let lastWasString=false;for(let i=0;i<ast.length;i++){state.key=i;state.paragraphIndex=i;const nodeOut=this.outputMarkdown(ast[i],state);const isString=typeof nodeOut==="string";if(typeof nodeOut==="string"&&lastWasString){result[result.length-1]+=nodeOut;}else {result.push(nodeOut);}lastWasString=isString;}state.key=oldKey;return result}this._foundTextNodes=false;state.foundFullWidth=false;const output=this.outputNested(ast,state);let className;if(this.translationIndex!=null){className=null;}else {className=classNames$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(_.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.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$z.mobileZoomableParentFix};const wrappedOutput=jsxRuntimeExports.jsx("div",{style:{...mobileInnerStyle,overflowX:"auto"},children:jsxRuntimeExports.jsx(ErrorBoundary,{children:jsxRuntimeExports.jsx(Zoomable,{animateHeight:true,children:output})})});return jsxRuntimeExports.jsx("div",{style:outerStyle,children:wrappedOutput})}return jsxRuntimeExports.jsx(ErrorBoundary,{children:PerseusMarkdown.ruleOutput(node,nestedOutput,state)},state.key)},this.handleRender=prevProps=>{const onRender=this.props.onRender;const oldOnRender=prevProps.onRender;if(onRender!==noopOnRender||oldOnRender!==noopOnRender){const $images=$(ReactDOM__default.findDOMNode(this)).find("img");if(oldOnRender!==noopOnRender){$images.off("load",oldOnRender);}if(onRender!==noopOnRender){$images.on("load",onRender);}}onRender();},this._setCurrentFocus=path=>{const apiOptions=this.getApiOptions();if(!isIdPathPrefix(path,this._currentFocus)){const prevFocus=this._currentFocus;if(prevFocus){this.blurPath(prevFocus);}this._currentFocus=path;apiOptions.onFocusChange(this._currentFocus,prevFocus);}},this.focus=()=>{let id;let focusResult;for(let i=0;i<this.widgetIds.length;i++){const widgetId=this.widgetIds[i];const widget=this.getWidgetInstance(widgetId);const widgetFocusResult=widget?.focus?.();if(widgetFocusResult){id=widgetId;focusResult=widgetFocusResult;break}}if(id){let path;if(typeof focusResult==="object"){path=[id].concat(focusResult.path||[]);Log.error("Renderer received a focus result of type 'object' "+"instead of the expected type 'boolean'",Errors.Internal,{loggedMetadata:{focusResult:JSON.stringify(focusResult)}});}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.contextType=PerseusI18nContext;Renderer.defaultProps={content:"",widgets:{},images:{},showSolutions:"none",onRender:noopOnRender,findExternalWidgets:()=>[],alwaysUpdate:false,reviewMode:false,linterContext:PerseusLinter.linterContextDefault};const styles$z={mobileZoomableParentFix:{transform:"translate3d(0,0,0)"}};
1676
1676
 
1677
1677
  const{captureScratchpadTouchStart: captureScratchpadTouchStart$1}=Util;const InputWithExamples=forwardRef(function InputWithExamples({shouldShowExamples=true,onFocus=()=>{},onBlur=()=>{},disabled=false,linterContext=PerseusLinter.linterContextDefault,className="",...props},ref){const context=React.useContext(PerseusI18nContext);const inputRef=React.useRef(null);const[inputFocused,setInputFocused]=React.useState(false);const id=useId();const ariaId=`aria-for-${id}`;useImperativeHandle(ref,()=>({current:inputRef.current,focus:()=>{if(inputRef.current){inputRef.current.focus();}},blur:()=>{if(inputRef.current){inputRef.current.blur();}}}));const getInputClassName=()=>{let inputClassName=ClassNames.INPUT;if(inputFocused){inputClassName+=" "+ClassNames.FOCUSED;}if(className){inputClassName+=" "+className;}return inputClassName};const handleFocus=()=>{onFocus();setInputFocused(true);};const handleBlur=()=>{onBlur();setInputFocused(false);};const renderInput=()=>{const examplesAria=shouldShowExamples?`${props.examples[0]}
1678
1678
  ${props.examples.slice(1).join(", or\n")}`.replace(/\*/g,"").replace(/\$/g,"").replace(/\\ \\text{pi}/g," pi").replace(/\\ /g," and "):"";const inputProps={id:id,"aria-describedby":shouldShowExamples?ariaId:undefined,ref:inputRef,className:getInputClassName(),labelText:props.labelText,value:props.value,onFocus:handleFocus,onBlur:handleBlur,disabled:disabled,style:props.style,onChange:props.onChange,onTouchStart:captureScratchpadTouchStart$1,autoCapitalize:"off",autoComplete:"off",autoCorrect:"off",spellCheck:"false"};return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(TextInput,{...inputProps}),jsxRuntimeExports.jsx("span",{id:ariaId,style:{display:"none"},children:examplesAria})]})};const renderTooltipContent=()=>{return jsxRuntimeExports.jsx(TooltipContent,{children:jsxRuntimeExports.jsx("div",{id:id,className:"input-with-examples-tooltip",children:jsxRuntimeExports.jsx(Renderer,{content:examplesContent,linterContext:PerseusLinter.pushContextStack(linterContext,"input-with-examples"),strings:context.strings})})})};const examplesContent=props.examples.length<=2?props.examples.join(" "):props.examples.map((example,index)=>{return index===0&&example.startsWith("**")?`${example}
@@ -1688,7 +1688,7 @@ const styles$w={inputWithExamples:{borderRadius:"0.3rem",borderWidth:"var(--wb-b
1688
1688
 
1689
1689
  const NumericExampleStrings={integer:(form,strings)=>strings.integerExample,proper:(form,strings)=>form.simplify==="optional"?strings.properExample:strings.simplifiedProperExample,improper:(form,strings)=>form.simplify==="optional"?strings.improperExample:strings.simplifiedImproperExample,mixed:(form,strings)=>strings.mixedExample,decimal:(form,strings)=>strings.decimalExample,pi:(form,strings)=>strings.piExample};const generateExamples=(answerForms,strings)=>{if(answerForms.length===0){return []}const uniqueForms=getUniqueAnswerForms(answerForms);const examples=uniqueForms.map(form=>{return NumericExampleStrings[form.name](form,strings)});return [strings.yourAnswer].concat(examples)};const shouldShowExamples=answerForms=>{if(answerForms.length===0){return false}const answerFormNames=getUniqueAnswerForms(answerForms).map(form=>form.name);const allFormsAccepted=answerFormNames.length>=Object.keys(NumericExampleStrings).length;return !allFormsAccepted};const getUniqueAnswerForms=function(list){const foundForms=new Set;return list.filter(form=>{if(foundForms.has(form.name)){return false}foundForms.add(form.name);return true})};const unionAnswerForms=function(answerFormsList){const allForms=answerFormsList.flat();const uniqueForms=getUniqueAnswerForms(allForms);const formExampleKeys=Object.keys(NumericExampleStrings);return uniqueForms.sort((a,b)=>{return formExampleKeys.indexOf(a.name)-formExampleKeys.indexOf(b.name)})};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}})}))}
1690
1690
 
1691
- const NumericInputComponent=forwardRef(function NumericInputComponent(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,()=>({current:inputRef.current,focus:()=>{if(inputRef.current){inputRef.current?.focus();setIsFocused(true);}},blur:()=>{if(inputRef.current){inputRef.current?.blur();setIsFocused(false);}}}));const handleChange=newValue=>{props.handleUserInput({currentValue:newValue});props.trackInteraction();};const handleFocus=()=>{props.onFocus([]);setIsFocused(true);};const handleBlur=()=>{props.onBlur([]);setIsFocused(false);};const legacyStylesToUse={...styles$w.inputWithExamples,...isFocused?styles$w.isFocused:{},...props.rightAlign?styles$w.rightAlign:{},...props.size==="small"?styles$w.sizeSmall:{}};const classesToUse=[styles$x.inputWithExamples];if(isFocused){classesToUse.push(styles$x.isFocused);}if(props.rightAlign){classesToUse.push(styles$x.rightAlign);}if(props.size==="small"){classesToUse.push(styles$x.sizeSmall);}if(props.apiOptions.customKeypad){const alignmentClass=props.rightAlign?"perseus-input-right-align":undefined;return jsxRuntimeExports.jsx("div",{className:alignmentClass,children:jsxRuntimeExports.jsx(SimpleKeypadInput,{ref:inputRef,value:props.userInput.currentValue,keypadElement:props.keypadElement,onChange:handleChange,onFocus:handleFocus,onBlur:handleBlur})})}return jsxRuntimeExports.jsx(InputWithExamples,{ref:inputRef,value:props.userInput.currentValue,onChange:handleChange,labelText:props.labelText,examples:generateExamples(props.answerForms,context.strings),shouldShowExamples:shouldShowExamples(props.answerForms),onFocus:handleFocus,onBlur:handleBlur,id:props.widgetId,disabled:props.apiOptions.readOnly,style:legacyStylesToUse,className:classesToUse.join(" ")})});
1691
+ const NumericInputComponent=forwardRef(function NumericInputComponent(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,()=>({current:inputRef.current,focus:()=>{if(inputRef.current){inputRef.current?.focus();setIsFocused(true);}},blur:()=>{if(inputRef.current){inputRef.current?.blur();setIsFocused(false);}}}));const handleChange=newValue=>{props.handleUserInput({currentValue:newValue});props.trackInteraction();};const handleFocus=()=>{props.onFocus([]);setIsFocused(true);};const handleBlur=()=>{props.onBlur([]);setIsFocused(false);};const legacyStylesToUse={...styles$w.inputWithExamples,...isFocused?styles$w.isFocused:{},...props.rightAlign?styles$w.rightAlign:{},...props.size==="small"?styles$w.sizeSmall:{}};const classesToUse=[styles$x.inputWithExamples];if(isFocused){classesToUse.push(styles$x.isFocused);}if(props.rightAlign){classesToUse.push(styles$x.rightAlign);}if(props.size==="small"){classesToUse.push(styles$x.sizeSmall);}if(props.apiOptions.customKeypad){const alignmentClass=props.rightAlign?"perseus-input-right-align":undefined;return jsxRuntimeExports.jsx("div",{className:alignmentClass,children:jsxRuntimeExports.jsx(SimpleKeypadInput,{ref:inputRef,value:props.userInput.currentValue,keypadElement:props.keypadElement,onChange:handleChange,onFocus:handleFocus,onBlur:handleBlur})})}return jsxRuntimeExports.jsx(InputWithExamples,{ref:inputRef,value:props.userInput.currentValue,onChange:handleChange,labelText:props.labelText||context.strings.yourAnswerLabel,examples:generateExamples(props.answerForms,context.strings),shouldShowExamples:shouldShowExamples(props.answerForms),onFocus:handleFocus,onBlur:handleBlur,id:props.widgetId,disabled:props.apiOptions.readOnly,style:legacyStylesToUse,className:classesToUse.join(" ")})});
1692
1692
 
1693
1693
  class NumericInput extends React.Component{getPromptJSON(){return getPromptJSON$p(this.props)}getSerializedState(){const{userInput,answers:_,...rest}=this.props;return {...rest,currentValue:userInput.currentValue}}render(){return jsxRuntimeExports.jsx(NumericInputComponent,{...this.props,answerForms:normalizeCorrectAnswerForms(this.props.answers),ref:this.inputRef})}constructor(...args){super(...args),this.inputRef=React.createRef(),this.focus=()=>{this.inputRef.current?.focus();return true},this.focusInputPath=()=>{this.inputRef.current?.focus();},this.blurInputPath=()=>{this.inputRef.current?.blur();},this.getInputPaths=()=>{return [[]]};}}NumericInput.defaultProps={size:"normal",rightAlign:false,apiOptions:ApiOptions.defaults,coefficient:false,answerForms:[],labelText:"",linterContext:linterContextDefault,userInput:{currentValue:""}};function getStartUserInput$h(){return {currentValue:""}}function getUserInputFromSerializedState$g(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:""}}var NumericInput$1 = {name:"numeric-input",displayName:"Numeric input",widget:NumericInput,isLintable:true,getCorrectUserInput: getCorrectUserInput$8,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]},getStartUserInput: getStartUserInput$h,getUserInputFromSerializedState: getUserInputFromSerializedState$g};
1694
1694
 
@@ -1718,7 +1718,7 @@ const RadioComponent=({choices,countChoices,multipleSelect=false,numCorrect,onCh
1718
1718
 
1719
1719
  const getChoiceStates=({choices,showSolutions,choiceStates,reviewMode})=>{if(showSolutions==="all"||reviewMode){return choices.map(choice=>({selected:!!choice.correct}))}if(choiceStates){return choiceStates}return choices.map(()=>({selected:false}))};
1720
1720
 
1721
- const RadioWidget=forwardRef(function RadioWidget(props,ref){const{multipleSelect=false,countChoices=false,showSolutions="none",apiOptions,handleUserInput,trackInteraction,findWidgets,reviewMode,widgetId}=props;const{strings}=usePerseusI18n();const{analytics}=useDependencies();const randomSeed=(props.problemNum??0)+(props.widgetIndex??0);const choices=useMemo(()=>{return [...choiceTransform(props.choices,props.randomize,strings,randomSeed)]},[props.choices,props.randomize,strings,randomSeed]);useOnMountEffect(()=>{analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:multipleSelect?"multiple-select":"single-select",widgetType:"radio",widgetId:widgetId}});});useImperativeHandle(ref,()=>({getPromptJSON:()=>{return getPromptJSON$o({...props,choices},props.userInput)},getSerializedState(){const{userInput:_,randomize:__,static:___,...rest}=props;return {...rest,numCorrect:props.numCorrect??0,choices,hasNoneOfTheAbove:props.hasNoneOfTheAbove??false,choiceStates:choices.map(choice=>{const selected=props.userInput?.selectedChoiceIds.includes(choice?.id)??false;return {selected,highlighted:false,readOnly:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false}})}}}),[choices,props]);const renderContent=(content="")=>{const linterContext={contentType:"radio",highlightLint:false,paths:[],stack:[]};return jsxRuntimeExports.jsx(MathRenderingContext.Provider,{value:{shouldAddAriaLabels:true},children:jsxRuntimeExports.jsx(Renderer,{content:content,findExternalWidgets:findWidgets,alwaysUpdate:true,linterContext:linterContext,strings:strings},"choiceContentRenderer")})};const handleChoiceChange=(choiceId,newCheckedState)=>{const checkedChoiceIds=[];const choiceStates=choices.map(choice=>{return {selected:props.userInput?.selectedChoiceIds.includes(choice.id)??false,id:choice.id}});const currentSelectedIds=choiceStates.filter(choice=>choice.selected).map(choice=>choice.id);if(newCheckedState&&!multipleSelect){checkedChoiceIds.push(choiceId);}else if(newCheckedState&&multipleSelect){checkedChoiceIds.push(...currentSelectedIds,choiceId);}else {checkedChoiceIds.push(...currentSelectedIds.filter(id=>id!==choiceId));}handleUserInput({selectedChoiceIds:checkedChoiceIds});trackInteraction();announceChoiceChange(checkedChoiceIds.length);};const announceChoiceChange=newCheckedCount=>{let screenReaderMessage="";if(!props.multipleSelect){screenReaderMessage=newCheckedCount===0?strings.notSelected:"";}else {screenReaderMessage=strings.choicesSelected({num:newCheckedCount});}announceMessage({message:screenReaderMessage});if(props.multipleSelect){setTimeout(()=>{announceMessage({message:""});},300);}};const buildChoiceProps=choiceStates=>{return choices.map((choice,i)=>{const content=choice.isNoneOfTheAbove&&!choice.content?strings.noneOfTheAbove:choice.content;const{selected=false}=choiceStates[i]??{};return {id:choice.id,content:renderContent(content),checked:selected,correct:!!choice.correct,hasRationale:!!choice.rationale,rationale:renderContent(choice.rationale),isNoneOfTheAbove:!!choice.isNoneOfTheAbove}})};const prepareChoicesProps=()=>{const choiceStates=choices.map(choice=>{return {selected:props.userInput?.selectedChoiceIds.includes(choice.id)??false}});const processedChoiceStates=getChoiceStates({choices,showSolutions,choiceStates,reviewMode});return buildChoiceProps(processedChoiceStates)};const choicesProps=prepareChoicesProps();const numCorrect=props.numCorrect;const isReviewMode=reviewMode||showSolutions==="all";const onChoiceChange=apiOptions.readOnly||isReviewMode?()=>{}:handleChoiceChange;return jsxRuntimeExports.jsx(RadioComponent,{reviewMode:isReviewMode,multipleSelect:multipleSelect,countChoices:countChoices,numCorrect:numCorrect,choices:choicesProps,onChoiceChange:onChoiceChange})});let Radio$1 = class Radio extends React.Component{getSerializedState(){if(!this.radioRef.current){throw new Error("Radio widget is not mounted; getSerializedState is unavailable.")}return this.radioRef.current.getSerializedState()}getPromptJSON(){if(!this.radioRef.current){throw new Error("Radio widget is not mounted; getPromptJSON is unavailable.")}return this.radioRef.current.getPromptJSON()}render(){return jsxRuntimeExports.jsx(RadioWidget,{ref:this.radioRef,...this.props})}constructor(...args){super(...args),this.radioRef=React.createRef();}};
1721
+ const RadioWidget=forwardRef(function RadioWidget(props,ref){const{multipleSelect=false,countChoices=false,showSolutions="none",apiOptions,handleUserInput,trackInteraction,findWidgets,reviewMode,widgetId}=props;const{strings}=usePerseusI18n();const{analytics}=useDependencies();const randomSeed=(props.problemNum??0)+(props.widgetIndex??0);const choices=useMemo(()=>{return [...choiceTransform(props.choices,props.randomize,strings,randomSeed)]},[props.choices,props.randomize,strings,randomSeed]);useOnMountEffect(()=>{analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:multipleSelect?"multiple-select":"single-select",widgetType:"radio",widgetId:widgetId}});});useImperativeHandle(ref,()=>({getPromptJSON:()=>{return getPromptJSON$o({...props,choices},props.userInput)},getSerializedState(){const{userInput:_,randomize:__,static:___,...rest}=props;return {...rest,numCorrect:props.numCorrect??0,choices,hasNoneOfTheAbove:props.hasNoneOfTheAbove??false,choiceStates:choices.map(choice=>{const selected=props.userInput?.selectedChoiceIds.includes(choice?.id)??false;return {selected,highlighted:false,readOnly:false,rationaleShown:false,correctnessShown:false,previouslyAnswered:false}})}}}),[choices,props]);const renderContent=(content="")=>{const linterContext={contentType:"radio",highlightLint:false,stack:[]};return jsxRuntimeExports.jsx(MathRenderingContext.Provider,{value:{shouldAddAriaLabels:true},children:jsxRuntimeExports.jsx(Renderer,{content:content,findExternalWidgets:findWidgets,alwaysUpdate:true,linterContext:linterContext,strings:strings},"choiceContentRenderer")})};const handleChoiceChange=(choiceId,newCheckedState)=>{const checkedChoiceIds=[];const choiceStates=choices.map(choice=>{return {selected:props.userInput?.selectedChoiceIds.includes(choice.id)??false,id:choice.id}});const currentSelectedIds=choiceStates.filter(choice=>choice.selected).map(choice=>choice.id);if(newCheckedState&&!multipleSelect){checkedChoiceIds.push(choiceId);}else if(newCheckedState&&multipleSelect){checkedChoiceIds.push(...currentSelectedIds,choiceId);}else {checkedChoiceIds.push(...currentSelectedIds.filter(id=>id!==choiceId));}handleUserInput({selectedChoiceIds:checkedChoiceIds});trackInteraction();announceChoiceChange(checkedChoiceIds.length);};const announceChoiceChange=newCheckedCount=>{let screenReaderMessage="";if(!props.multipleSelect){screenReaderMessage=newCheckedCount===0?strings.notSelected:"";}else {screenReaderMessage=strings.choicesSelected({num:newCheckedCount});}announceMessage({message:screenReaderMessage});if(props.multipleSelect){setTimeout(()=>{announceMessage({message:""});},300);}};const buildChoiceProps=choiceStates=>{return choices.map((choice,i)=>{const content=choice.isNoneOfTheAbove&&!choice.content?strings.noneOfTheAbove:choice.content;const{selected=false}=choiceStates[i]??{};return {id:choice.id,content:renderContent(content),checked:selected,correct:!!choice.correct,hasRationale:!!choice.rationale,rationale:renderContent(choice.rationale),isNoneOfTheAbove:!!choice.isNoneOfTheAbove}})};const prepareChoicesProps=()=>{const choiceStates=choices.map(choice=>{return {selected:props.userInput?.selectedChoiceIds.includes(choice.id)??false}});const processedChoiceStates=getChoiceStates({choices,showSolutions,choiceStates,reviewMode});return buildChoiceProps(processedChoiceStates)};const choicesProps=prepareChoicesProps();const numCorrect=props.numCorrect;const isReviewMode=reviewMode||showSolutions==="all";const onChoiceChange=apiOptions.readOnly||isReviewMode?()=>{}:handleChoiceChange;return jsxRuntimeExports.jsx(RadioComponent,{reviewMode:isReviewMode,multipleSelect:multipleSelect,countChoices:countChoices,numCorrect:numCorrect,choices:choicesProps,onChoiceChange:onChoiceChange})});let Radio$1 = class Radio extends React.Component{getSerializedState(){if(!this.radioRef.current){throw new Error("Radio widget is not mounted; getSerializedState is unavailable.")}return this.radioRef.current.getSerializedState()}getPromptJSON(){if(!this.radioRef.current){throw new Error("Radio widget is not mounted; getPromptJSON is unavailable.")}return this.radioRef.current.getPromptJSON()}render(){return jsxRuntimeExports.jsx(RadioWidget,{ref:this.radioRef,...this.props})}constructor(...args){super(...args),this.radioRef=React.createRef();}};
1722
1722
 
1723
1723
  function getStartUserInput$g(){return {selectedChoiceIds:[]}}var Radio = {name:"radio",displayName:"Radio / Multiple choice",widget:Radio$1,getStartUserInput: getStartUserInput$g,version:radioLogic.version,isLintable:true,getUserInputFromSerializedState:serializedState=>{return getUserInputFromSerializedState$f(serializedState)}};
1724
1724
 
@@ -1730,7 +1730,7 @@ const{radioBorderColor,checkedColor,circleSize,radioMarginWidth}=constants;var s
1730
1730
 
1731
1731
  const getPromptJSON$n=widgetData=>{return {type:"categorizer",options:{items:widgetData.items,categories:widgetData.categories},userInput:{itemToCategoryMapping:widgetData.userInput.values}}};
1732
1732
 
1733
- class Categorizer extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:"null",widgetType:"categorizer",widgetId:this.props.widgetId}});}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,values:userInput.values}}getPromptJSON(){return getPromptJSON$n(this.props)}_handleUserInput(itemNum,catNum){const values=[...this.props.userInput.values];values[itemNum]=catNum;this.props.handleUserInput({values});this.props.trackInteraction();}render(){const self=this;const isMobile=this.props.apiOptions.isMobile;let indexedItems=this.props.items.map((item,n)=>[item,n]);if(this.props.randomizeItems){indexedItems=shuffle(indexedItems,this.props.problemNum);}const table=jsxRuntimeExports.jsxs("table",{className:"categorizer-table "+css(styles$r.mobileTable),children:[jsxRuntimeExports.jsx("thead",{children:jsxRuntimeExports.jsxs("tr",{children:[jsxRuntimeExports.jsx("td",{className:css(styles$r.emptyHeaderCell)}),this.props.categories.map((category,i)=>{return jsxRuntimeExports.jsx("th",{className:css(styles$r.header),children:jsxRuntimeExports.jsx(Renderer,{content:category,linterContext:this.props.linterContext,strings:this.context.strings})},i)})]})}),jsxRuntimeExports.jsx("tbody",{children:indexedItems.map(indexedItem=>{const item=indexedItem[0];const itemNum=indexedItem[1];const uniqueId=self.state.uniqueId+"_"+itemNum;return jsxRuntimeExports.jsxs("tr",{children:[jsxRuntimeExports.jsx("td",{children:jsxRuntimeExports.jsx(Renderer,{content:item,linterContext:this.props.linterContext,strings:this.context.strings})}),self.props.categories.map((catName,catNum)=>{const selected=self.props.userInput.values[itemNum]===catNum;return jsxRuntimeExports.jsx("td",{className:"category "+css(styles$r.cell),children:jsxRuntimeExports.jsxs("div",{role:"button","aria-label":catName,onClick:()=>this._handleUserInput(itemNum,catNum),children:[isMobile&&jsxRuntimeExports.jsx("input",{type:"radio",name:uniqueId,className:css(sharedStyles.responsiveInput,sharedStyles.responsiveRadioInput),checked:selected,onChange:()=>this._handleUserInput(itemNum,catNum),onClick:e=>e.stopPropagation()}),!isMobile&&jsxRuntimeExports.jsx("span",{className:css(styles$r.radioSpan,selected&&styles$r.checkedRadioSpan,this.props.static&&selected&&styles$r.staticCheckedRadioSpan),children:selected?jsxRuntimeExports.jsx(InlineIcon,{...iconCircle}):jsxRuntimeExports.jsx(InlineIcon,{...iconCircleThin})})]})},catNum)})]},itemNum)})})]});const extraClassNames=classNames$1({"categorizer-container":true,"static-mode":this.props.static});const inlineStyles=this.props.apiOptions.isMobile?[styles$r.fullBleedContainer]:[];return jsxRuntimeExports.jsx("div",{className:extraClassNames+" "+css(...inlineStyles),children:table})}constructor(...args){super(...args),this.state={uniqueId:_.uniqueId("perseus_radio_")};}}Categorizer.contextType=PerseusI18nContext;Categorizer.defaultProps={items:[],categories:[],linterContext:linterContextDefault,userInput:{values:[]}};const styles$r=StyleSheet.create({mobileTable:{[mediaQueries.smOrSmaller]:{minWidth:"auto"}},fullBleedContainer:{[mediaQueries.mdOrSmaller]:{marginLeft:-16,marginRight:-16,overflowX:"auto"}},header:{textAlign:"center",verticalAlign:"bottom"},cell:{textAlign:"center",padding:0,color:"#ccc",verticalAlign:"middle"},emptyHeaderCell:{backgroundColor:"inherit",borderBottom:"2px solid #ccc"},radioSpan:{fontSize:30,paddingRight:3,":hover":{color:"#999"}},checkedRadioSpan:{color:"#333"},staticCheckedRadioSpan:{color:"#888"}});function getUserInputFromSerializedState$e(serializedState){return {values:serializedState.values}}function getCorrectUserInput$7(options){return {values:options.values}}function getStartUserInput$f(){return {values:[]}}const WrappedCategorizer=withDependencies(Categorizer);var Categorizer$1 = {name:"categorizer",displayName:"Categorizer",hidden:true,widget:WrappedCategorizer,getUserInputFromSerializedState: getUserInputFromSerializedState$e,getCorrectUserInput: getCorrectUserInput$7,getStartUserInput: getStartUserInput$f,isLintable:true};
1733
+ class Categorizer extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:"null",widgetType:"categorizer",widgetId:this.props.widgetId}});}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,values:userInput.values}}getPromptJSON(){return getPromptJSON$n(this.props)}_handleUserInput(itemNum,catNum){const values=[...this.props.userInput.values];values[itemNum]=catNum;this.props.handleUserInput({values});this.props.trackInteraction();}render(){const self=this;const isMobile=this.props.apiOptions.isMobile;let indexedItems=this.props.items.map((item,n)=>[item,n]);if(this.props.randomizeItems){indexedItems=shuffle(indexedItems,this.props.problemNum);}const table=jsxRuntimeExports.jsxs("table",{className:"categorizer-table "+css(styles$r.mobileTable),children:[jsxRuntimeExports.jsx("thead",{children:jsxRuntimeExports.jsxs("tr",{children:[jsxRuntimeExports.jsx("td",{className:css(styles$r.emptyHeaderCell)}),this.props.categories.map((category,i)=>{return jsxRuntimeExports.jsx("th",{className:css(styles$r.header),children:jsxRuntimeExports.jsx(Renderer,{content:category,linterContext:this.props.linterContext,strings:this.context.strings})},i)})]})}),jsxRuntimeExports.jsx("tbody",{children:indexedItems.map(indexedItem=>{const item=indexedItem[0];const itemNum=indexedItem[1];const uniqueId=self.state.uniqueId+"_"+itemNum;return jsxRuntimeExports.jsxs("tr",{children:[jsxRuntimeExports.jsx("td",{children:jsxRuntimeExports.jsx(Renderer,{content:item,linterContext:this.props.linterContext,strings:this.context.strings})}),self.props.categories.map((catName,catNum)=>{const selected=self.props.userInput.values[itemNum]===catNum;return jsxRuntimeExports.jsx("td",{className:"category "+css(styles$r.cell),children:jsxRuntimeExports.jsxs("div",{role:"button","aria-label":catName,onClick:()=>this._handleUserInput(itemNum,catNum),children:[isMobile&&jsxRuntimeExports.jsx("input",{type:"radio",name:uniqueId,className:css(sharedStyles.responsiveInput,sharedStyles.responsiveRadioInput),checked:selected,onChange:()=>this._handleUserInput(itemNum,catNum),onClick:e=>e.stopPropagation()}),!isMobile&&jsxRuntimeExports.jsx("span",{className:css(styles$r.radioSpan,selected&&styles$r.checkedRadioSpan,this.props.static&&selected&&styles$r.staticCheckedRadioSpan),children:selected?jsxRuntimeExports.jsx(InlineIcon,{...iconCircle}):jsxRuntimeExports.jsx(InlineIcon,{...iconCircleThin})})]})},catNum)})]},itemNum)})})]});const extraClassNames=classNames$2({"categorizer-container":true,"static-mode":this.props.static});const inlineStyles=this.props.apiOptions.isMobile?[styles$r.fullBleedContainer]:[];return jsxRuntimeExports.jsx("div",{className:extraClassNames+" "+css(...inlineStyles),children:table})}constructor(...args){super(...args),this.state={uniqueId:_.uniqueId("perseus_radio_")};}}Categorizer.contextType=PerseusI18nContext;Categorizer.defaultProps={items:[],categories:[],linterContext:linterContextDefault,userInput:{values:[]}};const styles$r=StyleSheet.create({mobileTable:{[mediaQueries.smOrSmaller]:{minWidth:"auto"}},fullBleedContainer:{[mediaQueries.mdOrSmaller]:{marginLeft:-16,marginRight:-16,overflowX:"auto"}},header:{textAlign:"center",verticalAlign:"bottom"},cell:{textAlign:"center",padding:0,color:"#ccc",verticalAlign:"middle"},emptyHeaderCell:{backgroundColor:"inherit",borderBottom:"2px solid #ccc"},radioSpan:{fontSize:30,paddingRight:3,":hover":{color:"#999"}},checkedRadioSpan:{color:"#333"},staticCheckedRadioSpan:{color:"#888"}});function getUserInputFromSerializedState$e(serializedState){return {values:serializedState.values}}function getCorrectUserInput$7(options){return {values:options.values}}function getStartUserInput$f(){return {values:[]}}const WrappedCategorizer=withDependencies(Categorizer);var Categorizer$1 = {name:"categorizer",displayName:"Categorizer",hidden:true,widget:WrappedCategorizer,getUserInputFromSerializedState: getUserInputFromSerializedState$e,getCorrectUserInput: getCorrectUserInput$7,getStartUserInput: getStartUserInput$f,isLintable:true};
1734
1734
 
1735
1735
  const isFileProtocol=protocol=>{if(protocol&&protocol.toLowerCase()==="file:"){return true}return false};
1736
1736
 
@@ -1766,17 +1766,17 @@ const styles$m={buttonStyleOverrides:{height:"auto",lineHeight:"inherit",marginL
1766
1766
 
1767
1767
  function mediaQueryIsMatched(mediaQuery){if(typeof window.matchMedia!=="function"){return false}return window.matchMedia(mediaQuery).matches}class Explanation extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:"null",widgetType:"explanation",widgetId:this.props.widgetId}});}getPromptJSON(){return getPromptJSON$j(this.props)}render(){const promptText=this.state.expanded?this.props.hidePrompt:this.props.showPrompt;const caretIcon=this.state.expanded?caretUp:caretDown;const legacyContentStyling=[styles$m.content,this.state.expanded?styles$m.contentExpanded:styles$m.contentCollapsed,mediaQueryIsMatched("(prefers-reduced-motion: no-preference)")&&(this.state.expanded?styles$m.transitionExpanded:styles$m.transitionCollapsed)];const contentClasses=[styles$n.content,this.state.expanded?styles$n.contentExpanded:styles$n.contentCollapsed,this.state.expanded?styles$n.transitionExpanded:styles$n.transitionCollapsed];return jsxRuntimeExports.jsx(Id,{children:contentId=>jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Button,{"aria-expanded":this.state.expanded,"aria-controls":contentId,endIcon:caretIcon,kind:"tertiary",labelStyle:styles$m.labelStyle,onClick:this._onClick,size:"small",style:styles$m.buttonStyleOverrides,children:promptText}),jsxRuntimeExports.jsx(View,{id:contentId,style:legacyContentStyling,className:contentClasses.join(" "),"aria-hidden":!this.state.expanded,testId:"content-container",children:jsxRuntimeExports.jsx(View,{className:styles$n.contentWrapper,style:styles$m.contentWrapper,children:jsxRuntimeExports.jsx(UserInputManager,{widgets:this.props.widgets,problemNum:0,children:({userInput,handleUserInput,initializeUserInput})=>{return jsxRuntimeExports.jsx(Renderer,{apiOptions:this.props.apiOptions,content:this.props.explanation,widgets:this.props.widgets,linterContext:this.props.linterContext,strings:this.context.strings,userInput:userInput,handleUserInput:handleUserInput,initializeUserInput:initializeUserInput})}})})})]})})}constructor(...args){super(...args),this.isWidget=true,this.state={expanded:false},this._onClick=()=>{this.setState({expanded:!this.state.expanded});this.props.trackInteraction();};}}Explanation.contextType=PerseusI18nContext;Explanation.defaultProps={showPrompt:"Explain",hidePrompt:"Hide explanation",explanation:"explanation goes here\n\nmore explanation",widgets:{},linterContext:linterContextDefault};const WrappedExplanation=withDependencies(Explanation);var Explanation$1 = {name:"explanation",displayName:"Explanation",widget:WrappedExplanation,isLintable:true};
1768
1768
 
1769
- class FreeResponse extends React.Component{isOverLimit(){return !this.props.allowUnlimitedCharacters&&this.characterCount()>this.props.characterLimit}renderCharacterCount(){if(this.props.allowUnlimitedCharacters){return null}const characterCountText=this.context.strings.characterCount({used:this.characterCount(),num:this.props.characterLimit});return jsxRuntimeExports.jsx(View,{children:jsxRuntimeExports.jsxs(Text$1,{role:"status",style:[styles$l.characterCountText,this.isOverLimit()?styles$l.overCharacterLimit:undefined],children:[this.isOverLimit()&&jsxRuntimeExports.jsx(PhosphorIcon,{icon:warningCircleIcon,size:"small",style:styles$l.warningCircleIcon}),characterCountText]})})}render(){return jsxRuntimeExports.jsxs(View,{style:styles$l.container,className:"free-response",children:[jsxRuntimeExports.jsx(LabeledField,{label:jsxRuntimeExports.jsx(View,{className:"free-response-question",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.question,strings:this.context.strings})}),field:jsxRuntimeExports.jsx(TextArea,{error:this.isOverLimit(),onChange:this._handleUserInput,placeholder:this.props.placeholder,style:styles$l.textarea,value:this.props.userInput.currentValue})}),this.renderCharacterCount()]})}constructor(...args){super(...args),this.isWidget=true,this.characterCount=()=>{return this.props.userInput.currentValue.replace(/\n/g,"").length},this._handleUserInput=newValue=>{this.props.handleUserInput({currentValue:newValue});};}}FreeResponse.contextType=PerseusI18nContext;FreeResponse.defaultProps={userInput:{currentValue:""}};function getStartUserInput$c(){return {currentValue:""}}var FreeResponse$1 = {name:"free-response",accessible:true,displayName:"Free Response (Assessments only)",widget:FreeResponse,hidden:false,getUserInputFromSerializedState:getStartUserInput$c,getStartUserInput: getStartUserInput$c};const styles$l=StyleSheet.create({container:{gap:spacing.xSmall_8},characterCountText:{color:semanticColor.core.foreground.neutral.default,fontSize:font.size.small},overCharacterLimit:{color:semanticColor.core.foreground.critical.default},textarea:{padding:spacing.medium_16},warningCircleIcon:{marginInlineEnd:spacing.xSmall_8}});
1769
+ class FreeResponse extends React.Component{isOverLimit(){return !this.props.allowUnlimitedCharacters&&this.characterCount()>this.props.characterLimit}render(){const isOverLimit=this.isOverLimit();const characterCountText=this.props.allowUnlimitedCharacters?undefined:this.context.strings.characterCount({used:this.characterCount(),num:this.props.characterLimit});if(characterCountText){this.announceCharacterCount(characterCountText,isOverLimit);}return jsxRuntimeExports.jsx(View,{style:styles$l.container,className:"free-response",children:jsxRuntimeExports.jsx(LabeledField,{label:jsxRuntimeExports.jsx(View,{className:"free-response-question",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.question,strings:this.context.strings})}),field:jsxRuntimeExports.jsx(TextArea,{error:isOverLimit,onChange:this._handleUserInput,placeholder:this.props.placeholder,style:styles$l.textarea,value:this.props.userInput.currentValue}),additionalHelperMessage:jsxRuntimeExports.jsxs("p",{className:css(styles$l.characterCountText,isOverLimit?styles$l.overCharacterLimit:undefined),children:[isOverLimit&&jsxRuntimeExports.jsx(PhosphorIcon,{"aria-label":"Error:",icon:warningCircleIcon,size:"small",style:styles$l.warningCircleIcon}),characterCountText]})})})}constructor(...args){super(...args),this.isWidget=true,this.announceCharacterCount=(message,isOverLimit)=>{const level=isOverLimit?"assertive":"polite";announceMessage({message,level,debounceThreshold:750});},this.characterCount=()=>{return this.props.userInput.currentValue.replace(/\n/g,"").length},this._handleUserInput=newValue=>{this.props.handleUserInput({currentValue:newValue});};}}FreeResponse.contextType=PerseusI18nContext;FreeResponse.defaultProps={userInput:{currentValue:""}};function getStartUserInput$c(){return {currentValue:""}}var FreeResponse$1 = {name:"free-response",accessible:true,displayName:"Free Response (Assessments only)",widget:FreeResponse,hidden:false,getUserInputFromSerializedState:getStartUserInput$c,getStartUserInput: getStartUserInput$c};const styles$l=StyleSheet.create({container:{gap:spacing.xSmall_8},characterCountText:{color:semanticColor.core.foreground.neutral.default,fontSize:font.body.size.small,margin:0},overCharacterLimit:{color:semanticColor.core.foreground.critical.default},textarea:{padding:spacing.medium_16},warningCircleIcon:{marginInlineEnd:spacing.xSmall_8}});
1770
1770
 
1771
1771
  const getPromptJSON$i=(title,rendererJSON,hintRendererJSON)=>{if(!rendererJSON){return {type:"graded-group",title,content:"",widgets:{},hint:hintRendererJSON}}return {...rendererJSON,title,type:"graded-group",hint:hintRendererJSON}};
1772
1772
 
1773
1773
  class GradedGroupAnswerBar extends React.Component{render(){const{apiOptions,answerBarState,onCheckAnswer,onNextQuestion}=this.props;const{keepTrying,tryAgain,check,correctExcited,nextQuestion}=this.context.strings;const answerBarStyle={...styles$k.answerBar,backgroundColor:answerBarState==="CORRECT"?semanticColor.core.background.base.subtle:semanticColor.core.background.base.default,justifyContent:answerBarState==="CORRECT"&&!onNextQuestion?"center":"space-between"};const message=answerBarState==="INCORRECT"?jsxRuntimeExports.jsxs("span",{style:styles$k.text,children:[jsxRuntimeExports.jsx("span",{style:styles$k.tryAgainIcon,children:jsxRuntimeExports.jsx(InlineIcon,{...iconTryAgain})}),jsxRuntimeExports.jsx("span",{style:{marginLeft:8},children:keepTrying})]}):jsxRuntimeExports.jsx("span",{});if(answerBarState!=="CORRECT"){const buttonLabel=answerBarState==="INCORRECT"?tryAgain:check;return jsxRuntimeExports.jsxs("div",{style:answerBarStyle,children:[message,jsxRuntimeExports.jsx(Button,{disabled:apiOptions.readOnly||answerBarState!=="ACTIVE",onClick:onCheckAnswer,children:buttonLabel})]})}return jsxRuntimeExports.jsxs("div",{style:answerBarStyle,children:[jsxRuntimeExports.jsxs("span",{style:styles$k.text,children:[jsxRuntimeExports.jsx("span",{style:{fontSize:28,color:semanticColor.core.foreground.success.default},children:jsxRuntimeExports.jsx(InlineIcon,{...iconStar,style:{marginBottom:5}})}),jsxRuntimeExports.jsx("span",{role:"alert","aria-label":correctExcited,style:{marginLeft:8},children:correctExcited})]}),onNextQuestion&&jsxRuntimeExports.jsx(Button,{onClick:onNextQuestion,children:nextQuestion})]})}}GradedGroupAnswerBar.contextType=PerseusI18nContext;const fontSize$1=17;const styles$k={answerBar:{display:"flex",alignItems:"center",height:68,marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin,marginBottom:negativePhoneMargin,marginTop:phoneMargin,paddingLeft:phoneMargin,paddingRight:10,borderTop:`1px solid ${semanticColor.core.border.neutral.default}`},tryAgainIcon:{fontSize:28,color:"#63D9EA",transform:"scale(-1,1) rotate(-268deg)"},text:{display:"flex",flexDirection:"row",alignItems:"center",fontWeight:"bold",fontSize:fontSize$1}};
1774
1774
 
1775
- const GRADING_STATUSES={ungraded:"ungraded",correct:"correct",incorrect:"incorrect",invalid:"invalid"};const getNextState=(currentState,answerable)=>{switch(currentState){case "ACTIVE":return !answerable?"INACTIVE":currentState;case "INACTIVE":return answerable?"ACTIVE":currentState;case "INCORRECT":return answerable?"ACTIVE":"INACTIVE";default:return currentState}};class GradedGroup extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"graded-group",widgetSubType:"null",widgetId:this.props.widgetId}});}shouldComponentUpdate(nextProps,nextState){return nextProps!==this.props||nextState!==this.state}_handleUserInput(_userInput,widgetsEmpty){this.setState({status:GRADING_STATUSES.ungraded,message:""});const answerable=!widgetsEmpty;const answerBarState=this.state.answerBarState;const nextState=getNextState(answerBarState,answerable);this.setState({answerBarState:nextState});}getPromptJSON(){const hint=this.hintRendererRef.current?.getPromptJSON()||{content:this.props.hint?.content||"",widgets:{}};return getPromptJSON$i(this.props.title,this.rendererRef.current?.getPromptJSON(),hint)}render(){const apiOptions=_.extend({},ApiOptions.defaults,this.props.apiOptions,{onFocusChange:(newFocus,oldFocus)=>{if(oldFocus){this.props.onBlur(oldFocus);}if(newFocus){this.props.onFocus(newFocus);}}});let gradeStatus=null;let icon=null;if(this.state.status===GRADING_STATUSES.correct){icon=jsxRuntimeExports.jsx(InlineIcon,{...iconOk,style:{color:"#526f03"}});gradeStatus=this.context.strings.correct;}else if(this.state.status===GRADING_STATUSES.incorrect){icon=jsxRuntimeExports.jsx(InlineIcon,{...iconRemove,style:{color:"#ff5454"}});gradeStatus=this.context.strings.incorrect;}const mobileClass=this.props.inGradedGroupSet?css(styles$j.gradedGroupInSet):css(styles$j.gradedGroup);const classes=classNames$1({[mobileClass]:apiOptions.isMobile,"perseus-graded-group":true,"answer-correct":apiOptions.isMobile?false:this.state.status===GRADING_STATUSES.correct,"answer-incorrect":apiOptions.isMobile?false:this.state.status===GRADING_STATUSES.incorrect});const{answerBarState}=this.state;const isCorrect=answerBarState==="CORRECT";const readOnly=apiOptions.readOnly||apiOptions.isMobile&&isCorrect;const showSolutions=isCorrect?"all":"none";return jsxRuntimeExports.jsxs("div",{className:classes,children:[!!this.props.title&&jsxRuntimeExports.jsx("div",{className:css(styles$j.title),children:this.props.title}),jsxRuntimeExports.jsx(UserInputManager,{widgets:this.props.widgets,handleUserInput:(userInput,widgetsEmpty)=>this._handleUserInput(userInput,widgetsEmpty),problemNum:0,children:({userInput,handleUserInput})=>jsxRuntimeExports.jsx(Renderer,{content:this.props.content,widgets:this.props.widgets,images:this.props.images,userInput:userInput,handleUserInput:handleUserInput,problemNum:0,ref:this.rendererRef,keypadElement:this.props.keypadElement,apiOptions:{...apiOptions,readOnly},showSolutions:showSolutions,linterContext:this.props.linterContext,strings:this.context.strings})}),!apiOptions.isMobile&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[icon!=null&&jsxRuntimeExports.jsx("div",{className:"group-icon",children:icon}),gradeStatus&&jsxRuntimeExports.jsx("div",{className:css(a11y.srOnly),role:"alert","aria-label":gradeStatus,children:gradeStatus}),jsxRuntimeExports.jsx("div",{role:"status","aria-live":"polite",children:jsxRuntimeExports.jsx(Renderer,{content:this.state.message,strings:this.context.strings})}),jsxRuntimeExports.jsx(Button,{kind:"secondary",disabled:this.props.apiOptions.readOnly,onClick:this._checkAnswer,children:this.context.strings.check}),isCorrect&&this.props.onNextQuestion&&jsxRuntimeExports.jsx(Button,{kind:"secondary",disabled:this.props.apiOptions.readOnly,onClick:this.props.onNextQuestion,style:{marginLeft:5},children:this.context.strings.nextQuestion})]}),this.props.hint?.content&&(this.state.showHint?jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx("button",{tabIndex:"0",className:css(styles$j.explanationTitle),onClick:()=>this.setState({showHint:false}),onKeyPress:e=>{e.preventDefault();this.setState({showHint:false});},children:this.context.strings.hideExplanation}),jsxRuntimeExports.jsx(UserInputManager,{widgets:this.props.hint.widgets,problemNum:0,children:({userInput,handleUserInput,initializeUserInput})=>{const{content,widgets,images}=this.props.hint;return jsxRuntimeExports.jsx(Renderer,{content:content,widgets:widgets,images:images,userInput:userInput,handleUserInput:handleUserInput,initializeUserInput:initializeUserInput,ref:this.hintRendererRef,apiOptions:apiOptions,linterContext:this.props.linterContext,strings:this.context.strings,showSolutions:showSolutions})}})]}):jsxRuntimeExports.jsx("button",{tabIndex:"0",onClick:()=>this.setState({showHint:true}),onKeyPress:e=>{e.preventDefault();this.setState({showHint:true});},className:css(styles$j.showHintLink),children:this.context.strings.explain})),apiOptions.isMobile&&jsxRuntimeExports.jsx(GradedGroupAnswerBar,{apiOptions:apiOptions,answerBarState:answerBarState,onCheckAnswer:this._checkAnswer,onNextQuestion:this.props.onNextQuestion})]})}constructor(...args){super(...args),this.state={status:GRADING_STATUSES.ungraded,showHint:false,message:"",answerBarState:"INACTIVE"},this.rendererRef=React.createRef(),this.hintRendererRef=React.createRef(),this._checkAnswer=()=>{const score=this.rendererRef.current?.score()||{type:"invalid"};const{INVALID_MESSAGE_PREFIX,DEFAULT_INVALID_MESSAGE_1,DEFAULT_INVALID_MESSAGE_2}=this.context.strings;const status=score.type==="points"?score.total===score.earned?GRADING_STATUSES.correct:GRADING_STATUSES.incorrect:GRADING_STATUSES.invalid;const message=score.type==="points"?score.message||"":score.message?`${INVALID_MESSAGE_PREFIX} ${mapErrorToString(score.message,this.context.strings)}`:`${INVALID_MESSAGE_PREFIX} ${DEFAULT_INVALID_MESSAGE_1}${DEFAULT_INVALID_MESSAGE_2}`;this.setState({status:status,message:message,answerBarState:status==="correct"?"CORRECT":"INCORRECT"});this.props.trackInteraction({status:status});},this.getInputPaths=()=>{return this.rendererRef.current?.getInputPaths()||[]},this.focus=()=>{return !!this.rendererRef.current?.focus()},this.focusInputPath=path=>{this.rendererRef.current?.focusPath(path);},this.blurInputPath=path=>{this.rendererRef.current?.blurPath(path);};}}GradedGroup.contextType=PerseusI18nContext;GradedGroup.defaultProps={title:"",content:"",widgets:{},images:{},hint:null,hasHint:false,linterContext:linterContextDefault};const styles$j=StyleSheet.create({gradedGroupInSet:{marginLeft:0,paddingLeft:0},gradedGroup:{borderTop:`1px solid ${gray76}`,borderBottom:`1px solid ${gray76}`,backgroundColor:tableBackgroundAccent,marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin,paddingBottom:phoneMargin,paddingLeft:phoneMargin,paddingRight:phoneMargin,paddingTop:10,width:"auto"},showHintLink:{backgroundColor:"unset",fontSize:14,padding:0,border:"none",marginTop:20,color:semanticColor.core.foreground.instructive.default,cursor:"pointer",display:"block",clear:"both"},explanationTitle:{backgroundColor:"unset",marginTop:20,color:semanticColor.core.foreground.instructive.default,marginBottom:10,cursor:"pointer",fontSize:14,padding:0,border:"none",display:"block",clear:"both"},title:{fontSize:12,color:gray68,textTransform:"uppercase",marginBottom:11,letterSpacing:.8}});const WrappedGradedGroup=withDependencies(GradedGroup);var GradedGroup$1 = {name:"graded-group",displayName:"Graded group (articles only)",widget:WrappedGradedGroup,hidden:false,tracking:"all",isLintable:true};
1775
+ const GRADING_STATUSES={ungraded:"ungraded",correct:"correct",incorrect:"incorrect",invalid:"invalid"};const getNextState=(currentState,answerable)=>{switch(currentState){case "ACTIVE":return !answerable?"INACTIVE":currentState;case "INACTIVE":return answerable?"ACTIVE":currentState;case "INCORRECT":return answerable?"ACTIVE":"INACTIVE";default:return currentState}};class GradedGroup extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"graded-group",widgetSubType:"null",widgetId:this.props.widgetId}});}shouldComponentUpdate(nextProps,nextState){return nextProps!==this.props||nextState!==this.state}_handleUserInput(_userInput,widgetsEmpty){this.setState({status:GRADING_STATUSES.ungraded,message:""});const answerable=!widgetsEmpty;const answerBarState=this.state.answerBarState;const nextState=getNextState(answerBarState,answerable);this.setState({answerBarState:nextState});}getPromptJSON(){const hint=this.hintRendererRef.current?.getPromptJSON()||{content:this.props.hint?.content||"",widgets:{}};return getPromptJSON$i(this.props.title,this.rendererRef.current?.getPromptJSON(),hint)}render(){const apiOptions=_.extend({},ApiOptions.defaults,this.props.apiOptions,{onFocusChange:(newFocus,oldFocus)=>{if(oldFocus){this.props.onBlur(oldFocus);}if(newFocus){this.props.onFocus(newFocus);}}});let gradeStatus=null;let icon=null;if(this.state.status===GRADING_STATUSES.correct){icon=jsxRuntimeExports.jsx(InlineIcon,{...iconOk,style:{color:"#526f03"}});gradeStatus=this.context.strings.correct;}else if(this.state.status===GRADING_STATUSES.incorrect){icon=jsxRuntimeExports.jsx(InlineIcon,{...iconRemove,style:{color:"#ff5454"}});gradeStatus=this.context.strings.incorrect;}const mobileClass=this.props.inGradedGroupSet?css(styles$j.gradedGroupInSet):css(styles$j.gradedGroup);const classes=classNames$2({[mobileClass]:apiOptions.isMobile,"perseus-graded-group":true,"answer-correct":apiOptions.isMobile?false:this.state.status===GRADING_STATUSES.correct,"answer-incorrect":apiOptions.isMobile?false:this.state.status===GRADING_STATUSES.incorrect});const{answerBarState}=this.state;const isCorrect=answerBarState==="CORRECT";const readOnly=apiOptions.readOnly||apiOptions.isMobile&&isCorrect;const showSolutions=isCorrect?"all":"none";return jsxRuntimeExports.jsxs("div",{className:classes,children:[!!this.props.title&&jsxRuntimeExports.jsx("div",{className:css(styles$j.title),children:this.props.title}),jsxRuntimeExports.jsx(UserInputManager,{widgets:this.props.widgets,handleUserInput:(userInput,widgetsEmpty)=>this._handleUserInput(userInput,widgetsEmpty),problemNum:0,children:({userInput,handleUserInput})=>jsxRuntimeExports.jsx(Renderer,{content:this.props.content,widgets:this.props.widgets,images:this.props.images,userInput:userInput,handleUserInput:handleUserInput,problemNum:0,ref:this.rendererRef,keypadElement:this.props.keypadElement,apiOptions:{...apiOptions,readOnly},showSolutions:showSolutions,linterContext:this.props.linterContext,strings:this.context.strings})}),!apiOptions.isMobile&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[icon!=null&&jsxRuntimeExports.jsx("div",{className:"group-icon",children:icon}),gradeStatus&&jsxRuntimeExports.jsx("div",{className:css(a11y.srOnly),role:"alert","aria-label":gradeStatus,children:gradeStatus}),jsxRuntimeExports.jsx("div",{role:"status","aria-live":"polite",children:jsxRuntimeExports.jsx(Renderer,{content:this.state.message,strings:this.context.strings})}),jsxRuntimeExports.jsx(Button,{kind:"secondary",disabled:this.props.apiOptions.readOnly,onClick:this._checkAnswer,children:this.context.strings.check}),isCorrect&&this.props.onNextQuestion&&jsxRuntimeExports.jsx(Button,{kind:"secondary",disabled:this.props.apiOptions.readOnly,onClick:this.props.onNextQuestion,style:{marginLeft:5},children:this.context.strings.nextQuestion})]}),this.props.hint?.content&&(this.state.showHint?jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx("button",{tabIndex:"0",className:css(styles$j.explanationTitle),onClick:()=>this.setState({showHint:false}),onKeyPress:e=>{e.preventDefault();this.setState({showHint:false});},children:this.context.strings.hideExplanation}),jsxRuntimeExports.jsx(UserInputManager,{widgets:this.props.hint.widgets,problemNum:0,children:({userInput,handleUserInput,initializeUserInput})=>{const{content,widgets,images}=this.props.hint;return jsxRuntimeExports.jsx(Renderer,{content:content,widgets:widgets,images:images,userInput:userInput,handleUserInput:handleUserInput,initializeUserInput:initializeUserInput,ref:this.hintRendererRef,apiOptions:apiOptions,linterContext:this.props.linterContext,strings:this.context.strings,showSolutions:showSolutions})}})]}):jsxRuntimeExports.jsx("button",{tabIndex:"0",onClick:()=>this.setState({showHint:true}),onKeyPress:e=>{e.preventDefault();this.setState({showHint:true});},className:css(styles$j.showHintLink),children:this.context.strings.explain})),apiOptions.isMobile&&jsxRuntimeExports.jsx(GradedGroupAnswerBar,{apiOptions:apiOptions,answerBarState:answerBarState,onCheckAnswer:this._checkAnswer,onNextQuestion:this.props.onNextQuestion})]})}constructor(...args){super(...args),this.state={status:GRADING_STATUSES.ungraded,showHint:false,message:"",answerBarState:"INACTIVE"},this.rendererRef=React.createRef(),this.hintRendererRef=React.createRef(),this._checkAnswer=()=>{const score=this.rendererRef.current?.score()||{type:"invalid"};const{INVALID_MESSAGE_PREFIX,DEFAULT_INVALID_MESSAGE_1,DEFAULT_INVALID_MESSAGE_2}=this.context.strings;const status=score.type==="points"?score.total===score.earned?GRADING_STATUSES.correct:GRADING_STATUSES.incorrect:GRADING_STATUSES.invalid;const message=score.type==="points"?score.message||"":score.message?`${INVALID_MESSAGE_PREFIX} ${mapErrorToString(score.message,this.context.strings)}`:`${INVALID_MESSAGE_PREFIX} ${DEFAULT_INVALID_MESSAGE_1}${DEFAULT_INVALID_MESSAGE_2}`;this.setState({status:status,message:message,answerBarState:status==="correct"?"CORRECT":"INCORRECT"});this.props.trackInteraction({status:status});},this.getInputPaths=()=>{return this.rendererRef.current?.getInputPaths()||[]},this.focus=()=>{return !!this.rendererRef.current?.focus()},this.focusInputPath=path=>{this.rendererRef.current?.focusPath(path);},this.blurInputPath=path=>{this.rendererRef.current?.blurPath(path);};}}GradedGroup.contextType=PerseusI18nContext;GradedGroup.defaultProps={title:"",content:"",widgets:{},images:{},hint:null,hasHint:false,linterContext:linterContextDefault};const styles$j=StyleSheet.create({gradedGroupInSet:{marginLeft:0,paddingLeft:0},gradedGroup:{borderTop:`1px solid ${gray76}`,borderBottom:`1px solid ${gray76}`,backgroundColor:tableBackgroundAccent,marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin,paddingBottom:phoneMargin,paddingLeft:phoneMargin,paddingRight:phoneMargin,paddingTop:10,width:"auto"},showHintLink:{backgroundColor:"unset",fontSize:14,padding:0,border:"none",marginTop:20,color:semanticColor.core.foreground.instructive.default,cursor:"pointer",display:"block",clear:"both"},explanationTitle:{backgroundColor:"unset",marginTop:20,color:semanticColor.core.foreground.instructive.default,marginBottom:10,cursor:"pointer",fontSize:14,padding:0,border:"none",display:"block",clear:"both"},title:{fontSize:12,color:gray68,textTransform:"uppercase",marginBottom:11,letterSpacing:.8}});const WrappedGradedGroup=withDependencies(GradedGroup);var GradedGroup$1 = {name:"graded-group",displayName:"Graded group (articles only)",widget:WrappedGradedGroup,hidden:false,tracking:"all",isLintable:true};
1776
1776
 
1777
1777
  const getPromptJSON$h=(widgetData,activeGroupJSON)=>{return {type:"graded-group-set",options:{groupCount:widgetData.gradedGroups.length,currentGroup:activeGroupJSON}}};
1778
1778
 
1779
- class Indicators extends React.Component{render(){return jsxRuntimeExports.jsx("ul",{className:classNames$1(css(styles$i.indicatorContainer),"indicatorContainer"),children:this.props.gradedGroups.map(({title},i)=>jsxRuntimeExports.jsx("li",{className:css(styles$i.indicator),children:jsxRuntimeExports.jsx(Clickable,{role:"button","aria-label":title,"aria-current":i===this.props.currentGroup,style:styles$i.indicatorButton,onClick:()=>this.props.onChangeCurrentGroup(i),onKeyDown:e=>this.handleKeyDown(e,i),children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx(View,{style:[styles$i.indicatorDot,(hovered||focused||pressed)&&styles$i.indicatorDotFocused],children:i===this.props.currentGroup&&jsxRuntimeExports.jsx(View,{style:styles$i.indicatorDotActive})})})},i))})}constructor(...args){super(...args),this.handleKeyDown=(e,i)=>{if(e.key==="Enter"||e.key===" "){this.props.onChangeCurrentGroup(i);}};}}Indicators.contextType=PerseusI18nContext;class GradedGroupSet extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"graded-group-set",widgetSubType:"null",widgetId:this.props.widgetId}});}shouldComponentUpdate(nextProps,nextState){return nextProps!==this.props||nextState!==this.state}getPromptJSON(){const activeGroupPromptJSON=this._childGroup.getPromptJSON();return getPromptJSON$h(this.props,activeGroupPromptJSON)}render(){const{JIPT}=getDependencies();if(JIPT.useJIPT&&this.props.gradedGroups.length>1){return jsxRuntimeExports.jsx("div",{className:css(styles$i.container),children:this.props.gradedGroups.map((gradedGroup,i)=>{return jsxRuntimeExports.jsx(GradedGroup,{...this.props,...gradedGroup,inGradedGroupSet:false,linterContext:this.props.linterContext},i)})})}const currentGroup=this.props.gradedGroups[this.state.currentGroup];if(!currentGroup){return jsxRuntimeExports.jsx("span",{children:"No current group..."})}const numGroups=this.props.gradedGroups.length;const handleNextQuestion=this.state.currentGroup<numGroups-1?this.handleNextQuestion:null;return jsxRuntimeExports.jsxs("div",{className:css(styles$i.container),children:[jsxRuntimeExports.jsxs("div",{className:css(styles$i.top),children:[jsxRuntimeExports.jsx("div",{className:css(styles$i.title),children:currentGroup.title}),jsxRuntimeExports.jsx("div",{className:css(styles$i.spacer)}),jsxRuntimeExports.jsx(Indicators,{currentGroup:this.state.currentGroup,gradedGroups:this.props.gradedGroups,onChangeCurrentGroup:currentGroup=>this.setState({currentGroup})})]}),jsxRuntimeExports.jsx(GradedGroup,{ref:comp=>this._childGroup=comp,...this.props,...currentGroup,inGradedGroupSet:true,title:null,onNextQuestion:handleNextQuestion,linterContext:this.props.linterContext},this.state.currentGroup)]})}constructor(...args){super(...args),this.state={currentGroup:0},this.getInputPaths=()=>{return this._childGroup.getInputPaths()},this.focus=()=>{return this._childGroup.focus()},this.focusInputPath=path=>{this._childGroup.focusInputPath(path);},this.blurInputPath=path=>{this._childGroup.blurInputPath(path);},this.handleNextQuestion=()=>{const{currentGroup}=this.state;const numGroups=this.props.gradedGroups.length;if(currentGroup<numGroups-1){this.setState({currentGroup:currentGroup+1});}};}}GradedGroupSet.defaultProps={gradedGroups:[],linterContext:linterContextDefault};const WrappedGradedGroupSet=withDependencies(GradedGroupSet);var GradedGroupSet$1 = {name:"graded-group-set",displayName:"Graded group set (articles only)",widget:WrappedGradedGroupSet,hidden:false,tracking:"all",isLintable:true};const styles$i=StyleSheet.create({top:{display:"flex",flexDirection:"row"},spacer:{flex:1},title:{fontSize:12,color:semanticColor.core.foreground.neutral.subtle,textTransform:"uppercase",marginBottom:11,letterSpacing:.8},indicatorContainer:{display:"flex",flexDirection:"row",listStyleType:'""',margin:"unset",paddingInlineStart:"unset",flexWrap:"wrap"},indicator:{width:24,height:24},indicatorButton:{width:"100%",height:"100%",display:"flex",flexWrap:"wrap",placeContent:"center",cursor:"pointer",":focus":{outline:"none"}},indicatorDot:{boxSizing:"content-box",width:10,height:10,borderRadius:"100%",borderWidth:2,borderColor:semanticColor.core.border.instructive.default,borderStyle:"solid"},indicatorDotFocused:{borderWidth:5,borderStyle:"double"},indicatorDotActive:{backgroundColor:semanticColor.core.background.instructive.default,width:"100%",height:"100%"},container:{borderTop:`1px solid ${gray76}`,borderBottom:`1px solid ${gray76}`,backgroundColor:tableBackgroundAccent,marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin,paddingBottom:phoneMargin,paddingLeft:phoneMargin,paddingRight:phoneMargin,paddingTop:10,width:"auto"}});
1779
+ class Indicators extends React.Component{render(){return jsxRuntimeExports.jsx("ul",{className:classNames$2(css(styles$i.indicatorContainer),"indicatorContainer"),children:this.props.gradedGroups.map(({title},i)=>jsxRuntimeExports.jsx("li",{className:css(styles$i.indicator),children:jsxRuntimeExports.jsx(Clickable,{role:"button","aria-label":title,"aria-current":i===this.props.currentGroup,style:styles$i.indicatorButton,onClick:()=>this.props.onChangeCurrentGroup(i),onKeyDown:e=>this.handleKeyDown(e,i),children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx(View,{style:[styles$i.indicatorDot,(hovered||focused||pressed)&&styles$i.indicatorDotFocused],children:i===this.props.currentGroup&&jsxRuntimeExports.jsx(View,{style:styles$i.indicatorDotActive})})})},i))})}constructor(...args){super(...args),this.handleKeyDown=(e,i)=>{if(e.key==="Enter"||e.key===" "){this.props.onChangeCurrentGroup(i);}};}}Indicators.contextType=PerseusI18nContext;class GradedGroupSet extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"graded-group-set",widgetSubType:"null",widgetId:this.props.widgetId}});}shouldComponentUpdate(nextProps,nextState){return nextProps!==this.props||nextState!==this.state}getPromptJSON(){const activeGroupPromptJSON=this._childGroup.getPromptJSON();return getPromptJSON$h(this.props,activeGroupPromptJSON)}render(){const{JIPT}=getDependencies();if(JIPT.useJIPT&&this.props.gradedGroups.length>1){return jsxRuntimeExports.jsx("div",{className:css(styles$i.container),children:this.props.gradedGroups.map((gradedGroup,i)=>{return jsxRuntimeExports.jsx(GradedGroup,{...this.props,...gradedGroup,inGradedGroupSet:false,linterContext:this.props.linterContext},i)})})}const currentGroup=this.props.gradedGroups[this.state.currentGroup];if(!currentGroup){return jsxRuntimeExports.jsx("span",{children:"No current group..."})}const numGroups=this.props.gradedGroups.length;const handleNextQuestion=this.state.currentGroup<numGroups-1?this.handleNextQuestion:null;return jsxRuntimeExports.jsxs("div",{className:css(styles$i.container),children:[jsxRuntimeExports.jsxs("div",{className:css(styles$i.top),children:[jsxRuntimeExports.jsx("div",{className:css(styles$i.title),children:currentGroup.title}),jsxRuntimeExports.jsx("div",{className:css(styles$i.spacer)}),jsxRuntimeExports.jsx(Indicators,{currentGroup:this.state.currentGroup,gradedGroups:this.props.gradedGroups,onChangeCurrentGroup:currentGroup=>this.setState({currentGroup})})]}),jsxRuntimeExports.jsx(GradedGroup,{ref:comp=>this._childGroup=comp,...this.props,...currentGroup,inGradedGroupSet:true,title:null,onNextQuestion:handleNextQuestion,linterContext:this.props.linterContext},this.state.currentGroup)]})}constructor(...args){super(...args),this.state={currentGroup:0},this.getInputPaths=()=>{return this._childGroup.getInputPaths()},this.focus=()=>{return this._childGroup.focus()},this.focusInputPath=path=>{this._childGroup.focusInputPath(path);},this.blurInputPath=path=>{this._childGroup.blurInputPath(path);},this.handleNextQuestion=()=>{const{currentGroup}=this.state;const numGroups=this.props.gradedGroups.length;if(currentGroup<numGroups-1){this.setState({currentGroup:currentGroup+1});}};}}GradedGroupSet.defaultProps={gradedGroups:[],linterContext:linterContextDefault};const WrappedGradedGroupSet=withDependencies(GradedGroupSet);var GradedGroupSet$1 = {name:"graded-group-set",displayName:"Graded group set (articles only)",widget:WrappedGradedGroupSet,hidden:false,tracking:"all",isLintable:true};const styles$i=StyleSheet.create({top:{display:"flex",flexDirection:"row"},spacer:{flex:1},title:{fontSize:12,color:semanticColor.core.foreground.neutral.subtle,textTransform:"uppercase",marginBottom:11,letterSpacing:.8},indicatorContainer:{display:"flex",flexDirection:"row",listStyleType:'""',margin:"unset",paddingInlineStart:"unset",flexWrap:"wrap"},indicator:{width:24,height:24},indicatorButton:{width:"100%",height:"100%",display:"flex",flexWrap:"wrap",placeContent:"center",cursor:"pointer",":focus":{outline:"none"}},indicatorDot:{boxSizing:"content-box",width:10,height:10,borderRadius:"100%",borderWidth:2,borderColor:semanticColor.core.border.instructive.default,borderStyle:"solid"},indicatorDotFocused:{borderWidth:5,borderStyle:"double"},indicatorDotActive:{backgroundColor:semanticColor.core.background.instructive.default,width:"100%",height:"100%"},container:{borderTop:`1px solid ${gray76}`,borderBottom:`1px solid ${gray76}`,backgroundColor:tableBackgroundAccent,marginLeft:negativePhoneMargin,marginRight:negativePhoneMargin,paddingBottom:phoneMargin,paddingLeft:phoneMargin,paddingRight:phoneMargin,paddingTop:10,width:"auto"}});
1780
1780
 
1781
1781
  class ButtonGroup extends React.Component{componentWillUnmount(){this.container=null;}focus(){if(this.container){this.container.focus();return true}}toggleSelect(newValue){const value=this.props.value;if(this.props.allowEmpty){this.props.onChange(value!==newValue?newValue:null);}else {this.props.onChange(newValue);}}render(){const value=this.props.value;const buttons=this.props.buttons.map((button,i)=>{return jsxRuntimeExports.jsx("button",{title:button.title,type:"button",ref:"button"+i,className:css(styles$h.buttonStyle,button.value===value&&styles$h.selectedStyle,button.value===value&&this.props.selectedButtonStyle),onClick:()=>this.toggleSelect(button.value),children:button.content||""+button.value},""+i)});const outerStyle={display:"inline-block"};return jsxRuntimeExports.jsx("div",{style:outerStyle,ref:node=>this.container=node,children:buttons})}}ButtonGroup.defaultProps={value:null,allowEmpty:true};const styles$h=StyleSheet.create({buttonStyle:{backgroundColor:"white",border:"1px solid #ccc",borderLeft:"0",cursor:"pointer",margin:"0",padding:"5px 10px",position:"relative",":first-child":{borderLeft:"1px solid #ccc",borderTopLeftRadius:"3px",borderBottomLeftRadius:"3px"},":last-child":{borderRight:"1px solid #ccc",borderTopRightRadius:"3px",borderBottomRightRadius:"3px"},":hover":{backgroundColor:"#ccc"},":focus":{zIndex:2}},selectedStyle:{backgroundColor:"#ddd"}});
1782
1782
 
@@ -1788,7 +1788,7 @@ const MovablePoint$3=Graphie.MovablePoint;const MovableLine$2=Graphie.MovableLin
1788
1788
 
1789
1789
  const getPromptJSON$f=rendererJSON=>{if(!rendererJSON){return {type:"group",content:"",widgets:{}}}return {...rendererJSON,type:"group"}};
1790
1790
 
1791
- class Group extends React.Component{componentDidMount(){this.forceUpdate();}getPromptJSON(){return getPromptJSON$f(this.rendererRef?.getPromptJSON())}getInputPaths(){return this.rendererRef?.getInputPaths()??[]}focus(){return this.rendererRef?.focus()??false}render(){const apiOptions={...ApiOptions.defaults,...this.props.apiOptions,onFocusChange:(newFocus,oldFocus)=>{if(oldFocus){this.props.onBlur(oldFocus);}if(newFocus){this.props.onFocus(newFocus);}}};return jsxRuntimeExports.jsx("div",{className:classNames$1({"perseus-group":true}),children:jsxRuntimeExports.jsx(Renderer,{userInput:this.props.userInput,handleUserInput:(widgetId,userInput)=>{this.props.handleUserInput({...this.props.userInput,[widgetId]:userInput});},content:this.props.content,widgets:this.props.widgets,images:this.props.images,ref:ref=>this.rendererRef=ref,apiOptions:apiOptions,findExternalWidgets:this.props.findWidgets,reviewMode:this.props.reviewMode,showSolutions:this.props.showSolutions,linterContext:this.props.linterContext,strings:this.context.strings})})}constructor(...args){super(...args),this.getSerializedState=()=>{return this.rendererRef?.getSerializedState()},this.focusInputPath=path=>{this.rendererRef?.focusPath(path);},this.blurInputPath=path=>{this.rendererRef?.blurPath(path);};}}Group.contextType=PerseusI18nContext;Group.defaultProps={content:"",widgets:{},images:{},linterContext:linterContextDefault};function getStartUserInput$a(options,problemNum){return sharedInitializeUserInput(options.widgets,problemNum)}function getUserInputFromSerializedState$a(serializedState,widgetOptions){return deriveUserInputFromSerializedState(serializedState,widgetOptions.widgets)}var Group$1 = {name:"group",displayName:"Group (SAT only)",widget:Group,hidden:true,isLintable:true,getStartUserInput: getStartUserInput$a,getUserInputFromSerializedState: getUserInputFromSerializedState$a};
1791
+ class Group extends React.Component{componentDidMount(){this.forceUpdate();}getPromptJSON(){return getPromptJSON$f(this.rendererRef?.getPromptJSON())}getInputPaths(){return this.rendererRef?.getInputPaths()??[]}focus(){return this.rendererRef?.focus()??false}render(){const apiOptions={...ApiOptions.defaults,...this.props.apiOptions,onFocusChange:(newFocus,oldFocus)=>{if(oldFocus){this.props.onBlur(oldFocus);}if(newFocus){this.props.onFocus(newFocus);}}};return jsxRuntimeExports.jsx("div",{className:classNames$2({"perseus-group":true}),children:jsxRuntimeExports.jsx(Renderer,{userInput:this.props.userInput,handleUserInput:(widgetId,userInput)=>{this.props.handleUserInput({...this.props.userInput,[widgetId]:userInput});},content:this.props.content,widgets:this.props.widgets,images:this.props.images,ref:ref=>this.rendererRef=ref,apiOptions:apiOptions,findExternalWidgets:this.props.findWidgets,reviewMode:this.props.reviewMode,showSolutions:this.props.showSolutions,linterContext:this.props.linterContext,strings:this.context.strings})})}constructor(...args){super(...args),this.getSerializedState=()=>{return this.rendererRef?.getSerializedState()},this.focusInputPath=path=>{this.rendererRef?.focusPath(path);},this.blurInputPath=path=>{this.rendererRef?.blurPath(path);};}}Group.contextType=PerseusI18nContext;Group.defaultProps={content:"",widgets:{},images:{},linterContext:linterContextDefault};function getStartUserInput$a(options,problemNum){return sharedInitializeUserInput(options.widgets,problemNum)}function getUserInputFromSerializedState$a(serializedState,widgetOptions){return deriveUserInputFromSerializedState(serializedState,widgetOptions.widgets)}var Group$1 = {name:"group",displayName:"Group (SAT only)",widget:Group,hidden:true,isLintable:true,getStartUserInput: getStartUserInput$a,getUserInputFromSerializedState: getUserInputFromSerializedState$a};
1792
1792
 
1793
1793
  const getPromptJSON$e=()=>{return getUnsupportedPromptJSON("iframe")};
1794
1794
 
@@ -1798,7 +1798,7 @@ const getPromptJSON$d=widgetData=>{return {type:"image",options:{altText:widgetD
1798
1798
 
1799
1799
  var styles$g = {"titleContainer":"perseus_aAJy9Er3","infoAreaContainer":"perseus_a7ZnxMAZ","modalContainer":"perseus_-p2ghr1-","modalTitleContainer":"perseus_YxnjOOa7","modalPanelContainer":"perseus_ryDvI8uD","modalImageContainer":"perseus_yCoMyk8e","modalDescriptionContainer":"perseus_femvuTvR","modalCaptionContainer":"perseus_y4DO7MoR","spacerHorizontal":"perseus_fBhKbquh","spacerVertical":"perseus_9XgZ2QXH"};
1800
1800
 
1801
- function ExploreImageButton({hasCaption,onClick}){const context=React.useContext(PerseusI18nContext);if(!hasCaption){return jsxRuntimeExports.jsx(Button,{kind:"secondary",startIcon:infoIconBold,onClick:onClick,children:context.strings.imageExploreButton})}return jsxRuntimeExports.jsx(IconButton,{"aria-label":context.strings.imageExploreButton,icon:infoIconBold,kind:"secondary",onClick:onClick,style:{flexShrink:0}})}
1801
+ function ExploreImageButton({hasCaption,onClick}){const context=React.useContext(PerseusI18nContext);if(!hasCaption){return jsxRuntimeExports.jsx(Button,{"aria-label":context.strings.imageExploreButtonAriaLabel,kind:"secondary",startIcon:infoIconBold,onClick:onClick,children:context.strings.imageExploreButton})}return jsxRuntimeExports.jsx(IconButton,{"aria-label":context.strings.imageExploreButtonAriaLabel,icon:infoIconBold,kind:"secondary",onClick:onClick,style:{flexShrink:0}})}
1802
1802
 
1803
1803
  const VisibleIcon=()=>jsxRuntimeExports.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",className:css(styles$f.icon),children:[jsxRuntimeExports.jsx("defs",{children:jsxRuntimeExports.jsx("path",{id:"a",d:"M7.401 10.035c-1.424.748-2.599 1.905-3.544 "+"3.48a1 1 0 0 1-1.714-1.03C4.325 8.849 7.652 7 "+"12 7c4.348 0 7.675 1.848 9.857 5.486a1 1 0 0 "+"1-1.714 1.028c-.945-1.574-2.12-2.73-3.544-"+"3.48a5 5 0 1 1-9.198 0zM12 15a3 3 0 1 0 0-6 3 3 "+"0 0 0 0 6z"})}),jsxRuntimeExports.jsxs("g",{fill:"none",fillRule:"evenodd",children:[jsxRuntimeExports.jsx("path",{fill:"none",d:"M0 0h24v24H0z"}),jsxRuntimeExports.jsx("mask",{id:"b",fill:"#fff",children:jsxRuntimeExports.jsx("use",{href:"#a"})}),jsxRuntimeExports.jsx("use",{fill:"#fff",fillRule:"nonzero",href:"#a"}),jsxRuntimeExports.jsx("g",{fill:"#fff",mask:"url(#b)",children:jsxRuntimeExports.jsx("path",{d:"M0 0h24v24H0z"})})]})]});const HiddenIcon=()=>jsxRuntimeExports.jsxs("svg",{width:"24",height:"24",viewBox:"0 0 24 24",className:css(styles$f.icon),children:[jsxRuntimeExports.jsx("defs",{children:jsxRuntimeExports.jsx("path",{id:"a",d:"M8.794 7.38C9.791 7.127 10.86 7 12 7c4.348 0 "+"7.675 1.848 9.857 5.486a1 1 0 0 1-1.714 "+"1.028c-.945-1.574-2.12-2.73-3.544-3.48.258."+"604.401 1.268.401 1.966 0 1.02-.305 "+"1.967-.828 2.757l2.535 2.536a1 1 0 0 "+"1-1.414 1.414l-12-12a1 1 0 0 1 "+"1.414-1.414L8.794 7.38zm5.914 5.913a3 3 0 0 "+"0-4.001-4.001l4 4.001zM6.072 8.486l2.976 "+"2.976a3 3 0 0 0 3.49 3.49l1.579 1.58A5 5 0 "+"0 1 7.4 10.035c-1.424.747-2.599 1.904-3.544 "+"3.478a1 1 0 0 1-1.714-1.028c1.049-1.75 "+"2.363-3.085 3.929-4z"})}),jsxRuntimeExports.jsxs("g",{fill:"none",fillRule:"evenodd",children:[jsxRuntimeExports.jsx("path",{fill:"none",d:"M0 0h24v24H0z"}),jsxRuntimeExports.jsx("mask",{id:"b",fill:"#fff",children:jsxRuntimeExports.jsx("use",{href:"#a"})}),jsxRuntimeExports.jsx("use",{fill:"#fff",fillRule:"nonzero",href:"#a"}),jsxRuntimeExports.jsx("g",{fill:"#fff",mask:"url(#b)",children:jsxRuntimeExports.jsx("path",{d:"M0 0h24v24H0z"})})]})]});const HUD=({message,enabled,onClick,fixedPosition=true})=>{let state;let icon;if(enabled){state=styles$f.enabled;icon=jsxRuntimeExports.jsx(VisibleIcon,{});}else {state=styles$f.disabled;icon=jsxRuntimeExports.jsx(HiddenIcon,{});}return jsxRuntimeExports.jsxs("button",{className:css(styles$f.hud,fixedPosition&&styles$f.hudFixedPosition,state),onClick:e=>{onClick();},children:[icon,message]})};const styles$f=StyleSheet.create({hud:{boxSizing:"border-box",height:36,padding:"9px 16px",borderRadius:18,fontFamily:boldFontFamily,fontSize:"15px",lineHeight:"18px",color:white,userSelect:"none",borderWidth:0},hudFixedPosition:{bottom:20,position:"fixed",right:20,zIndex:1},icon:{width:24,height:24,marginRight:8,marginTop:-3,verticalAlign:"middle"},enabled:{backgroundColor:warningColor,":hover":{backgroundColor:warningColorHover},":active":{backgroundColor:warningColorActive}},disabled:{backgroundColor:gray76,":hover":{backgroundColor:"#a1a5a9"},":active":{backgroundColor:gray68}}});
1804
1804
 
@@ -1812,7 +1812,7 @@ class MultiButtonGroup extends React.Component{focus(){this.buttonContainerRef.c
1812
1812
 
1813
1813
  function isPiMultiple(value){if(Number.isInteger(value)){return false}return value%Math.PI===0||value%(Math.PI/2)===0||value%(Math.PI/3)===0||value%(Math.PI/4)===0||value%(Math.PI/6)===0}
1814
1814
 
1815
- const{firstNumericalParse,captureScratchpadTouchStart}=Util;const toNumericString=KhanMath.toNumericString;const getNumericFormat=KhanMath.getNumericFormat;class NumberInput extends React.Component{componentDidMount(){const value=this.getValue();if(this.props.allowPiTruncation&&value!==null&&value!==0){if(this.state.format!=="pi"&&isPiMultiple(value)){this._setValue(value/Math.PI,"pi");this.setState({format:"pi"});}}}componentDidUpdate(prevProps){if(!number.equal(this.getValue(),this.props.value)){this._setValue(this.props.value,this.state.format);}}render(){let classes=classNames$1({"number-input":true,"invalid-input":!this._checkValidity(this.props.value),mini:this.props.size==="mini",small:this.props.size==="small",normal:this.props.size==="normal"});if(this.props.className!=null){classes=classes+" "+this.props.className;}const{onFormatChange:_,checkValidity:__,useArrowKeys:___,allowPiTruncation:____,...restProps}=this.props;return jsxRuntimeExports.jsx("input",{...restProps,className:classes,type:"text",ref:this.inputRef,onChange:this._handleChange,onFocus:this._handleFocus,onBlur:this._handleBlur,onKeyPress:this._handleBlur,onKeyDown:this._onKeyDown,onTouchStart:captureScratchpadTouchStart,defaultValue:toNumericString(this.props.value,this.state.format),value:undefined})}constructor(...args){super(...args),this.inputRef=React.createRef(),this.state={format:this.props.format},this._getInput=()=>{if(!this.inputRef.current){throw new PerseusError("Input ref accessed before set",Errors.Internal)}return this.inputRef.current},this.getValue=()=>{return this.parseInputValue(this._getInput().value)},this.getStringValue=()=>{return this._getInput().toString()},this.parseInputValue=value=>{if(value===""){const placeholder=this.props.placeholder;return _.isFinite(placeholder)?+placeholder:null}const result=firstNumericalParse(value);return _.isFinite(result)?result:this.props.value},this.focus=()=>{this._getInput().focus();this._handleFocus();},this.blur=()=>{this._getInput().blur();this._handleBlur();},this.setSelectionRange=(selectionStart,selectionEnd)=>{this._getInput().setSelectionRange(selectionStart,selectionEnd);},this.getSelectionStart=()=>{return this._getInput().selectionStart},this.getSelectionEnd=()=>{return this._getInput().selectionEnd},this._checkValidity=value=>{if(value==null){return true}const val=firstNumericalParse(value);const checkValidity=this.props.checkValidity;return _.isFinite(val)&&checkValidity(val)},this._handleChange=e=>{const text=e.target.value;const value=this.parseInputValue(text);const format=getNumericFormat(text);this.props.onChange(value);if(format){this.props.onFormatChange(value,format);this.setState({format:format});}},this._handleFocus=()=>{if(this.props.onFocus){this.props.onFocus();}},this._handleBlur=e=>{if(e&&e.type==="keypress"&&e.keyCode!==13){return}this._setValue(this.props.value,this.state.format);if(this.props.onBlur){this.props.onBlur();}},this._onKeyDown=e=>{if(this.props.onKeyDown){this.props.onKeyDown(e);}if(!this.props.useArrowKeys||!_.contains(["ArrowUp","ArrowDown"],e.key)){return}let val=this.getValue();if(val!==Math.floor(val)){return}if(e.key==="ArrowUp"){val=val+1;}else if(e.key==="ArrowDown"){val=val-1;}if(this._checkValidity(val)){this.props.onChange(val);}},this._setValue=(val,format)=>{this._getInput().value=toNumericString(val,format);};}}NumberInput.propTypes={value:PropTypes.number,format:PropTypes.string,placeholder:PropTypes.oneOfType([PropTypes.string,PropTypes.number]),onChange:PropTypes.func.isRequired,onFormatChange:PropTypes.func,checkValidity:PropTypes.func,size:PropTypes.oneOf(["mini","small","normal"]),label:PropTypes.oneOf(["put your labels outside your inputs!"]),allowPiTruncation:PropTypes.bool};NumberInput.defaultProps={value:null,placeholder:null,format:null,onFormatChange:()=>null,checkValidity:()=>true,useArrowKeys:false};
1815
+ const{firstNumericalParse,captureScratchpadTouchStart}=Util;const toNumericString=KhanMath.toNumericString;const getNumericFormat=KhanMath.getNumericFormat;class NumberInput extends React.Component{componentDidMount(){const value=this.getValue();if(this.props.allowPiTruncation&&value!==null&&value!==0){if(this.state.format!=="pi"&&isPiMultiple(value)){this._setValue(value/Math.PI,"pi");this.setState({format:"pi"});}}}componentDidUpdate(prevProps){if(!number.equal(this.getValue(),this.props.value)){this._setValue(this.props.value,this.state.format);}}render(){let classes=classNames$2({"number-input":true,"invalid-input":!this._checkValidity(this.props.value),mini:this.props.size==="mini",small:this.props.size==="small",normal:this.props.size==="normal"});if(this.props.className!=null){classes=classes+" "+this.props.className;}const{onFormatChange:_,checkValidity:__,useArrowKeys:___,allowPiTruncation:____,...restProps}=this.props;return jsxRuntimeExports.jsx("input",{...restProps,className:classes,type:"text",ref:this.inputRef,onChange:this._handleChange,onFocus:this._handleFocus,onBlur:this._handleBlur,onKeyPress:this._handleBlur,onKeyDown:this._onKeyDown,onTouchStart:captureScratchpadTouchStart,defaultValue:toNumericString(this.props.value,this.state.format),value:undefined})}constructor(...args){super(...args),this.inputRef=React.createRef(),this.state={format:this.props.format},this._getInput=()=>{if(!this.inputRef.current){throw new PerseusError("Input ref accessed before set",Errors.Internal)}return this.inputRef.current},this.getValue=()=>{return this.parseInputValue(this._getInput().value)},this.getStringValue=()=>{return this._getInput().toString()},this.parseInputValue=value=>{if(value===""){const placeholder=this.props.placeholder;return _.isFinite(placeholder)?+placeholder:null}const result=firstNumericalParse(value);return _.isFinite(result)?result:this.props.value},this.focus=()=>{this._getInput().focus();this._handleFocus();},this.blur=()=>{this._getInput().blur();this._handleBlur();},this.setSelectionRange=(selectionStart,selectionEnd)=>{this._getInput().setSelectionRange(selectionStart,selectionEnd);},this.getSelectionStart=()=>{return this._getInput().selectionStart},this.getSelectionEnd=()=>{return this._getInput().selectionEnd},this._checkValidity=value=>{if(value==null){return true}const val=firstNumericalParse(value);const checkValidity=this.props.checkValidity;return _.isFinite(val)&&checkValidity(val)},this._handleChange=e=>{const text=e.target.value;const value=this.parseInputValue(text);const format=getNumericFormat(text);this.props.onChange(value);if(format){this.props.onFormatChange(value,format);this.setState({format:format});}},this._handleFocus=()=>{if(this.props.onFocus){this.props.onFocus();}},this._handleBlur=e=>{if(e&&e.type==="keypress"&&e.keyCode!==13){return}this._setValue(this.props.value,this.state.format);if(this.props.onBlur){this.props.onBlur();}},this._onKeyDown=e=>{if(this.props.onKeyDown){this.props.onKeyDown(e);}if(!this.props.useArrowKeys||!_.contains(["ArrowUp","ArrowDown"],e.key)){return}let val=this.getValue();if(val!==Math.floor(val)){return}if(e.key==="ArrowUp"){val=val+1;}else if(e.key==="ArrowDown"){val=val-1;}if(this._checkValidity(val)){this.props.onChange(val);}},this._setValue=(val,format)=>{this._getInput().value=toNumericString(val,format);};}}NumberInput.propTypes={value:PropTypes.number,format:PropTypes.string,placeholder:PropTypes.oneOfType([PropTypes.string,PropTypes.number]),onChange:PropTypes.func.isRequired,onFormatChange:PropTypes.func,checkValidity:PropTypes.func,size:PropTypes.oneOf(["mini","small","normal"]),label:PropTypes.oneOf(["put your labels outside your inputs!"]),allowPiTruncation:PropTypes.bool};NumberInput.defaultProps={value:null,placeholder:null,format:null,onFormatChange:()=>null,checkValidity:()=>true,useArrowKeys:false};
1816
1816
 
1817
1817
  const truth=()=>true;class RangeInput extends React.Component{render(){const value=this.props.value;const checkValidity=this.props.checkValidity||truth;return jsxRuntimeExports.jsxs("div",{className:"range-input",children:[jsxRuntimeExports.jsx(NumberInput,{...this.props,value:value[0],checkValidity:val=>checkValidity([val,value[1]]),onChange:this.onChange.bind(this,0),placeholder:this.props.placeholder[0],allowPiTruncation:this.props.allowPiTruncation}),jsxRuntimeExports.jsx(NumberInput,{...this.props,value:value[1],checkValidity:val=>checkValidity([value[0],val]),onChange:this.onChange.bind(this,1),placeholder:this.props.placeholder[1],allowPiTruncation:this.props.allowPiTruncation})]})}constructor(...args){super(...args),this.onChange=(i,newVal)=>{const value=this.props.value;if(i===0){this.props.onChange([newVal,value[1]]);}else {this.props.onChange([value[0],newVal]);}};}}RangeInput.propTypes={value:PropTypes.array.isRequired,onChange:PropTypes.func.isRequired,placeholder:PropTypes.array,checkValidity:PropTypes.func,allowPiTruncation:PropTypes.bool};RangeInput.defaultProps={placeholder:[null,null]};
1818
1818
 
@@ -1837,9 +1837,9 @@ var components = /*#__PURE__*/Object.freeze({
1837
1837
 
1838
1838
  const GifControlsButton=({isPlaying,onToggle})=>{const strings=usePerseusI18n().strings;return jsxRuntimeExports.jsx(Button,{kind:"secondary",startIcon:isPlaying?pauseIcon:playIcon,onClick:onToggle,style:{width:"fit-content"},children:isPlaying?strings.gifPauseButtonLabel:strings.gifPlayButtonLabel})};
1839
1839
 
1840
- const MODAL_HEIGHT=568;function ExploreImageModalContent({backgroundImage,scale:contentScale,caption,alt,longDescription,linterContext,apiOptions,box,labels,range,zoomSize}){const[isGifPlaying,setIsGifPlaying]=React.useState(false);const context=React.useContext(PerseusI18nContext);if(!backgroundImage.url){return null}const scaleFF=isFeatureOn({apiOptions},"image-widget-upgrade-scale");const gifControlsFF=isFeatureOn({apiOptions},"image-widget-upgrade-gif-controls");const[zoomWidth,zoomHeight]=zoomSize;const imageIsGif=isGif(backgroundImage.url);const imageIsSvg=isSvg(backgroundImage.url);let scale=1;if(backgroundImage.width&&backgroundImage.height){scale=imageIsSvg?Math.max(contentScale,2):Math.max(contentScale,1);}let height=backgroundImage.height;let width=backgroundImage.width;height=Math.min(MODAL_HEIGHT,zoomHeight);width=zoomWidth/zoomHeight*height;if(scaleFF){if(backgroundImage.height&&backgroundImage.width){width=backgroundImage.width;height=backgroundImage.height;const maxScale=MODAL_HEIGHT/backgroundImage.height;scale=Math.min(scale,maxScale);}}return jsxRuntimeExports.jsxs("div",{className:styles$g.modalPanelContainer,children:[jsxRuntimeExports.jsx("div",{className:styles$g.modalImageContainer,children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{src:backgroundImage.url,allowZoom:false,alt:caption===alt?"":alt,width:width,height:height,scale:scaleFF?scale:1,preloader:apiOptions.imagePreloader,extraGraphie:{box:box,range:range,labels:labels??[]},zoomToFullSizeOnMobile:apiOptions.isMobile,constrainHeight:apiOptions.isMobile,allowFullBleed:apiOptions.isMobile,setAssetStatus:setAssetStatus,isGifPlaying:gifControlsFF&&imageIsGif?isGifPlaying:undefined,onGifLoop:gifControlsFF&&imageIsGif?()=>setIsGifPlaying(false):undefined})})}),jsxRuntimeExports.jsxs("div",{className:`perseus-image-modal-description ${styles$g.modalDescriptionContainer}`,children:[gifControlsFF&&imageIsGif&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(GifControlsButton,{isPlaying:isGifPlaying,onToggle:()=>setIsGifPlaying(!isGifPlaying)}),jsxRuntimeExports.jsx("div",{className:styles$g.spacerVertical})]}),caption&&jsxRuntimeExports.jsx("div",{className:styles$g.modalCaptionContainer,children:jsxRuntimeExports.jsx(Renderer,{content:caption,apiOptions:apiOptions,linterContext:linterContext,strings:context.strings})}),jsxRuntimeExports.jsx(Heading,{size:"large",tag:"h2",style:wbStyles$1.descriptionHeading,children:context.strings.imageDescriptionLabel}),jsxRuntimeExports.jsx(Renderer,{content:longDescription,apiOptions:apiOptions,linterContext:linterContext,strings:context.strings})]})]})}const wbStyles$1={descriptionHeading:{marginBlockEnd:sizing.size_160}};
1840
+ const MODAL_HEIGHT=568;function ExploreImageModalContent({backgroundImage,scale:contentScale,caption,alt,longDescription,linterContext,apiOptions,box,labels,range,zoomSize,captionId,longDescId}){const[isGifPlaying,setIsGifPlaying]=React.useState(false);const context=React.useContext(PerseusI18nContext);if(!backgroundImage.url){return null}const scaleFF=isFeatureOn({apiOptions},"image-widget-upgrade-scale");const gifControlsFF=isFeatureOn({apiOptions},"image-widget-upgrade-gif-controls");const[zoomWidth,zoomHeight]=zoomSize;const imageIsGif=isGif(backgroundImage.url);const imageIsSvg=isSvg(backgroundImage.url);let scale=1;if(backgroundImage.width&&backgroundImage.height){scale=imageIsSvg?Math.max(contentScale,2):Math.max(contentScale,1);}let height=backgroundImage.height;let width=backgroundImage.width;height=Math.min(MODAL_HEIGHT,zoomHeight);width=zoomWidth/zoomHeight*height;if(scaleFF){if(backgroundImage.height&&backgroundImage.width){width=backgroundImage.width;height=backgroundImage.height;const maxScale=MODAL_HEIGHT/backgroundImage.height;scale=Math.min(scale,maxScale);}}return jsxRuntimeExports.jsxs("div",{className:styles$g.modalPanelContainer,children:[jsxRuntimeExports.jsx("div",{className:styles$g.modalImageContainer,children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{src:backgroundImage.url,allowZoom:false,alt:caption===alt?"":alt,width:width,height:height,scale:scaleFF?scale:1,preloader:apiOptions.imagePreloader,extraGraphie:{box:box,range:range,labels:labels??[]},zoomToFullSizeOnMobile:apiOptions.isMobile,constrainHeight:apiOptions.isMobile,allowFullBleed:apiOptions.isMobile,setAssetStatus:setAssetStatus,isGifPlaying:gifControlsFF&&imageIsGif?isGifPlaying:undefined,onGifLoop:gifControlsFF&&imageIsGif?()=>setIsGifPlaying(false):undefined})})}),jsxRuntimeExports.jsxs("div",{className:`perseus-image-modal-description ${styles$g.modalDescriptionContainer}`,children:[gifControlsFF&&imageIsGif&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(GifControlsButton,{isPlaying:isGifPlaying,onToggle:()=>setIsGifPlaying(!isGifPlaying)}),jsxRuntimeExports.jsx("div",{className:styles$g.spacerVertical})]}),caption&&jsxRuntimeExports.jsx("div",{id:captionId,className:styles$g.modalCaptionContainer,children:jsxRuntimeExports.jsx(Renderer,{content:caption,apiOptions:apiOptions,linterContext:linterContext,strings:context.strings})}),jsxRuntimeExports.jsx(Heading,{size:"large",tag:"h2",style:wbStyles$1.descriptionHeading,children:context.strings.imageDescriptionLabel}),jsxRuntimeExports.jsx("div",{id:longDescId,children:jsxRuntimeExports.jsx(Renderer,{content:longDescription,apiOptions:apiOptions,linterContext:linterContext,strings:context.strings})})]})]})}const wbStyles$1={descriptionHeading:{marginBlockEnd:sizing.size_160}};
1841
1841
 
1842
- const ExploreImageModal=props=>{const context=React__default.useContext(PerseusI18nContext);const titleText=props.title||context.strings.imageAlternativeTitle;const title=jsxRuntimeExports.jsx("h1",{className:`perseus-image-modal-title ${styles$g.modalTitleContainer}`,children:jsxRuntimeExports.jsx(Renderer,{content:titleText,apiOptions:props.apiOptions,linterContext:props.linterContext,strings:context.strings})});return jsxRuntimeExports.jsx("div",{className:`framework-perseus ${styles$g.modalContainer}`,children:jsxRuntimeExports.jsx(FlexibleDialog,{title:title,content:jsxRuntimeExports.jsx(ExploreImageModalContent,{...props}),styles:{root:wbStyles.root}})})};const wbStyles={root:{borderRadius:sizing.size_120,maxWidth:"100%"}};
1842
+ const ExploreImageModal=props=>{const context=React__default.useContext(PerseusI18nContext);const uniqueId=React__default.useId();const captionId=`${uniqueId}-caption`;const longDescId=`${uniqueId}-long-desc`;const titleText=props.title||context.strings.imageAlternativeTitle;const title=jsxRuntimeExports.jsx("h1",{className:`perseus-image-modal-title ${styles$g.modalTitleContainer}`,children:jsxRuntimeExports.jsx(Renderer,{content:titleText,apiOptions:props.apiOptions,linterContext:props.linterContext,strings:context.strings})});return jsxRuntimeExports.jsx("div",{className:`framework-perseus ${styles$g.modalContainer}`,children:jsxRuntimeExports.jsx(FlexibleDialog,{title:title,content:jsxRuntimeExports.jsx(ExploreImageModalContent,{...props,captionId:captionId,longDescId:longDescId}),"aria-describedby":props.caption?`${captionId} ${longDescId}`:longDescId,styles:{root:wbStyles.root}})})};const wbStyles={root:{borderRadius:sizing.size_120,maxWidth:"100%"}};
1843
1843
 
1844
1844
  const GifControlsIcon=({isPlaying,onToggle})=>{const strings=usePerseusI18n().strings;return jsxRuntimeExports.jsx(IconButton,{icon:isPlaying?pauseIcon:playIcon,kind:"secondary","aria-label":isPlaying?strings.gifPauseButtonLabel:strings.gifPlayButtonLabel,onClick:onToggle,style:{flexShrink:0}})};
1845
1845
 
@@ -1853,11 +1853,11 @@ const getPromptJSON$c=()=>{return getUnsupportedPromptJSON("interaction")};
1853
1853
 
1854
1854
  const Label=Graphie.Label;const Line$2=Graphie.Line;const MovablePoint$2=Graphie.MovablePoint;const MovableLine$1=Graphie.MovableLine;const Plot=Graphie.Plot;const PlotParametric=Graphie.PlotParametric;const Point=Graphie.Point;const Rect=Graphie.Rect;const{unescapeMathMode}=Util;const KAShashFunc=(expr,options)=>{options=options||{};let result=expr+"||"+options.decimal_separator+"||";const functions=options.functions;const functionsLength=functions?functions.length:0;for(let i=0;i<functionsLength;i++){result+=functions[i]+"|";}return result};const _parseCache=Object.create(null);const KASparse=(expr,options)=>{const hash=KAShashFunc(expr,options);let cached=_parseCache[hash];if(cached){return cached}cached=KAS.parse(expr,options);_parseCache[hash]=cached;return cached};const _compileCache=Object.create(null);const KAScompile=(expr,options)=>{const hash=KAShashFunc(expr,options);let cached=_compileCache[hash];if(cached){return cached}const parsed=KAS.parse(expr,options).expr;cached=parsed?parsed.compile():function(){return 0};_compileCache[hash]=cached;return cached};class Interaction extends React.Component{UNSAFE_componentWillReceiveProps(nextProps){if(!_.isEqual(this.props.elements,nextProps.elements)){this.setState({variables:_getInitialVariables(nextProps.elements),functions:_getInitialFunctions(nextProps.elements)});}}getPromptJSON(){return getPromptJSON$c()}render(){const range=this.props.graph.range;let labels=this.props.graph.labels;if(this.props.graph.markings==="graph"){labels=this.props.graph.labels.map(label=>label.startsWith("$")&&label.endsWith("$")?label.slice(1,-1):label);}return jsxRuntimeExports.jsxs(Graphie,{box:this.props.graph.box,range:this.props.graph.range,options:this.props.graph,setup:this._setupGraphie,setDrawingAreaAvailable:this.props.apiOptions.setDrawingAreaAvailable,children:[this.props.graph.markings==="graph"&&jsxRuntimeExports.jsx(Label,{coord:[0,range[1][1]],text:labels[1],direction:"above"}),this.props.graph.markings==="graph"&&jsxRuntimeExports.jsx(Label,{coord:[range[0][1],0],text:labels[0],direction:"right"}),_.map(this.props.elements,function(element,n){if(element.type==="point"){return jsxRuntimeExports.jsx(Point,{coord:[this._eval(element.options.coordX),this._eval(element.options.coordY)],color:element.options.color},element.key)}if(element.type==="line"){const start=[this._eval(element.options.startX),this._eval(element.options.startY)];const end=[this._eval(element.options.endX),this._eval(element.options.endY)];return jsxRuntimeExports.jsx(Line$2,{start:start,end:end,style:{stroke:element.options.color,strokeWidth:element.options.strokeWidth,strokeDasharray:element.options.strokeDasharray,arrows:element.options.arrows}},element.key)}if(element.type==="movable-point"){const constraints=[coord=>{const coordX=Math.max(this._eval(element.options.constraintXMin),Math.min(this._eval(element.options.constraintXMax),coord[0]));const coordY=Math.max(this._eval(element.options.constraintYMin),Math.min(this._eval(element.options.constraintYMax),coord[1]));return [coordX,coordY]}];if(element.options.constraint==="snap"){constraints.push(MovablePoint$2.constraints.snap(element.options.snap));}else if(element.options.constraint==="x"){constraints.push(coord=>{return [this._eval(element.options.constraintFn,{y:coord[1]}),coord[1]]});}else if(element.options.constraint==="y"){constraints.push(coord=>{return [coord[0],this._eval(element.options.constraintFn,{x:coord[0]})]});}return jsxRuntimeExports.jsx(MovablePoint$2,{coord:[this.state.variables["x_"+element.options.varSubscript],this.state.variables["y_"+element.options.varSubscript]],constraints:constraints,foo_x:element.options.constraint,foo_y:element.options.constraintFn,foo_z:element.options.snap,onMove:_.partial(this._updatePointLocation,element.options.varSubscript)},element.key)}if(element.type==="movable-line"){const constraints=[coord=>{const coordX=Math.max(this._eval(element.options.constraintXMin),Math.min(this._eval(element.options.constraintXMax),coord[0]));const coordY=Math.max(this._eval(element.options.constraintYMin),Math.min(this._eval(element.options.constraintYMax),coord[1]));return [coordX,coordY]}];if(element.options.constraint==="snap"){constraints.push(MovablePoint$2.constraints.snap(element.options.snap));}else if(element.options.constraint==="x"){constraints.push(coord=>{return [this._eval(element.options.constraintFn,{y:coord[1]}),coord[1]]});}else if(element.options.constraint==="y"){constraints.push(coord=>{return [coord[0],this._eval(element.options.constraintFn,{x:coord[0]})]});}const start=[this.state.variables["x_"+element.options.startSubscript],this.state.variables["y_"+element.options.startSubscript]];const end=[this.state.variables["x_"+element.options.endSubscript],this.state.variables["y_"+element.options.endSubscript]];return jsxRuntimeExports.jsxs(MovableLine$1,{constraints:constraints,onMove:_.bind(this._updateLineLocation,this,element.options),foo_x:element.options.constraint,foo_y:element.options.constraintFn,foo_z:element.options.snap,children:[jsxRuntimeExports.jsx(MovablePoint$2,{coord:start,static:true,normalStyle:{stroke:"none",fill:"none"}}),jsxRuntimeExports.jsx(MovablePoint$2,{coord:end,static:true,normalStyle:{stroke:"none",fill:"none"}})]},element.key)}if(element.type==="function"){const fn=x=>{return this._eval(element.options.value,{x:x})};const vars=_.without(this._extractVars(KASparse(element.options.value).expr),"x");const varValues=_.object(vars,_.map(vars,v=>this.state.variables[v]));const range=[this._eval(element.options.rangeMin,this.state.variables),this._eval(element.options.rangeMax,this.state.variables)];return jsxRuntimeExports.jsx(Plot,{fn:fn,foo_fn:element.options.value,foo_varvalues:varValues,range:range,style:{stroke:element.options.color,strokeWidth:element.options.strokeWidth,strokeDasharray:element.options.strokeDasharray,plotPoints:100}},element.key)}if(element.type==="parametric"){const fn=t=>{return [this._eval(element.options.x,{t:t}),this._eval(element.options.y,{t:t})]};const vars=_.without(this._extractVars(KASparse(element.options.x).expr).concat(this._extractVars(KASparse(element.options.y).expr)),"t");const varValues=_.object(vars,_.map(vars,v=>this.state.variables[v]));const range=[this._eval(element.options.rangeMin,this.state.variables),this._eval(element.options.rangeMax,this.state.variables)];return jsxRuntimeExports.jsx(PlotParametric,{fn:fn,foo_fnx:element.options.x,foo_fny:element.options.y,foo_varvalues:varValues,range:range,style:{stroke:element.options.color,strokeWidth:element.options.strokeWidth,strokeDasharray:element.options.strokeDasharray,plotPoints:100}},element.key)}if(element.type==="label"){const coord=[this._eval(element.options.coordX),this._eval(element.options.coordY)];return jsxRuntimeExports.jsx(Label,{coord:coord,text:unescapeMathMode(element.options.label),style:{color:element.options.color}},n+1)}if(element.type==="rectangle"){return jsxRuntimeExports.jsx(Rect,{x:this._eval(element.options.coordX),y:this._eval(element.options.coordY),width:_.max([this._eval(element.options.width),0]),height:_.max([this._eval(element.options.height),0]),style:{stroke:"none",fill:element.options.color}},n+1)}},this)]})}constructor(...args){super(...args),this.isWidget=true,this.state={variables:_getInitialVariables(this.props.elements),functions:_getInitialFunctions(this.props.elements)},this._setupGraphie=(graphie,options)=>{graphie.graphInit(_.extend({},options,{grid:_.contains(["graph","grid"],this.props.graph.markings),axes:_.contains(["graph"],this.props.graph.markings),ticks:_.contains(["graph"],this.props.graph.markings),labels:_.contains(["graph"],this.props.graph.markings),labelFormat:function(s){return "\\small{"+s+"}"},axisArrows:"<->",unityLabels:false}));},this._updatePointLocation=(subscript,coord)=>{const variables=_.clone(this.state.variables);variables["x_"+subscript]=coord[0];variables["y_"+subscript]=coord[1];this.setState({variables:variables});this.props.trackInteraction();},this._updateLineLocation=(options,startCoord)=>{const xDiff=this._eval("("+options.endX+")-("+options.startX+")");const yDiff=this._eval("("+options.endY+")-("+options.startY+")");const endCoord=vector$3.add(startCoord,[xDiff,yDiff]);const variables=_.clone(this.state.variables);variables["x_"+options.startSubscript]=startCoord[0];variables["y_"+options.startSubscript]=startCoord[1];variables["x_"+options.endSubscript]=endCoord[0];variables["y_"+options.endSubscript]=endCoord[1];this.setState({variables:variables});this.props.trackInteraction();},this._eval=(expression,variables)=>{const func=KAScompile(expression,{functions:this.state.functions});const compiledVars=_.extend({},this.state.variables,variables);_.each(_.keys(compiledVars),name=>{if(_.isString(compiledVars[name])){const func=KAScompile(compiledVars[name],{functions:this.state.functions});compiledVars[name]=function(x){return func(_.extend({},compiledVars,{x:x}))};}});return func(compiledVars)||0},this._extractVars=expr=>{if(expr==null){return []}let vars=[];_.each(expr.args(),function(arg){if(arg&&arg.constructor.name==="Expr"){vars=vars.concat(this._extractVars(arg));}},this);if(expr.name()==="Var"){vars.push(expr.prettyPrint());}return vars};}}Interaction.defaultProps={graph:{box:[400,400],labels:["x","y"],range:[[-10,10],[-10,10]],tickStep:[1,1],gridStep:[1,1],markings:"graph"},elements:[]};const _getInitialVariables=elements=>{const variables={};_.each(_.where(elements,{type:"movable-point"}),element=>{const subscript=element.options.varSubscript;const startXExpr=KASparse(element.options.startX||"0").expr;const startYExpr=KASparse(element.options.startY||"0").expr;let startX=0;let startY=0;if(startXExpr){startX=startXExpr.eval({})||0;}if(startYExpr){startY=startYExpr.eval({})||0;}variables["x_"+subscript]=startX;variables["y_"+subscript]=startY;});_.each(_.where(elements,{type:"movable-line"}),element=>{const startSubscript=element.options.startSubscript;const endSubscript=element.options.endSubscript;const startXExpr=KASparse(element.options.startX||"0").expr;const startYExpr=KASparse(element.options.startY||"0").expr;const endXExpr=KASparse(element.options.endX||"0").expr;const endYExpr=KASparse(element.options.endY||"0").expr;let startX=0;let startY=0;let endX=0;let endY=0;if(startXExpr){startX=startXExpr.eval({})||0;}if(startYExpr){startY=startYExpr.eval({})||0;}if(endXExpr){endX=endXExpr.eval({})||0;}if(endYExpr){endY=endYExpr.eval({})||0;}variables["x_"+startSubscript]=startX;variables["y_"+startSubscript]=startY;variables["x_"+endSubscript]=endX;variables["y_"+endSubscript]=endY;});_.each(_.where(elements,{type:"function"}),element=>{variables[element.options.funcName]=element.options.value;});return variables};const _getInitialFunctions=elements=>{return _.map(_.where(elements,{type:"function"}),element=>element.options.funcName)};var Interactive = {name:"interaction",displayName:"Interaction",widget:Interaction,hidden:true};
1855
1855
 
1856
- const getPromptJSON$b=(props,userInput)=>{return {type:"interactive-graph",options:{graph:getGraphOptionsForProps(props),backgroundImageUrl:props.backgroundImage?.url,range:props.range,labels:props.labels},userInput:getUserInput(userInput)}};const getGraphOptionsForProps=props=>{const type=props.userInput.type;switch(type){case "angle":return {type:props.userInput.type,angleOffsetDegrees:props.userInput.angleOffsetDeg,startCoords:props.userInput.startCoords};case "circle":return {type:props.userInput.type,startParams:{center:props.userInput.startCoords?.center,radius:props.userInput.startCoords?.radius}};case "linear":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "linear-system":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "point":return {type:props.userInput.type,numPoints:props.userInput.numPoints,startCoords:props.userInput.startCoords};case "polygon":return {type:props.userInput.type,match:props.userInput.match,numSides:props.userInput.numSides,startCoords:props.userInput.startCoords};case "quadratic":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "ray":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "segment":return {type:props.userInput.type,numSegments:props.userInput.numSegments,startCoords:props.userInput.startCoords};case "sinusoid":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "tangent":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "none":return {};case "absolute-value":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "exponential":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "logarithm":return {type:props.userInput.type,startCoords:props.userInput.startCoords};default:throw new UnreachableCaseError(type)}};const getUserInput=userInput=>{const type=userInput.type;switch(type){case "angle":return {coords:userInput.coords,angleOffsetDegrees:userInput.angleOffsetDeg};case "circle":return {center:userInput.center,radius:userInput.radius};case "linear":return {coords:userInput.coords};case "linear-system":return {coords:userInput.coords};case "point":return {coords:userInput.coords};case "polygon":return {coords:userInput.coords};case "quadratic":return {coords:userInput.coords};case "ray":return {coords:userInput.coords};case "segment":return {coords:userInput.coords};case "sinusoid":return {coords:userInput.coords};case "tangent":return {coords:userInput.coords};case "none":return {};case "absolute-value":return {coords:userInput.coords};case "exponential":return {coords:userInput.coords,asymptote:userInput.asymptote};case "logarithm":return {coords:userInput.coords,asymptote:userInput.asymptote};default:throw new UnreachableCaseError(type)}};
1856
+ const getPromptJSON$b=(props,userInput)=>{return {type:"interactive-graph",options:{graph:getGraphOptionsForProps(props),backgroundImageUrl:props.backgroundImage?.url,range:props.range,labels:props.labels},userInput:getUserInput(userInput)}};const getGraphOptionsForProps=props=>{const type=props.userInput.type;switch(type){case "angle":return {type:props.userInput.type,angleOffsetDegrees:props.userInput.angleOffsetDeg,startCoords:props.userInput.startCoords};case "circle":return {type:props.userInput.type,startParams:{center:props.userInput.startCoords?.center,radius:props.userInput.startCoords?.radius}};case "linear":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "linear-system":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "point":return {type:props.userInput.type,numPoints:props.userInput.numPoints,startCoords:props.userInput.startCoords};case "polygon":return {type:props.userInput.type,match:props.userInput.match,numSides:props.userInput.numSides,startCoords:props.userInput.startCoords};case "quadratic":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "ray":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "segment":return {type:props.userInput.type,numSegments:props.userInput.numSegments,startCoords:props.userInput.startCoords};case "sinusoid":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "tangent":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "none":return {};case "absolute-value":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "exponential":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "logarithm":return {type:props.userInput.type,startCoords:props.userInput.startCoords};case "vector":return {type:props.userInput.type,startCoords:props.userInput.startCoords};default:throw new UnreachableCaseError(type)}};const getUserInput=userInput=>{const type=userInput.type;switch(type){case "angle":return {coords:userInput.coords,angleOffsetDegrees:userInput.angleOffsetDeg};case "circle":return {center:userInput.center,radius:userInput.radius};case "linear":return {coords:userInput.coords};case "linear-system":return {coords:userInput.coords};case "point":return {coords:userInput.coords};case "polygon":return {coords:userInput.coords};case "quadratic":return {coords:userInput.coords};case "ray":return {coords:userInput.coords};case "segment":return {coords:userInput.coords};case "sinusoid":return {coords:userInput.coords};case "tangent":return {coords:userInput.coords};case "none":return {};case "absolute-value":return {coords:userInput.coords};case "exponential":return {coords:userInput.coords,asymptote:userInput.asymptote};case "logarithm":return {coords:userInput.coords,asymptote:userInput.asymptote};case "vector":return {coords:userInput.coords};default:throw new UnreachableCaseError(type)}};
1857
1857
 
1858
1858
  const X=0;const Y=1;
1859
1859
 
1860
- const actions={global:{deleteIntent,changeInteractionMode,changeKeyboardInvitationVisibility},angle:{movePoint},circle:{moveCenter,moveRadiusPoint},linear:{moveLine:delta=>moveLine(0,delta),movePoint:(pointIndex,destination)=>movePointInFigure(0,pointIndex,destination)},linearSystem:{moveLine,movePointInFigure},pointGraph:{movePoint,addPoint,removePoint,focusPoint,blurPoint,clickPoint},polygon:{movePoint,moveAll,addPoint,removePoint,focusPoint,blurPoint,clickPoint,closePolygon,openPolygon},quadratic:{movePoint},ray:{moveRay:delta=>moveLine(0,delta),movePoint:(pointIndex,destination)=>movePointInFigure(0,pointIndex,destination)},segment:{movePointInFigure,moveLine},sinusoid:{movePoint},exponential:{movePoint,moveCenter},logarithm:{movePoint,moveCenter},absoluteValue:{movePoint},tangent:{movePoint}};const DELETE_INTENT="delete-intent";function deleteIntent(){return {type:DELETE_INTENT}}const MOVE_LINE="move-line";function moveLine(itemIndex,delta){return {type:MOVE_LINE,itemIndex,delta}}const ADD_POINT="add-point";function addPoint(location){return {type:ADD_POINT,location}}const REMOVE_POINT="remove-point";function removePoint(index){return {type:REMOVE_POINT,index}}const FOCUS_POINT="focus-point";function focusPoint(index){return {type:FOCUS_POINT,index}}const BLUR_POINT="blur-point";function blurPoint(){return {type:BLUR_POINT}}const CLICK_POINT="click-point";function clickPoint(index){return {type:CLICK_POINT,index}}const CHANGE_INTERACTION_MODE="point-change-interaction-mode";function changeInteractionMode(mode){return {type:CHANGE_INTERACTION_MODE,mode}}const CHANGE_KEYBOARD_INVITATION_VISIBILITY="change-keyboard-interaction-invitation-visibility";function changeKeyboardInvitationVisibility(shouldShow){return {type:CHANGE_KEYBOARD_INVITATION_VISIBILITY,shouldShow}}const CLOSE_POLYGON="close-polygon";function closePolygon(){return {type:CLOSE_POLYGON}}const OPEN_POLYGON="open-polygon";function openPolygon(){return {type:OPEN_POLYGON}}const MOVE_ALL="move-all";function moveAll(delta){return {type:MOVE_ALL,delta}}const MOVE_POINT="move-point";function movePoint(index,destination){return {type:MOVE_POINT,index,destination}}const MOVE_POINT_IN_FIGURE="move-point-in-figure";function movePointInFigure(figureIndex,pointIndex,destination){return {type:MOVE_POINT_IN_FIGURE,figureIndex,pointIndex,destination}}const MOVE_CENTER="move-center";function moveCenter(destination){return {type:MOVE_CENTER,destination}}const MOVE_RADIUS_POINT="MOVE_RADIUS_POINT";function moveRadiusPoint(destination){return {type:MOVE_RADIUS_POINT,destination}}const CHANGE_SNAP_STEP="change-snap-step";function changeSnapStep(snapStep){return {type:CHANGE_SNAP_STEP,snapStep}}const CHANGE_RANGE="change-range";function changeRange(range){return {type:CHANGE_RANGE,range}}const REINITIALIZE="reinitialize";function reinitialize(params){return {type:REINITIALIZE,params}}
1860
+ const actions={global:{deleteIntent,changeInteractionMode,changeKeyboardInvitationVisibility},angle:{movePoint},circle:{moveCenter,moveRadiusPoint},linear:{moveLine:newStart=>moveLine(0,newStart),movePoint:(pointIndex,destination)=>movePointInFigure(0,pointIndex,destination)},linearSystem:{moveLine,movePointInFigure},pointGraph:{movePoint,addPoint,removePoint,focusPoint,blurPoint,clickPoint},polygon:{movePoint,moveAll,addPoint,removePoint,focusPoint,blurPoint,clickPoint,closePolygon,openPolygon},quadratic:{movePoint},ray:{moveRay:newStart=>moveLine(0,newStart),movePoint:(pointIndex,destination)=>movePointInFigure(0,pointIndex,destination)},segment:{movePointInFigure,moveLine},sinusoid:{movePoint},exponential:{movePoint,moveCenter},logarithm:{movePoint,moveCenter},absoluteValue:{movePoint},tangent:{movePoint},vector:{moveTip:destination=>movePoint(1,destination),moveVector:newStart=>moveLine(0,newStart)}};const DELETE_INTENT="delete-intent";function deleteIntent(){return {type:DELETE_INTENT}}const MOVE_LINE="move-line";function moveLine(itemIndex,newStart){return {type:MOVE_LINE,itemIndex,newStart}}const ADD_POINT="add-point";function addPoint(location){return {type:ADD_POINT,location}}const REMOVE_POINT="remove-point";function removePoint(index){return {type:REMOVE_POINT,index}}const FOCUS_POINT="focus-point";function focusPoint(index){return {type:FOCUS_POINT,index}}const BLUR_POINT="blur-point";function blurPoint(){return {type:BLUR_POINT}}const CLICK_POINT="click-point";function clickPoint(index){return {type:CLICK_POINT,index}}const CHANGE_INTERACTION_MODE="point-change-interaction-mode";function changeInteractionMode(mode){return {type:CHANGE_INTERACTION_MODE,mode}}const CHANGE_KEYBOARD_INVITATION_VISIBILITY="change-keyboard-interaction-invitation-visibility";function changeKeyboardInvitationVisibility(shouldShow){return {type:CHANGE_KEYBOARD_INVITATION_VISIBILITY,shouldShow}}const CLOSE_POLYGON="close-polygon";function closePolygon(){return {type:CLOSE_POLYGON}}const OPEN_POLYGON="open-polygon";function openPolygon(){return {type:OPEN_POLYGON}}const MOVE_ALL="move-all";function moveAll(delta){return {type:MOVE_ALL,delta}}const MOVE_POINT="move-point";function movePoint(index,destination){return {type:MOVE_POINT,index,destination}}const MOVE_POINT_IN_FIGURE="move-point-in-figure";function movePointInFigure(figureIndex,pointIndex,destination){return {type:MOVE_POINT_IN_FIGURE,figureIndex,pointIndex,destination}}const MOVE_CENTER="move-center";function moveCenter(destination){return {type:MOVE_CENTER,destination}}const MOVE_RADIUS_POINT="MOVE_RADIUS_POINT";function moveRadiusPoint(destination){return {type:MOVE_RADIUS_POINT,destination}}const CHANGE_SNAP_STEP="change-snap-step";function changeSnapStep(snapStep){return {type:CHANGE_SNAP_STEP,snapStep}}const CHANGE_RANGE="change-range";function changeRange(range){return {type:CHANGE_RANGE,range}}const REINITIALIZE="reinitialize";function reinitialize(params){return {type:REINITIALIZE,params}}
1861
1861
 
1862
1862
  const defaultGraphConfig={range:[[0,1],[0,1]],tickStep:[1,1],gridStep:[1,1],snapStep:[1,1],markings:"none",showTooltips:false,graphDimensionsInPixels:[1,1],width:0,height:0,labels:[],labelLocation:"onAxis",disableKeyboardInteraction:false,interactiveColor:"var(--mafs-blue)",showAxisArrows:{xMin:true,xMax:true,yMin:true,yMax:true}};const GraphConfigContext=createContext(defaultGraphConfig);function useGraphConfig(){return React__default.useContext(GraphConfigContext)}
1863
1863
 
@@ -1881,13 +1881,13 @@ function useDraggable(args){const{gestureTarget:target,onMove,onDragStart,onDrag
1881
1881
 
1882
1882
  const matrixBuilder=vec.matrixBuilder;function vectorsToPixels(vectors,graphState,translation=[0,0]){const{range,width,height}=graphState;const[[xMin,xMax],[yMin,yMax]]=range;const transformToPx=matrixBuilder().translate(...translation).scale(width/(xMax-xMin),-height/(yMax-yMin)).get();return vectors.map(p=>vec.transform(p,transformToPx))}function dimensionsToPixels(dimens,graphState){const{range,width,height}=graphState;const[[xMin,xMax],[yMin,yMax]]=range;const transformToPx=matrixBuilder().scale(width/(xMax-xMin),height/(yMax-yMin)).get();return dimens.map(d=>vec.transform(d,transformToPx))}function pointToPixel(point,graphState){const[[xMin],[,yMax]]=graphState.range;const[a,b]=[-xMin,-yMax];return vectorsToPixels([point],graphState,[a,b])[0]}const useTransformVectorsToPixels=(...vectors)=>{const graphState=useGraphConfig();return vectorsToPixels(vectors,graphState)};const useTransformDimensionsToPixels=(...dimens)=>{const graphState=useGraphConfig();return dimensionsToPixels(dimens,graphState)};function pixelsToVectors(pixels,graphState){const[[xMin,xMax],[yMin,yMax]]=graphState.range;const{width,height}=graphState;const xSpan=xMax-xMin;const ySpan=yMax-yMin;return pixels.map(pixel=>{const x=pixel[X]/width*xSpan+xMin;const y=yMax-pixel[Y]/height*ySpan;return [x,y]})}
1883
1883
 
1884
- const fontSize=14;const fontSizeYAxisLabelMultiplier=1.25;const clampLabelPosition=(labelPosition,graphInfo)=>{const x=Math.max(Math.min(labelPosition[X],graphInfo.width+fontSize*1.25),-14*1.5);const y=Math.max(Math.min(labelPosition[Y],graphInfo.height+fontSize*fontSizeYAxisLabelMultiplier),-14*2);return [x,y]};const getLabelTransform=labelLocation=>{const isOnAxis=labelLocation===undefined||labelLocation==="onAxis";const xLabelTransform=isOnAxis?"translate(7px, -50%)":"translate(-50%, -50%)";const yLabelTransform=isOnAxis?"translate(-50%, 0px)":"translate(-50%, 0px) rotate(-90deg)";return {xLabelTransform,yLabelTransform}};const calculateMaxDigitsInRange=(range,tickStep)=>{const maxDigitsInRange=Math.max(String(Math.abs(Math.floor(range[MIN]))).length,String(Math.abs(Math.ceil(range[MAX]))).length);const tickStepSigFigs=countSignificantDecimals(tickStep)+2;return Math.max(maxDigitsInRange,tickStepSigFigs)};const getLabelPosition=(graphInfo,labelLocation,tickStep)=>{if(labelLocation==="alongEdge"){const xAxisLabelOffset=graphInfo.range[Y][MIN]>=0?[0,fontSize*3]:[0,fontSize*1.5];const isRelativelyCloseToZero=graphInfo.range[X][MIN]<0&&Math.abs(graphInfo.range[X][MIN])<(graphInfo.range[X][MAX]-graphInfo.range[X][MIN])*.07;const needsExtraSpacing=graphInfo.range[X][MIN]>=0||isRelativelyCloseToZero;let paddingRequiredForTickLabels=0;if(needsExtraSpacing){const maxDigits=calculateMaxDigitsInRange(graphInfo.range[Y],tickStep[Y]);paddingRequiredForTickLabels=maxDigits*(fontSize*.75)+(graphInfo.range[Y][MIN]<0&&graphInfo.range[X][MIN]<=0?fontSize*.5:0);}const yAxisLabelOffset=[-14*1.25-paddingRequiredForTickLabels,-14];const xAxisLabelLocation=[(graphInfo.range[X][MIN]+graphInfo.range[X][MAX])/2,graphInfo.range[Y][MIN]];const yAxisLabelLocation=[graphInfo.range[X][MIN],(graphInfo.range[Y][MIN]+graphInfo.range[Y][MAX])/2];if(isRelativelyCloseToZero){yAxisLabelLocation[X]=yAxisLabelLocation[X]-graphInfo.range[X][MIN];}const xLabel=vec.add(pointToPixel(xAxisLabelLocation,graphInfo),xAxisLabelOffset);const yLabel=vec.add(pointToPixel(yAxisLabelLocation,graphInfo),yAxisLabelOffset);return [xLabel,yLabel]}const xLabelInitial=[graphInfo.range[X][MAX],0];const yLabelInitial=[0,graphInfo.range[Y][MAX]];const yLabelOffset=[0,-14*2];let xLabel=pointToPixel(xLabelInitial,graphInfo);let yLabel=vec.add(pointToPixel(yLabelInitial,graphInfo),yLabelOffset);xLabel=clampLabelPosition(xLabel,graphInfo);yLabel=clampLabelPosition(yLabel,graphInfo);return [xLabel,yLabel]};const shouldShowLabel=(currentTick,range,tickStep)=>{let showLabel=true;if(range[X][MIN]<-tickStep&&range[X][MAX]>0&&currentTick===-tickStep){showLabel=false;}return showLabel};function generateTickLocations(tickStep,min,max,otherAxisMin){const ticks=[];const decimalSigFigs=countSignificantDecimals(tickStep);const start=Math.max(min,0);const startOffset=otherAxisMin>=0?0:tickStep;for(let i=start+startOffset;i<max;i+=tickStep){ticks.push(parseFloat(i.toFixed(decimalSigFigs)));}let i=Math.min(max,0)-tickStep;for(i;i>min;i-=tickStep){ticks.push(i);}return ticks}const countSignificantDecimals=number=>{const numStr=number.toString();if(!numStr.includes(".")){return 0}return numStr.split(".")[1].length};function divideByAndShowPi(value){const dividedValue=value/Math.PI;switch(dividedValue){case 1:return "π";case -1:return "-π";case 0:return "0";default:return dividedValue+"π"}}
1884
+ const fontSize=14;const fontSizeYAxisLabelMultiplier=1.25;const clampLabelPosition=(labelPosition,graphInfo)=>{const x=Math.max(Math.min(labelPosition[X],graphInfo.width+fontSize*1.25),-14*1.5);const y=Math.max(Math.min(labelPosition[Y],graphInfo.height+fontSize*fontSizeYAxisLabelMultiplier),-14*2);return [x,y]};const getLabelTransform=labelLocation=>{const isOnAxis=labelLocation===undefined||labelLocation==="onAxis";const xLabelTransform=isOnAxis?"translate(7px, -50%)":"translate(-50%, -50%)";const yLabelTransform=isOnAxis?"translate(-50%, 0px)":"translate(-50%, 0px) rotate(-90deg)";return {xLabelTransform,yLabelTransform}};const calculateMaxDigitsInRange=(range,tickStep)=>{const maxDigitsInRange=Math.max(String(Math.abs(Math.floor(range[MIN]))).length,String(Math.abs(Math.ceil(range[MAX]))).length);const tickStepSigFigs=countSignificantDecimals(tickStep)+2;return Math.max(maxDigitsInRange,tickStepSigFigs)};const getLabelPosition=(graphInfo,labelLocation,tickStep)=>{if(labelLocation==="alongEdge"){const xAxisLabelOffset=graphInfo.range[Y][MIN]>=0?[0,fontSize*3]:[0,fontSize*1.5];const isRelativelyCloseToZero=graphInfo.range[X][MIN]<0&&Math.abs(graphInfo.range[X][MIN])<(graphInfo.range[X][MAX]-graphInfo.range[X][MIN])*.07;const needsExtraSpacing=graphInfo.range[X][MIN]>=0||isRelativelyCloseToZero;let paddingRequiredForTickLabels=0;if(needsExtraSpacing){const maxDigits=calculateMaxDigitsInRange(graphInfo.range[Y],tickStep[Y]);paddingRequiredForTickLabels=maxDigits*(fontSize*.75)+(graphInfo.range[Y][MIN]<0&&graphInfo.range[X][MIN]<=0?fontSize*.5:0);}const yAxisLabelOffset=[-14*1.25-paddingRequiredForTickLabels,-14];const xAxisLabelLocation=[(graphInfo.range[X][MIN]+graphInfo.range[X][MAX])/2,graphInfo.range[Y][MIN]];const yAxisLabelLocation=[graphInfo.range[X][MIN],(graphInfo.range[Y][MIN]+graphInfo.range[Y][MAX])/2];if(isRelativelyCloseToZero){yAxisLabelLocation[X]=yAxisLabelLocation[X]-graphInfo.range[X][MIN];}const xLabel=vec.add(pointToPixel(xAxisLabelLocation,graphInfo),xAxisLabelOffset);const yLabel=vec.add(pointToPixel(yAxisLabelLocation,graphInfo),yAxisLabelOffset);return [xLabel,yLabel]}const xLabelInitial=[graphInfo.range[X][MAX],0];const yLabelInitial=[0,graphInfo.range[Y][MAX]];const yLabelOffset=[0,-14*2];let xLabel=pointToPixel(xLabelInitial,graphInfo);let yLabel=vec.add(pointToPixel(yLabelInitial,graphInfo),yLabelOffset);xLabel=clampLabelPosition(xLabel,graphInfo);yLabel=clampLabelPosition(yLabel,graphInfo);return [xLabel,yLabel]};const getGraphBottomMargin=(xAxisLabelLocationY,graphHeight,hasXAxisLabel,showsAxisLabels)=>{const baseMargin=30;if(!showsAxisLabels||!hasXAxisLabel){return baseMargin}const xLabelBottomOverflow=Math.max(0,xAxisLabelLocationY+fontSize-graphHeight);return Math.max(baseMargin,xLabelBottomOverflow)};const shouldShowLabel=(currentTick,range,tickStep)=>{let showLabel=true;if(range[X][MIN]<-tickStep&&range[X][MAX]>0&&currentTick===-tickStep){showLabel=false;}return showLabel};function generateTickLocations(tickStep,min,max,otherAxisMin){const ticks=[];const decimalSigFigs=countSignificantDecimals(tickStep);const start=Math.max(min,0);const startOffset=otherAxisMin>=0?0:tickStep;for(let i=start+startOffset;i<max;i+=tickStep){ticks.push(parseFloat(i.toFixed(decimalSigFigs)));}let i=Math.min(max,0)-tickStep;for(i;i>min;i-=tickStep){ticks.push(i);}return ticks}const countSignificantDecimals=number=>{const numStr=number.toString();if(!numStr.includes(".")){return 0}return numStr.split(".")[1].length};function divideByAndShowPi(value){const dividedValue=value/Math.PI;switch(dividedValue){case 1:return "π";case -1:return "-π";case 0:return "0";default:return dividedValue+"π"}}
1885
1885
 
1886
1886
  function Hairlines(props){const{point}=props;const{range}=useGraphConfig();const[[xMin,xMax],[yMin,yMax]]=range;const[[x,y]]=useTransformVectorsToPixels(point);const[[verticalStartX]]=useTransformVectorsToPixels([xMin,0]);const[[verticalEndX]]=useTransformVectorsToPixels([xMax,0]);const[[_,horizontalStartY]]=useTransformVectorsToPixels([0,yMin]);const[[__,horizontalEndY]]=useTransformVectorsToPixels([0,yMax]);return jsxRuntimeExports.jsxs("g",{"aria-hidden":true,children:[jsxRuntimeExports.jsx("line",{x1:verticalStartX,y1:y,x2:verticalEndX,y2:y,stroke:semanticColor.core.border.instructive.default}),jsxRuntimeExports.jsx("line",{x1:x,y1:horizontalStartY,x2:x,y2:horizontalEndY,stroke:semanticColor.core.border.instructive.default})]})}
1887
1887
 
1888
- const hitboxSizePx=48;const MovablePointView=forwardRef(function MovablePointViewWithRef(props,hitboxRef){const{markings,showTooltips,interactiveColor,disableKeyboardInteraction,snapStep}=useGraphConfig();const{point,dragging,focused,cursor,showFocusRing,onClick=()=>{}}=props;const wbColorName=disableKeyboardInteraction?"fadedOffBlack64":"blue";const pointClasses=classNames("movable-point",dragging&&"movable-point--dragging",showFocusRing&&"movable-point--focus");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-hitbox",r:hitboxSizePx/2,cx:x,cy:y}),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,backgroundColor:wbColorName,content:pointTooltipContent,contentStyle:{color:"white"},children:svgForPoint}):svgForPoint]})});function classNames(...names){return names.filter(Boolean).join(" ")}
1888
+ const hitboxSizePx$1=48;const MovablePointView=forwardRef(function MovablePointViewWithRef(props,hitboxRef){const{markings,showTooltips,interactiveColor,disableKeyboardInteraction,snapStep}=useGraphConfig();const{point,dragging,focused,cursor,showFocusRing,onClick=()=>{}}=props;const wbColorName=disableKeyboardInteraction?"fadedOffBlack64":"blue";const pointClasses=classNames$1("movable-point",dragging&&"movable-point--dragging",showFocusRing&&"movable-point--focus");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-hitbox",r:hitboxSizePx$1/2,cx:x,cy:y}),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,backgroundColor:wbColorName,content:pointTooltipContent,contentStyle:{color:"white"},children:svgForPoint}):svgForPoint]})});function classNames$1(...names){return names.filter(Boolean).join(" ")}
1889
1889
 
1890
- function useControlPoint(params){const{snapStep,disableKeyboardInteraction}=useGraphConfig();const{point,ariaDescribedBy,ariaLabel,ariaLive="polite",constrain=p=>snap(snapStep,p),cursor,forwardedRef=noop,sequenceNumber=1,onMove=noop,onDragStart=noop,onDragEnd=noop,onClick=noop,onFocus=noop,onBlur=noop}=params;const{strings,locale}=usePerseusI18n();const[focused,setFocused]=useState(false);const focusableHandleRef=useRef(null);useDraggable({gestureTarget:focusableHandleRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const visiblePointRef=useRef(null);const{dragging}=useDraggable({gestureTarget:visiblePointRef,point,onMove,onDragStart,onDragEnd,constrainKeyboardMovement:constrain});const pointAriaLabel=ariaLabel||strings.srPointAtCoordinates({num:sequenceNumber,x:srFormatNumber(point[X],locale),y:srFormatNumber(point[Y],locale)});useLayoutEffect(()=>{setForwardedRef(forwardedRef,focusableHandleRef.current);},[forwardedRef]);useLayoutEffect(()=>{if(dragging&&!focused){focusableHandleRef.current?.focus();}},[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-live":ariaLive,"aria-disabled":disableKeyboardInteraction,onFocus:event=>{onFocus(event);setFocused(true);},onBlur:event=>{onBlur(event);setFocused(false);}});const visiblePoint=jsxRuntimeExports.jsx(MovablePointView,{cursor:cursor,onClick:()=>{onClick();focusableHandleRef.current?.focus();},point:point,dragging:dragging,focused:focused,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=()=>{};
1890
+ function useControlPoint(params){const{snapStep,disableKeyboardInteraction}=useGraphConfig();const{point,ariaDescribedBy,ariaLabel,ariaLive="polite",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 focusableHandleRef=useRef(null);useDraggable({gestureTarget:focusableHandleRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const visiblePointRef=useRef(null);const{dragging}=useDraggable({gestureTarget:visiblePointRef,point,onMove,onDragStart,onDragEnd,constrainKeyboardMovement:constrain});const pointAriaLabel=ariaLabel||strings.srPointAtCoordinates({num:sequenceNumber,x:srFormatNumber(point[X],locale),y:srFormatNumber(point[Y],locale)});useLayoutEffect(()=>{setForwardedRef(forwardedRef,focusableHandleRef.current);},[forwardedRef]);useLayoutEffect(()=>{if(dragging&&!focused){focusableHandleRef.current?.focus();}},[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-live":ariaLive,"aria-disabled":disableKeyboardInteraction,onFocus:event=>{onFocus(event);setFocused(true);},onBlur:event=>{onBlur(event);setFocused(false);}});const visiblePoint=jsxRuntimeExports.jsx(MovablePointView,{cursor:cursor,onClick:()=>{onClick();focusableHandleRef.current?.focus();},point:point,dragging:dragging,focused:focused,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=()=>{};
1891
1891
 
1892
1892
  const MovablePoint$1=React.forwardRef(function MovablePointWithRef(props,pointRef){const{visiblePoint,focusableHandle}=useControlPoint({...props,forwardedRef:pointRef});return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[focusableHandle,visiblePoint]})});
1893
1893
 
@@ -1897,7 +1897,7 @@ function renderAbsoluteValueGraph(state,dispatch,i18n){return {graph:jsxRuntimeE
1897
1897
 
1898
1898
  function pathBuilder(){return new PathBuilder}class PathBuilder{build(){return this.path.map(scaleCommandBy(this.scaleFactor)).map(commandToString).join("")}move(x,y){this.path.push({action:"M",args:[x,y]});return this}line(x,y){this.path.push({action:"L",args:[x,y]});return this}circularArc(radius,toX,toY,{sweep=false,largeArc=false}={}){this.path.push({action:"A",args:[radius,radius,0,largeArc?1:0,sweep?1:0,toX,toY]});return this}curve(control1X,control1Y,control2X,control2Y,endX,endY){this.path.push({action:"C",args:[control1X,control1Y,control2X,control2Y,endX,endY]});return this}scale(factor){this.scaleFactor*=factor;return this}constructor(){this.path=[];this.scaleFactor=1;}}function commandToString(command){return `${command.action}${command.args.join(" ")}`}function scaleCommandBy(scaleFactor){return command=>{switch(command.action){case "A":return {...command,args:[command.args[0]*scaleFactor,command.args[1]*scaleFactor,command.args[2],command.args[3],command.args[4],command.args[5]*scaleFactor,command.args[6]*scaleFactor]};default:return {...command,args:command.args.map(c=>c*scaleFactor)}}}}
1899
1899
 
1900
- const arrowPath=pathBuilder().move(-3,4).curve(-2.75,2.5,0,.25,.75,0).curve(0,-0.25,-2.75,-2.5,-3,-4).scale(1.4).build();function Arrowhead(props){const[point]=useTransformVectorsToPixels(props.tip);return jsxRuntimeExports.jsx("g",{"aria-hidden":true,className:"interactive-graph-arrowhead",transform:`translate(${point[X]} ${point[Y]}) rotate(${props.angle})`,children:jsxRuntimeExports.jsx("g",{transform:"translate(-1.5)",children:jsxRuntimeExports.jsx("path",{d:arrowPath,fill:"none",style:{stroke:props.color??"inherit"},strokeLinejoin:"round",strokeLinecap:"round",strokeWidth:`${props.strokeWidth??2}px`})})})}
1900
+ const arrowPath$1=pathBuilder().move(-3,4).curve(-2.75,2.5,0,.25,.75,0).curve(0,-0.25,-2.75,-2.5,-3,-4).scale(1.4).build();function Arrowhead(props){const[point]=useTransformVectorsToPixels(props.tip);return jsxRuntimeExports.jsx("g",{"aria-hidden":true,className:"interactive-graph-arrowhead",transform:`translate(${point[X]} ${point[Y]}) rotate(${props.angle})`,children:jsxRuntimeExports.jsx("g",{transform:"translate(-1.5)",children:jsxRuntimeExports.jsx("path",{d:arrowPath$1,fill:"none",style:{stroke:props.color??"inherit"},strokeLinejoin:"round",strokeLinecap:"round",strokeWidth:`${props.strokeWidth??2}px`})})})}
1901
1901
 
1902
1902
  function AxisArrows(){const{range:[[xMin,xMax],[yMin,yMax]],showAxisArrows}=useGraphConfig();const axisColor="var(--mafs-fg)";return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[!(yMin>0||yMax<0)&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[showAxisArrows.xMin&&jsxRuntimeExports.jsx(Arrowhead,{color:axisColor,tip:[xMin,0],angle:180}),showAxisArrows.xMax&&jsxRuntimeExports.jsx(Arrowhead,{color:axisColor,tip:[xMax,0],angle:0})]}),!(xMin>0||xMax<0)&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[showAxisArrows.yMin&&jsxRuntimeExports.jsx(Arrowhead,{color:axisColor,tip:[0,yMin],angle:90}),showAxisArrows.yMax&&jsxRuntimeExports.jsx(Arrowhead,{color:axisColor,tip:[0,yMax],angle:270})]})]})}
1903
1903
 
@@ -1921,11 +1921,11 @@ const LockedFunction=props=>{const{range}=useGraphConfig();const[equation,setEqu
1921
1921
 
1922
1922
  function SVGLine(props){const{start,end,style,className,testId}=props;return jsxRuntimeExports.jsx("line",{"aria-hidden":true,x1:start[X],y1:start[Y],x2:end[X],y2:end[Y],style:style,className:className,"data-testid":testId})}
1923
1923
 
1924
- const{calculateAngleInDegrees: calculateAngleInDegrees$3}=angles;function Vector(props){const{interactiveColor}=useGraphConfig();const{tail,tip,color=interactiveColor,strokeWidth=2,style,testId}=props;const[tailPx,tipPx]=useTransformVectorsToPixels(tail,tip);const direction=vec.sub(tipPx,tailPx);const angle=calculateAngleInDegrees$3(direction);return jsxRuntimeExports.jsxs("g",{style:{stroke:color,strokeWidth},"data-testid":testId,children:[jsxRuntimeExports.jsx(SVGLine,{start:tailPx,end:tipPx,style:style}),jsxRuntimeExports.jsx(Arrowhead,{angle:angle,tip:tip,color:color,strokeWidth:strokeWidth})]})}
1924
+ const{calculateAngleInDegrees: calculateAngleInDegrees$4}=angles;function Vector(props){const{interactiveColor}=useGraphConfig();const{tail,tip,color=interactiveColor,strokeWidth=2,style,testId}=props;const[tailPx,tipPx]=useTransformVectorsToPixels(tail,tip);const direction=vec.sub(tipPx,tailPx);const angle=calculateAngleInDegrees$4(direction);return jsxRuntimeExports.jsxs("g",{style:{stroke:color,strokeWidth},"data-testid":testId,children:[jsxRuntimeExports.jsx(SVGLine,{start:tailPx,end:tipPx,style:style}),jsxRuntimeExports.jsx(Arrowhead,{angle:angle,tip:tip,color:color,strokeWidth:strokeWidth})]})}
1925
1925
 
1926
- const getIntersectionOfRayWithBox=(initialPoint,throughPoint,box)=>{const[[xMin,xMax],[yMin,yMax]]=box;const[aX,aY]=initialPoint;const[bX,bY]=throughPoint;const yDiff=bY-aY;const xDiff=bX-aX;const slope=yDiff/xDiff;const inverseSlope=1/slope;const xExtreme=xDiff<0?xMin:xMax;const yExtreme=yDiff<0?yMin:yMax;const yAtXExtreme=aY+(xExtreme-aX)*slope;const xAtYExtreme=aX+(yExtreme-aY)*inverseSlope;switch(true){case isBetween(yAtXExtreme,yMin,yMax):return [xExtreme,yAtXExtreme];case isBetween(xAtYExtreme,xMin,xMax):return [xAtYExtreme,yExtreme];default:return [xExtreme,yExtreme]}};function isBetween(x,low,high){return x>=low&&x<=high}function getArrayWithoutDuplicates(array){const returnArray=[];for(const coordPair of array){if(!returnArray.some(([x,y])=>x===coordPair[0]&&y===coordPair[1])){returnArray.push(coordPair);}}return returnArray}function getSlopeStringForLine(line,strings){const slope=(line[1][1]-line[0][1])/(line[1][0]-line[0][0]);if(!Number.isFinite(slope)){return strings.srLinearGraphSlopeVertical}if(slope===0){return strings.srLinearGraphSlopeHorizontal}return slope>0?strings.srLinearGraphSlopeIncreasing:strings.srLinearGraphSlopeDecreasing}function getInterceptStringForLine(line,strings,locale){const slope=(line[1][1]-line[0][1])/(line[1][0]-line[0][0]);const xIntercept=(0-line[0][1])/slope+line[0][0];const yIntercept=line[0][1]-slope*line[0][0];const overlapsXAxis=line[0][1]===0&&line[1][1]===0;const overlapsYAxis=line[0][0]===0&&line[1][0]===0;const hasXIntercept=Number.isFinite(xIntercept)&&!overlapsXAxis;const hasYIntercept=Number.isFinite(yIntercept)&&!overlapsYAxis;if(hasXIntercept&&hasYIntercept){return xIntercept===0&&yIntercept===0?strings.srLinearGraphOriginIntercept:strings.srLinearGraphBothIntercepts({xIntercept:srFormatNumber(xIntercept,locale),yIntercept:srFormatNumber(yIntercept,locale)})}return hasXIntercept?strings.srLinearGraphXOnlyIntercept({xIntercept:srFormatNumber(xIntercept,locale)}):strings.srLinearGraphYOnlyIntercept({yIntercept:srFormatNumber(yIntercept,locale)})}function getCoordQuadrant(coord){const[unroundedX,unroundedY]=coord;const x=Number(unroundedX.toFixed(3));const y=Number(unroundedY.toFixed(3));if(x===0&&y===0){return "origin"}if(y===0){return "x-axis"}if(x===0){return "y-axis"}if(x>0&&y>0){return 1}if(x<0&&y>0){return 2}if(x<0&&y<0){return 3}return 4}function getQuadraticVertexString(vertex,strings){const location=getCoordQuadrant(vertex);switch(location){case "origin":return strings.srQuadraticGraphVertexOrigin;case "x-axis":return strings.srQuadraticGraphVertexXAxis;case "y-axis":return strings.srQuadraticGraphVertexYAxis;default:return strings.srQuadraticGraphVertexQuadrant({quadrant:location})}}function getQuadraticPointString(pointNumber,coord,strings,locale){const location=getCoordQuadrant(coord);const[x,y]=coord;switch(location){case "origin":return strings.srQuadraticPointOrigin({pointNumber:pointNumber});case "x-axis":case "y-axis":return strings.srQuadraticPointAxis({pointNumber:pointNumber,x:srFormatNumber(x,locale),y:srFormatNumber(y,locale)});default:return strings.srQuadraticPointQuadrant({pointNumber:pointNumber,quadrant:location,x:srFormatNumber(x,locale),y:srFormatNumber(y,locale)})}}function getQuadraticXIntercepts(a,b,c){if(a===0){if(b===0){return []}return [-c/b]}const discriminant=b*b-4*a*c;if(discriminant<0){return []}const x1=(-b+Math.sqrt(discriminant))/(2*a);const x2=(-b-Math.sqrt(discriminant))/(2*a);if(x1===x2){return [x1]}return [x1,x2]}function getAngleFromPoints(points,i){if(i<0||i>=points.length||!Number.isInteger(i)){return null}if(points.length<3){return null}const point=points.at(i);const pt1=points.at(i-1);const pt2=points[(i+1)%points.length];if(!point||!pt1||!pt2){return null}const a=vec.dist(point,pt1);const b=vec.dist(point,pt2);const c=vec.dist(pt1,pt2);let lawOfCosinesRadicand=(a**2+b**2-c**2)/(2*a*b);if(lawOfCosinesRadicand<-1||lawOfCosinesRadicand>1){lawOfCosinesRadicand=Math.round(lawOfCosinesRadicand);}const angle=Math.acos(lawOfCosinesRadicand);return angle}function getSideLengthsFromPoints(points,i,isPolygonOpen){if(i<0||i>=points.length||!Number.isInteger(i)){return []}if(points.length<2){return []}const returnArray=[];const point=points[i];const point1Index=i===0?points.length-1:i-1;const point1=points[point1Index];const side1=i!==point1Index?vec.dist(point,point1):null;if(side1&&!(isPolygonOpen&&i===0)){returnArray.push({pointIndex:point1Index,sideLength:side1});}const point2Index=(i+1)%points.length;const point2=points[point2Index];const side2=i!==point2Index&&point2Index!==point1Index?vec.dist(point,point2):null;if(side2&&!(isPolygonOpen&&i===points.length-1)){returnArray.push({pointIndex:point2Index,sideLength:side2});}return returnArray}function getPolygonSideString(sideLength,pointIndex,strings,locale){return Number.isInteger(sideLength)?strings.srPolygonSideLength({pointNum:pointIndex+1,length:`${sideLength}`}):strings.srPolygonSideLengthApprox({pointNum:pointIndex+1,length:srFormatNumber(sideLength,locale,1)})}function calculateScaledRadius(range){const[[xMin,xMax],[yMin,yMax]]=range;const xSpan=xMax-xMin;const ySpan=yMax-yMin;const minSpan=Math.min(xSpan,ySpan);return minSpan*.06}function getAsymptoteGraphKeyboardConstraint(coords,snapStep,pointIndex,isValidPosition){const coordToBeMoved=coords[pointIndex];const movePointWithConstraint=moveFunc=>{let movedCoord=moveFunc(coordToBeMoved);for(let i=0;i<3&&!isValidPosition(movedCoord);i++){movedCoord=moveFunc(movedCoord);}if(!isValidPosition(movedCoord)){return coordToBeMoved}return movedCoord};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,snapStep[1]])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,snapStep[1]])),left:movePointWithConstraint(coord=>vec.sub(coord,[snapStep[0],0])),right:movePointWithConstraint(coord=>vec.add(coord,[snapStep[0],0]))}}function constrainAsymptoteKeyboardMovement(p,coords,snapStep,orientation){const snapped=snap(snapStep,p);const axis=orientation==="horizontal"?1:0;const otherAxis=orientation==="horizontal"?0:1;let newVal=snapped[axis];const step=snapStep[axis];const maxVal=Math.max(coords[0][axis],coords[1][axis]);const minVal=Math.min(coords[0][axis],coords[1][axis]);const allGreater=coords[0][axis]>newVal&&coords[1][axis]>newVal;const allLesser=coords[0][axis]<newVal&&coords[1][axis]<newVal;if(!allGreater&&!allLesser){const midpoint=(maxVal+minVal)/2;if(newVal>=midpoint){newVal=maxVal+step;}else {newVal=minVal-step;}}const result=[0,0];result[axis]=newVal;result[otherAxis]=snapped[otherAxis];return result}
1926
+ const getIntersectionOfRayWithBox=(initialPoint,throughPoint,box)=>{const[[xMin,xMax],[yMin,yMax]]=box;const[aX,aY]=initialPoint;const[bX,bY]=throughPoint;const yDiff=bY-aY;const xDiff=bX-aX;const slope=yDiff/xDiff;const inverseSlope=1/slope;const xExtreme=xDiff<0?xMin:xMax;const yExtreme=yDiff<0?yMin:yMax;const yAtXExtreme=aY+(xExtreme-aX)*slope;const xAtYExtreme=aX+(yExtreme-aY)*inverseSlope;switch(true){case isBetween(yAtXExtreme,yMin,yMax):return [xExtreme,yAtXExtreme];case isBetween(xAtYExtreme,xMin,xMax):return [xAtYExtreme,yExtreme];default:return [xExtreme,yExtreme]}};function isBetween(x,low,high){return x>=low&&x<=high}function getArrayWithoutDuplicates(array){const returnArray=[];for(const coordPair of array){if(!returnArray.some(([x,y])=>x===coordPair[0]&&y===coordPair[1])){returnArray.push(coordPair);}}return returnArray}function getSlopeStringForLine(line,strings){const slope=(line[1][1]-line[0][1])/(line[1][0]-line[0][0]);if(!Number.isFinite(slope)){return strings.srLinearGraphSlopeVertical}if(slope===0){return strings.srLinearGraphSlopeHorizontal}return slope>0?strings.srLinearGraphSlopeIncreasing:strings.srLinearGraphSlopeDecreasing}function getInterceptStringForLine(line,strings,locale){const slope=(line[1][1]-line[0][1])/(line[1][0]-line[0][0]);const xIntercept=(0-line[0][1])/slope+line[0][0];const yIntercept=line[0][1]-slope*line[0][0];const overlapsXAxis=line[0][1]===0&&line[1][1]===0;const overlapsYAxis=line[0][0]===0&&line[1][0]===0;const hasXIntercept=Number.isFinite(xIntercept)&&!overlapsXAxis;const hasYIntercept=Number.isFinite(yIntercept)&&!overlapsYAxis;if(hasXIntercept&&hasYIntercept){return xIntercept===0&&yIntercept===0?strings.srLinearGraphOriginIntercept:strings.srLinearGraphBothIntercepts({xIntercept:srFormatNumber(xIntercept,locale),yIntercept:srFormatNumber(yIntercept,locale)})}return hasXIntercept?strings.srLinearGraphXOnlyIntercept({xIntercept:srFormatNumber(xIntercept,locale)}):strings.srLinearGraphYOnlyIntercept({yIntercept:srFormatNumber(yIntercept,locale)})}function getCoordQuadrant(coord){const[unroundedX,unroundedY]=coord;const x=Number(unroundedX.toFixed(3));const y=Number(unroundedY.toFixed(3));if(x===0&&y===0){return "origin"}if(y===0){return "x-axis"}if(x===0){return "y-axis"}if(x>0&&y>0){return 1}if(x<0&&y>0){return 2}if(x<0&&y<0){return 3}return 4}function getQuadraticVertexString(vertex,strings){const location=getCoordQuadrant(vertex);switch(location){case "origin":return strings.srQuadraticGraphVertexOrigin;case "x-axis":return strings.srQuadraticGraphVertexXAxis;case "y-axis":return strings.srQuadraticGraphVertexYAxis;default:return strings.srQuadraticGraphVertexQuadrant({quadrant:location})}}function getQuadraticPointString(pointNumber,coord,strings,locale){const location=getCoordQuadrant(coord);const[x,y]=coord;switch(location){case "origin":return strings.srQuadraticPointOrigin({pointNumber:pointNumber});case "x-axis":case "y-axis":return strings.srQuadraticPointAxis({pointNumber:pointNumber,x:srFormatNumber(x,locale),y:srFormatNumber(y,locale)});default:return strings.srQuadraticPointQuadrant({pointNumber:pointNumber,quadrant:location,x:srFormatNumber(x,locale),y:srFormatNumber(y,locale)})}}function getQuadraticXIntercepts(a,b,c){if(a===0){if(b===0){return []}return [-c/b]}const discriminant=b*b-4*a*c;if(discriminant<0){return []}const x1=(-b+Math.sqrt(discriminant))/(2*a);const x2=(-b-Math.sqrt(discriminant))/(2*a);if(x1===x2){return [x1]}return [x1,x2]}function getAngleFromPoints(points,i){if(i<0||i>=points.length||!Number.isInteger(i)){return null}if(points.length<3){return null}const point=points.at(i);const pt1=points.at(i-1);const pt2=points[(i+1)%points.length];if(!point||!pt1||!pt2){return null}const a=vec.dist(point,pt1);const b=vec.dist(point,pt2);const c=vec.dist(pt1,pt2);let lawOfCosinesRadicand=(a**2+b**2-c**2)/(2*a*b);if(lawOfCosinesRadicand<-1||lawOfCosinesRadicand>1){lawOfCosinesRadicand=Math.round(lawOfCosinesRadicand);}const angle=Math.acos(lawOfCosinesRadicand);return angle}function getSideLengthsFromPoints(points,i,isPolygonOpen){if(i<0||i>=points.length||!Number.isInteger(i)){return []}if(points.length<2){return []}const returnArray=[];const point=points[i];const point1Index=i===0?points.length-1:i-1;const point1=points[point1Index];const side1=i!==point1Index?vec.dist(point,point1):null;if(side1&&!(isPolygonOpen&&i===0)){returnArray.push({pointIndex:point1Index,sideLength:side1});}const point2Index=(i+1)%points.length;const point2=points[point2Index];const side2=i!==point2Index&&point2Index!==point1Index?vec.dist(point,point2):null;if(side2&&!(isPolygonOpen&&i===points.length-1)){returnArray.push({pointIndex:point2Index,sideLength:side2});}return returnArray}function getPolygonSideString(sideLength,pointIndex,strings,locale){return Number.isInteger(sideLength)?strings.srPolygonSideLength({pointNum:pointIndex+1,length:`${sideLength}`}):strings.srPolygonSideLengthApprox({pointNum:pointIndex+1,length:srFormatNumber(sideLength,locale,1)})}function calculateScaledRadius(range){const[[xMin,xMax],[yMin,yMax]]=range;const xSpan=xMax-xMin;const ySpan=yMax-yMin;const minSpan=Math.min(xSpan,ySpan);return minSpan*.06}function getAsymptoteGraphKeyboardConstraint(coords,snapStep,pointIndex,isValidPosition){const coordToBeMoved=coords[pointIndex];const movePointWithConstraint=moveFunc=>{let movedCoord=moveFunc(coordToBeMoved);for(let i=0;i<3&&!isValidPosition(movedCoord);i++){movedCoord=moveFunc(movedCoord);}if(!isValidPosition(movedCoord)){return coordToBeMoved}return movedCoord};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,snapStep[1]])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,snapStep[1]])),left:movePointWithConstraint(coord=>vec.sub(coord,[snapStep[0],0])),right:movePointWithConstraint(coord=>vec.add(coord,[snapStep[0],0]))}}function getAsymptoteHandleCoord(orientation,range,asymptote){if(orientation==="horizontal"){const midX=(range[0][0]+range[0][1])/2;return [midX,asymptote]}const midY=(range[1][0]+range[1][1])/2;return [asymptote,midY]}function skipAsymptoteKeyboardOverPoint(proposed,currentAsymptote,coords,handleCoord,snapStep,orientation){const snapped=snap(snapStep,proposed);const moveAxis=orientation==="horizontal"?1:0;const pinAxis=orientation==="horizontal"?0:1;const step=snapStep[moveAxis];const dir=Math.sign(snapped[moveAxis]-currentAsymptote);if(dir===0){return snapped}const pinValue=handleCoord[pinAxis];const isForbidden=v=>coords.some(c=>c[pinAxis]===pinValue&&c[moveAxis]===v);let value=snapped[moveAxis];for(let i=0;i<3&&isForbidden(value);i++){value+=dir*step;}const result=[0,0];result[moveAxis]=value;result[pinAxis]=snapped[pinAxis];return result}
1927
1927
 
1928
- const{calculateAngleInDegrees: calculateAngleInDegrees$2}=angles;const LockedLine=props=>{const{color,lineStyle,kind,points,showPoint1,showPoint2,weight,ariaLabel,range}=props;const[point1,point2]=points;const hasAria=!!ariaLabel;let line;if(kind==="ray"){const extendedPoint=getIntersectionOfRayWithBox(point1.coord,point2.coord,range);line=jsxRuntimeExports.jsx(Vector,{tail:point1.coord,tip:extendedPoint,color:lockedFigureColors[color],strokeWidth:strokeWeights[weight],style:{strokeDasharray:lineStyle==="dashed"?"var(--mafs-line-stroke-dash-style)":undefined}});}else {const LineType=kind==="segment"?Line$4.Segment:Line$4.ThroughPoints;let arrowTip=kind==="segment"?point2.coord:getIntersectionOfRayWithBox(point1.coord,point2.coord,range);const[tailPx,tipPx]=useTransformVectorsToPixels(point2.coord,point1.coord);const direction=vec.sub(tailPx,tipPx);let angle=calculateAngleInDegrees$2(direction);const startArrowHead=kind!=="segment"&&jsxRuntimeExports.jsx(Arrowhead,{angle:angle,tip:arrowTip,color:lockedFigureColors[color],strokeWidth:strokeWeights[weight]});arrowTip=kind==="segment"?point1.coord:getIntersectionOfRayWithBox(point2.coord,point1.coord,range);angle=angle>180?angle-180:angle+180;const endArrowHead=kind!=="segment"&&jsxRuntimeExports.jsx(Arrowhead,{angle:angle,tip:arrowTip,color:lockedFigureColors[color],strokeWidth:strokeWeights[weight]});line=jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[startArrowHead,jsxRuntimeExports.jsx(LineType,{point1:point1.coord,point2:point2.coord,color:lockedFigureColors[color],weight:strokeWeights[weight],style:lineStyle}),endArrowHead]});}return jsxRuntimeExports.jsxs("g",{className:kind==="ray"?"locked-ray":"locked-line","aria-label":hasAria?ariaLabel:undefined,"aria-hidden":!hasAria,role:"img",children:[line,showPoint1&&jsxRuntimeExports.jsx(Point$2,{x:point1.coord[X],y:point1.coord[Y],svgCircleProps:{style:{fill:point1.filled?lockedFigureColors[point1.color]:semanticColor.core.background.base.default,stroke:lockedFigureColors[point1.color],strokeWidth:spacing.xxxxSmall_2}}}),showPoint2&&jsxRuntimeExports.jsx(Point$2,{x:point2.coord[X],y:point2.coord[Y],svgCircleProps:{style:{fill:point2.filled?lockedFigureColors[point2.color]:semanticColor.core.background.base.default,stroke:lockedFigureColors[point2.color],strokeWidth:spacing.xxxxSmall_2}}})]})};
1928
+ const{calculateAngleInDegrees: calculateAngleInDegrees$3}=angles;const LockedLine=props=>{const{color,lineStyle,kind,points,showPoint1,showPoint2,weight,ariaLabel,range}=props;const[point1,point2]=points;const hasAria=!!ariaLabel;let line;if(kind==="ray"){const extendedPoint=getIntersectionOfRayWithBox(point1.coord,point2.coord,range);line=jsxRuntimeExports.jsx(Vector,{tail:point1.coord,tip:extendedPoint,color:lockedFigureColors[color],strokeWidth:strokeWeights[weight],style:{strokeDasharray:lineStyle==="dashed"?"var(--mafs-line-stroke-dash-style)":undefined}});}else {const LineType=kind==="segment"?Line$4.Segment:Line$4.ThroughPoints;let arrowTip=kind==="segment"?point2.coord:getIntersectionOfRayWithBox(point1.coord,point2.coord,range);const[tailPx,tipPx]=useTransformVectorsToPixels(point2.coord,point1.coord);const direction=vec.sub(tailPx,tipPx);let angle=calculateAngleInDegrees$3(direction);const startArrowHead=kind!=="segment"&&jsxRuntimeExports.jsx(Arrowhead,{angle:angle,tip:arrowTip,color:lockedFigureColors[color],strokeWidth:strokeWeights[weight]});arrowTip=kind==="segment"?point1.coord:getIntersectionOfRayWithBox(point2.coord,point1.coord,range);angle=angle>180?angle-180:angle+180;const endArrowHead=kind!=="segment"&&jsxRuntimeExports.jsx(Arrowhead,{angle:angle,tip:arrowTip,color:lockedFigureColors[color],strokeWidth:strokeWeights[weight]});line=jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[startArrowHead,jsxRuntimeExports.jsx(LineType,{point1:point1.coord,point2:point2.coord,color:lockedFigureColors[color],weight:strokeWeights[weight],style:lineStyle}),endArrowHead]});}return jsxRuntimeExports.jsxs("g",{className:kind==="ray"?"locked-ray":"locked-line","aria-label":hasAria?ariaLabel:undefined,"aria-hidden":!hasAria,role:"img",children:[line,showPoint1&&jsxRuntimeExports.jsx(Point$2,{x:point1.coord[X],y:point1.coord[Y],svgCircleProps:{style:{fill:point1.filled?lockedFigureColors[point1.color]:semanticColor.core.background.base.default,stroke:lockedFigureColors[point1.color],strokeWidth:spacing.xxxxSmall_2}}}),showPoint2&&jsxRuntimeExports.jsx(Point$2,{x:point2.coord[X],y:point2.coord[Y],svgCircleProps:{style:{fill:point2.filled?lockedFigureColors[point2.color]:semanticColor.core.background.base.default,stroke:lockedFigureColors[point2.color],strokeWidth:spacing.xxxxSmall_2}}})]})};
1929
1929
 
1930
1930
  const LockedPoint=props=>{const{color,coord,filled,ariaLabel}=props;const[x,y]=coord;const hasAria=!!ariaLabel;return jsxRuntimeExports.jsx("g",{className:"locked-point","aria-label":hasAria?ariaLabel:undefined,"aria-hidden":!hasAria,role:"img",children:jsxRuntimeExports.jsx(Point$2,{x:x,y:y,svgCircleProps:{style:{fill:filled?lockedFigureColors[color]:semanticColor.core.background.base.default,stroke:lockedFigureColors[color],strokeWidth:spacing.xxxxSmall_2}}})})};
1931
1931
 
@@ -1937,57 +1937,63 @@ const GraphLockedLayer=props=>{const{lockedFigures}=props;return jsxRuntimeExpor
1937
1937
 
1938
1938
  const MafsCssTransformWrapper=({children})=>jsxRuntimeExports.jsx("g",{style:{transform:`var(--mafs-view-transform) var(--mafs-user-transform)`},children:children});
1939
1939
 
1940
- const TextLabel=({children,...rest})=>jsxRuntimeExports.jsx(Text$2,{size:16,svgTextProps:{filter:"url(#background)",fontWeight:"bold"},...rest,children:children});const SvgDefs=()=>jsxRuntimeExports.jsx("defs",{children:jsxRuntimeExports.jsxs("filter",{id:"background",x:"-5%",width:"110%",y:"0%",height:"100%",children:[jsxRuntimeExports.jsx("feFlood",{floodColor:"#FFF",floodOpacity:"0.64"}),jsxRuntimeExports.jsx("feComposite",{operator:"over",in:"SourceGraphic"})]})});
1940
+ const TextLabel=({children,...rest})=>jsxRuntimeExports.jsx(Text$1,{size:16,svgTextProps:{filter:"url(#background)",fontWeight:"bold"},...rest,children:children});const SvgDefs=()=>jsxRuntimeExports.jsx("defs",{children:jsxRuntimeExports.jsxs("filter",{id:"background",x:"-5%",width:"110%",y:"0%",height:"100%",children:[jsxRuntimeExports.jsx("feFlood",{floodColor:"#FFF",floodOpacity:"0.64"}),jsxRuntimeExports.jsx("feComposite",{operator:"over",in:"SourceGraphic"})]})});
1941
1941
 
1942
1942
  const{clockwise: clockwise$1}=geometry;const{getAngleFromVertex: getAngleFromVertex$1}=angles;const PolygonAngle=({centerPoint,endPoints,showAngles,snapTo,areEndPointsClockwise})=>{const{range}=useGraphConfig();const[centerX,centerY]=centerPoint;const[[startX,startY],[endX,endY]]=areEndPointsClockwise?endPoints:endPoints.reverse();const radius=calculateScaledRadius(range);const a=vec.dist(centerPoint,endPoints[0]);const b=vec.dist(centerPoint,endPoints[1]);const c=vec.dist(endPoints[0],endPoints[1]);let lawOfCosinesRadicand=(a**2+b**2-c**2)/(2*a*b);if(lawOfCosinesRadicand<-1||lawOfCosinesRadicand>1){lawOfCosinesRadicand=Math.round(lawOfCosinesRadicand);}const angle=Math.acos(lawOfCosinesRadicand);const y1=centerY+(startY-centerY)/a*radius;const x2=centerX+(endX-centerX)/b*radius;const x1=centerX+(startX-centerX)/a*radius;const y2=centerY+(endY-centerY)/b*radius;const[x3,y3]=vec.add(centerPoint,vec.add(vec.sub([x1,y1],centerPoint),vec.sub([x2,y2],centerPoint)));if(!showAngles){return isRightPolygonAngle(angle)?jsxRuntimeExports.jsx(RightAngleSquare,{start:[x1,y1],vertex:[x2,y2],end:[x3,y3],nonScalingStroke:true}):null}const isConcave=isConcavePolygonVertex(centerPoint,endPoints);const largeArcFlag=isConcave?1:0;const arc=`M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2}`;let angleInDegrees=angle*(180/Math.PI);if(isConcave){angleInDegrees=360-angleInDegrees;}const angleLabelNumber=parseFloat(angleInDegrees.toFixed(snapTo==="angles"?0:1));const angleLabel=Number.isInteger(angleLabelNumber)?angleLabelNumber:"≈ "+angleLabelNumber;return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx("defs",{children:jsxRuntimeExports.jsxs("filter",{id:"background",x:"-5%",width:"110%",y:"0%",height:"100%",children:[jsxRuntimeExports.jsx("feFlood",{floodColor:"#FFF",floodOpacity:"0.5"}),jsxRuntimeExports.jsx("feComposite",{operator:"over",in:"SourceGraphic"})]})}),!isConcave&&isRightPolygonAngle(angle)?jsxRuntimeExports.jsx(RightAngleSquare,{start:[x1,y1],vertex:[x2,y2],end:[x3,y3],nonScalingStroke:true}):jsxRuntimeExports.jsx(Arc,{arc:arc,nonScalingStroke:true}),jsxRuntimeExports.jsxs(TextLabel,{x:x3,y:y3,attach:y3-centerY>0?"s":"n",attachDistance:Math.abs(y3-centerY)<.2||vec.dist([x3,y3],centerPoint)<.3?20:10,children:[angleLabel,"°"]})]})};const Angle=({vertex,coords,showAngles,allowReflexAngles,range})=>{const areClockwise=clockwise$1([...coords,vertex]);const shouldReverseCoords=areClockwise&&!allowReflexAngles;const clockwiseCoords=shouldReverseCoords?coords:coords.reverse();const startAngle=getAngleFromVertex$1(clockwiseCoords[0],vertex);const endAngle=getAngleFromVertex$1(clockwiseCoords[1],vertex);const angle=(startAngle+360-endAngle)%360;const isReflexive=angle>180;const[centerX,centerY]=vertex;const[point1,point2]=clockwiseCoords;const[startX,startY]=point1;const[endX,endY]=point2;const a=vec.dist(vertex,point1);const b=vec.dist(vertex,point2);const radius=2;const y1=centerY+(startY-centerY)/a*radius;const x2=centerX+(endX-centerX)/b*radius;const x1=centerX+(startX-centerX)/a*radius;const y2=centerY+(endY-centerY)/b*radius;const[x3,y3]=vec.add(vertex,vec.add(vec.sub([x1,y1],vertex),vec.sub([x2,y2],vertex)));const isOutside=shouldDrawArcOutside([x3,y3],vertex,range,[[vertex,point1],[vertex,point2]]);const largeArcFlag=isOutside||isReflexive?1:0;const sweepFlag=isOutside&&isReflexive?1:0;const arc=`M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;const angleLabel=parseFloat(angle.toFixed(0));const[textX,textY]=calculateBisectorPoint(point1,point2,vertex,isReflexive,allowReflexAngles,radius);const{disableKeyboardInteraction}=useGraphConfig();const arcClassName=disableKeyboardInteraction?"angle-arc-static":"angle-arc-interactive";return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx("defs",{children:jsxRuntimeExports.jsxs("filter",{id:"background",x:"-5%",width:"110%",y:"0%",height:"100%",children:[jsxRuntimeExports.jsx("feFlood",{floodColor:"#FFF",floodOpacity:"0.5"}),jsxRuntimeExports.jsx("feComposite",{operator:"over",in:"SourceGraphic"})]})}),isRightAngle(angle)?jsxRuntimeExports.jsx(RightAngleSquare,{start:[x1,y1],vertex:[x2,y2],end:[x3,y3],className:arcClassName}):jsxRuntimeExports.jsx(Arc,{arc:arc,className:arcClassName}),showAngles&&jsxRuntimeExports.jsxs(TextLabel,{x:textX,y:textY,color:semanticColor.core.foreground.instructive.default,children:[angleLabel,"°"]})]})};const RightAngleSquare=({start:[x1,y1],vertex:[x2,y2],end:[x3,y3],className,nonScalingStroke})=>jsxRuntimeExports.jsx(MafsCssTransformWrapper,{children:jsxRuntimeExports.jsx("path",{"aria-hidden":true,d:`M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,strokeWidth:1,fill:"none",className:className,"data-testid":"angle-indicators__right-angle",vectorEffect:nonScalingStroke?"non-scaling-stroke":"none"})});const Arc=({arc,className,nonScalingStroke})=>{return jsxRuntimeExports.jsx(MafsCssTransformWrapper,{children:jsxRuntimeExports.jsx("path",{"aria-hidden":true,d:arc,strokeWidth:1,fill:"none",className:className,"data-testid":"angle-indicators__arc",vectorEffect:nonScalingStroke?"non-scaling-stroke":"none"})})};const isRightPolygonAngle=angle=>Math.abs(angle-Math.PI/2)<.01;const isRightAngle=angle=>Math.round(angle)===90;const shouldDrawArcOutside=(midpoint,vertex,range,polygonLines)=>{const rangeIntersectionPoint=getIntersectionOfRayWithBox(midpoint,vertex,range);let lineIntersectionCount=0;polygonLines.forEach(line=>segmentsIntersect([vertex,rangeIntersectionPoint],line)&&lineIntersectionCount++);return !isEven(lineIntersectionCount)};const isEven=n=>n%2===0;function isConcavePolygonVertex(centerPoint,clockwiseEndpoints){const v1=vec.sub(clockwiseEndpoints[1],centerPoint);const v2=vec.sub(clockwiseEndpoints[0],centerPoint);const crossProduct=v1[0]*v2[1]-v1[1]*v2[0];return crossProduct>0}function calculateBisectorPoint(point1,point2,vertex,isReflex,allowReflex,arcRadius){const[originX,originY]=vertex;const[x1,y1]=point1;const[x2,y2]=point2;const vectorA=[x1-originX,y1-originY];const vectorB=[x2-originX,y2-originY];const angleA=Math.atan2(vectorA[1],vectorA[0]);const angleB=Math.atan2(vectorB[1],vectorB[0]);let averageAngle=(angleA+angleB)/2;const angleRadians=Math.abs(angleA-angleB);if(allowReflex){if(angleRadians<=Math.PI&&isReflex||angleB>angleA){averageAngle+=Math.PI;}}else {if(angleRadians>Math.PI){averageAngle-=Math.PI;}}const sum=[Math.cos(averageAngle),Math.sin(averageAngle)];const sumMagnitude=Math.sqrt(sum[0]**2+sum[1]**2);const bisectorDirection=[sum[0]/sumMagnitude,sum[1]/sumMagnitude];const initialDistance=Math.sqrt(bisectorDirection[0]**2+bisectorDirection[1]**2);const requiredDistance=arcRadius*1.75;let radius=requiredDistance/initialDistance;if(initialDistance>=requiredDistance){radius=1;}const bisectorPoint=[bisectorDirection[0]*radius,bisectorDirection[1]*radius];const scaledBisector=vec.add(bisectorPoint,vertex);return scaledBisector}
1943
1943
 
1944
- const MovableLine=props=>{const{points:[start,end],ariaLabels,ariaDescribedBy,extend,onMoveLine=()=>{},onMovePoint=()=>{}}=props;const{snapStep}=useGraphConfig();const[ariaLives,setAriaLives]=React.useState(["off","off","off"]);const{visiblePoint:visiblePoint1,focusableHandle:focusableHandle1}=useControlPoint({ariaLabel:ariaLabels?.point1AriaLabel,ariaDescribedBy:ariaDescribedBy,ariaLive:ariaLives[0],point:start,sequenceNumber:1,onMove:p=>{setAriaLives(["polite","off","off"]);onMovePoint(0,p);},constrain:getMovableLineKeyboardConstraint([start,end],snapStep,0)});const{visiblePoint:visiblePoint2,focusableHandle:focusableHandle2}=useControlPoint({ariaLabel:ariaLabels?.point2AriaLabel,ariaDescribedBy:ariaDescribedBy,ariaLive:ariaLives[1],point:end,sequenceNumber:2,onMove:p=>{setAriaLives(["off","polite","off"]);onMovePoint(1,p);},constrain:getMovableLineKeyboardConstraint([start,end],snapStep,1)});const line=jsxRuntimeExports.jsx(Line$1,{ariaLabel:ariaLabels?.grabHandleAriaLabel,ariaDescribedBy:ariaDescribedBy,ariaLive:ariaLives[2],start:start,end:end,extend:extend,onMove:delta=>{setAriaLives(["off","off","polite"]);onMoveLine(delta);}});return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[focusableHandle1,line,focusableHandle2,visiblePoint1,visiblePoint2]})};const Line$1=props=>{const{start,end,ariaLabel,ariaDescribedBy,ariaLive,extend,onMove}=props;const[startPtPx,endPtPx]=useTransformVectorsToPixels(start,end);const{range,graphDimensionsInPixels,snapStep,disableKeyboardInteraction,interactiveColor}=useGraphConfig();let startExtend=undefined;let endExtend=undefined;if(extend){const trimmedRange=trimRange(range,graphDimensionsInPixels);startExtend=extend.start?getIntersectionOfRayWithBox(end,start,trimmedRange):undefined;endExtend=extend.end?getIntersectionOfRayWithBox(start,end,trimmedRange):undefined;}const line=useRef(null);const{dragging}=useDraggable({gestureTarget:line,point:start,onMove:newPoint=>{onMove(vec.sub(newPoint,start));},constrainKeyboardMovement:p=>snap(snapStep,p)});return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsxs("g",{ref:line,tabIndex:disableKeyboardInteraction?-1:0,"aria-label":ariaLabel,"aria-describedby":ariaDescribedBy,"aria-live":ariaLive,"aria-disabled":disableKeyboardInteraction,className:"movable-line","data-testid":"movable-line",style:{cursor:dragging?"grabbing":"grab"},role:"button",children:[jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,style:{stroke:"transparent",strokeWidth:TARGET_SIZE}}),jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,className:"movable-line-focus-outline",style:{}}),jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,className:"movable-line-focus-outline-gap",style:{}}),jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,style:{stroke:interactiveColor,strokeWidth:"var(--movable-line-stroke-weight)"},className:dragging?"movable-dragging":"",testId:"movable-line__line"})]}),startExtend&&jsxRuntimeExports.jsx(Vector,{tail:start,tip:startExtend,testId:"movable-line__vector"}),endExtend&&jsxRuntimeExports.jsx(Vector,{tail:end,tip:endExtend,testId:"movable-line__vector"})]})};const getMovableLineKeyboardConstraint=(line,snapStep,pointIndex)=>{const coordToBeMoved=line[pointIndex];const otherPoint=line[1-pointIndex];const movePointWithConstraint=moveFunc=>{let movedCoord=moveFunc(coordToBeMoved);if(vec.dist(movedCoord,otherPoint)===0){movedCoord=moveFunc(movedCoord);}return movedCoord};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,snapStep[1]])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,snapStep[1]])),left:movePointWithConstraint(coord=>vec.sub(coord,[snapStep[0],0])),right:movePointWithConstraint(coord=>vec.add(coord,[snapStep[0],0]))}};function trimRange(range,graphDimensionsInPixels){const pixelsToTrim=4;const[xRange,yRange]=range;const[pixelsWide,pixelsTall]=graphDimensionsInPixels;const graphUnitsPerPixelX=size(xRange)/pixelsWide;const graphUnitsPerPixelY=size(yRange)/pixelsTall;const graphUnitsToTrimX=pixelsToTrim*graphUnitsPerPixelX;const graphUnitsToTrimY=pixelsToTrim*graphUnitsPerPixelY;return inset([graphUnitsToTrimX,graphUnitsToTrimY],range)}
1944
+ const MovableLine=props=>{const{points:[start,end],ariaLabels,ariaDescribedBy,extend,onMoveLine=()=>{},onMovePoint=()=>{}}=props;const{snapStep}=useGraphConfig();const[ariaLives,setAriaLives]=React.useState(["off","off","off"]);const{visiblePoint:visiblePoint1,focusableHandle:focusableHandle1}=useControlPoint({ariaLabel:ariaLabels?.point1AriaLabel,ariaDescribedBy:ariaDescribedBy,ariaLive:ariaLives[0],point:start,sequenceNumber:1,onMove:p=>{setAriaLives(["polite","off","off"]);onMovePoint(0,p);},constrain:getMovableLineKeyboardConstraint([start,end],snapStep,0)});const{visiblePoint:visiblePoint2,focusableHandle:focusableHandle2}=useControlPoint({ariaLabel:ariaLabels?.point2AriaLabel,ariaDescribedBy:ariaDescribedBy,ariaLive:ariaLives[1],point:end,sequenceNumber:2,onMove:p=>{setAriaLives(["off","polite","off"]);onMovePoint(1,p);},constrain:getMovableLineKeyboardConstraint([start,end],snapStep,1)});const line=jsxRuntimeExports.jsx(Line$1,{ariaLabel:ariaLabels?.grabHandleAriaLabel,ariaDescribedBy:ariaDescribedBy,ariaLive:ariaLives[2],start:start,end:end,extend:extend,onMove:newStart=>{setAriaLives(["off","off","polite"]);onMoveLine(newStart);}});return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[focusableHandle1,line,focusableHandle2,visiblePoint1,visiblePoint2]})};const Line$1=props=>{const{start,end,ariaLabel,ariaDescribedBy,ariaLive,extend,onMove}=props;const[startPtPx,endPtPx]=useTransformVectorsToPixels(start,end);const{range,graphDimensionsInPixels,snapStep,disableKeyboardInteraction,interactiveColor}=useGraphConfig();let startExtend=undefined;let endExtend=undefined;if(extend){const trimmedRange=trimRange(range,graphDimensionsInPixels);startExtend=extend.start?getIntersectionOfRayWithBox(end,start,trimmedRange):undefined;endExtend=extend.end?getIntersectionOfRayWithBox(start,end,trimmedRange):undefined;}const line=useRef(null);const{dragging}=useDraggable({gestureTarget:line,point:start,onMove,constrainKeyboardMovement:p=>snap(snapStep,p)});return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsxs("g",{ref:line,tabIndex:disableKeyboardInteraction?-1:0,"aria-label":ariaLabel,"aria-describedby":ariaDescribedBy,"aria-live":ariaLive,"aria-disabled":disableKeyboardInteraction,className:"movable-line","data-testid":"movable-line",style:{cursor:dragging?"grabbing":"grab"},role:"button",children:[jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,style:{stroke:"transparent",strokeWidth:TARGET_SIZE}}),jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,className:"movable-line-focus-outline",style:{}}),jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,className:"movable-line-focus-outline-gap",style:{}}),jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,style:{stroke:interactiveColor,strokeWidth:"var(--movable-line-stroke-weight)"},className:dragging?"movable-dragging":"",testId:"movable-line__line"})]}),startExtend&&jsxRuntimeExports.jsx(Vector,{tail:start,tip:startExtend,testId:"movable-line__vector"}),endExtend&&jsxRuntimeExports.jsx(Vector,{tail:end,tip:endExtend,testId:"movable-line__vector"})]})};const getMovableLineKeyboardConstraint=(line,snapStep,pointIndex)=>{const coordToBeMoved=line[pointIndex];const otherPoint=line[1-pointIndex];const movePointWithConstraint=moveFunc=>{let movedCoord=moveFunc(coordToBeMoved);if(vec.dist(movedCoord,otherPoint)===0){movedCoord=moveFunc(movedCoord);}return movedCoord};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,snapStep[1]])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,snapStep[1]])),left:movePointWithConstraint(coord=>vec.sub(coord,[snapStep[0],0])),right:movePointWithConstraint(coord=>vec.add(coord,[snapStep[0],0]))}};function trimRange(range,graphDimensionsInPixels){const pixelsToTrim=4;const[xRange,yRange]=range;const[pixelsWide,pixelsTall]=graphDimensionsInPixels;const graphUnitsPerPixelX=size(xRange)/pixelsWide;const graphUnitsPerPixelY=size(yRange)/pixelsTall;const graphUnitsToTrimX=pixelsToTrim*graphUnitsPerPixelX;const graphUnitsToTrimY=pixelsToTrim*graphUnitsPerPixelY;return inset([graphUnitsToTrimX,graphUnitsToTrimY],range)}
1945
1945
 
1946
- const{calculateAngleInDegrees: calculateAngleInDegrees$1,getClockwiseAngle: getClockwiseAngle$2,polar: polar$1}=angles;function renderAngleGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(AngleGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getAngleGraphDescription(state,i18n)}}function AngleGraph(props){const{dispatch,graphState}=props;const{graphDimensionsInPixels,interactiveColor}=useGraphConfig();const i18n=usePerseusI18n();const id=React.useId();const descriptionId=id+"-description";const{coords,showAngles,range,allowReflexAngles,snapDegrees}=graphState;const endPoints=[coords[0],coords[2]];const centerPoint=coords[1];const angleLines=[[centerPoint,endPoints[0]],[centerPoint,endPoints[1]]];const linePixelCoords=[useTransformVectorsToPixels(centerPoint,endPoints[0]),useTransformVectorsToPixels(centerPoint,endPoints[1])];const svgLines=linePixelCoords.map(([startPtPx,endPtPx],i)=>{const trimmedRange=trimRange(range,graphDimensionsInPixels);const endExtend=getIntersectionOfRayWithBox(angleLines[i][0],angleLines[i][1],trimmedRange);return jsxRuntimeExports.jsxs("g",{children:[jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,style:{stroke:interactiveColor,strokeWidth:"var(--movable-line-stroke-weight)"},testId:"angle-graph__line"}),jsxRuntimeExports.jsx(Vector,{tail:angleLines[i][1],tip:endExtend,testId:"angle-graph__vector"})]},`line-${i}`)});const angleParams={vertex:centerPoint,coords:endPoints,allowReflexAngles:allowReflexAngles||false,snapDegrees:snapDegrees||1,range:range,showAngles:showAngles||false};const{srAngleGraphAriaLabel,srAngleGraphAriaDescription,srAngleStartingSide,srAngleEndingSide,srAngleVertex}=describeAngleGraph(graphState,i18n);return jsxRuntimeExports.jsxs("g",{"aria-label":srAngleGraphAriaLabel,"aria-describedby":descriptionId,children:[svgLines,jsxRuntimeExports.jsx(Angle,{...angleParams}),jsxRuntimeExports.jsx(MovablePoint$1,{point:coords[1],sequenceNumber:1,constrain:p=>p,onMove:destination=>dispatch(actions.angle.movePoint(1,destination)),ariaLabel:srAngleVertex}),jsxRuntimeExports.jsx(MovablePoint$1,{point:coords[0],sequenceNumber:2,constrain:getAngleSideConstraint(coords[0],coords[1],snapDegrees||1),onMove:destination=>dispatch(actions.angle.movePoint(0,destination)),ariaLabel:srAngleEndingSide}),jsxRuntimeExports.jsx(MovablePoint$1,{point:coords[2],sequenceNumber:3,constrain:getAngleSideConstraint(coords[2],coords[1],snapDegrees||1),onMove:destination=>dispatch(actions.angle.movePoint(2,destination)),ariaLabel:srAngleStartingSide}),jsxRuntimeExports.jsx(SRDescInSVG,{id:descriptionId,children:srAngleGraphAriaDescription})]})}function getAngleGraphDescription(state,i18n){const{strings,locale}=i18n;const{coords}=state;return strings.srInteractiveElements({elements:strings.srAngleInteractiveElements({vertexX:srFormatNumber(coords[1][X],locale),vertexY:srFormatNumber(coords[1][Y],locale),startingSideX:srFormatNumber(coords[2][X],locale),startingSideY:srFormatNumber(coords[2][Y],locale),endingSideX:srFormatNumber(coords[0][X],locale),endingSideY:srFormatNumber(coords[0][Y],locale)})})}function describeAngleGraph(state,i18n){const{strings,locale}=i18n;const{coords,allowReflexAngles}=state;const[endingSide,vertex,startingSide]=coords;const angleMeasure=srFormatNumber(getClockwiseAngle$2(coords,allowReflexAngles),locale);const srAngleGraphAriaLabel=strings.srAngleGraphAriaLabel;const srAngleGraphAriaDescription=strings.srAngleGraphAriaDescription({angleMeasure,vertexX:srFormatNumber(vertex[X],locale),vertexY:srFormatNumber(vertex[Y],locale),startingSideX:srFormatNumber(startingSide[X],locale),startingSideY:srFormatNumber(startingSide[Y],locale),endingSideX:srFormatNumber(endingSide[X],locale),endingSideY:srFormatNumber(endingSide[Y],locale)});const srAngleStartingSide=strings.srAngleStartingSide({x:srFormatNumber(startingSide[X],locale),y:srFormatNumber(startingSide[Y],locale)});const srAngleEndingSide=strings.srAngleEndingSide({x:srFormatNumber(endingSide[X],locale),y:srFormatNumber(endingSide[Y],locale)});const srAngleVertex=strings.srAngleVertexWithAngleMeasure({x:srFormatNumber(vertex[X],locale),y:srFormatNumber(vertex[Y],locale),angleMeasure});return {srAngleGraphAriaLabel,srAngleGraphAriaDescription,srAngleStartingSide,srAngleEndingSide,srAngleVertex}}const positiveX=[1,0];const negativeX=[-1,0];const positiveY=[0,1];const negativeY=[0,-1];function getAngleSideConstraint(sidePoint,vertex,snapDegrees){const currentAngle=calculateAngleInDegrees$1(vec.sub(sidePoint,vertex));const leftRay=[sidePoint,vec.add(sidePoint,negativeX)];const rightRay=[sidePoint,vec.add(sidePoint,positiveX)];const upRay=[sidePoint,vec.add(sidePoint,positiveY)];const downRay=[sidePoint,vec.add(sidePoint,negativeY)];const oneStepCounterClockwise=currentAngle+snapDegrees;const oneStepClockwise=currentAngle-snapDegrees;const counterClockwiseRay=[vertex,vec.add(vertex,polar$1(1,oneStepCounterClockwise))];const clockwiseRay=[vertex,vec.add(vertex,polar$1(1,oneStepClockwise))];const left=findIntersectionOfRays(leftRay,counterClockwiseRay)??findIntersectionOfRays(leftRay,clockwiseRay);const right=findIntersectionOfRays(rightRay,counterClockwiseRay)??findIntersectionOfRays(rightRay,clockwiseRay);const up=findIntersectionOfRays(upRay,counterClockwiseRay)??findIntersectionOfRays(upRay,clockwiseRay);const down=findIntersectionOfRays(downRay,counterClockwiseRay)??findIntersectionOfRays(downRay,clockwiseRay);return {up:up??sidePoint,down:down??sidePoint,left:left??sidePoint,right:right??sidePoint}}
1946
+ const{calculateAngleInDegrees: calculateAngleInDegrees$2,getClockwiseAngle: getClockwiseAngle$2,polar: polar$1}=angles;function renderAngleGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(AngleGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getAngleGraphDescription(state,i18n)}}function AngleGraph(props){const{dispatch,graphState}=props;const{graphDimensionsInPixels,interactiveColor}=useGraphConfig();const i18n=usePerseusI18n();const id=React.useId();const descriptionId=id+"-description";const{coords,showAngles,range,allowReflexAngles,snapDegrees}=graphState;const endPoints=[coords[0],coords[2]];const centerPoint=coords[1];const angleLines=[[centerPoint,endPoints[0]],[centerPoint,endPoints[1]]];const linePixelCoords=[useTransformVectorsToPixels(centerPoint,endPoints[0]),useTransformVectorsToPixels(centerPoint,endPoints[1])];const svgLines=linePixelCoords.map(([startPtPx,endPtPx],i)=>{const trimmedRange=trimRange(range,graphDimensionsInPixels);const endExtend=getIntersectionOfRayWithBox(angleLines[i][0],angleLines[i][1],trimmedRange);return jsxRuntimeExports.jsxs("g",{children:[jsxRuntimeExports.jsx(SVGLine,{start:startPtPx,end:endPtPx,style:{stroke:interactiveColor,strokeWidth:"var(--movable-line-stroke-weight)"},testId:"angle-graph__line"}),jsxRuntimeExports.jsx(Vector,{tail:angleLines[i][1],tip:endExtend,testId:"angle-graph__vector"})]},`line-${i}`)});const angleParams={vertex:centerPoint,coords:endPoints,allowReflexAngles:allowReflexAngles||false,snapDegrees:snapDegrees||1,range:range,showAngles:showAngles||false};const{srAngleGraphAriaLabel,srAngleGraphAriaDescription,srAngleStartingSide,srAngleEndingSide,srAngleVertex}=describeAngleGraph(graphState,i18n);return jsxRuntimeExports.jsxs("g",{"aria-label":srAngleGraphAriaLabel,"aria-describedby":descriptionId,children:[svgLines,jsxRuntimeExports.jsx(Angle,{...angleParams}),jsxRuntimeExports.jsx(MovablePoint$1,{point:coords[1],sequenceNumber:1,constrain:p=>p,onMove:destination=>dispatch(actions.angle.movePoint(1,destination)),ariaLabel:srAngleVertex}),jsxRuntimeExports.jsx(MovablePoint$1,{point:coords[0],sequenceNumber:2,constrain:getAngleSideConstraint(coords[0],coords[1],snapDegrees||1),onMove:destination=>dispatch(actions.angle.movePoint(0,destination)),ariaLabel:srAngleEndingSide}),jsxRuntimeExports.jsx(MovablePoint$1,{point:coords[2],sequenceNumber:3,constrain:getAngleSideConstraint(coords[2],coords[1],snapDegrees||1),onMove:destination=>dispatch(actions.angle.movePoint(2,destination)),ariaLabel:srAngleStartingSide}),jsxRuntimeExports.jsx(SRDescInSVG,{id:descriptionId,children:srAngleGraphAriaDescription})]})}function getAngleGraphDescription(state,i18n){const{strings,locale}=i18n;const{coords}=state;return strings.srInteractiveElements({elements:strings.srAngleInteractiveElements({vertexX:srFormatNumber(coords[1][X],locale),vertexY:srFormatNumber(coords[1][Y],locale),startingSideX:srFormatNumber(coords[2][X],locale),startingSideY:srFormatNumber(coords[2][Y],locale),endingSideX:srFormatNumber(coords[0][X],locale),endingSideY:srFormatNumber(coords[0][Y],locale)})})}function describeAngleGraph(state,i18n){const{strings,locale}=i18n;const{coords,allowReflexAngles}=state;const[endingSide,vertex,startingSide]=coords;const angleMeasure=srFormatNumber(getClockwiseAngle$2(coords,allowReflexAngles),locale);const srAngleGraphAriaLabel=strings.srAngleGraphAriaLabel;const srAngleGraphAriaDescription=strings.srAngleGraphAriaDescription({angleMeasure,vertexX:srFormatNumber(vertex[X],locale),vertexY:srFormatNumber(vertex[Y],locale),startingSideX:srFormatNumber(startingSide[X],locale),startingSideY:srFormatNumber(startingSide[Y],locale),endingSideX:srFormatNumber(endingSide[X],locale),endingSideY:srFormatNumber(endingSide[Y],locale)});const srAngleStartingSide=strings.srAngleStartingSide({x:srFormatNumber(startingSide[X],locale),y:srFormatNumber(startingSide[Y],locale)});const srAngleEndingSide=strings.srAngleEndingSide({x:srFormatNumber(endingSide[X],locale),y:srFormatNumber(endingSide[Y],locale)});const srAngleVertex=strings.srAngleVertexWithAngleMeasure({x:srFormatNumber(vertex[X],locale),y:srFormatNumber(vertex[Y],locale),angleMeasure});return {srAngleGraphAriaLabel,srAngleGraphAriaDescription,srAngleStartingSide,srAngleEndingSide,srAngleVertex}}const positiveX=[1,0];const negativeX=[-1,0];const positiveY=[0,1];const negativeY=[0,-1];function getAngleSideConstraint(sidePoint,vertex,snapDegrees){const currentAngle=calculateAngleInDegrees$2(vec.sub(sidePoint,vertex));const leftRay=[sidePoint,vec.add(sidePoint,negativeX)];const rightRay=[sidePoint,vec.add(sidePoint,positiveX)];const upRay=[sidePoint,vec.add(sidePoint,positiveY)];const downRay=[sidePoint,vec.add(sidePoint,negativeY)];const oneStepCounterClockwise=currentAngle+snapDegrees;const oneStepClockwise=currentAngle-snapDegrees;const counterClockwiseRay=[vertex,vec.add(vertex,polar$1(1,oneStepCounterClockwise))];const clockwiseRay=[vertex,vec.add(vertex,polar$1(1,oneStepClockwise))];const left=findIntersectionOfRays(leftRay,counterClockwiseRay)??findIntersectionOfRays(leftRay,clockwiseRay);const right=findIntersectionOfRays(rightRay,counterClockwiseRay)??findIntersectionOfRays(rightRay,clockwiseRay);const up=findIntersectionOfRays(upRay,counterClockwiseRay)??findIntersectionOfRays(upRay,clockwiseRay);const down=findIntersectionOfRays(downRay,counterClockwiseRay)??findIntersectionOfRays(downRay,clockwiseRay);return {up:up??sidePoint,down:down??sidePoint,left:left??sidePoint,right:right??sidePoint}}
1947
1947
 
1948
- function getGradableGraph(state,initialGraph){if(!state.hasBeenInteractedWith){return {...initialGraph}}if(initialGraph.type==="linear-system"&&state.type==="linear-system"){return {...initialGraph,coords:state.coords}}if(state.type==="segment"&&initialGraph.type==="segment"){return {...initialGraph,coords:state.coords}}if(state.type==="linear"&&initialGraph.type==="linear"){return {...initialGraph,coords:state.coords}}if(state.type==="ray"&&initialGraph.type==="ray"){return {...initialGraph,coords:state.coords}}if(state.type==="polygon"&&initialGraph.type==="polygon"){if(state.numSides==="unlimited"&&!state.closedPolygon){return {...initialGraph,coords:null}}return {...initialGraph,coords:state.coords}}if(state.type==="point"&&initialGraph.type==="point"){if(state.numPoints==="unlimited"&&state.coords.length===0){return {...initialGraph,coords:null}}return {...initialGraph,coords:state.coords}}if(state.type==="circle"&&initialGraph.type==="circle"){return {...initialGraph,center:state.center,radius:getRadius(state)}}if(state.type==="quadratic"&&initialGraph.type==="quadratic"){return {...initialGraph,coords:state.coords}}if(state.type==="sinusoid"&&initialGraph.type==="sinusoid"){return {...initialGraph,coords:state.coords}}if(state.type==="exponential"&&initialGraph.type==="exponential"){return {...initialGraph,coords:state.coords,asymptote:state.asymptote}}if(state.type==="logarithm"&&initialGraph.type==="logarithm"){return {...initialGraph,coords:state.coords,asymptote:state.asymptote}}if(state.type==="tangent"&&initialGraph.type==="tangent"){return {...initialGraph,coords:state.coords}}if(state.type==="angle"&&initialGraph.type==="angle"){return {...initialGraph,coords:state.coords,allowReflexAngles:state.allowReflexAngles}}if(state.type==="none"&&initialGraph.type==="none"){return {type:"none"}}if(state.type==="absolute-value"&&initialGraph.type==="absolute-value"){return {...initialGraph,coords:state.coords}}throw new Error("Mafs is not yet implemented for graph type: "+initialGraph.type)}function getRadius(graph){const[centerX,centerY]=graph.center;const[edgeX,edgeY]=graph.radiusPoint;return Math.sqrt(Math.pow(edgeX-centerX,2)+Math.pow(edgeY-centerY,2))}
1948
+ function getGradableGraph(state,initialGraph){if(!state.hasBeenInteractedWith){return {...initialGraph}}if(initialGraph.type==="linear-system"&&state.type==="linear-system"){return {...initialGraph,coords:state.coords}}if(state.type==="segment"&&initialGraph.type==="segment"){return {...initialGraph,coords:state.coords}}if(state.type==="linear"&&initialGraph.type==="linear"){return {...initialGraph,coords:state.coords}}if(state.type==="ray"&&initialGraph.type==="ray"){return {...initialGraph,coords:state.coords}}if(state.type==="vector"&&initialGraph.type==="vector"){return {...initialGraph,coords:state.coords}}if(state.type==="polygon"&&initialGraph.type==="polygon"){if(state.numSides==="unlimited"&&!state.closedPolygon){return {...initialGraph,coords:null}}return {...initialGraph,coords:state.coords}}if(state.type==="point"&&initialGraph.type==="point"){if(state.numPoints==="unlimited"&&state.coords.length===0){return {...initialGraph,coords:null}}return {...initialGraph,coords:state.coords}}if(state.type==="circle"&&initialGraph.type==="circle"){return {...initialGraph,center:state.center,radius:getRadius(state)}}if(state.type==="quadratic"&&initialGraph.type==="quadratic"){return {...initialGraph,coords:state.coords}}if(state.type==="sinusoid"&&initialGraph.type==="sinusoid"){return {...initialGraph,coords:state.coords}}if(state.type==="exponential"&&initialGraph.type==="exponential"){return {...initialGraph,coords:state.coords,asymptote:state.asymptote}}if(state.type==="logarithm"&&initialGraph.type==="logarithm"){return {...initialGraph,coords:state.coords,asymptote:state.asymptote}}if(state.type==="tangent"&&initialGraph.type==="tangent"){return {...initialGraph,coords:state.coords}}if(state.type==="angle"&&initialGraph.type==="angle"){return {...initialGraph,coords:state.coords,allowReflexAngles:state.allowReflexAngles}}if(state.type==="none"&&initialGraph.type==="none"){return {type:"none"}}if(state.type==="absolute-value"&&initialGraph.type==="absolute-value"){return {...initialGraph,coords:state.coords}}throw new Error("Mafs is not yet implemented for graph type: "+initialGraph.type)}function getRadius(graph){const[centerX,centerY]=graph.center;const[edgeX,edgeY]=graph.radiusPoint;return Math.sqrt(Math.pow(edgeX-centerX,2)+Math.pow(edgeY-centerY,2))}
1949
1949
 
1950
1950
  function renderCircleGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(CircleGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getCircleGraphDescription(state,i18n)}}function CircleGraph(props){const{dispatch,graphState}=props;const{center,radiusPoint,snapStep}=graphState;const{strings,locale}=usePerseusI18n();const[radiusPointAriaLive,setRadiusPointAriaLive]=React.useState("off");const radius=getRadius(graphState);const id=React.useId();const circleId=id+"-circle";const radiusId=id+"-radius";const outerPointsId=id+"-outer-points";const{srCircleGraph,srCircleShape,srCircleRadiusPoint,srCircleRadius,srCircleOuterPoints}=describeCircleGraph(graphState,{strings,locale});return jsxRuntimeExports.jsxs("g",{"aria-label":srCircleGraph,"aria-describedby":`${circleId} ${radiusId} ${outerPointsId}`,children:[jsxRuntimeExports.jsx(MovableCircle,{id:circleId,ariaLabel:srCircleShape,ariaDescribedBy:`${radiusId} ${outerPointsId}`,center:center,radius:radius,onMove:c=>{setRadiusPointAriaLive("off");dispatch(actions.circle.moveCenter(c));}}),jsxRuntimeExports.jsx(MovablePoint$1,{ariaLabel:`${srCircleRadiusPoint} ${srCircleRadius}`,ariaDescribedBy:`${outerPointsId}`,ariaLive:radiusPointAriaLive,point:radiusPoint,sequenceNumber:1,cursor:"ew-resize",onMove:newRadiusPoint=>{setRadiusPointAriaLive("polite");dispatch(actions.circle.moveRadiusPoint(newRadiusPoint));},constrain:getCircleKeyboardConstraint(center,radiusPoint,snapStep)}),jsxRuntimeExports.jsx(SRDescInSVG,{id:radiusId,children:srCircleRadius}),jsxRuntimeExports.jsx(SRDescInSVG,{id:outerPointsId,children:srCircleOuterPoints})]})}function MovableCircle(props){const{id,ariaLabel,ariaDescribedBy,center,radius,onMove}=props;const{snapStep,disableKeyboardInteraction,interactiveColor}=useGraphConfig();const[focused,setFocused]=React.useState(false);const draggableRef=useRef(null);const{dragging}=useDraggable({gestureTarget:draggableRef,point:center,onMove,constrainKeyboardMovement:p=>snap(snapStep,p)});const[centerPx]=useTransformVectorsToPixels(center);const[radiiPx]=useTransformDimensionsToPixels([radius,radius]);return jsxRuntimeExports.jsxs("g",{"aria-label":ariaLabel,"aria-describedby":ariaDescribedBy,"aria-live":"polite","aria-disabled":disableKeyboardInteraction,ref:draggableRef,role:"button",tabIndex:disableKeyboardInteraction?-1:0,className:`movable-circle ${dragging?"movable-circle--dragging":""}`,onFocus:()=>setFocused(true),onBlur:()=>setFocused(false),children:[jsxRuntimeExports.jsx("ellipse",{className:"focus-ring",cx:centerPx[X],cy:centerPx[Y],rx:radiiPx[X]+3,ry:radiiPx[Y]+3}),jsxRuntimeExports.jsx("ellipse",{id:id,className:"circle",cx:centerPx[X],cy:centerPx[Y],rx:radiiPx[X],ry:radiiPx[Y],stroke:interactiveColor,"data-testid":"movable-circle__circle"}),jsxRuntimeExports.jsx(DragHandle,{center:center,dragging:dragging,focused:focused})]})}const dragHandleDimensions=[24,14];const dragHandleDotPositions=crossProduct([-4.4,0,4.4],[-2.1,2.1]);function DragHandle(props){const{center,dragging,focused}=props;const[centerPx]=useTransformVectorsToPixels(center);const{markings,interactiveColor}=useGraphConfig();const cornerRadius=Math.min(...dragHandleDimensions)/2;const topLeft=vec.sub(centerPx,vec.scale(dragHandleDimensions,.5));const showHairlines=(dragging||focused)&&markings!=="none";return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[showHairlines&&jsxRuntimeExports.jsx(Hairlines,{point:center}),jsxRuntimeExports.jsx("rect",{className:"movable-circle-handle",x:topLeft[X],y:topLeft[Y],width:dragHandleDimensions[X],height:dragHandleDimensions[Y],rx:cornerRadius,ry:cornerRadius,fill:interactiveColor,"data-testid":"movable-circle__handle"}),dragHandleDotPositions.map(offsetPx=>{const[xPx,yPx]=vec.add(offsetPx,centerPx);return jsxRuntimeExports.jsx("circle",{className:"movable-circle-handle-dot",cx:xPx,cy:yPx},`circle-${xPx}-${yPx}`)})]})}function crossProduct(as,bs){const result=[];for(const a of as){for(const b of bs){result.push([a,b]);}}return result}function getCircleGraphDescription(state,i18n){const strings=describeCircleGraph(state,i18n);return strings.srCircleInteractiveElement}const getCircleKeyboardConstraint=(center,radiusPoint,snapStep)=>{const movePointWithConstraint=moveFunc=>{let movedCoord=moveFunc(radiusPoint);if(vec.dist(movedCoord,center)===0){movedCoord=moveFunc(movedCoord);}return movedCoord};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,snapStep[1]])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,snapStep[1]])),left:movePointWithConstraint(coord=>vec.sub(coord,[snapStep[0],0])),right:movePointWithConstraint(coord=>vec.add(coord,[snapStep[0],0]))}};function describeCircleGraph(state,i18n){const{strings,locale}=i18n;const{center,radiusPoint}=state;const radius=getRadius(state);const isRadiusOnRight=radiusPoint[X]>=center[X];const srCircleGraph=strings.srCircleGraph;const srCircleShape=strings.srCircleShape({centerX:srFormatNumber(center[0],locale),centerY:srFormatNumber(center[1],locale)});const srCircleRadiusPoint=isRadiusOnRight?strings.srCircleRadiusPointRight({radiusPointX:srFormatNumber(radiusPoint[0],locale),radiusPointY:srFormatNumber(radiusPoint[1],locale)}):strings.srCircleRadiusPointLeft({radiusPointX:srFormatNumber(radiusPoint[0],locale),radiusPointY:srFormatNumber(radiusPoint[1],locale)});const srCircleRadius=strings.srCircleRadius({radius});const srCircleOuterPoints=strings.srCircleOuterPoints({point1X:srFormatNumber(center[0]+radius,locale),point1Y:srFormatNumber(center[1],locale),point2X:srFormatNumber(center[0],locale),point2Y:srFormatNumber(center[1]+radius,locale),point3X:srFormatNumber(center[0]-radius,locale),point3Y:srFormatNumber(center[1],locale),point4X:srFormatNumber(center[0],locale),point4Y:srFormatNumber(center[1]-radius,locale)});const srCircleInteractiveElement=strings.srInteractiveElements({elements:[srCircleShape,srCircleRadius].join(" ")});return {srCircleGraph,srCircleShape,srCircleRadiusPoint,srCircleRadius,srCircleOuterPoints,srCircleInteractiveElement}}
1951
1951
 
1952
- const ACTIVE_MAJOR=22;const ACTIVE_MINOR=12;const INACTIVE_MAJOR=16;const INACTIVE_MINOR=6;const RING_PAD=2;const HALO_PAD=3;const FOCUS_RING_PAD=2;const GRIP_DOT_MAJOR_OFFSETS=[-3,0,3];const GRIP_DOT_MINOR_OFFSETS=[-2,2];function AsymptoteDragHandle(props){const{center,active,focused,orientation}=props;const[x,y]=center;const{interactiveColor}=useGraphConfig();const isHorizontal=orientation==="horizontal";const majorSize=active?ACTIVE_MAJOR:INACTIVE_MAJOR;const minorSize=active?ACTIVE_MINOR:INACTIVE_MINOR;const centerW=isHorizontal?majorSize:minorSize;const centerH=isHorizontal?minorSize:majorSize;const haloW=centerW+(RING_PAD+HALO_PAD)*2;const haloH=centerH+(RING_PAD+HALO_PAD)*2;const ringW=centerW+RING_PAD*2;const ringH=centerH+RING_PAD*2;const focusRingW=haloW+FOCUS_RING_PAD*2;const focusRingH=haloH+FOCUS_RING_PAD*2;const pillRadius=(isHorizontal?centerH:centerW)/2;const haloRadius=(isHorizontal?haloH:haloW)/2;const ringRadius=(isHorizontal?ringH:ringW)/2;const focusRingRadius=(isHorizontal?focusRingH:focusRingW)/2;const dotXOffsets=isHorizontal?GRIP_DOT_MAJOR_OFFSETS:GRIP_DOT_MINOR_OFFSETS;const dotYOffsets=isHorizontal?GRIP_DOT_MINOR_OFFSETS:GRIP_DOT_MAJOR_OFFSETS;return jsxRuntimeExports.jsxs("g",{"aria-hidden":true,style:{pointerEvents:"none"},children:[focused&&jsxRuntimeExports.jsx("rect",{className:"movable-asymptote-handle-focus-ring","data-testid":"asymptote-handle-focus-ring",x:x-focusRingW/2,y:y-focusRingH/2,width:focusRingW,height:focusRingH,rx:focusRingRadius,ry:focusRingRadius,stroke:interactiveColor}),jsxRuntimeExports.jsx("rect",{className:"movable-asymptote-handle-halo",x:x-haloW/2,y:y-haloH/2,width:haloW,height:haloH,rx:haloRadius,ry:haloRadius,fill:interactiveColor}),jsxRuntimeExports.jsx("rect",{className:"movable-asymptote-handle-ring",x:x-ringW/2,y:y-ringH/2,width:ringW,height:ringH,rx:ringRadius,ry:ringRadius}),jsxRuntimeExports.jsx("rect",{className:"movable-asymptote-handle","data-testid":"asymptote-handle-pill",x:x-centerW/2,y:y-centerH/2,width:centerW,height:centerH,rx:pillRadius,ry:pillRadius,fill:interactiveColor}),active&&dotYOffsets.map(dy=>dotXOffsets.map(dx=>jsxRuntimeExports.jsx("circle",{className:"movable-asymptote-handle-dot","data-testid":"asymptote-handle-dot",cx:x+dx,cy:y+dy},`${dx},${dy}`)))]})}
1952
+ const ACTIVE_MAJOR=22;const ACTIVE_MINOR=12;const INACTIVE_MAJOR=16;const INACTIVE_MINOR=6;const RING_PAD=2;const HALO_PAD=3;const FOCUS_RING_PAD=2;const GRIP_DOT_MAJOR_OFFSETS=[-3,0,3];const GRIP_DOT_MINOR_OFFSETS=[-2,2];function MovablePillHandle(props){const{center,active,focused,rotation=0}=props;const[cx,cy]=center;const{interactiveColor}=useGraphConfig();const majorSize=active?ACTIVE_MAJOR:INACTIVE_MAJOR;const minorSize=active?ACTIVE_MINOR:INACTIVE_MINOR;const pillW=majorSize;const pillH=minorSize;const pillRadius=pillH/2;const haloW=pillW+(RING_PAD+HALO_PAD)*2;const haloH=pillH+(RING_PAD+HALO_PAD)*2;const haloRadius=haloH/2;const ringW=pillW+RING_PAD*2;const ringH=pillH+RING_PAD*2;const ringRadius=ringH/2;const focusRingW=haloW+FOCUS_RING_PAD*2;const focusRingH=haloH+FOCUS_RING_PAD*2;const focusRingRadius=focusRingH/2;return jsxRuntimeExports.jsxs("g",{"aria-hidden":true,style:{pointerEvents:"none"},transform:`translate(${cx} ${cy}) rotate(${rotation})`,"data-testid":"movable-pill-handle",children:[focused&&jsxRuntimeExports.jsx("rect",{className:"movable-pill-handle-focus-ring","data-testid":"movable-pill-handle-focus-ring",x:-focusRingW/2,y:-focusRingH/2,width:focusRingW,height:focusRingH,rx:focusRingRadius,ry:focusRingRadius,stroke:interactiveColor}),jsxRuntimeExports.jsx("rect",{className:"movable-pill-handle-halo",x:-haloW/2,y:-haloH/2,width:haloW,height:haloH,rx:haloRadius,ry:haloRadius,fill:interactiveColor}),jsxRuntimeExports.jsx("rect",{className:"movable-pill-handle-ring",x:-ringW/2,y:-ringH/2,width:ringW,height:ringH,rx:ringRadius,ry:ringRadius}),jsxRuntimeExports.jsx("rect",{className:"movable-pill-handle","data-testid":"movable-pill-handle-pill",x:-pillW/2,y:-pillH/2,width:pillW,height:pillH,rx:pillRadius,ry:pillRadius,fill:interactiveColor}),active&&GRIP_DOT_MINOR_OFFSETS.map(dy=>GRIP_DOT_MAJOR_OFFSETS.map(dx=>jsxRuntimeExports.jsx("circle",{className:"movable-pill-handle-dot","data-testid":"movable-pill-handle-dot",cx:dx,cy:dy},`${dx},${dy}`)))]})}
1953
1953
 
1954
- function MovableAsymptote(props){const{start,end,mid,point,onMove,constrainKeyboardMovement,orientation,ariaLabel}=props;const{interactiveColor,disableKeyboardInteraction}=useGraphConfig();const[focused,setFocused]=React.useState(false);const[hovered,setHovered]=React.useState(false);const groupRef=React.useRef(null);const{dragging}=useDraggable({gestureTarget:groupRef,point,onMove,constrainKeyboardMovement:constrainKeyboardMovement??(p=>p)});return jsxRuntimeExports.jsxs("g",{ref:groupRef,tabIndex:disableKeyboardInteraction?-1:0,"aria-disabled":disableKeyboardInteraction,"aria-label":ariaLabel,"aria-live":"polite",className:"movable-line",style:{cursor:dragging?"grabbing":"grab"},role:"button","data-testid":"movable-asymptote",onFocus:()=>setFocused(true),onBlur:()=>setFocused(false),onMouseEnter:()=>setHovered(true),onMouseLeave:()=>setHovered(false),children:[jsxRuntimeExports.jsx(SVGLine,{start:start,end:end,style:{stroke:"transparent",strokeWidth:TARGET_SIZE}}),jsxRuntimeExports.jsx(SVGLine,{start:start,end:end,style:{stroke:interactiveColor,strokeWidth:"var(--movable-line-stroke-weight)"},className:dragging?"movable-dragging":"",testId:"movable-asymptote__line"}),jsxRuntimeExports.jsx(AsymptoteDragHandle,{center:mid,orientation:orientation,active:dragging||focused||hovered,focused:focused})]})}
1954
+ function MovableAsymptote(props){const{start,end,mid,point,onMove,constrainKeyboardMovement,orientation,ariaLabel,children}=props;const{interactiveColor,disableKeyboardInteraction}=useGraphConfig();const[focused,setFocused]=React.useState(false);const[hovered,setHovered]=React.useState(false);const groupRef=React.useRef(null);const{dragging}=useDraggable({gestureTarget:groupRef,point,onMove,constrainKeyboardMovement:constrainKeyboardMovement??(p=>p)});return jsxRuntimeExports.jsxs("g",{ref:groupRef,tabIndex:disableKeyboardInteraction?-1:0,"aria-disabled":disableKeyboardInteraction,"aria-label":ariaLabel,"aria-live":"polite",className:"movable-line",style:{cursor:dragging?"grabbing":"grab"},role:"button","data-testid":"movable-asymptote",onFocus:()=>setFocused(true),onBlur:()=>setFocused(false),onMouseEnter:()=>setHovered(true),onMouseLeave:()=>setHovered(false),children:[jsxRuntimeExports.jsx(SVGLine,{start:start,end:end,style:{stroke:"transparent",strokeWidth:TARGET_SIZE}}),jsxRuntimeExports.jsx(SVGLine,{start:start,end:end,style:{stroke:"white",strokeWidth:"var(--movable-asymptote-stroke-weight)",strokeLinecap:"round"},className:dragging?"movable-dragging":""}),jsxRuntimeExports.jsx(SVGLine,{start:start,end:end,style:{stroke:interactiveColor,strokeWidth:"var(--movable-asymptote-stroke-weight)",strokeDasharray:"var(--movable-asymptote-dash-length) var(--movable-asymptote-dash-gap)",strokeLinecap:"round"},className:dragging?"movable-dragging":"",testId:"movable-asymptote__line"}),children,jsxRuntimeExports.jsx(MovablePillHandle,{center:mid,rotation:orientation==="vertical"?90:0,active:dragging||focused||hovered,focused:focused})]})}
1955
1955
 
1956
- const{getExponentialCoefficients: getExponentialCoefficients$1}=coefficients;function renderExponentialGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(ExponentialGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getExponentialDescription(state,i18n)}}function ExponentialGraph(props){const{dispatch,graphState}=props;const{interactiveColor,range}=useGraphConfig();const i18n=usePerseusI18n();const id=React.useId();const descriptionId=id+"-description";const{coords,asymptote,snapStep}=graphState;const coeffRef=React.useRef({a:1,b:1,c:0});const coeffs=getExponentialCoefficients$1(coords,asymptote);if(coeffs!==undefined){coeffRef.current=coeffs;}const asymptoteY=asymptote;const yMin=range[1][0];const yMax=range[1][1];const yPadding=(yMax-yMin)*2;const{srExponentialGraph,srExponentialDescription,srExponentialPoint1,srExponentialPoint2,srExponentialAsymptote}=describeExponentialGraph(graphState,i18n);const asymptoteLeft=[range[0][0],asymptoteY];const asymptoteRight=[range[0][1],asymptoteY];const asymptoteMidX=(range[0][0]+range[0][1])/2;const asymptoteMid=[asymptoteMidX,asymptoteY];const[leftPx,rightPx,midPx]=useTransformVectorsToPixels(asymptoteLeft,asymptoteRight,asymptoteMid);return jsxRuntimeExports.jsxs("g",{"aria-label":srExponentialGraph,"aria-describedby":descriptionId,children:[jsxRuntimeExports.jsx(Plot$2.OfX,{y:x=>{const y=computeExponential(x,coeffRef.current);if(y<yMin-yPadding||y>yMax+yPadding){return NaN}return y},color:interactiveColor,svgPathProps:{"aria-hidden":true}}),jsxRuntimeExports.jsx(MovableAsymptote,{start:leftPx,end:rightPx,mid:midPx,point:asymptoteLeft,onMove:newPoint=>dispatch(actions.exponential.moveCenter(newPoint)),constrainKeyboardMovement:p=>constrainAsymptoteKeyboard$1(p,coords,snapStep),orientation:"horizontal",ariaLabel:srExponentialAsymptote}),coords.map((coord,i)=>jsxRuntimeExports.jsx(MovablePoint$1,{ariaLabel:i===0?srExponentialPoint1:srExponentialPoint2,point:coord,sequenceNumber:i+1,constrain:getExponentialKeyboardConstraint(coords,asymptote,snapStep,i,range),onMove:destination=>dispatch(actions.exponential.movePoint(i,destination))},"point-"+i)),jsxRuntimeExports.jsx(SRDescInSVG,{id:descriptionId,children:srExponentialDescription})]})}const constrainAsymptoteKeyboard$1=(p,coords,snapStep)=>constrainAsymptoteKeyboardMovement(p,coords,snapStep,"horizontal");const getExponentialKeyboardConstraint=(coords,asymptote,snapStep,pointIndex,range)=>{const otherPoint=coords[1-pointIndex];const asymptoteY=asymptote;return getAsymptoteGraphKeyboardConstraint(coords,snapStep,pointIndex,coord=>{const clamped=snap(snapStep,bound$1({snapStep,range,point:coord}));const clampedX=clamped[X];const clampedY=clamped[Y];if(coord[Y]===asymptoteY||clampedY===asymptoteY){return false}if(coord[X]===otherPoint[X]||clampedX===otherPoint[X]){return false}const currentPoint=coords[pointIndex];const currentSide=currentPoint[Y]>asymptoteY;const proposedSide=coord[Y]>asymptoteY;if(currentSide!==proposedSide){const reflectedY=2*asymptoteY-otherPoint[Y];const clampedReflectedY=snap(snapStep,bound$1({snapStep,range,point:[0,reflectedY]}))[Y];if(reflectedY===coord[Y]||clampedReflectedY===coord[Y]||clampedReflectedY===clampedY){return false}}return true})};const computeExponential=function(x,coefficients){const{a,b,c}=coefficients;return a*Math.exp(b*x)+c};function getExponentialDescription(state,i18n){const strings=describeExponentialGraph(state,i18n);return strings.srExponentialInteractiveElements}function describeExponentialGraph(state,i18n){const{strings,locale}=i18n;const{coords,asymptote}=state;const[point1,point2]=coords;const formattedPoint1={x:srFormatNumber(point1[X],locale),y:srFormatNumber(point1[Y],locale)};const formattedPoint2={x:srFormatNumber(point2[X],locale),y:srFormatNumber(point2[Y],locale)};const asymptoteYFormatted=srFormatNumber(asymptote,locale);return {srExponentialGraph:strings.srExponentialGraph,srExponentialDescription:strings.srExponentialDescription({point1X:formattedPoint1.x,point1Y:formattedPoint1.y,point2X:formattedPoint2.x,point2Y:formattedPoint2.y,asymptoteY:asymptoteYFormatted}),srExponentialAsymptote:strings.srExponentialAsymptote({asymptoteY:asymptoteYFormatted}),srExponentialPoint1:strings.srExponentialPoint1(formattedPoint1),srExponentialPoint2:strings.srExponentialPoint2(formattedPoint2),srExponentialInteractiveElements:strings.srInteractiveElements({elements:strings.srExponentialInteractiveElements({point1X:srFormatNumber(point1[X],locale),point1Y:srFormatNumber(point1[Y],locale),point2X:srFormatNumber(point2[X],locale),point2Y:srFormatNumber(point2[Y],locale),asymptoteY:asymptoteYFormatted})})}}
1956
+ const{getExponentialCoefficients: getExponentialCoefficients$1}=coefficients;function renderExponentialGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(ExponentialGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getExponentialDescription(state,i18n)}}function ExponentialGraph(props){const{dispatch,graphState}=props;const{interactiveColor,range}=useGraphConfig();const i18n=usePerseusI18n();const id=React.useId();const descriptionId=id+"-description";const{coords,asymptote,snapStep}=graphState;const coeffs=getExponentialCoefficients$1(coords,asymptote);const asymptoteY=asymptote;const yMin=range[1][0];const yMax=range[1][1];const yPadding=(yMax-yMin)*4;const{srExponentialGraph,srExponentialDescription,srExponentialPoint1,srExponentialPoint2,srExponentialAsymptote}=describeExponentialGraph(graphState,i18n);const asymptoteLeft=[range[0][0],asymptoteY];const asymptoteRight=[range[0][1],asymptoteY];const handleCoord=getAsymptoteHandleCoord("horizontal",range,asymptote);const[leftPx,rightPx,midPx]=useTransformVectorsToPixels(asymptoteLeft,asymptoteRight,handleCoord);return jsxRuntimeExports.jsxs("g",{"aria-label":srExponentialGraph,"aria-describedby":descriptionId,children:[jsxRuntimeExports.jsx(MovableAsymptote,{start:leftPx,end:rightPx,mid:midPx,point:handleCoord,onMove:newPoint=>dispatch(actions.exponential.moveCenter(newPoint)),constrainKeyboardMovement:p=>skipAsymptoteKeyboardOverPoint(p,asymptote,coords,handleCoord,snapStep,"horizontal"),orientation:"horizontal",ariaLabel:srExponentialAsymptote,children:coeffs!==undefined&&jsxRuntimeExports.jsx(Plot$2.OfX,{y:x=>{const y=computeExponential(x,coeffs);if(y<yMin-yPadding||y>yMax+yPadding){return NaN}return y},color:interactiveColor,svgPathProps:{"aria-hidden":true,style:{pointerEvents:"none"}}})}),coords.map((coord,i)=>jsxRuntimeExports.jsx(MovablePoint$1,{ariaLabel:i===0?srExponentialPoint1:srExponentialPoint2,point:coord,sequenceNumber:i+1,constrain:getExponentialKeyboardConstraint(coords,asymptote,snapStep,i,range),onMove:destination=>dispatch(actions.exponential.movePoint(i,destination))},"point-"+i)),jsxRuntimeExports.jsx(SRDescInSVG,{id:descriptionId,children:srExponentialDescription})]})}const getExponentialKeyboardConstraint=(coords,asymptote,snapStep,pointIndex,range)=>{const otherPoint=coords[1-pointIndex];const handleCoord=getAsymptoteHandleCoord("horizontal",range,asymptote);return getAsymptoteGraphKeyboardConstraint(coords,snapStep,pointIndex,coord=>{const clamped=snap(snapStep,bound$1({snapStep,range,point:coord}));const clampedX=clamped[X];const clampedY=clamped[Y];if(coord[X]===otherPoint[X]||clampedX===otherPoint[X]){return false}if(clampedX===handleCoord[X]&&clampedY===handleCoord[Y]){return false}return true})};const computeExponential=function(x,coefficients){const{a,b,c}=coefficients;return a*Math.exp(b*x)+c};function getExponentialDescription(state,i18n){const strings=describeExponentialGraph(state,i18n);return strings.srExponentialInteractiveElements}function describeExponentialGraph(state,i18n){const{strings,locale}=i18n;const{coords,asymptote}=state;const[point1,point2]=coords;const formattedPoint1={x:srFormatNumber(point1[X],locale),y:srFormatNumber(point1[Y],locale)};const formattedPoint2={x:srFormatNumber(point2[X],locale),y:srFormatNumber(point2[Y],locale)};const asymptoteYFormatted=srFormatNumber(asymptote,locale);return {srExponentialGraph:strings.srExponentialGraph,srExponentialDescription:strings.srExponentialDescription({point1X:formattedPoint1.x,point1Y:formattedPoint1.y,point2X:formattedPoint2.x,point2Y:formattedPoint2.y,asymptoteY:asymptoteYFormatted}),srExponentialAsymptote:strings.srExponentialAsymptote({asymptoteY:asymptoteYFormatted}),srExponentialPoint1:strings.srExponentialPoint1(formattedPoint1),srExponentialPoint2:strings.srExponentialPoint2(formattedPoint2),srExponentialInteractiveElements:strings.srInteractiveElements({elements:strings.srExponentialInteractiveElements({point1X:srFormatNumber(point1[X],locale),point1Y:srFormatNumber(point1[Y],locale),point2X:srFormatNumber(point2[X],locale),point2Y:srFormatNumber(point2[Y],locale),asymptoteY:asymptoteYFormatted})})}}
1957
1957
 
1958
- function renderLinearGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(LinearGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getLinearGraphDescription(state,i18n)}}const LinearGraph=(props,key)=>{const{dispatch}=props;const{coords:line}=props.graphState;const{strings,locale}=usePerseusI18n();const id=React.useId();const pointsDescriptionId=id+"-points";const interceptDescriptionId=id+"-intercept";const slopeDescriptionId=id+"-slope";const{srLinearGraph,srLinearGraphPoints,srLinearGrabHandle,slopeString,interceptString}=describeLinearGraph(props.graphState,{strings,locale});return jsxRuntimeExports.jsxs("g",{"aria-label":srLinearGraph,"aria-describedby":`${pointsDescriptionId} ${interceptDescriptionId} ${slopeDescriptionId}`,children:[jsxRuntimeExports.jsx(MovableLine,{ariaLabels:{grabHandleAriaLabel:srLinearGrabHandle},ariaDescribedBy:`${interceptDescriptionId} ${slopeDescriptionId}`,points:line,onMoveLine:delta=>{dispatch(actions.linear.moveLine(delta));},extend:{start:true,end:true},onMovePoint:(endpointIndex,destination)=>dispatch(actions.linear.movePoint(endpointIndex,destination))},0),jsxRuntimeExports.jsx(SRDescInSVG,{id:pointsDescriptionId,children:srLinearGraphPoints}),jsxRuntimeExports.jsx(SRDescInSVG,{id:interceptDescriptionId,children:interceptString}),jsxRuntimeExports.jsx(SRDescInSVG,{id:slopeDescriptionId,children:slopeString})]})};function getLinearGraphDescription(state,i18n){const strings=describeLinearGraph(state,i18n);return strings.srLinearInteractiveElement}function describeLinearGraph(state,i18n){const{coords:line}=state;const{strings,locale}=i18n;const srLinearGraph=strings.srLinearGraph;const srLinearGraphPoints=strings.srLinearGraphPoints({point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)});const srLinearGrabHandle=strings.srLinearGrabHandle({point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)});const slopeString=getSlopeStringForLine(line,strings);const interceptString=getInterceptStringForLine(line,strings,locale);const srLinearInteractiveElement=strings.srInteractiveElements({elements:[srLinearGraph,srLinearGraphPoints].join(" ")});return {srLinearGraph,srLinearGraphPoints,srLinearGrabHandle,slopeString,interceptString,srLinearInteractiveElement}}
1958
+ function renderLinearGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(LinearGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getLinearGraphDescription(state,i18n)}}const LinearGraph=(props,key)=>{const{dispatch}=props;const{coords:line}=props.graphState;const{strings,locale}=usePerseusI18n();const id=React.useId();const pointsDescriptionId=id+"-points";const interceptDescriptionId=id+"-intercept";const slopeDescriptionId=id+"-slope";const{srLinearGraph,srLinearGraphPoints,srLinearGrabHandle,slopeString,interceptString}=describeLinearGraph(props.graphState,{strings,locale});return jsxRuntimeExports.jsxs("g",{"aria-label":srLinearGraph,"aria-describedby":`${pointsDescriptionId} ${interceptDescriptionId} ${slopeDescriptionId}`,children:[jsxRuntimeExports.jsx(MovableLine,{ariaLabels:{grabHandleAriaLabel:srLinearGrabHandle},ariaDescribedBy:`${interceptDescriptionId} ${slopeDescriptionId}`,points:line,onMoveLine:newStart=>{dispatch(actions.linear.moveLine(newStart));},extend:{start:true,end:true},onMovePoint:(endpointIndex,destination)=>dispatch(actions.linear.movePoint(endpointIndex,destination))},0),jsxRuntimeExports.jsx(SRDescInSVG,{id:pointsDescriptionId,children:srLinearGraphPoints}),jsxRuntimeExports.jsx(SRDescInSVG,{id:interceptDescriptionId,children:interceptString}),jsxRuntimeExports.jsx(SRDescInSVG,{id:slopeDescriptionId,children:slopeString})]})};function getLinearGraphDescription(state,i18n){const strings=describeLinearGraph(state,i18n);return strings.srLinearInteractiveElement}function describeLinearGraph(state,i18n){const{coords:line}=state;const{strings,locale}=i18n;const srLinearGraph=strings.srLinearGraph;const srLinearGraphPoints=strings.srLinearGraphPoints({point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)});const srLinearGrabHandle=strings.srLinearGrabHandle({point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)});const slopeString=getSlopeStringForLine(line,strings);const interceptString=getInterceptStringForLine(line,strings,locale);const srLinearInteractiveElement=strings.srInteractiveElements({elements:[srLinearGraph,srLinearGraphPoints].join(" ")});return {srLinearGraph,srLinearGraphPoints,srLinearGrabHandle,slopeString,interceptString,srLinearInteractiveElement}}
1959
1959
 
1960
- function renderLinearSystemGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(LinearSystemGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getLinearSystemGraphDescription(state,i18n)}}const LinearSystemGraph=props=>{const{dispatch}=props;const{coords:lines}=props.graphState;const{strings,locale}=usePerseusI18n();const id=React.useId();const intersectionId=`${id}-intersection`;const intersectionPoint=geometry.getLineIntersection(lines[0],lines[1]);const intersectionDescription=intersectionPoint?strings.srLinearSystemIntersection({x:srFormatNumber(intersectionPoint[0],locale),y:srFormatNumber(intersectionPoint[1],locale)}):strings.srLinearSystemParallel;const linesAriaInfo=lines.map((line,i)=>{return {pointsDescriptionId:`${id}-line${i+1}-points`,interceptDescriptionId:`${id}-line${i+1}-intercept`,slopeDescriptionId:`${id}-line${i+1}-slope`,pointsDescription:strings.srLinearSystemPoints({lineNumber:i+1,point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)}),interceptDescription:getInterceptStringForLine(line,strings,locale),slopeDescription:getSlopeStringForLine(line,strings)}});const individualLineDescriptions=linesAriaInfo.map(({pointsDescriptionId,interceptDescriptionId,slopeDescriptionId})=>`${pointsDescriptionId} ${interceptDescriptionId} ${slopeDescriptionId}`).join(" ");return jsxRuntimeExports.jsxs("g",{"aria-label":strings.srLinearSystemGraph,"aria-describedby":`${individualLineDescriptions} ${intersectionId}`,children:[lines?.map((line,i)=>jsxRuntimeExports.jsx(MovableLine,{points:line,ariaLabels:{point1AriaLabel:strings.srLinearSystemPoint({lineNumber:i+1,pointSequence:1,x:srFormatNumber(line[0][0],locale),y:srFormatNumber(line[0][1],locale)}),point2AriaLabel:strings.srLinearSystemPoint({lineNumber:i+1,pointSequence:2,x:srFormatNumber(line[1][0],locale),y:srFormatNumber(line[1][1],locale)}),grabHandleAriaLabel:strings.srLinearSystemGrabHandle({lineNumber:i+1,point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)})},ariaDescribedBy:`${linesAriaInfo[i].interceptDescriptionId} ${linesAriaInfo[i].slopeDescriptionId} ${intersectionId}`,onMoveLine:delta=>{dispatch(actions.linearSystem.moveLine(i,delta));},extend:{start:true,end:true},onMovePoint:(endpointIndex,destination)=>dispatch(actions.linearSystem.movePointInFigure(i,endpointIndex,destination))},i)),linesAriaInfo.map(({pointsDescriptionId,interceptDescriptionId,slopeDescriptionId,pointsDescription,interceptDescription,slopeDescription},i)=>jsxRuntimeExports.jsxs("span",{children:[jsxRuntimeExports.jsx(SRDescInSVG,{id:pointsDescriptionId,children:pointsDescription}),jsxRuntimeExports.jsx(SRDescInSVG,{id:interceptDescriptionId,children:interceptDescription}),jsxRuntimeExports.jsx(SRDescInSVG,{id:slopeDescriptionId,children:slopeDescription})]},`line-descriptions-${i}`)),jsxRuntimeExports.jsx(SRDescInSVG,{id:intersectionId,children:intersectionDescription})]})};function getLinearSystemGraphDescription(state,i18n){const{strings,locale}=i18n;const{coords:lines}=state;const graphDescription=strings.srLinearSystemGraph;const lineDescriptions=lines.map((line,i)=>{const point1=line[0];const point2=line[1];return strings.srLinearSystemPoints({lineNumber:i+1,point1X:srFormatNumber(point1[0],locale),point1Y:srFormatNumber(point1[1],locale),point2X:srFormatNumber(point2[0],locale),point2Y:srFormatNumber(point2[1],locale)})});const allDescriptions=[graphDescription,...lineDescriptions];return strings.srInteractiveElements({elements:allDescriptions.join(" ")})}
1960
+ function renderLinearSystemGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(LinearSystemGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getLinearSystemGraphDescription(state,i18n)}}const LinearSystemGraph=props=>{const{dispatch}=props;const{coords:lines}=props.graphState;const{strings,locale}=usePerseusI18n();const id=React.useId();const intersectionId=`${id}-intersection`;const intersectionPoint=geometry.getLineIntersection(lines[0],lines[1]);const intersectionDescription=intersectionPoint?strings.srLinearSystemIntersection({x:srFormatNumber(intersectionPoint[0],locale),y:srFormatNumber(intersectionPoint[1],locale)}):strings.srLinearSystemParallel;const linesAriaInfo=lines.map((line,i)=>{return {pointsDescriptionId:`${id}-line${i+1}-points`,interceptDescriptionId:`${id}-line${i+1}-intercept`,slopeDescriptionId:`${id}-line${i+1}-slope`,pointsDescription:strings.srLinearSystemPoints({lineNumber:i+1,point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)}),interceptDescription:getInterceptStringForLine(line,strings,locale),slopeDescription:getSlopeStringForLine(line,strings)}});const individualLineDescriptions=linesAriaInfo.map(({pointsDescriptionId,interceptDescriptionId,slopeDescriptionId})=>`${pointsDescriptionId} ${interceptDescriptionId} ${slopeDescriptionId}`).join(" ");return jsxRuntimeExports.jsxs("g",{"aria-label":strings.srLinearSystemGraph,"aria-describedby":`${individualLineDescriptions} ${intersectionId}`,children:[lines?.map((line,i)=>jsxRuntimeExports.jsx(MovableLine,{points:line,ariaLabels:{point1AriaLabel:strings.srLinearSystemPoint({lineNumber:i+1,pointSequence:1,x:srFormatNumber(line[0][0],locale),y:srFormatNumber(line[0][1],locale)}),point2AriaLabel:strings.srLinearSystemPoint({lineNumber:i+1,pointSequence:2,x:srFormatNumber(line[1][0],locale),y:srFormatNumber(line[1][1],locale)}),grabHandleAriaLabel:strings.srLinearSystemGrabHandle({lineNumber:i+1,point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)})},ariaDescribedBy:`${linesAriaInfo[i].interceptDescriptionId} ${linesAriaInfo[i].slopeDescriptionId} ${intersectionId}`,onMoveLine:newStart=>{dispatch(actions.linearSystem.moveLine(i,newStart));},extend:{start:true,end:true},onMovePoint:(endpointIndex,destination)=>dispatch(actions.linearSystem.movePointInFigure(i,endpointIndex,destination))},i)),linesAriaInfo.map(({pointsDescriptionId,interceptDescriptionId,slopeDescriptionId,pointsDescription,interceptDescription,slopeDescription},i)=>jsxRuntimeExports.jsxs("span",{children:[jsxRuntimeExports.jsx(SRDescInSVG,{id:pointsDescriptionId,children:pointsDescription}),jsxRuntimeExports.jsx(SRDescInSVG,{id:interceptDescriptionId,children:interceptDescription}),jsxRuntimeExports.jsx(SRDescInSVG,{id:slopeDescriptionId,children:slopeDescription})]},`line-descriptions-${i}`)),jsxRuntimeExports.jsx(SRDescInSVG,{id:intersectionId,children:intersectionDescription})]})};function getLinearSystemGraphDescription(state,i18n){const{strings,locale}=i18n;const{coords:lines}=state;const graphDescription=strings.srLinearSystemGraph;const lineDescriptions=lines.map((line,i)=>{const point1=line[0];const point2=line[1];return strings.srLinearSystemPoints({lineNumber:i+1,point1X:srFormatNumber(point1[0],locale),point1Y:srFormatNumber(point1[1],locale),point2X:srFormatNumber(point2[0],locale),point2Y:srFormatNumber(point2[1],locale)})});const allDescriptions=[graphDescription,...lineDescriptions];return strings.srInteractiveElements({elements:allDescriptions.join(" ")})}
1961
1961
 
1962
- const{getLogarithmCoefficients: getLogarithmCoefficients$1}=coefficients;function renderLogarithmGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(LogarithmGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getLogarithmDescription(state,i18n)}}function LogarithmGraph(props){const{dispatch,graphState}=props;const{interactiveColor,range}=useGraphConfig();const i18n=usePerseusI18n();const id=React.useId();const descriptionId=id+"-description";const{coords,asymptote,snapStep}=graphState;const coeffRef=React.useRef({a:1,b:1,c:0});const coeffs=getLogarithmCoefficients$1(coords,asymptote);if(coeffs!==undefined){coeffRef.current=coeffs;}const asymptoteX=asymptote;const xMin=range[0][0];const xMax=range[0][1];const yMin=range[1][0];const yMax=range[1][1];const yPadding=(yMax-yMin)*2;const pointsRightOfAsymptote=coords[0][X]>asymptoteX;const{srLogarithmGraph,srLogarithmDescription,srLogarithmPoint1,srLogarithmPoint2,srLogarithmAsymptote}=describeLogarithmGraph(graphState,i18n);const asymptoteBottom=[asymptoteX,yMin];const asymptoteTop=[asymptoteX,yMax];const asymptoteMidY=(yMin+yMax)/2;const asymptoteMid=[asymptoteX,asymptoteMidY];const[bottomPx,topPx,midPx]=useTransformVectorsToPixels(asymptoteBottom,asymptoteTop,asymptoteMid);return jsxRuntimeExports.jsxs("g",{"aria-label":srLogarithmGraph,"aria-describedby":descriptionId,children:[jsxRuntimeExports.jsx(Plot$2.OfX,{y:x=>{const y=computeLogarithm(coeffRef.current,x);if(isNaN(y)){return NaN}if(y<yMin-yPadding||y>yMax+yPadding){return NaN}return y},color:interactiveColor,svgPathProps:{"aria-hidden":true},domain:pointsRightOfAsymptote?[asymptoteX+.001,xMax]:[xMin,asymptoteX-.001]}),jsxRuntimeExports.jsx(MovableAsymptote,{start:bottomPx,end:topPx,mid:midPx,point:asymptoteMid,onMove:newPoint=>dispatch(actions.logarithm.moveCenter(newPoint)),constrainKeyboardMovement:p=>constrainAsymptoteKeyboard(p,coords,snapStep),orientation:"vertical",ariaLabel:srLogarithmAsymptote}),coords.map((coord,i)=>jsxRuntimeExports.jsx(MovablePoint$1,{ariaLabel:i===0?srLogarithmPoint1:srLogarithmPoint2,point:coord,sequenceNumber:i+1,constrain:getLogarithmKeyboardConstraint(coords,asymptote,snapStep,i,range),onMove:destination=>dispatch(actions.logarithm.movePoint(i,destination))},"point-"+i)),jsxRuntimeExports.jsx(SRDescInSVG,{id:descriptionId,children:srLogarithmDescription})]})}const constrainAsymptoteKeyboard=(p,coords,snapStep)=>constrainAsymptoteKeyboardMovement(p,coords,snapStep,"vertical");const getLogarithmKeyboardConstraint=(coords,asymptote,snapStep,pointIndex,range)=>{const otherPoint=coords[1-pointIndex];const asymptoteX=asymptote;return getAsymptoteGraphKeyboardConstraint(coords,snapStep,pointIndex,coord=>{const clamped=snap(snapStep,bound$1({snapStep,range,point:coord}));const clampedX=clamped[X];const clampedY=clamped[Y];if(coord[X]===asymptoteX||clampedX===asymptoteX){return false}if(coord[X]===otherPoint[X]||clampedX===otherPoint[X]){return false}if(coord[Y]===otherPoint[Y]||clampedY===otherPoint[Y]){return false}const currentPoint=coords[pointIndex];const currentSide=currentPoint[X]>asymptoteX;const proposedSide=coord[X]>asymptoteX;if(currentSide!==proposedSide){const reflectedX=2*asymptoteX-otherPoint[X];const clampedReflectedX=snap(snapStep,bound$1({snapStep,range,point:[reflectedX,0]}))[X];if(reflectedX===coord[X]||clampedReflectedX===coord[X]||clampedReflectedX===clampedX){return false}}return true})};const computeLogarithm=function(coefficients,x){const{a,b,c}=coefficients;const arg=b*x+c;if(arg<=0){return NaN}return a*Math.log(arg)};function getLogarithmDescription(state,i18n){const strings=describeLogarithmGraph(state,i18n);return strings.srLogarithmInteractiveElements}function describeLogarithmGraph(state,i18n){const{strings,locale}=i18n;const{coords,asymptote}=state;const[point1,point2]=coords;const formattedPoint1={x:srFormatNumber(point1[X],locale),y:srFormatNumber(point1[Y],locale)};const formattedPoint2={x:srFormatNumber(point2[X],locale),y:srFormatNumber(point2[Y],locale)};const asymptoteXFormatted=srFormatNumber(asymptote,locale);return {srLogarithmGraph:strings.srLogarithmGraph,srLogarithmDescription:strings.srLogarithmDescription({point1X:formattedPoint1.x,point1Y:formattedPoint1.y,point2X:formattedPoint2.x,point2Y:formattedPoint2.y,asymptoteX:asymptoteXFormatted}),srLogarithmAsymptote:strings.srLogarithmAsymptote({asymptoteX:asymptoteXFormatted}),srLogarithmPoint1:strings.srLogarithmPoint1(formattedPoint1),srLogarithmPoint2:strings.srLogarithmPoint2(formattedPoint2),srLogarithmInteractiveElements:strings.srInteractiveElements({elements:strings.srLogarithmInteractiveElements({point1X:srFormatNumber(point1[X],locale),point1Y:srFormatNumber(point1[Y],locale),point2X:srFormatNumber(point2[X],locale),point2Y:srFormatNumber(point2[Y],locale),asymptoteX:asymptoteXFormatted})})}}
1962
+ const{getLogarithmCoefficients: getLogarithmCoefficients$1}=coefficients;function renderLogarithmGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(LogarithmGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getLogarithmDescription(state,i18n)}}function LogarithmGraph(props){const{dispatch,graphState}=props;const{interactiveColor,range}=useGraphConfig();const i18n=usePerseusI18n();const id=React.useId();const descriptionId=id+"-description";const{coords,asymptote,snapStep}=graphState;const coeffs=getLogarithmCoefficients$1(coords,asymptote);const asymptoteX=asymptote;const xMin=range[0][0];const xMax=range[0][1];const yMin=range[1][0];const yMax=range[1][1];const{srLogarithmGraph,srLogarithmDescription,srLogarithmPoint1,srLogarithmPoint2,srLogarithmAsymptote}=describeLogarithmGraph(graphState,i18n);const asymptoteBottom=[asymptoteX,yMin];const asymptoteTop=[asymptoteX,yMax];const handleCoord=getAsymptoteHandleCoord("vertical",range,asymptote);const[bottomPx,topPx,midPx]=useTransformVectorsToPixels(asymptoteBottom,asymptoteTop,handleCoord);return jsxRuntimeExports.jsxs("g",{"aria-label":srLogarithmGraph,"aria-describedby":descriptionId,children:[jsxRuntimeExports.jsx(MovableAsymptote,{start:bottomPx,end:topPx,mid:midPx,point:handleCoord,onMove:newPoint=>dispatch(actions.logarithm.moveCenter(newPoint)),constrainKeyboardMovement:p=>skipAsymptoteKeyboardOverPoint(p,asymptote,coords,handleCoord,snapStep,"vertical"),orientation:"vertical",ariaLabel:srLogarithmAsymptote,children:coeffs!==undefined&&renderLogarithmCurve({coeffs,coords,asymptoteX,xMin,xMax,yMin,yMax,interactiveColor})}),coords.map((coord,i)=>jsxRuntimeExports.jsx(MovablePoint$1,{ariaLabel:i===0?srLogarithmPoint1:srLogarithmPoint2,point:coord,sequenceNumber:i+1,constrain:getLogarithmKeyboardConstraint(coords,asymptote,snapStep,i,range),onMove:destination=>dispatch(actions.logarithm.movePoint(i,destination))},"point-"+i)),jsxRuntimeExports.jsx(SRDescInSVG,{id:descriptionId,children:srLogarithmDescription})]})}const getLogarithmKeyboardConstraint=(coords,asymptote,snapStep,pointIndex,range)=>{const otherPoint=coords[1-pointIndex];const handleCoord=getAsymptoteHandleCoord("vertical",range,asymptote);return getAsymptoteGraphKeyboardConstraint(coords,snapStep,pointIndex,coord=>{const clamped=snap(snapStep,bound$1({snapStep,range,point:coord}));const clampedX=clamped[X];const clampedY=clamped[Y];if(coord[X]===otherPoint[X]||clampedX===otherPoint[X]){return false}if(coord[Y]===otherPoint[Y]||clampedY===otherPoint[Y]){return false}if(clampedX===handleCoord[X]&&clampedY===handleCoord[Y]){return false}return true})};const computeLogarithm=function(coefficients,x){const{a,b,c}=coefficients;const arg=b*x+c;if(arg<=0){return NaN}return a*Math.log(arg)};function renderLogarithmCurve({coeffs,coords,asymptoteX,xMin,xMax,yMin,yMax,interactiveColor}){const offScreenMargin=2;const{a,b,c}=coeffs;const targetY=a>0?yMin-offScreenMargin:yMax+offScreenMargin;const computedX=(Math.exp(targetY/a)-c)/b;const computedOffset=Math.abs(computedX-asymptoteX);const domainOffset=isFinite(computedOffset)&&computedOffset>0?computedOffset:1e-8;const pointsRightOfAsymptote=coords[0][X]>asymptoteX;return jsxRuntimeExports.jsx(Plot$2.OfX,{y:x=>computeLogarithm(coeffs,x),color:interactiveColor,svgPathProps:{"aria-hidden":true,style:{pointerEvents:"none"}},domain:pointsRightOfAsymptote?[asymptoteX+domainOffset,xMax]:[xMin,asymptoteX-domainOffset]})}function getLogarithmDescription(state,i18n){const strings=describeLogarithmGraph(state,i18n);return strings.srLogarithmInteractiveElements}function describeLogarithmGraph(state,i18n){const{strings,locale}=i18n;const{coords,asymptote}=state;const[point1,point2]=coords;const formattedPoint1={x:srFormatNumber(point1[X],locale),y:srFormatNumber(point1[Y],locale)};const formattedPoint2={x:srFormatNumber(point2[X],locale),y:srFormatNumber(point2[Y],locale)};const asymptoteXFormatted=srFormatNumber(asymptote,locale);return {srLogarithmGraph:strings.srLogarithmGraph,srLogarithmDescription:strings.srLogarithmDescription({point1X:formattedPoint1.x,point1Y:formattedPoint1.y,point2X:formattedPoint2.x,point2Y:formattedPoint2.y,asymptoteX:asymptoteXFormatted}),srLogarithmAsymptote:strings.srLogarithmAsymptote({asymptoteX:asymptoteXFormatted}),srLogarithmPoint1:strings.srLogarithmPoint1(formattedPoint1),srLogarithmPoint2:strings.srLogarithmPoint2(formattedPoint2),srLogarithmInteractiveElements:strings.srInteractiveElements({elements:strings.srLogarithmInteractiveElements({point1X:srFormatNumber(point1[X],locale),point1Y:srFormatNumber(point1[Y],locale),point2X:srFormatNumber(point2[X],locale),point2Y:srFormatNumber(point2[Y],locale),asymptoteX:asymptoteXFormatted})})}}
1963
1963
 
1964
1964
  function renderPointGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(PointGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getPointGraphDescription(state,i18n)}}function PointGraph(props){const{numPoints}=props.graphState;const graphConfig=useGraphConfig();const pointsRef=React.useRef([]);const{range:[x,y]}=graphConfig;const[[left,top]]=useTransformVectorsToPixels([x[0],y[1]]);React.useEffect(()=>{const focusedIndex=props.graphState.focusedPointIndex;if(focusedIndex!=null){pointsRef.current[focusedIndex]?.focus();}},[props.graphState.focusedPointIndex,props.graphState.coords.length,pointsRef]);const statefulProps={...props,graphConfig,pointsRef,top,left};if(numPoints==="unlimited"){return UnlimitedPointGraph(statefulProps)}return LimitedPointGraph(statefulProps)}function LimitedPointGraph(statefulProps){const{dispatch}=statefulProps;return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:statefulProps.graphState.coords.map((point,i)=>jsxRuntimeExports.jsx(MovablePoint$1,{point:point,sequenceNumber:i+1,onMove:destination=>dispatch(actions.pointGraph.movePoint(i,destination))},i))})}function UnlimitedPointGraph(statefulProps){const{dispatch,graphConfig,pointsRef,top,left}=statefulProps;const{coords}=statefulProps.graphState;const[isCurrentlyDragging,setIsCurrentlyDragging]=React.useState(false);const dragEndCallbackTimer=useTimeout(()=>setIsCurrentlyDragging(false),400);const{graphDimensionsInPixels}=graphConfig;const widthPx=graphDimensionsInPixels[0];const heightPx=graphDimensionsInPixels[1];return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx("rect",{style:{fill:"rgba(0,0,0,0)",cursor:"crosshair"},width:widthPx,height:heightPx,x:left,y:top,onClick:event=>{if(isCurrentlyDragging){return}const elementRect=event.currentTarget.getBoundingClientRect();const zoomFactor=getCSSZoomFactor(event.currentTarget);const x=(event.clientX-elementRect.x)/zoomFactor;const y=(event.clientY-elementRect.y)/zoomFactor;const graphCoordinates=pixelsToVectors([[x,y]],graphConfig);dispatch(actions.pointGraph.addPoint(graphCoordinates[0]));}}),coords.map((point,i)=>jsxRuntimeExports.jsx(MovablePoint$1,{point:point,sequenceNumber:i+1,onDragStart:()=>{dragEndCallbackTimer.clear();setIsCurrentlyDragging(true);},onMove:destination=>{dispatch(actions.pointGraph.movePoint(i,destination));},onDragEnd:()=>{dragEndCallbackTimer.set();},ref:ref=>{pointsRef.current[i]=ref;},onFocus:()=>{dispatch(actions.pointGraph.focusPoint(i));},onClick:()=>{dispatch(actions.pointGraph.clickPoint(i));}},i))]})}function getPointGraphDescription(state,i18n){const{strings,locale}=i18n;if(state.coords.length===0){return strings.srNoInteractiveElements}const pointDescriptions=state.coords.map(([x,y],index)=>strings.srPointAtCoordinates({num:index+1,x:srFormatNumber(x,locale),y:srFormatNumber(y,locale)}));return strings.srInteractiveElements({elements:pointDescriptions.join(" ")})}
1965
1965
 
1966
- const{magnitude: magnitude$2,vector: vector$2}=geometry;function initializeGraphState(params){const{graph,step,snapStep,range}=params;const shared={hasBeenInteractedWith:false,range,snapStep};switch(graph.type){case "segment":return {...shared,type:"segment",coords:getSegmentCoords(graph,range,step)};case "linear":return {...shared,type:graph.type,coords:getLineCoords(graph,range,step)};case "ray":return {...shared,type:graph.type,coords:getLineCoords(graph,range,step)};case "linear-system":return {...shared,type:graph.type,coords:getLinearSystemCoords(graph,range,step)};case "polygon":return {...shared,type:"polygon",numSides:graph.numSides||0,showAngles:Boolean(graph.showAngles),showSides:Boolean(graph.showSides),coords:getPolygonCoords(graph,range,step),snapTo:graph.snapTo??"grid",focusedPointIndex:null,showRemovePointButton:false,interactionMode:"mouse",showKeyboardInteractionInvitation:false,closedPolygon:false};case "point":return {...shared,type:graph.type,coords:getPointCoords(graph,range,step),numPoints:graph.numPoints||0,focusedPointIndex:null,showRemovePointButton:false,interactionMode:"mouse",showKeyboardInteractionInvitation:false};case "circle":return {...shared,type:graph.type,...getCircleCoords(graph)};case "quadratic":return {...shared,type:graph.type,coords:getQuadraticCoords(graph,range,step)};case "sinusoid":return {...shared,type:graph.type,coords:getSinusoidCoords(graph,range,step)};case "exponential":return {...shared,type:graph.type,...getExponentialCoords(graph,range,step)};case "angle":return {...shared,type:graph.type,showAngles:Boolean(graph.showAngles),coords:getAngleCoords({graph,range,step}),angleOffsetDeg:Number(graph.angleOffsetDeg),allowReflexAngles:Boolean(graph.allowReflexAngles),snapDegrees:Number(graph.snapDegrees)};case "none":return {...shared,type:"none"};case "absolute-value":return {...shared,type:graph.type,coords:getAbsoluteValueCoords(graph,range,step)};case "tangent":return {...shared,type:graph.type,coords:getTangentCoords(graph,range,step)};case "logarithm":return {...shared,type:graph.type,...getLogarithmCoords(graph,range,step)};default:throw new UnreachableCaseError(graph)}}function getPointCoords(graph,range,step){const numPoints=graph.numPoints||1;let coords=graph.coords?.slice();if(coords){return coords}const startCoords=graph.startCoords?.slice();if(startCoords){return startCoords}switch(numPoints){case 1:coords=[graph.coord||[0,0]];break;case 2:coords=[[-5,0],[5,0]];break;case 3:coords=[[-5,0],[0,0],[5,0]];break;case 4:coords=[[-6,0],[-2,0],[2,0],[6,0]];break;case 5:coords=[[-6,0],[-3,0],[0,0],[3,0],[6,0]];break;case 6:coords=[[-5,0],[-3,0],[-1,0],[1,0],[3,0],[5,0]];break;default:coords=[];break}const newCoords=normalizeCoords(coords,[[-10,10],[-10,10]]);return normalizePoints(range,step,newCoords)}function getSegmentCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}const ys=n=>{switch(n){case 2:return [5,-5];case 3:return [5,0,-5];case 4:return [6,2,-2,-6];case 5:return [6,3,0,-3,-6];case 6:return [5,3,1,-1,-3,-5];default:return [5]}};const defaultRange=[[-10,10],[-10,10]];return ys(graph.numSegments).map(y=>{let endpoints=[[-5,y],[5,y]];endpoints=normalizeCoords(endpoints,defaultRange);endpoints=normalizePoints(range,step,endpoints);return endpoints})}const defaultLinearCoords=[[[.25,.75],[.75,.75]],[[.25,.25],[.75,.25]]];function getLineCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}return normalizePoints(range,step,defaultLinearCoords[0])}function getLinearSystemCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}return defaultLinearCoords.map(points=>normalizePoints(range,step,points))}function getPolygonCoords(graph,range,step){let coords=graph.coords?.slice();if(coords){return coords}const startCoords=graph.startCoords?.slice();if(startCoords){return startCoords}const n=graph.numSides||3;if(n==="unlimited"){coords=[];}else {const angle=2*Math.PI/n;const offset=(1/n-1/2)*Math.PI;const radius=graph.snapTo==="sides"?Math.sqrt(3)/3*7:4;coords=[...Array(n).keys()].map(i=>[radius*Math.cos(i*angle+offset),radius*Math.sin(i*angle+offset)]);}coords=normalizeCoords(coords,[[-10,10],[-10,10]]);const snapToGrid=!["angles","sides"].includes(graph.snapTo||"");coords=normalizePoints(range,step,coords,!snapToGrid);return coords}function getSinusoidCoords(graph,range,step){if(graph.coords){return [graph.coords[0],graph.coords[1]]}if(graph.startCoords){return [graph.startCoords[0],graph.startCoords[1]]}let coords=[[.5,.5],[.65,.6]];coords=normalizePoints(range,step,coords,true);return coords}function getAbsoluteValueCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}const defaultCoords=[[.5,.5],[.75,.75]];return normalizePoints(range,step,defaultCoords,true)}function getTangentCoords(graph,range,step){if(graph.coords){return [graph.coords[0],graph.coords[1]]}if(graph.startCoords){return [graph.startCoords[0],graph.startCoords[1]]}let coords=[[.5,.5],[.75,.75]];coords=normalizePoints(range,step,coords,true);return coords}function getQuadraticCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}const defaultCoords=[[.25,.75],[.5,.25],[.75,.75]];return normalizePoints(range,step,defaultCoords,true)}function getCircleCoords(graph){if(graph.center!=null&&graph.radius!=null){return {center:graph.center,radiusPoint:vec.add(graph.center,[graph.radius,0])}}if(graph.startCoords?.center&&graph.startCoords.radius){return {center:graph.startCoords.center,radiusPoint:vec.add(graph.startCoords.center,[graph.startCoords.radius,0])}}return {center:[0,0],radiusPoint:[2,0]}}function getExponentialCoords(graph,range,step){if(graph.coords&&graph.asymptote!=null){return {coords:[graph.coords[0],graph.coords[1]],asymptote:graph.asymptote}}let defaultCoords=[[.5,.55],[.75,.75]];defaultCoords=normalizePoints(range,step,defaultCoords,true);const coords=graph.startCoords?graph.startCoords.coords:defaultCoords;const asymptote=graph.startCoords?graph.startCoords.asymptote:0;return {coords,asymptote}}function getLogarithmCoords(graph,range,step){if(graph.coords&&graph.asymptote!=null){return {coords:[graph.coords[0],graph.coords[1]],asymptote:graph.asymptote}}let defaultCoords=[[.55,.55],[.75,.75]];defaultCoords=normalizePoints(range,step,defaultCoords,true);const coords=graph.startCoords?graph.startCoords.coords:defaultCoords;const asymptote=graph.startCoords?graph.startCoords.asymptote:0;return {coords,asymptote}}const getAngleCoords=params=>{const{graph,range,step}=params;if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}const{snapDegrees,angleOffsetDeg}=graph;const snap=snapDegrees||1;let angle=snap;while(angle<20){angle+=snap;}angle=angle*Math.PI/180;const offset=(angleOffsetDeg||0)*Math.PI/180;let defaultCoords=[[.85,.5],[.5,.5]];defaultCoords=normalizePoints(range,step,defaultCoords,true);const radius=magnitude$2(vector$2(...defaultCoords));const coords=[...defaultCoords,[0,0]];coords[0]=[coords[1][0]+radius*Math.cos(offset),coords[1][1]+radius*Math.sin(offset)];coords[2]=[coords[1][0]+radius*Math.cos(angle+offset),coords[1][1]+radius*Math.sin(angle+offset)];return coords};
1966
+ const{magnitude: magnitude$2,vector: vector$2}=geometry;function initializeGraphState(params){const{graph,step,snapStep,range}=params;const shared={hasBeenInteractedWith:false,range,snapStep};switch(graph.type){case "segment":return {...shared,type:"segment",coords:getSegmentCoords(graph,range,step)};case "linear":return {...shared,type:graph.type,coords:getLineCoords(graph,range,step)};case "ray":return {...shared,type:graph.type,coords:getLineCoords(graph,range,step)};case "linear-system":return {...shared,type:graph.type,coords:getLinearSystemCoords(graph,range,step)};case "polygon":return {...shared,type:"polygon",numSides:graph.numSides||0,showAngles:Boolean(graph.showAngles),showSides:Boolean(graph.showSides),coords:getPolygonCoords(graph,range,step),snapTo:graph.snapTo??"grid",focusedPointIndex:null,showRemovePointButton:false,interactionMode:"mouse",showKeyboardInteractionInvitation:false,closedPolygon:false};case "point":return {...shared,type:graph.type,coords:getPointCoords(graph,range,step),numPoints:graph.numPoints||0,focusedPointIndex:null,showRemovePointButton:false,interactionMode:"mouse",showKeyboardInteractionInvitation:false};case "circle":return {...shared,type:graph.type,...getCircleCoords(graph)};case "quadratic":return {...shared,type:graph.type,coords:getQuadraticCoords(graph,range,step)};case "sinusoid":return {...shared,type:graph.type,coords:getSinusoidCoords(graph,range,step)};case "exponential":return {...shared,type:graph.type,...getExponentialCoords(graph,range,step)};case "angle":return {...shared,type:graph.type,showAngles:Boolean(graph.showAngles),coords:getAngleCoords({graph,range,step}),angleOffsetDeg:Number(graph.angleOffsetDeg),allowReflexAngles:Boolean(graph.allowReflexAngles),snapDegrees:Number(graph.snapDegrees)};case "none":return {...shared,type:"none"};case "absolute-value":return {...shared,type:graph.type,coords:getAbsoluteValueCoords(graph,range,step)};case "tangent":return {...shared,type:graph.type,coords:getTangentCoords(graph,range,step)};case "logarithm":return {...shared,type:graph.type,...getLogarithmCoords(graph,range,step)};case "vector":return {...shared,type:graph.type,coords:getVectorCoords(graph,range,step)};default:throw new UnreachableCaseError(graph)}}function getPointCoords(graph,range,step){const numPoints=graph.numPoints||1;let coords=graph.coords?.slice();if(coords){return coords}const startCoords=graph.startCoords?.slice();if(startCoords){return startCoords}switch(numPoints){case 1:coords=[graph.coord||[0,0]];break;case 2:coords=[[-5,0],[5,0]];break;case 3:coords=[[-5,0],[0,0],[5,0]];break;case 4:coords=[[-6,0],[-2,0],[2,0],[6,0]];break;case 5:coords=[[-6,0],[-3,0],[0,0],[3,0],[6,0]];break;case 6:coords=[[-5,0],[-3,0],[-1,0],[1,0],[3,0],[5,0]];break;default:coords=[];break}const newCoords=normalizeCoords(coords,[[-10,10],[-10,10]]);return normalizePoints(range,step,newCoords)}function getSegmentCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}const ys=n=>{switch(n){case 2:return [5,-5];case 3:return [5,0,-5];case 4:return [6,2,-2,-6];case 5:return [6,3,0,-3,-6];case 6:return [5,3,1,-1,-3,-5];default:return [5]}};const defaultRange=[[-10,10],[-10,10]];return ys(graph.numSegments).map(y=>{let endpoints=[[-5,y],[5,y]];endpoints=normalizeCoords(endpoints,defaultRange);endpoints=normalizePoints(range,step,endpoints);return endpoints})}const defaultLinearCoords=[[[.25,.75],[.75,.75]],[[.25,.25],[.75,.25]]];function getLineCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}return normalizePoints(range,step,defaultLinearCoords[0])}function getVectorCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}return normalizePoints(range,step,[[.6,.6],[.85,.85]])}function getLinearSystemCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}return defaultLinearCoords.map(points=>normalizePoints(range,step,points))}function getPolygonCoords(graph,range,step){let coords=graph.coords?.slice();if(coords){return coords}const startCoords=graph.startCoords?.slice();if(startCoords){return startCoords}const n=graph.numSides||3;if(n==="unlimited"){coords=[];}else {const angle=2*Math.PI/n;const offset=(1/n-1/2)*Math.PI;const radius=graph.snapTo==="sides"?Math.sqrt(3)/3*7:4;coords=[...Array(n).keys()].map(i=>[radius*Math.cos(i*angle+offset),radius*Math.sin(i*angle+offset)]);}coords=normalizeCoords(coords,[[-10,10],[-10,10]]);const snapToGrid=!["angles","sides"].includes(graph.snapTo||"");coords=normalizePoints(range,step,coords,!snapToGrid);return coords}function getSinusoidCoords(graph,range,step){if(graph.coords){return [graph.coords[0],graph.coords[1]]}if(graph.startCoords){return [graph.startCoords[0],graph.startCoords[1]]}let coords=[[.5,.5],[.65,.6]];coords=normalizePoints(range,step,coords,true);return coords}function getAbsoluteValueCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}const defaultCoords=[[.5,.5],[.75,.75]];return normalizePoints(range,step,defaultCoords,true)}function getTangentCoords(graph,range,step){if(graph.coords){return [graph.coords[0],graph.coords[1]]}if(graph.startCoords){return [graph.startCoords[0],graph.startCoords[1]]}let coords=[[.5,.5],[.75,.75]];coords=normalizePoints(range,step,coords,true);return coords}function getQuadraticCoords(graph,range,step){if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}const defaultCoords=[[.25,.75],[.5,.25],[.75,.75]];return normalizePoints(range,step,defaultCoords,true)}function getCircleCoords(graph){if(graph.center!=null&&graph.radius!=null){return {center:graph.center,radiusPoint:vec.add(graph.center,[graph.radius,0])}}if(graph.startCoords?.center&&graph.startCoords.radius){return {center:graph.startCoords.center,radiusPoint:vec.add(graph.startCoords.center,[graph.startCoords.radius,0])}}return {center:[0,0],radiusPoint:[2,0]}}function getExponentialCoords(graph,range,step){if(graph.coords&&graph.asymptote!=null){return {coords:[graph.coords[0],graph.coords[1]],asymptote:graph.asymptote}}let defaultCoords=[[.5,.6],[.75,.75]];defaultCoords=normalizePoints(range,step,defaultCoords,true);const coords=graph.startCoords?graph.startCoords.coords:defaultCoords;const asymptote=graph.startCoords?graph.startCoords.asymptote:0;return {coords,asymptote}}function getLogarithmCoords(graph,range,step){if(graph.coords&&graph.asymptote!=null){return {coords:[graph.coords[0],graph.coords[1]],asymptote:graph.asymptote}}let defaultCoords=[[.6,.55],[.75,.75]];defaultCoords=normalizePoints(range,step,defaultCoords,true);const coords=graph.startCoords?graph.startCoords.coords:defaultCoords;const asymptote=graph.startCoords?graph.startCoords.asymptote:0;return {coords,asymptote}}const getAngleCoords=params=>{const{graph,range,step}=params;if(graph.coords){return graph.coords}if(graph.startCoords){return graph.startCoords}const{snapDegrees,angleOffsetDeg}=graph;const snap=snapDegrees||1;let angle=snap;while(angle<20){angle+=snap;}angle=angle*Math.PI/180;const offset=(angleOffsetDeg||0)*Math.PI/180;let defaultCoords=[[.85,.5],[.5,.5]];defaultCoords=normalizePoints(range,step,defaultCoords,true);const radius=magnitude$2(vector$2(...defaultCoords));const coords=[...defaultCoords,[0,0]];coords[0]=[coords[1][0]+radius*Math.cos(offset),coords[1][1]+radius*Math.sin(offset)];coords[2]=[coords[1][0]+radius*Math.cos(angle+offset),coords[1][1]+radius*Math.sin(angle+offset)];return coords};
1967
1967
 
1968
- const{getAngleFromVertex,getClockwiseAngle: getClockwiseAngle$1,polar}=angles;const{angleMeasures,ccw,lawOfCosines,magnitude: magnitude$1,polygonSidesIntersect,reverseVector,sign,vector: vector$1}=geometry;const{getQuadraticCoefficients: getQuadraticCoefficients$2}=coefficients;const minDistanceBetweenAngleVertexAndSidePoint=2;function interactiveGraphReducer(state,action){switch(action.type){case REINITIALIZE:return initializeGraphState(action.params);case MOVE_POINT_IN_FIGURE:return doMovePointInFigure(state,action);case MOVE_LINE:return doMoveLine(state,action);case MOVE_ALL:return doMoveAll(state,action);case MOVE_POINT:return doMovePoint(state,action);case MOVE_CENTER:return doMoveCenter(state,action);case MOVE_RADIUS_POINT:return doMoveRadiusPoint(state,action);case CHANGE_SNAP_STEP:return doChangeSnapStep(state,action);case CHANGE_RANGE:return doChangeRange(state,action);case ADD_POINT:return doAddPoint(state,action);case REMOVE_POINT:return doRemovePoint(state,action);case FOCUS_POINT:return doFocusPoint(state,action);case BLUR_POINT:return doBlurPoint(state);case DELETE_INTENT:return doDeleteIntent(state);case CLICK_POINT:return doClickPoint(state,action);case CLOSE_POLYGON:return doClosePolygon(state);case OPEN_POLYGON:return doOpenPolygon(state);case CHANGE_INTERACTION_MODE:return doChangeInteractionMode(state,action);case CHANGE_KEYBOARD_INVITATION_VISIBILITY:return doChangeKeyboardInvitationVisibility(state,action);default:throw new UnreachableCaseError(action)}}function doDeleteIntent(state,action){if(isUnlimitedGraphState(state)){if(state.focusedPointIndex!==null){return doRemovePoint(state,actions.pointGraph.removePoint(state.focusedPointIndex))}}return state}function doFocusPoint(state,action){switch(state.type){case "polygon":case "point":return {...state,focusedPointIndex:action.index};default:return state}}function doBlurPoint(state,action){switch(state.type){case "polygon":case "point":const nextState={...state,showRemovePointButton:false};if(state.interactionMode==="mouse"){nextState.focusedPointIndex=null;}return nextState;default:return state}}function doClickPoint(state,action){if(isUnlimitedGraphState(state)){return {...state,focusedPointIndex:action.index,showRemovePointButton:true}}return state}function doClosePolygon(state){if(isUnlimitedGraphState(state)&&state.type==="polygon"){const noDupedPoints=getArrayWithoutDuplicates(state.coords);return {...state,coords:noDupedPoints,closedPolygon:true}}return state}function doOpenPolygon(state){if(isUnlimitedGraphState(state)&&state.type==="polygon"){return {...state,closedPolygon:false}}return state}function doChangeInteractionMode(state,action){if(isUnlimitedGraphState(state)){const nextKeyboardInvitation=action.mode==="keyboard"?false:state.showKeyboardInteractionInvitation;return {...state,interactionMode:action.mode,showKeyboardInteractionInvitation:nextKeyboardInvitation}}return state}function doChangeKeyboardInvitationVisibility(state,action){if(isUnlimitedGraphState(state)){return {...state,showKeyboardInteractionInvitation:action.shouldShow,hasBeenInteractedWith:true}}return state}function doMovePointInFigure(state,action){switch(state.type){case "segment":case "linear-system":{const newCoords=updateAtIndex({array:state.coords,index:action.figureIndex,update:tuple=>setAtIndex({array:tuple,index:action.pointIndex,newValue:boundAndSnapToGrid(action.destination,state)})});const coordsToCheck=newCoords[action.figureIndex];if(coordsOverlap(coordsToCheck)){return state}return {...state,hasBeenInteractedWith:true,coords:newCoords}}case "linear":case "ray":{const newCoords=setAtIndex({array:state.coords,index:action.pointIndex,newValue:boundAndSnapToGrid(action.destination,state)});return {...state,hasBeenInteractedWith:true,coords:newCoords}}case "circle":throw new Error(`Don't use movePointInFigure for circle graphs. Use moveCenter or moveRadiusPoint.`);case "angle":case "none":case "point":case "polygon":case "quadratic":case "sinusoid":case "absolute-value":case "tangent":case "exponential":case "logarithm":throw new Error(`Don't use movePointInFigure for ${state.type} graphs. Use movePoint instead!`);default:throw new UnreachableCaseError(state)}}function doMoveLine(state,action){const{snapStep,range}=state;switch(state.type){case "segment":case "linear-system":{if(action.itemIndex===undefined){throw new Error("Please provide index of line to move")}const currentLine=state.coords[action.itemIndex];if(!currentLine){throw new Error("No line to move")}const change=getChange(currentLine,action.delta,{snapStep,range});const newLine=[snap(snapStep,vec.add(currentLine[0],change)),snap(snapStep,vec.add(currentLine[1],change))];const newCoords=setAtIndex({array:state.coords,index:action.itemIndex,newValue:newLine});return {...state,type:state.type,hasBeenInteractedWith:true,coords:newCoords}}case "linear":case "ray":{const currentLine=state.coords;const change=getChange(currentLine,action.delta,{snapStep,range});const newLine=[snap(snapStep,vec.add(currentLine[0],change)),snap(snapStep,vec.add(currentLine[1],change))];return {...state,type:state.type,hasBeenInteractedWith:true,coords:newLine}}default:return state}}function doMoveAll(state,action){const{snapStep,range}=state;switch(state.type){case "polygon":{let newCoords;if(state.snapTo==="sides"||state.snapTo==="angles"){const change=getChange(state.coords,action.delta,{snapStep:[0,0],range});newCoords=state.coords.map(point=>vec.add(point,change));}else {const change=getChange(state.coords,action.delta,{snapStep,range});newCoords=state.coords.map(point=>snap(snapStep,vec.add(point,change)));}return {...state,hasBeenInteractedWith:true,coords:newCoords}}default:return state}}function doMovePoint(state,action){switch(state.type){case "angle":const newState=(()=>{if(action.index===1){const updatedCoords=boundAndSnapAngleVertex(state,action);return {...state,hasBeenInteractedWith:true,coords:updatedCoords}}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundAndSnapAngleEndPoints(action.destination,state,action.index)})}})();if(angleSidePointsTooCloseToVertex(newState)){return state}return newState;case "polygon":let newValue;if(state.snapTo==="sides"){newValue=boundAndSnapToSides(action.destination,state,action.index);}else if(state.snapTo==="angles"){newValue=boundAndSnapToPolygonAngle(action.destination,state,action.index);}else {newValue=boundAndSnapToGrid(action.destination,state);}const newCoords=setAtIndex({array:state.coords,index:action.index,newValue:newValue});const polygonSidesCanIntersect=state.numSides==="unlimited"&&!state.closedPolygon;if(!polygonSidesCanIntersect&&polygonSidesIntersect(newCoords)){return state}return {...state,hasBeenInteractedWith:true,coords:newCoords};case "point":{return {...state,hasBeenInteractedWith:true,focusedPointIndex:action.index,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundToEdgeAndSnapToGrid(action.destination,state)})}}case "sinusoid":{const destination=action.destination;const boundDestination=boundAndSnapToGrid(destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;if(newCoords[0][X]===newCoords[1][X]){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "exponential":{const boundDestination=boundAndSnapToGrid(action.destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;const asymptoteY=state.asymptote;if(boundDestination[Y]===asymptoteY){return state}if(newCoords[0][X]===newCoords[1][X]){return state}const otherIndex=1-action.index;const otherPoint=state.coords[otherIndex];const movedSide=boundDestination[Y]>asymptoteY;const otherSide=otherPoint[Y]>asymptoteY;if(movedSide!==otherSide){const reflectedY=2*asymptoteY-otherPoint[Y];const updatedCoords=[...state.coords];updatedCoords[action.index]=boundDestination;updatedCoords[otherIndex]=boundAndSnapToGrid([otherPoint[X],reflectedY],state);if(updatedCoords[0][X]===updatedCoords[1][X]){return state}return {...state,hasBeenInteractedWith:true,coords:updatedCoords}}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "logarithm":{const boundDestination=boundAndSnapToGrid(action.destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;const asymptoteX=state.asymptote;if(boundDestination[X]===asymptoteX){return state}if(newCoords[0][X]===newCoords[1][X]){return state}if(newCoords[0][Y]===newCoords[1][Y]){return state}const otherIndex=1-action.index;const otherPoint=state.coords[otherIndex];const movedSide=boundDestination[X]>asymptoteX;const otherSide=otherPoint[X]>asymptoteX;if(movedSide!==otherSide){const reflectedX=2*asymptoteX-otherPoint[X];const updatedCoords=[...state.coords];updatedCoords[action.index]=boundDestination;updatedCoords[otherIndex]=boundAndSnapToGrid([reflectedX,otherPoint[Y]],state);if(updatedCoords[otherIndex][X]===asymptoteX){return state}if(updatedCoords[0][X]===updatedCoords[1][X]||updatedCoords[0][Y]===updatedCoords[1][Y]){return state}return {...state,hasBeenInteractedWith:true,coords:updatedCoords}}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "absolute-value":{const boundDestination=boundAndSnapToGrid(action.destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;if(newCoords[0][X]===newCoords[1][X]){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "tangent":{const boundDestination=boundAndSnapToGrid(action.destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;if(newCoords[0][X]===newCoords[1][X]){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "quadratic":{const newCoords=[...state.coords];const boundDestination=boundAndSnapToGrid(action.destination,state);newCoords[action.index]=boundDestination;const QuadraticCoefficients=getQuadraticCoefficients$2(newCoords);if(QuadraticCoefficients===undefined){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}default:throw new Error("The movePoint action is only for point, quadratic, and polygon graphs")}}function doMoveCenter(state,action){switch(state.type){case "circle":{const constrainedCenter=boundAndSnapToGrid(action.destination,state);const newRadiusPoint=[...vec.add(state.radiusPoint,vec.sub(constrainedCenter,state.center))];const[xMin,xMax]=state.range[X];const[radX]=newRadiusPoint;if(radX<xMin||radX>xMax){const xJumpDist=(radX-constrainedCenter[X])*2;const possibleNewX=radX-xJumpDist;if(possibleNewX>=xMin&&possibleNewX<=xMax){newRadiusPoint[X]=possibleNewX;}}return {...state,hasBeenInteractedWith:true,center:constrainedCenter,radiusPoint:newRadiusPoint}}case "exponential":{let newY=boundAndSnapToGrid(action.destination,state)[Y];const coords=state.coords;const stepY=state.snapStep[Y];const allAbove=coords[0][Y]>newY&&coords[1][Y]>newY;const allBelow=coords[0][Y]<newY&&coords[1][Y]<newY;if(!allAbove&&!allBelow){const topMost=Math.max(coords[0][Y],coords[1][Y]);const bottomMost=Math.min(coords[0][Y],coords[1][Y]);const midpoint=(topMost+bottomMost)/2;newY=newY>=midpoint?topMost+stepY:bottomMost-stepY;const insetY=inset(state.snapStep,state.range)[1];newY=clamp(newY,insetY[0],insetY[1]);const stillAllAbove=coords[0][Y]>newY&&coords[1][Y]>newY;const stillAllBelow=coords[0][Y]<newY&&coords[1][Y]<newY;if(!stillAllAbove&&!stillAllBelow){return state}}if(newY===coords[0][Y]||newY===coords[1][Y]){return state}return {...state,hasBeenInteractedWith:true,asymptote:newY}}case "logarithm":{let newX=boundAndSnapToGrid(action.destination,state)[X];const coords=state.coords;const stepX=state.snapStep[X];const allRight=coords[0][X]>newX&&coords[1][X]>newX;const allLeft=coords[0][X]<newX&&coords[1][X]<newX;if(!allRight&&!allLeft){const rightMost=Math.max(coords[0][X],coords[1][X]);const leftMost=Math.min(coords[0][X],coords[1][X]);const midpoint=(rightMost+leftMost)/2;newX=newX>=midpoint?rightMost+stepX:leftMost-stepX;const insetX=inset(state.snapStep,state.range)[0];newX=clamp(newX,insetX[0],insetX[1]);const stillAllRight=coords[0][X]>newX&&coords[1][X]>newX;const stillAllLeft=coords[0][X]<newX&&coords[1][X]<newX;if(!stillAllRight&&!stillAllLeft){return state}}if(newX===state.asymptote){return state}return {...state,hasBeenInteractedWith:true,asymptote:newX}}default:throw new Error("The doMoveCenter action is only for circle, exponential, or logarithm graphs")}}function doMoveRadiusPoint(state,action){switch(state.type){case "circle":{const[xMin,xMax]=state.range[X];const nextRadiusPoint=snap(state.snapStep,[clamp(action.destination[X]+0,xMin,xMax),state.center[1]]);if(_.isEqual(nextRadiusPoint,state.center)){return state}return {...state,hasBeenInteractedWith:true,radiusPoint:nextRadiusPoint}}default:throw new Error("The doMoveRadiusPoint action is only for circle graphs")}}function doChangeSnapStep(state,action){if(_.isEqual(state.snapStep,action.snapStep)){return state}return {...state,snapStep:action.snapStep}}function doChangeRange(state,action){if(_.isEqual(state.range,action.range)){return state}return {...state,range:action.range}}function doAddPoint(state,action){if(!isUnlimitedGraphState(state)){return state}const{snapStep}=state;const snappedPoint=snap(snapStep,action.location);for(const point of state.coords){if(point[X]===snappedPoint[X]&&point[Y]===snappedPoint[Y]){return state}}const newCoords=[...state.coords,snappedPoint];return {...state,hasBeenInteractedWith:true,coords:newCoords,showRemovePointButton:true,focusedPointIndex:newCoords.length-1}}function doRemovePoint(state,action){if(!isUnlimitedGraphState(state)){return state}const nextFocusedPointIndex=state.coords.length>1?state.coords.length-2:null;return {...state,coords:state.coords.filter((_,i)=>i!==action.index),focusedPointIndex:nextFocusedPointIndex,showRemovePointButton:nextFocusedPointIndex!==null?true:false}}const getDeltaVertex=(maxMoves,minMoves,delta)=>{const[deltaX,deltaY]=delta;const maxXMove=Math.min(...maxMoves.map(move=>move[X]));const maxYMove=Math.min(...maxMoves.map(move=>move[Y]));const minXMove=Math.max(...minMoves.map(move=>move[X]));const minYMove=Math.max(...minMoves.map(move=>move[Y]));const dx=clamp(deltaX,minXMove,maxXMove);const dy=clamp(deltaY,minYMove,maxYMove);return [dx,dy]};const getChange=(coords,delta,constraintOpts)=>{const maxMoves=coords.map(point=>maxMove({...constraintOpts,point}));const minMoves=coords.map(point=>minMove({...constraintOpts,point}));const[dx,dy]=getDeltaVertex(maxMoves,minMoves,delta);return [dx,dy]};function leq(a,b){return a<b||approximateEqual(a,b)}function boundAndSnapToGrid(point,{snapStep,range}){return snap(snapStep,bound$1({snapStep,range,point}))}function boundToEdgeAndSnapToGrid(point,{snapStep,range}){return snap(snapStep,boundToEdge({range,point}))}function boundAndSnapAngleVertex({range,coords,snapStep},{destination}){const coordsCopy=[...coords];const startingVertex=coordsCopy[1];const insetAmount=vec.add(snapStep,[minDistanceBetweenAngleVertexAndSidePoint,minDistanceBetweenAngleVertexAndSidePoint]);const newVertex=clampToBox(inset(insetAmount,range),snap(snapStep,destination));const delta=vec.add(newVertex,reverseVector(startingVertex));const newPoints={};for(const i of [0,2]){const oldPoint=coordsCopy[i];let newPoint=vec.add(oldPoint,delta);let angle=getAngleFromVertex(newVertex,newPoint);angle*=Math.PI/180;newPoint=constrainToBoundsOnAngle(newPoint,angle,range,snapStep);newPoints[i]=newPoint;}newPoints[1]=newVertex;Object.entries(newPoints).forEach(([i,newPoint])=>{coordsCopy[i]=newPoint;});return coordsCopy}function tooClose(point1,point2,range){const safeDistance=2;const distance=vec.dist(point1,point2);return distance<safeDistance}function constrainToBoundsOnAngle(point,angle,range,snapStep){const lower=[range[0][0]+snapStep[0],range[1][0]+snapStep[0]];const upper=[range[0][1]-snapStep[1],range[1][1]-snapStep[1]];let result=point;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}function boundAndSnapAngleEndPoints(destinationPoint,{range,coords,snapDegrees,angleOffsetDeg,snapStep},index){const snap=snapDegrees||1;const offsetDegrees=angleOffsetDeg||0;const coordsCopy=[...coords];const angleRange=[[range[0][0]+snapStep[0],range[0][1]-snapStep[0]],[range[1][0]+snapStep[1],range[1][1]-snapStep[1]]];const boundPoint=bound$1({snapStep:[0,0],range:angleRange,point:destinationPoint});coordsCopy[index]=boundPoint;const vertex=coords[1];let angle=getAngleFromVertex(coordsCopy[index],vertex);angle=Math.round((angle-offsetDegrees)/snap)*snap+offsetDegrees;const minDistance=minDistanceBetweenAngleVertexAndSidePoint+.01;const distance=Math.max(vec.dist(coordsCopy[index],vertex),minDistance);const snappedValue=vec.add(vertex,polar(distance,angle));return snappedValue}function angleSidePointsTooCloseToVertex(state){return tooClose(state.coords[0],state.coords[1],state.range)||tooClose(state.coords[2],state.coords[1],state.range)}function boundAndSnapToPolygonAngle(destinationPoint,{range,coords},index){const startingPoint=coords[index];return calculateAngleSnap(destinationPoint,range,coords,index,startingPoint)}function calculateAngleSnap(destinationPoint,range,coords,index,startingPoint){const coordsCopy=[...coords];coordsCopy[index]=bound$1({snapStep:[0,0],range,point:destinationPoint});const angles=angleMeasures(coordsCopy).map(angle=>angle*180/Math.PI);const rel=j=>{return (index+j+coordsCopy.length)%coordsCopy.length};_.each([-1,1],function(j){angles[rel(j)]=Math.round(angles[rel(j)]);});const getAngle=function(a,vertex,b){const angle=getClockwiseAngle$1([coordsCopy[rel(a)],coordsCopy[rel(vertex)],coordsCopy[rel(b)]]);return angle};const innerAngles=[angles[rel(-1)]-getAngle(-2,-1,1),angles[rel(1)]-getAngle(-1,1,2)];innerAngles[2]=180-(innerAngles[0]+innerAngles[1]);if(innerAngles.some(function(angle){return leq(angle,1)})){return startingPoint}const knownSide=magnitude$1(vector$1(coordsCopy[rel(-1)],coordsCopy[rel(1)]));const onLeft=sign(ccw(coordsCopy[rel(-1)],coordsCopy[rel(1)],coordsCopy[index]))===1;const side=Math.sin(innerAngles[1]*Math.PI/180)/Math.sin(innerAngles[2]*Math.PI/180)*knownSide;const outerAngle=getAngleFromVertex(coordsCopy[rel(1)],coordsCopy[rel(-1)]);const offset=polar(side,outerAngle+(onLeft?1:-1)*innerAngles[0]);return vector$3.add(coordsCopy[rel(-1)],offset)}function boundAndSnapToSides(destinationPoint,{range,coords},index){const startingPoint=coords[index];return calculateSideSnap(destinationPoint,range,coords,index,startingPoint)}function calculateSideSnap(destinationPoint,range,coords,index,startingPoint){const boundedDestinationPoint=bound$1({snapStep:[0,0],range,point:destinationPoint});const rel=j=>{return (index+j+coords.length)%coords.length};const sides=_.map([[coords[rel(-1)],boundedDestinationPoint],[boundedDestinationPoint,coords[rel(1)]],[coords[rel(-1)],coords[rel(1)]]],function(coords){return magnitude$1(vector$1(...coords))});_.each([0,1],function(j){sides[j]=Math.round(sides[j]);});if(leq(sides[1]+sides[2],sides[0])||leq(sides[0]+sides[2],sides[1])||leq(sides[0]+sides[1],sides[2])){return startingPoint}const innerAngle=lawOfCosines(sides[0],sides[2],sides[1]);const outerAngle=getAngleFromVertex(coords[rel(1)],coords[rel(-1)]);const onLeft=sign(ccw(coords[rel(-1)],coords[rel(1)],boundedDestinationPoint))===1;const offset=polar(sides[0],outerAngle+(onLeft?1:-1)*innerAngle);return vector$3.add(coords[rel(-1)],offset)}function maxMove({snapStep,range,point}){const topRight=bound$1({snapStep,range,point:[Infinity,Infinity]});return vec.sub(topRight,point)}function minMove({snapStep,range,point}){const bottomLeft=bound$1({snapStep,range,point:[-Infinity,-Infinity]});return vec.sub(bottomLeft,point)}const coordsOverlap=coords=>coords.some((coord,i)=>coords.some((c,j)=>i!==j&&vector$3.equal(coord,c)));function updateAtIndex(args){const{array,index,update}=args;const newValue=update(array[index]);return setAtIndex({array,index,newValue})}function setAtIndex(args){const{array,index,newValue}=args;const copy=[...array];copy[index]=newValue;return copy}
1968
+ const{getAngleFromVertex,getClockwiseAngle: getClockwiseAngle$1,polar}=angles;const{angleMeasures,ccw,lawOfCosines,magnitude: magnitude$1,polygonSidesIntersect,reverseVector,sign,vector: vector$1}=geometry;const{getQuadraticCoefficients: getQuadraticCoefficients$2}=coefficients;const minDistanceBetweenAngleVertexAndSidePoint=2;function interactiveGraphReducer(state,action){switch(action.type){case REINITIALIZE:return initializeGraphState(action.params);case MOVE_POINT_IN_FIGURE:return doMovePointInFigure(state,action);case MOVE_LINE:return doMoveLine(state,action);case MOVE_ALL:return doMoveAll(state,action);case MOVE_POINT:return doMovePoint(state,action);case MOVE_CENTER:return doMoveCenter(state,action);case MOVE_RADIUS_POINT:return doMoveRadiusPoint(state,action);case CHANGE_SNAP_STEP:return doChangeSnapStep(state,action);case CHANGE_RANGE:return doChangeRange(state,action);case ADD_POINT:return doAddPoint(state,action);case REMOVE_POINT:return doRemovePoint(state,action);case FOCUS_POINT:return doFocusPoint(state,action);case BLUR_POINT:return doBlurPoint(state);case DELETE_INTENT:return doDeleteIntent(state);case CLICK_POINT:return doClickPoint(state,action);case CLOSE_POLYGON:return doClosePolygon(state);case OPEN_POLYGON:return doOpenPolygon(state);case CHANGE_INTERACTION_MODE:return doChangeInteractionMode(state,action);case CHANGE_KEYBOARD_INVITATION_VISIBILITY:return doChangeKeyboardInvitationVisibility(state,action);default:throw new UnreachableCaseError(action)}}function doDeleteIntent(state,action){if(isUnlimitedGraphState(state)){if(state.focusedPointIndex!==null){return doRemovePoint(state,actions.pointGraph.removePoint(state.focusedPointIndex))}}return state}function doFocusPoint(state,action){switch(state.type){case "polygon":case "point":return {...state,focusedPointIndex:action.index};default:return state}}function doBlurPoint(state,action){switch(state.type){case "polygon":case "point":const nextState={...state,showRemovePointButton:false};if(state.interactionMode==="mouse"){nextState.focusedPointIndex=null;}return nextState;default:return state}}function doClickPoint(state,action){if(isUnlimitedGraphState(state)){return {...state,focusedPointIndex:action.index,showRemovePointButton:true}}return state}function doClosePolygon(state){if(isUnlimitedGraphState(state)&&state.type==="polygon"){const noDupedPoints=getArrayWithoutDuplicates(state.coords);return {...state,coords:noDupedPoints,closedPolygon:true}}return state}function doOpenPolygon(state){if(isUnlimitedGraphState(state)&&state.type==="polygon"){return {...state,closedPolygon:false}}return state}function doChangeInteractionMode(state,action){if(isUnlimitedGraphState(state)){const nextKeyboardInvitation=action.mode==="keyboard"?false:state.showKeyboardInteractionInvitation;return {...state,interactionMode:action.mode,showKeyboardInteractionInvitation:nextKeyboardInvitation}}return state}function doChangeKeyboardInvitationVisibility(state,action){if(isUnlimitedGraphState(state)){return {...state,showKeyboardInteractionInvitation:action.shouldShow,hasBeenInteractedWith:true}}return state}function doMovePointInFigure(state,action){switch(state.type){case "segment":case "linear-system":{const newCoords=updateAtIndex({array:state.coords,index:action.figureIndex,update:tuple=>setAtIndex({array:tuple,index:action.pointIndex,newValue:boundAndSnapToGrid(action.destination,state)})});const coordsToCheck=newCoords[action.figureIndex];if(coordsOverlap(coordsToCheck)){return state}return {...state,hasBeenInteractedWith:true,coords:newCoords}}case "linear":case "ray":{const newCoords=setAtIndex({array:state.coords,index:action.pointIndex,newValue:boundAndSnapToGrid(action.destination,state)});if(coordsOverlap(newCoords)){return state}return {...state,hasBeenInteractedWith:true,coords:newCoords}}case "circle":throw new Error(`Don't use movePointInFigure for circle graphs. Use moveCenter or moveRadiusPoint.`);case "angle":case "none":case "point":case "polygon":case "quadratic":case "sinusoid":case "absolute-value":case "tangent":case "exponential":case "logarithm":case "vector":throw new Error(`Don't use movePointInFigure for ${state.type} graphs. Use movePoint instead!`);default:throw new UnreachableCaseError(state)}}function doMoveLine(state,action){const{snapStep,range}=state;const{newStart}=action;switch(state.type){case "segment":case "linear-system":{if(action.itemIndex===undefined){throw new Error("Please provide index of line to move")}const currentLine=state.coords[action.itemIndex];const constrainedLine=constrainShapePreservingMove(currentLine,newStart,{snapStep,range});const newCoords=setAtIndex({array:state.coords,index:action.itemIndex,newValue:constrainedLine});return {...state,type:state.type,hasBeenInteractedWith:true,coords:newCoords}}case "linear":case "ray":case "vector":{const constrainedLine=constrainShapePreservingMove(state.coords,newStart,{snapStep,range});return {...state,type:state.type,hasBeenInteractedWith:true,coords:constrainedLine}}default:return state}}function doMoveAll(state,action){const{snapStep,range}=state;switch(state.type){case "polygon":{let newCoords;if(state.snapTo==="sides"||state.snapTo==="angles"){const change=getChange(state.coords,action.delta,{snapStep:[0,0],range});newCoords=state.coords.map(point=>vec.add(point,change));}else {const change=getChange(state.coords,action.delta,{snapStep,range});newCoords=state.coords.map(point=>snap(snapStep,vec.add(point,change)));}return {...state,hasBeenInteractedWith:true,coords:newCoords}}default:return state}}function doMovePoint(state,action){switch(state.type){case "angle":const newState=(()=>{if(action.index===1){const updatedCoords=boundAndSnapAngleVertex(state,action);return {...state,hasBeenInteractedWith:true,coords:updatedCoords}}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundAndSnapAngleEndPoints(action.destination,state,action.index)})}})();if(angleSidePointsTooCloseToVertex(newState)){return state}return newState;case "polygon":let newValue;if(state.snapTo==="sides"){newValue=boundAndSnapToSides(action.destination,state,action.index);}else if(state.snapTo==="angles"){newValue=boundAndSnapToPolygonAngle(action.destination,state,action.index);}else {newValue=boundAndSnapToGrid(action.destination,state);}const newCoords=setAtIndex({array:state.coords,index:action.index,newValue:newValue});const polygonSidesCanIntersect=state.numSides==="unlimited"&&!state.closedPolygon;if(!polygonSidesCanIntersect&&polygonSidesIntersect(newCoords)){return state}return {...state,hasBeenInteractedWith:true,coords:newCoords};case "point":{return {...state,hasBeenInteractedWith:true,focusedPointIndex:action.index,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundToEdgeAndSnapToGrid(action.destination,state)})}}case "sinusoid":{const destination=action.destination;const boundDestination=boundAndSnapToGrid(destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;if(newCoords[0][X]===newCoords[1][X]){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "exponential":{const boundDestination=boundAndSnapToGrid(action.destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;if(newCoords[0][X]===newCoords[1][X]){return state}const expHandle=getAsymptoteHandleCoord("horizontal",state.range,state.asymptote);if(boundDestination[X]===expHandle[X]&&boundDestination[Y]===expHandle[Y]){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "logarithm":{const boundDestination=boundAndSnapToGrid(action.destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;if(newCoords[0][X]===newCoords[1][X]){return state}if(newCoords[0][Y]===newCoords[1][Y]){return state}const logHandle=getAsymptoteHandleCoord("vertical",state.range,state.asymptote);if(boundDestination[X]===logHandle[X]&&boundDestination[Y]===logHandle[Y]){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "absolute-value":{const boundDestination=boundAndSnapToGrid(action.destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;if(newCoords[0][X]===newCoords[1][X]){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "tangent":{const boundDestination=boundAndSnapToGrid(action.destination,state);const newCoords=[...state.coords];newCoords[action.index]=boundDestination;if(newCoords[0][X]===newCoords[1][X]){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "vector":{const boundDestination=boundAndSnapToGrid(action.destination,state);if(vec.dist(boundDestination,state.coords[0])===0){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}case "quadratic":{const newCoords=[...state.coords];const boundDestination=boundAndSnapToGrid(action.destination,state);newCoords[action.index]=boundDestination;const QuadraticCoefficients=getQuadraticCoefficients$2(newCoords);if(QuadraticCoefficients===undefined){return state}return {...state,hasBeenInteractedWith:true,coords:setAtIndex({array:state.coords,index:action.index,newValue:boundDestination})}}default:throw new Error("The movePoint action is only for point, quadratic, and polygon graphs")}}function doMoveCenter(state,action){switch(state.type){case "circle":{const constrainedCenter=boundAndSnapToGrid(action.destination,state);const newRadiusPoint=[...vec.add(state.radiusPoint,vec.sub(constrainedCenter,state.center))];const[xMin,xMax]=state.range[X];const[radX]=newRadiusPoint;if(radX<xMin||radX>xMax){const xJumpDist=(radX-constrainedCenter[X])*2;const possibleNewX=radX-xJumpDist;if(possibleNewX>=xMin&&possibleNewX<=xMax){newRadiusPoint[X]=possibleNewX;}}return {...state,hasBeenInteractedWith:true,center:constrainedCenter,radiusPoint:newRadiusPoint}}case "exponential":{const newY=boundAndSnapToGrid(action.destination,state)[Y];if(newY===state.asymptote){return state}const expFutureHandle=getAsymptoteHandleCoord("horizontal",state.range,newY);if(state.coords.some(c=>c[X]===expFutureHandle[X]&&c[Y]===expFutureHandle[Y])){return state}return {...state,hasBeenInteractedWith:true,asymptote:newY}}case "logarithm":{const newX=boundAndSnapToGrid(action.destination,state)[X];if(newX===state.asymptote){return state}const logFutureHandle=getAsymptoteHandleCoord("vertical",state.range,newX);if(state.coords.some(c=>c[X]===logFutureHandle[X]&&c[Y]===logFutureHandle[Y])){return state}return {...state,hasBeenInteractedWith:true,asymptote:newX}}default:throw new Error("The doMoveCenter action is only for circle, exponential, or logarithm graphs")}}function doMoveRadiusPoint(state,action){switch(state.type){case "circle":{const[xMin,xMax]=state.range[X];const nextRadiusPoint=snap(state.snapStep,[clamp(action.destination[X]+0,xMin,xMax),state.center[1]]);if(_.isEqual(nextRadiusPoint,state.center)){return state}return {...state,hasBeenInteractedWith:true,radiusPoint:nextRadiusPoint}}default:throw new Error("The doMoveRadiusPoint action is only for circle graphs")}}function doChangeSnapStep(state,action){if(_.isEqual(state.snapStep,action.snapStep)){return state}return {...state,snapStep:action.snapStep}}function doChangeRange(state,action){if(_.isEqual(state.range,action.range)){return state}return {...state,range:action.range}}function doAddPoint(state,action){if(!isUnlimitedGraphState(state)){return state}const{snapStep}=state;const snappedPoint=snap(snapStep,action.location);for(const point of state.coords){if(point[X]===snappedPoint[X]&&point[Y]===snappedPoint[Y]){return state}}const newCoords=[...state.coords,snappedPoint];return {...state,hasBeenInteractedWith:true,coords:newCoords,showRemovePointButton:true,focusedPointIndex:newCoords.length-1}}function doRemovePoint(state,action){if(!isUnlimitedGraphState(state)){return state}const nextFocusedPointIndex=state.coords.length>1?state.coords.length-2:null;return {...state,coords:state.coords.filter((_,i)=>i!==action.index),focusedPointIndex:nextFocusedPointIndex,showRemovePointButton:nextFocusedPointIndex!==null?true:false}}const getDeltaVertex=(maxMoves,minMoves,delta)=>{const[deltaX,deltaY]=delta;const maxXMove=Math.min(...maxMoves.map(move=>move[X]));const maxYMove=Math.min(...maxMoves.map(move=>move[Y]));const minXMove=Math.max(...minMoves.map(move=>move[X]));const minYMove=Math.max(...minMoves.map(move=>move[Y]));const dx=clamp(deltaX,minXMove,maxXMove);const dy=clamp(deltaY,minYMove,maxYMove);return [dx,dy]};const getChange=(coords,delta,constraintOpts)=>{const maxMoves=coords.map(point=>maxMove({...constraintOpts,point}));const minMoves=coords.map(point=>minMove({...constraintOpts,point}));const[dx,dy]=getDeltaVertex(maxMoves,minMoves,delta);return [dx,dy]};const constrainShapePreservingMove=(currentLine,newStart,constraintOpts)=>{const desiredDelta=vec.sub(newStart,currentLine[0]);const change=getChange(currentLine,desiredDelta,constraintOpts);const{snapStep}=constraintOpts;return [snap(snapStep,vec.add(currentLine[0],change)),snap(snapStep,vec.add(currentLine[1],change))]};function leq(a,b){return a<b||approximateEqual(a,b)}function boundAndSnapToGrid(point,{snapStep,range}){return snap(snapStep,bound$1({snapStep,range,point}))}function boundToEdgeAndSnapToGrid(point,{snapStep,range}){return snap(snapStep,boundToEdge({range,point}))}function boundAndSnapAngleVertex({range,coords,snapStep},{destination}){const coordsCopy=[...coords];const startingVertex=coordsCopy[1];const insetAmount=vec.add(snapStep,[minDistanceBetweenAngleVertexAndSidePoint,minDistanceBetweenAngleVertexAndSidePoint]);const newVertex=clampToBox(inset(insetAmount,range),snap(snapStep,destination));const delta=vec.add(newVertex,reverseVector(startingVertex));const newPoints={};for(const i of [0,2]){const oldPoint=coordsCopy[i];let newPoint=vec.add(oldPoint,delta);let angle=getAngleFromVertex(newVertex,newPoint);angle*=Math.PI/180;newPoint=constrainToBoundsOnAngle(newPoint,angle,range,snapStep);newPoints[i]=newPoint;}newPoints[1]=newVertex;Object.entries(newPoints).forEach(([i,newPoint])=>{coordsCopy[i]=newPoint;});return coordsCopy}function tooClose(point1,point2,range){const safeDistance=2;const distance=vec.dist(point1,point2);return distance<safeDistance}function constrainToBoundsOnAngle(point,angle,range,snapStep){const lower=[range[0][0]+snapStep[0],range[1][0]+snapStep[0]];const upper=[range[0][1]-snapStep[1],range[1][1]-snapStep[1]];let result=point;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}function boundAndSnapAngleEndPoints(destinationPoint,{range,coords,snapDegrees,angleOffsetDeg,snapStep},index){const snap=snapDegrees||1;const offsetDegrees=angleOffsetDeg||0;const coordsCopy=[...coords];const angleRange=[[range[0][0]+snapStep[0],range[0][1]-snapStep[0]],[range[1][0]+snapStep[1],range[1][1]-snapStep[1]]];const boundPoint=bound$1({snapStep:[0,0],range:angleRange,point:destinationPoint});coordsCopy[index]=boundPoint;const vertex=coords[1];let angle=getAngleFromVertex(coordsCopy[index],vertex);angle=Math.round((angle-offsetDegrees)/snap)*snap+offsetDegrees;const minDistance=minDistanceBetweenAngleVertexAndSidePoint+.01;const distance=Math.max(vec.dist(coordsCopy[index],vertex),minDistance);const snappedValue=vec.add(vertex,polar(distance,angle));return snappedValue}function angleSidePointsTooCloseToVertex(state){return tooClose(state.coords[0],state.coords[1],state.range)||tooClose(state.coords[2],state.coords[1],state.range)}function boundAndSnapToPolygonAngle(destinationPoint,{range,coords},index){const startingPoint=coords[index];return calculateAngleSnap(destinationPoint,range,coords,index,startingPoint)}function calculateAngleSnap(destinationPoint,range,coords,index,startingPoint){const coordsCopy=[...coords];coordsCopy[index]=bound$1({snapStep:[0,0],range,point:destinationPoint});const angles=angleMeasures(coordsCopy).map(angle=>angle*180/Math.PI);const rel=j=>{return (index+j+coordsCopy.length)%coordsCopy.length};_.each([-1,1],function(j){angles[rel(j)]=Math.round(angles[rel(j)]);});const getAngle=function(a,vertex,b){const angle=getClockwiseAngle$1([coordsCopy[rel(a)],coordsCopy[rel(vertex)],coordsCopy[rel(b)]]);return angle};const innerAngles=[angles[rel(-1)]-getAngle(-2,-1,1),angles[rel(1)]-getAngle(-1,1,2)];innerAngles[2]=180-(innerAngles[0]+innerAngles[1]);if(innerAngles.some(function(angle){return leq(angle,1)})){return startingPoint}const knownSide=magnitude$1(vector$1(coordsCopy[rel(-1)],coordsCopy[rel(1)]));const onLeft=sign(ccw(coordsCopy[rel(-1)],coordsCopy[rel(1)],coordsCopy[index]))===1;const side=Math.sin(innerAngles[1]*Math.PI/180)/Math.sin(innerAngles[2]*Math.PI/180)*knownSide;const outerAngle=getAngleFromVertex(coordsCopy[rel(1)],coordsCopy[rel(-1)]);const offset=polar(side,outerAngle+(onLeft?1:-1)*innerAngles[0]);return vector$3.add(coordsCopy[rel(-1)],offset)}function boundAndSnapToSides(destinationPoint,{range,coords},index){const startingPoint=coords[index];return calculateSideSnap(destinationPoint,range,coords,index,startingPoint)}function calculateSideSnap(destinationPoint,range,coords,index,startingPoint){const boundedDestinationPoint=bound$1({snapStep:[0,0],range,point:destinationPoint});const rel=j=>{return (index+j+coords.length)%coords.length};const sides=_.map([[coords[rel(-1)],boundedDestinationPoint],[boundedDestinationPoint,coords[rel(1)]],[coords[rel(-1)],coords[rel(1)]]],function(coords){return magnitude$1(vector$1(...coords))});_.each([0,1],function(j){sides[j]=Math.round(sides[j]);});if(leq(sides[1]+sides[2],sides[0])||leq(sides[0]+sides[2],sides[1])||leq(sides[0]+sides[1],sides[2])){return startingPoint}const innerAngle=lawOfCosines(sides[0],sides[2],sides[1]);const outerAngle=getAngleFromVertex(coords[rel(1)],coords[rel(-1)]);const onLeft=sign(ccw(coords[rel(-1)],coords[rel(1)],boundedDestinationPoint))===1;const offset=polar(sides[0],outerAngle+(onLeft?1:-1)*innerAngle);return vector$3.add(coords[rel(-1)],offset)}function maxMove({snapStep,range,point}){const topRight=bound$1({snapStep,range,point:[Infinity,Infinity]});return vec.sub(topRight,point)}function minMove({snapStep,range,point}){const bottomLeft=bound$1({snapStep,range,point:[-Infinity,-Infinity]});return vec.sub(bottomLeft,point)}const coordsOverlap=coords=>coords.some((coord,i)=>coords.some((c,j)=>i!==j&&vector$3.equal(coord,c)));function updateAtIndex(args){const{array,index,update}=args;const newValue=update(array[index]);return setAtIndex({array,index,newValue})}function setAtIndex(args){const{array,index,newValue}=args;const copy=[...array];copy[index]=newValue;return copy}
1969
1969
 
1970
- const{clockwise}=geometry;const{convertRadiansToDegrees}=angles;function renderPolygonGraph(state,dispatch,i18n,markings){return {graph:jsxRuntimeExports.jsx(PolygonGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getPolygonGraphDescription(state,i18n,markings)}}const PolygonGraph=props=>{const{dispatch}=props;const{numSides,coords,snapStep,snapTo="grid"}=props.graphState;const graphConfig=useGraphConfig();const polygonRef=React.useRef(null);const pointsRef=React.useRef([]);const lastMoveTimeRef=React.useRef(0);const{range:[x,y]}=graphConfig;const[[left,top]]=useTransformVectorsToPixels([x[0],y[1]]);const points=coords??[[0,0]];const dragReferencePoint=points[0];const constrain=getKeyboardMovementConstraintForPolygon(snapStep,snapTo);const{dragging}=useDraggable({gestureTarget:polygonRef,point:dragReferencePoint,onMove:newPoint=>{const delta=vec.sub(newPoint,dragReferencePoint);dispatch(actions.polygon.moveAll(delta));},constrainKeyboardMovement:constrain});const[hovered,setHovered]=React.useState(false);const[focusVisible,setFocusVisible]=React.useState(false);React.useEffect(()=>{const focusedIndex=props.graphState.focusedPointIndex;if(focusedIndex!=null){pointsRef.current[focusedIndex]?.focus();}},[props.graphState.focusedPointIndex,props.graphState.coords.length,pointsRef]);React.useEffect(()=>{if(numSides==="unlimited"&&props.graphState.coords.length>2){dispatch(actions.polygon.closePolygon());}},[]);const statefulProps={...props,graphConfig,polygonRef,pointsRef,lastMoveTimeRef,left,top,dragging,points,hovered,setHovered,focusVisible,setFocusVisible};return numSides==="unlimited"?jsxRuntimeExports.jsx(UnlimitedPolygonGraph,{...statefulProps}):jsxRuntimeExports.jsx(LimitedPolygonGraph,{...statefulProps})};const LimitedPolygonGraph=statefulProps=>{const{dispatch,hovered,setHovered,focusVisible,setFocusVisible,graphConfig,polygonRef,lastMoveTimeRef,dragging,points}=statefulProps;const{showAngles,showSides,range,snapTo="grid",snapStep}=statefulProps.graphState;const{disableKeyboardInteraction,interactiveColor}=graphConfig;const{strings,locale}=usePerseusI18n();const id=React.useId();const pointsOffArray=Array(points.length).fill("off");const[ariaLives,setAriaLives]=React.useState(["off",...pointsOffArray]);const lines=getLines(points);const polygonPointsNumId=id+"-points-num";const polygonPointsId=id+"-points";const{srPolygonGraph,srPolygonGraphPointsNum,srPolygonGraphPoints,srPolygonElementsNum}=describePolygonGraph(statefulProps.graphState,{strings,locale},statefulProps.graphConfig.markings);return jsxRuntimeExports.jsxs("g",{"aria-label":srPolygonGraph,"aria-describedby":`${polygonPointsNumId} ${polygonPointsId}`,children:[jsxRuntimeExports.jsx(Polygon,{points:[...points],color:interactiveColor,svgPolygonProps:{strokeWidth:focusVisible?"var(--movable-line-stroke-weight-active)":"var(--movable-line-stroke-weight)",style:{fill:"transparent"},"aria-hidden":true}}),points.map((point,i)=>{const pt1=points.at(i-1);const pt2=points[(i+1)%points.length];if(!pt1||!pt2){return null}return jsxRuntimeExports.jsx(PolygonAngle,{centerPoint:point,endPoints:[pt1,pt2],areEndPointsClockwise:clockwise(points),showAngles:!!showAngles,snapTo:snapTo},"angle-"+i)}),showSides&&lines.map(([start,end],i)=>{const[x,y]=vec.midpoint(start,end);const length=vec.dist(start,end);const isApprox=!Number.isInteger(length);return jsxRuntimeExports.jsx(TextLabel,{x:x,y:y,children:isApprox?`≈ ${length.toFixed(snapTo==="sides"?0:1)}`:length},"side-"+i)}),jsxRuntimeExports.jsx(Polygon,{points:[...points],color:"transparent",svgPolygonProps:{ref:polygonRef,tabIndex:disableKeyboardInteraction?-1:0,strokeWidth:TARGET_SIZE,style:{cursor:dragging?"grabbing":"grab",fill:hovered?"var(--mafs-blue)":"transparent"},onMouseEnter:()=>setHovered(true),onMouseLeave:()=>setHovered(false),onKeyDownCapture:()=>{setFocusVisible(hasFocusVisible(polygonRef.current));},onFocus:()=>{setFocusVisible(hasFocusVisible(polygonRef.current));setAriaLives(()=>["polite",...pointsOffArray]);},onBlur:()=>setFocusVisible(hasFocusVisible(polygonRef.current)),className:"movable-polygon",role:"button","aria-label":srPolygonGraphPoints?`${srPolygonElementsNum} ${srPolygonGraphPoints}`:srPolygonElementsNum,"aria-live":ariaLives[0],"aria-disabled":disableKeyboardInteraction}}),points.map((point,i)=>{const angleId=`${id}-angle-${i}`;const side1Id=`${id}-point-${i}-side-1`;const side2Id=`${id}-point-${i}-side-2`;const angle=getAngleFromPoints(points,i);const angleDegree=angle?convertRadiansToDegrees(angle):null;const sidesArray=getSideLengthsFromPoints(points,i);const{pointIndex:point1Index,sideLength:side1Length}=sidesArray[0];const{pointIndex:point2Index,sideLength:side2Length}=sidesArray[1];return jsxRuntimeExports.jsxs("g",{children:[jsxRuntimeExports.jsx(MovablePoint$1,{ariaDescribedBy:`${angleId} ${side1Id} ${side2Id}`,ariaLive:ariaLives[i+1],constrain:getKeyboardMovementConstraintForPoint(points,i,range,snapStep,snapTo),point:point,sequenceNumber:i+1,onMove:destination=>{const now=Date.now();const targetFPS=40;const moveThresholdTime=1e3/targetFPS;if(now-lastMoveTimeRef.current>moveThresholdTime){dispatch(actions.polygon.movePoint(i,destination));lastMoveTimeRef.current=now;}},onFocus:()=>{const newPointAriaLives=[...pointsOffArray];newPointAriaLives[i]="polite";setAriaLives(["off",...newPointAriaLives]);}}),angleDegree&&jsxRuntimeExports.jsx(SRDescInSVG,{id:angleId,children:Number.isInteger(angleDegree)?strings.srPolygonPointAngle({angle:angleDegree}):strings.srPolygonPointAngleApprox({angle:srFormatNumber(angleDegree,locale,1)})}),jsxRuntimeExports.jsx(SRDescInSVG,{id:side1Id,children:getPolygonSideString(side1Length,point1Index,strings,locale)}),jsxRuntimeExports.jsx(SRDescInSVG,{id:side2Id,children:getPolygonSideString(side2Length,point2Index,strings,locale)})]},"point-"+i)}),jsxRuntimeExports.jsx(SRDescInSVG,{id:polygonPointsNumId,children:srPolygonGraphPointsNum}),srPolygonGraphPoints&&jsxRuntimeExports.jsx(SRDescInSVG,{id:polygonPointsId,children:srPolygonGraphPoints})]})};const UnlimitedPolygonGraph=statefulProps=>{const{dispatch,graphConfig,left,top,pointsRef,points}=statefulProps;const{coords,closedPolygon}=statefulProps.graphState;const{strings,locale}=usePerseusI18n();const{interactiveColor}=useGraphConfig();const[isCurrentlyDragging,setIsCurrentlyDragging]=useState(false);const dragEndCallbackTimer=useTimeout(()=>setIsCurrentlyDragging(false),400);const id=React.useId();const polygonPointsNumId=id+"-points-num";const polygonPointsId=id+"-points";const pointsOffArray=Array(points.length).fill("off");const[ariaLives,setAriaLives]=React.useState(pointsOffArray);if(closedPolygon){const closedPolygonProps={...statefulProps,numSides:coords.length};return jsxRuntimeExports.jsx(LimitedPolygonGraph,{...closedPolygonProps})}const{graphDimensionsInPixels}=graphConfig;const widthPx=graphDimensionsInPixels[0];const heightPx=graphDimensionsInPixels[1];const emptyGraph=coords.length===0;const{srPolygonGraph,srPolygonGraphPointsNum,srPolygonGraphPoints}=describePolygonGraph(statefulProps.graphState,{strings,locale},statefulProps.graphConfig.markings);return jsxRuntimeExports.jsxs("g",{"aria-label":emptyGraph?strings.srUnlimitedPolygonEmpty:srPolygonGraph,"aria-describedby":`${polygonPointsNumId} ${polygonPointsId}`,children:[jsxRuntimeExports.jsx(Polyline,{points:[...points],color:interactiveColor,svgPolylineProps:{strokeWidth:"var(--movable-line-stroke-weight)",style:{fill:"transparent"},"aria-hidden":true}}),jsxRuntimeExports.jsx("rect",{"aria-hidden":true,style:{fill:"rgba(0,0,0,0)",cursor:"crosshair"},width:widthPx,height:heightPx,x:left,y:top,onClick:event=>{if(isCurrentlyDragging){return}const elementRect=event.currentTarget.getBoundingClientRect();const zoomFactor=getCSSZoomFactor(event.currentTarget);const x=(event.clientX-elementRect.x)/zoomFactor;const y=(event.clientY-elementRect.y)/zoomFactor;const graphCoordinates=pixelsToVectors([[x,y]],graphConfig);dispatch(actions.polygon.addPoint(graphCoordinates[0]));}}),coords.map((point,i)=>{const angleId=`${id}-angle-${i}`;let sideIds="";const hasAngle=i>0&&i<coords.length-1;const angle=hasAngle?getAngleFromPoints(points,i):null;const angleDegree=angle?convertRadiansToDegrees(angle):null;const sidesArray=getSideLengthsFromPoints(points,i,true);for(let sideIndex=0;sideIndex<sidesArray.length;sideIndex++){sideIds+=`${id}-point-${i}-side-${sideIndex} `;}return jsxRuntimeExports.jsxs("g",{children:[jsxRuntimeExports.jsx(MovablePoint$1,{ariaDescribedBy:`${angleId} ${sideIds}`,ariaLive:ariaLives[i],point:point,sequenceNumber:i+1,onDragStart:()=>{dragEndCallbackTimer.clear();setIsCurrentlyDragging(true);},onMove:destination=>{dispatch(actions.polygon.movePoint(i,destination));},onDragEnd:()=>{dragEndCallbackTimer.set();},ref:ref=>{pointsRef.current[i]=ref;},onFocus:()=>{dispatch(actions.polygon.focusPoint(i));const newPointAriaLives=[...pointsOffArray];newPointAriaLives[i]="polite";setAriaLives([...newPointAriaLives]);},onClick:()=>{if(i===0&&getArrayWithoutDuplicates(coords).length>=3){dispatch(actions.polygon.closePolygon());}dispatch(actions.polygon.clickPoint(i));}}),angleDegree&&jsxRuntimeExports.jsx(SRDescInSVG,{id:angleId,children:Number.isInteger(angleDegree)?strings.srPolygonPointAngle({angle:angleDegree}):strings.srPolygonPointAngleApprox({angle:srFormatNumber(angleDegree,locale,1)})}),sidesArray.map(({pointIndex,sideLength},j)=>jsxRuntimeExports.jsx(SRDescInSVG,{id:`${id}-point-${i}-side-${j}`,children:getPolygonSideString(sideLength,pointIndex,strings,locale)},`${id}-point-${i}-side-${j}`))]},"point-"+i)}),coords.length>0&&jsxRuntimeExports.jsx(SRDescInSVG,{id:polygonPointsNumId,children:srPolygonGraphPointsNum}),srPolygonGraphPoints&&jsxRuntimeExports.jsx(SRDescInSVG,{id:polygonPointsId,children:srPolygonGraphPoints})]})};function getLines(points){return points.map((point,i)=>{const next=points[(i+1)%points.length];return [point,next]})}const hasFocusVisible=element=>{const matches=selector=>element?.matches(selector)??false;try{return matches(":focus-visible")}catch{return matches(":focus")}};function getPolygonGraphDescription(state,i18n,markings){const strings=describePolygonGraph(state,i18n,markings);return strings.srPolygonInteractiveElements}function describePolygonGraph(state,i18n,markings){const{strings,locale}=i18n;const{coords}=state;const isCoordinatePlane=markings==="axes"||markings==="graph";const hasOnePoint=coords.length===1;const srPolygonGraph=isCoordinatePlane?strings.srPolygonGraphCoordinatePlane:strings.srPolygonGraph;const srPolygonGraphPointsNum=hasOnePoint?strings.srPolygonGraphPointsOne:strings.srPolygonGraphPointsNum({num:coords.length});let srPolygonGraphPoints;if(isCoordinatePlane){const pointsString=coords.map((coord,i)=>{return strings.srPointAtCoordinates({num:i+1,x:srFormatNumber(coord[0],locale),y:srFormatNumber(coord[1],locale)})});srPolygonGraphPoints=pointsString.join(" ");}const srPolygonElementsNum=hasOnePoint?strings.srPolygonElementsOne:strings.srPolygonElementsNum({num:coords.length});const srPolygonInteractiveElements=coords.length>0?strings.srInteractiveElements({elements:[srPolygonElementsNum,srPolygonGraphPoints].join(" ")}):null;return {srPolygonGraph,srPolygonGraphPointsNum,srPolygonGraphPoints,srPolygonElementsNum,srPolygonInteractiveElements}}function getKeyboardMovementConstraintForPoint(points,index,range,snapStep,snapTo){switch(snapTo){case "grid":return p=>snap(snapStep,p);case "sides":return getSideSnapConstraint(points,index,range);case "angles":return getAngleSnapConstraint(points,index,range);default:throw new UnreachableCaseError(snapTo)}}function getKeyboardMovementConstraintForPolygon(snapStep,snapTo){switch(snapTo){case "grid":return p=>snap(snapStep,p);case "sides":case "angles":return p=>p;default:throw new UnreachableCaseError(snapTo)}}function getSideSnapConstraint(points,index,range){const newPoints=[...points];const pointToBeMoved=newPoints[index];const movePointWithConstraint=moveFunc=>{let destinationAttempt=moveFunc(pointToBeMoved);let newPoint=pointToBeMoved;while(newPoint[0]===pointToBeMoved[0]&&newPoint[1]===pointToBeMoved[1]&&isInBound({range,point:destinationAttempt})){newPoint=calculateSideSnap(destinationAttempt,range,newPoints,index,pointToBeMoved);destinationAttempt=moveFunc(destinationAttempt);}return newPoint};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,1])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,1])),left:movePointWithConstraint(coord=>vec.sub(coord,[1,0])),right:movePointWithConstraint(coord=>vec.add(coord,[1,0]))}}function getAngleSnapConstraint(points,index,range){const newPoints=[...points];const pointToBeMoved=newPoints[index];const movePointWithConstraint=moveFunc=>{let destinationAttempt=bound$1({snapStep:[0,0],range,point:moveFunc(pointToBeMoved)});let newPoint=pointToBeMoved;while(newPoint[0]===pointToBeMoved[0]&&newPoint[1]===pointToBeMoved[1]&&isInBound({range,point:destinationAttempt})){newPoint=calculateAngleSnap(destinationAttempt,range,newPoints,index,pointToBeMoved);destinationAttempt=moveFunc(destinationAttempt);}return newPoint};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,.1])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,.1])),left:movePointWithConstraint(coord=>vec.sub(coord,[.1,0])),right:movePointWithConstraint(coord=>vec.add(coord,[.1,0]))}}
1970
+ const{clockwise}=geometry;const{convertRadiansToDegrees}=angles;function renderPolygonGraph(state,dispatch,i18n,markings){return {graph:jsxRuntimeExports.jsx(PolygonGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getPolygonGraphDescription(state,i18n,markings)}}const PolygonGraph=props=>{const{dispatch}=props;const{numSides,coords,snapStep,snapTo="grid"}=props.graphState;const graphConfig=useGraphConfig();const polygonRef=React.useRef(null);const pointsRef=React.useRef([]);const lastMoveTimeRef=React.useRef(0);const{range:[x,y]}=graphConfig;const[[left,top]]=useTransformVectorsToPixels([x[0],y[1]]);const dragReferencePoint=coords[0];const constrain=getKeyboardMovementConstraintForPolygon(snapStep,snapTo);const{dragging}=useDraggable({gestureTarget:polygonRef,point:dragReferencePoint,onMove:newPoint=>{const delta=vec.sub(newPoint,dragReferencePoint);dispatch(actions.polygon.moveAll(delta));},constrainKeyboardMovement:constrain});const[hovered,setHovered]=React.useState(false);const[focusVisible,setFocusVisible]=React.useState(false);React.useEffect(()=>{const focusedIndex=props.graphState.focusedPointIndex;if(focusedIndex!=null){pointsRef.current[focusedIndex]?.focus();}},[props.graphState.focusedPointIndex,props.graphState.coords.length,pointsRef]);React.useEffect(()=>{if(numSides==="unlimited"&&props.graphState.coords.length>2){dispatch(actions.polygon.closePolygon());}},[]);const statefulProps={...props,graphConfig,polygonRef,pointsRef,lastMoveTimeRef,left,top,dragging,points:coords,hovered,setHovered,focusVisible,setFocusVisible};return numSides==="unlimited"?jsxRuntimeExports.jsx(UnlimitedPolygonGraph,{...statefulProps}):jsxRuntimeExports.jsx(LimitedPolygonGraph,{...statefulProps})};const LimitedPolygonGraph=statefulProps=>{const{dispatch,hovered,setHovered,focusVisible,setFocusVisible,graphConfig,polygonRef,lastMoveTimeRef,dragging,points}=statefulProps;const{showAngles,showSides,range,snapTo="grid",snapStep}=statefulProps.graphState;const{disableKeyboardInteraction,interactiveColor}=graphConfig;const{strings,locale}=usePerseusI18n();const id=React.useId();const pointsOffArray=Array(points.length).fill("off");const[ariaLives,setAriaLives]=React.useState(["off",...pointsOffArray]);const lines=getLines(points);const polygonPointsNumId=id+"-points-num";const polygonPointsId=id+"-points";const{srPolygonGraph,srPolygonGraphPointsNum,srPolygonGraphPoints,srPolygonElementsNum}=describePolygonGraph(statefulProps.graphState,{strings,locale},statefulProps.graphConfig.markings);return jsxRuntimeExports.jsxs("g",{"aria-label":srPolygonGraph,"aria-describedby":`${polygonPointsNumId} ${polygonPointsId}`,children:[jsxRuntimeExports.jsx(Polygon,{points:[...points],color:interactiveColor,svgPolygonProps:{strokeWidth:focusVisible?"var(--movable-line-stroke-weight-active)":"var(--movable-line-stroke-weight)",style:{fill:"transparent"},"aria-hidden":true}}),points.map((point,i)=>{const pt1=points.at(i-1);const pt2=points[(i+1)%points.length];if(!pt1||!pt2){return null}return jsxRuntimeExports.jsx(PolygonAngle,{centerPoint:point,endPoints:[pt1,pt2],areEndPointsClockwise:clockwise(points),showAngles:!!showAngles,snapTo:snapTo},"angle-"+i)}),showSides&&lines.map(([start,end],i)=>{const[x,y]=vec.midpoint(start,end);const length=vec.dist(start,end);const isApprox=!Number.isInteger(length);return jsxRuntimeExports.jsx(TextLabel,{x:x,y:y,children:isApprox?`≈ ${length.toFixed(snapTo==="sides"?0:1)}`:length},"side-"+i)}),jsxRuntimeExports.jsx(Polygon,{points:[...points],color:"transparent",svgPolygonProps:{ref:polygonRef,tabIndex:disableKeyboardInteraction?-1:0,strokeWidth:TARGET_SIZE,style:{cursor:dragging?"grabbing":"grab",fill:hovered?"var(--mafs-blue)":"transparent"},onMouseEnter:()=>setHovered(true),onMouseLeave:()=>setHovered(false),onKeyDownCapture:()=>{setFocusVisible(hasFocusVisible(polygonRef.current));},onFocus:()=>{setFocusVisible(hasFocusVisible(polygonRef.current));setAriaLives(()=>["polite",...pointsOffArray]);},onBlur:()=>setFocusVisible(hasFocusVisible(polygonRef.current)),className:"movable-polygon",role:"button","aria-label":srPolygonGraphPoints?`${srPolygonElementsNum} ${srPolygonGraphPoints}`:srPolygonElementsNum,"aria-live":ariaLives[0],"aria-disabled":disableKeyboardInteraction}}),points.map((point,i)=>{const angleId=`${id}-angle-${i}`;const side1Id=`${id}-point-${i}-side-1`;const side2Id=`${id}-point-${i}-side-2`;const angle=getAngleFromPoints(points,i);const angleDegree=angle?convertRadiansToDegrees(angle):null;const sidesArray=getSideLengthsFromPoints(points,i);const{pointIndex:point1Index,sideLength:side1Length}=sidesArray[0];const{pointIndex:point2Index,sideLength:side2Length}=sidesArray[1];return jsxRuntimeExports.jsxs("g",{children:[jsxRuntimeExports.jsx(MovablePoint$1,{ariaDescribedBy:`${angleId} ${side1Id} ${side2Id}`,ariaLive:ariaLives[i+1],constrain:getKeyboardMovementConstraintForPoint(points,i,range,snapStep,snapTo),point:point,sequenceNumber:i+1,onMove:destination=>{const now=Date.now();const targetFPS=40;const moveThresholdTime=1e3/targetFPS;if(now-lastMoveTimeRef.current>moveThresholdTime){dispatch(actions.polygon.movePoint(i,destination));lastMoveTimeRef.current=now;}},onFocus:()=>{const newPointAriaLives=[...pointsOffArray];newPointAriaLives[i]="polite";setAriaLives(["off",...newPointAriaLives]);}}),angleDegree&&jsxRuntimeExports.jsx(SRDescInSVG,{id:angleId,children:Number.isInteger(angleDegree)?strings.srPolygonPointAngle({angle:angleDegree}):strings.srPolygonPointAngleApprox({angle:srFormatNumber(angleDegree,locale,1)})}),jsxRuntimeExports.jsx(SRDescInSVG,{id:side1Id,children:getPolygonSideString(side1Length,point1Index,strings,locale)}),jsxRuntimeExports.jsx(SRDescInSVG,{id:side2Id,children:getPolygonSideString(side2Length,point2Index,strings,locale)})]},"point-"+i)}),jsxRuntimeExports.jsx(SRDescInSVG,{id:polygonPointsNumId,children:srPolygonGraphPointsNum}),srPolygonGraphPoints&&jsxRuntimeExports.jsx(SRDescInSVG,{id:polygonPointsId,children:srPolygonGraphPoints})]})};const UnlimitedPolygonGraph=statefulProps=>{const{dispatch,graphConfig,left,top,pointsRef,points}=statefulProps;const{coords,closedPolygon}=statefulProps.graphState;const{strings,locale}=usePerseusI18n();const{interactiveColor}=useGraphConfig();const[isCurrentlyDragging,setIsCurrentlyDragging]=useState(false);const dragEndCallbackTimer=useTimeout(()=>setIsCurrentlyDragging(false),400);const id=React.useId();const polygonPointsNumId=id+"-points-num";const polygonPointsId=id+"-points";const pointsOffArray=Array(points.length).fill("off");const[ariaLives,setAriaLives]=React.useState(pointsOffArray);if(closedPolygon){const closedPolygonProps={...statefulProps,numSides:coords.length};return jsxRuntimeExports.jsx(LimitedPolygonGraph,{...closedPolygonProps})}const{graphDimensionsInPixels}=graphConfig;const widthPx=graphDimensionsInPixels[0];const heightPx=graphDimensionsInPixels[1];const emptyGraph=coords.length===0;const{srPolygonGraph,srPolygonGraphPointsNum,srPolygonGraphPoints}=describePolygonGraph(statefulProps.graphState,{strings,locale},statefulProps.graphConfig.markings);return jsxRuntimeExports.jsxs("g",{"aria-label":emptyGraph?strings.srUnlimitedPolygonEmpty:srPolygonGraph,"aria-describedby":`${polygonPointsNumId} ${polygonPointsId}`,children:[jsxRuntimeExports.jsx(Polyline,{points:[...points],color:interactiveColor,svgPolylineProps:{strokeWidth:"var(--movable-line-stroke-weight)",style:{fill:"transparent"},"aria-hidden":true}}),jsxRuntimeExports.jsx("rect",{"aria-hidden":true,style:{fill:"rgba(0,0,0,0)",cursor:"crosshair"},width:widthPx,height:heightPx,x:left,y:top,onClick:event=>{if(isCurrentlyDragging){return}const elementRect=event.currentTarget.getBoundingClientRect();const zoomFactor=getCSSZoomFactor(event.currentTarget);const x=(event.clientX-elementRect.x)/zoomFactor;const y=(event.clientY-elementRect.y)/zoomFactor;const graphCoordinates=pixelsToVectors([[x,y]],graphConfig);dispatch(actions.polygon.addPoint(graphCoordinates[0]));}}),coords.map((point,i)=>{const angleId=`${id}-angle-${i}`;let sideIds="";const hasAngle=i>0&&i<coords.length-1;const angle=hasAngle?getAngleFromPoints(points,i):null;const angleDegree=angle?convertRadiansToDegrees(angle):null;const sidesArray=getSideLengthsFromPoints(points,i,true);for(let sideIndex=0;sideIndex<sidesArray.length;sideIndex++){sideIds+=`${id}-point-${i}-side-${sideIndex} `;}return jsxRuntimeExports.jsxs("g",{children:[jsxRuntimeExports.jsx(MovablePoint$1,{ariaDescribedBy:`${angleId} ${sideIds}`,ariaLive:ariaLives[i],point:point,sequenceNumber:i+1,onDragStart:()=>{dragEndCallbackTimer.clear();setIsCurrentlyDragging(true);},onMove:destination=>{dispatch(actions.polygon.movePoint(i,destination));},onDragEnd:()=>{dragEndCallbackTimer.set();},ref:ref=>{pointsRef.current[i]=ref;},onFocus:()=>{dispatch(actions.polygon.focusPoint(i));const newPointAriaLives=[...pointsOffArray];newPointAriaLives[i]="polite";setAriaLives([...newPointAriaLives]);},onClick:()=>{if(i===0&&getArrayWithoutDuplicates(coords).length>=3){dispatch(actions.polygon.closePolygon());}dispatch(actions.polygon.clickPoint(i));}}),angleDegree&&jsxRuntimeExports.jsx(SRDescInSVG,{id:angleId,children:Number.isInteger(angleDegree)?strings.srPolygonPointAngle({angle:angleDegree}):strings.srPolygonPointAngleApprox({angle:srFormatNumber(angleDegree,locale,1)})}),sidesArray.map(({pointIndex,sideLength},j)=>jsxRuntimeExports.jsx(SRDescInSVG,{id:`${id}-point-${i}-side-${j}`,children:getPolygonSideString(sideLength,pointIndex,strings,locale)},`${id}-point-${i}-side-${j}`))]},"point-"+i)}),coords.length>0&&jsxRuntimeExports.jsx(SRDescInSVG,{id:polygonPointsNumId,children:srPolygonGraphPointsNum}),srPolygonGraphPoints&&jsxRuntimeExports.jsx(SRDescInSVG,{id:polygonPointsId,children:srPolygonGraphPoints})]})};function getLines(points){return points.map((point,i)=>{const next=points[(i+1)%points.length];return [point,next]})}const hasFocusVisible=element=>{const matches=selector=>element?.matches(selector)??false;try{return matches(":focus-visible")}catch{return matches(":focus")}};function getPolygonGraphDescription(state,i18n,markings){const strings=describePolygonGraph(state,i18n,markings);return strings.srPolygonInteractiveElements}function describePolygonGraph(state,i18n,markings){const{strings,locale}=i18n;const{coords}=state;const isCoordinatePlane=markings==="axes"||markings==="graph";const hasOnePoint=coords.length===1;const srPolygonGraph=isCoordinatePlane?strings.srPolygonGraphCoordinatePlane:strings.srPolygonGraph;const srPolygonGraphPointsNum=hasOnePoint?strings.srPolygonGraphPointsOne:strings.srPolygonGraphPointsNum({num:coords.length});let srPolygonGraphPoints;if(isCoordinatePlane){const pointsString=coords.map((coord,i)=>{return strings.srPointAtCoordinates({num:i+1,x:srFormatNumber(coord[0],locale),y:srFormatNumber(coord[1],locale)})});srPolygonGraphPoints=pointsString.join(" ");}const srPolygonElementsNum=hasOnePoint?strings.srPolygonElementsOne:strings.srPolygonElementsNum({num:coords.length});const srPolygonInteractiveElements=coords.length>0?strings.srInteractiveElements({elements:[srPolygonElementsNum,srPolygonGraphPoints].join(" ")}):null;return {srPolygonGraph,srPolygonGraphPointsNum,srPolygonGraphPoints,srPolygonElementsNum,srPolygonInteractiveElements}}function getKeyboardMovementConstraintForPoint(points,index,range,snapStep,snapTo){switch(snapTo){case "grid":return p=>snap(snapStep,p);case "sides":return getSideSnapConstraint(points,index,range);case "angles":return getAngleSnapConstraint(points,index,range);default:throw new UnreachableCaseError(snapTo)}}function getKeyboardMovementConstraintForPolygon(snapStep,snapTo){switch(snapTo){case "grid":return p=>snap(snapStep,p);case "sides":case "angles":return p=>p;default:throw new UnreachableCaseError(snapTo)}}function getSideSnapConstraint(points,index,range){const newPoints=[...points];const pointToBeMoved=newPoints[index];const movePointWithConstraint=moveFunc=>{let destinationAttempt=moveFunc(pointToBeMoved);let newPoint=pointToBeMoved;while(newPoint[0]===pointToBeMoved[0]&&newPoint[1]===pointToBeMoved[1]&&isInBound({range,point:destinationAttempt})){newPoint=calculateSideSnap(destinationAttempt,range,newPoints,index,pointToBeMoved);destinationAttempt=moveFunc(destinationAttempt);}return newPoint};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,1])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,1])),left:movePointWithConstraint(coord=>vec.sub(coord,[1,0])),right:movePointWithConstraint(coord=>vec.add(coord,[1,0]))}}function getAngleSnapConstraint(points,index,range){const newPoints=[...points];const pointToBeMoved=newPoints[index];const movePointWithConstraint=moveFunc=>{let destinationAttempt=bound$1({snapStep:[0,0],range,point:moveFunc(pointToBeMoved)});let newPoint=pointToBeMoved;while(newPoint[0]===pointToBeMoved[0]&&newPoint[1]===pointToBeMoved[1]&&isInBound({range,point:destinationAttempt})){newPoint=calculateAngleSnap(destinationAttempt,range,newPoints,index,pointToBeMoved);destinationAttempt=moveFunc(destinationAttempt);}return newPoint};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,.1])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,.1])),left:movePointWithConstraint(coord=>vec.sub(coord,[.1,0])),right:movePointWithConstraint(coord=>vec.add(coord,[.1,0]))}}
1971
1971
 
1972
1972
  function renderQuadraticGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(QuadraticGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getQuadraticGraphDescription(state,i18n)}}function QuadraticGraph(props){const{dispatch,graphState}=props;const{coords,snapStep}=graphState;const{interactiveColor}=useGraphConfig();const{strings,locale}=usePerseusI18n();const id=React.useId();const quadraticDirectionId=id+"-direction";const quadraticVertexId=id+"-vertex";const quadraticInterceptsId=id+"-intercepts";const coeffRef=React.useRef([0,0,0]);const coeffs=getQuadraticCoefficients$1(coords);if(coeffs!==undefined){coeffRef.current=coeffs;}const[a,b,c]=coeffRef.current;const y=x=>(a*x+b)*x+c;const{srQuadraticGraph,srQuadraticDirection,srQuadraticVertex,srQuadraticXIntercepts,srQuadraticYIntercept}=describeQuadraticGraph(graphState,{strings,locale});return jsxRuntimeExports.jsxs("g",{"aria-label":srQuadraticGraph,"aria-describedby":`${quadraticDirectionId} ${quadraticVertexId} ${quadraticInterceptsId}`,children:[jsxRuntimeExports.jsx(Plot$2.OfX,{y:y,color:interactiveColor,svgPathProps:{"aria-hidden":true}}),coords.map((coord,i)=>{const srQuadraticPoint=getQuadraticPointString(i+1,coord,strings,locale);const srVertex=srQuadraticVertex?` ${srQuadraticVertex}`:"";return jsxRuntimeExports.jsx(MovablePoint$1,{ariaLabel:`${srQuadraticPoint}${srVertex}`,point:coord,sequenceNumber:i+1,constrain:getQuadraticKeyboardConstraint(coords,snapStep,i),onMove:destination=>dispatch(actions.quadratic.movePoint(i,destination))},"point-"+i)}),srQuadraticDirection&&jsxRuntimeExports.jsx(SRDescInSVG,{id:quadraticDirectionId,children:srQuadraticDirection}),srQuadraticVertex&&jsxRuntimeExports.jsx(SRDescInSVG,{id:quadraticVertexId,children:srQuadraticVertex}),jsxRuntimeExports.jsx(SRDescInSVG,{id:quadraticInterceptsId,children:srQuadraticXIntercepts?`${srQuadraticXIntercepts} ${srQuadraticYIntercept}`:`${srQuadraticYIntercept}`})]})}const getQuadraticCoefficients$1=coords=>{const p1=coords[0];const p2=coords[1];const p3=coords[2];const denom=(p1[0]-p2[0])*(p1[0]-p3[0])*(p2[0]-p3[0]);if(denom===0){return}const a=(p3[0]*(p2[1]-p1[1])+p2[0]*(p1[1]-p3[1])+p1[0]*(p3[1]-p2[1]))/denom;const b=(p3[0]*p3[0]*(p1[1]-p2[1])+p2[0]*p2[0]*(p3[1]-p1[1])+p1[0]*p1[0]*(p2[1]-p3[1]))/denom;const c=(p2[0]*p3[0]*(p2[0]-p3[0])*p1[1]+p3[0]*p1[0]*(p3[0]-p1[0])*p2[1]+p1[0]*p2[0]*(p1[0]-p2[0])*p3[1])/denom;return [a,b,c]};function getQuadraticGraphDescription(state,i18n){const strings=describeQuadraticGraph(state,i18n);return strings.srQuadraticInteractiveElements}function describeQuadraticGraph(state,i18n){const{strings,locale}=i18n;const coeffs=getQuadraticCoefficients$1(state.coords);const[a,b,c]=coeffs??[0,0,0];const vertex=[-b/(2*a),c-b*b/(4*a)];const xIntercepts=getQuadraticXIntercepts(a,b,c);const srQuadraticGraph=strings.srQuadraticGraph;const srQuadraticFaceUp=strings.srQuadraticFaceUp;const srQuadraticFaceDown=strings.srQuadraticFaceDown;const srQuadraticDirection=a===0?undefined:a>0?srQuadraticFaceUp:srQuadraticFaceDown;const srQuadraticVertex=a!==0?getQuadraticVertexString(vertex,strings):undefined;const srQuadraticXIntercepts=xIntercepts.length===2?strings.srQuadraticTwoXIntercepts({intercept1:srFormatNumber(xIntercepts[0],locale),intercept2:srFormatNumber(xIntercepts[1],locale)}):xIntercepts.length===1?strings.srQuadraticOneXIntercept({intercept:srFormatNumber(xIntercepts[0],locale)}):undefined;const srQuadraticYIntercept=strings.srQuadraticYIntercept({intercept:srFormatNumber(c,locale)});const srQuadraticInteractiveElements=strings.srInteractiveElements({elements:strings.srQuadraticInteractiveElements({point1X:srFormatNumber(state.coords[0][0],locale),point1Y:srFormatNumber(state.coords[0][1],locale),point2X:srFormatNumber(state.coords[1][0],locale),point2Y:srFormatNumber(state.coords[1][1],locale),point3X:srFormatNumber(state.coords[2][0],locale),point3Y:srFormatNumber(state.coords[2][1],locale)})});return {srQuadraticGraph,srQuadraticDirection,srQuadraticVertex,srQuadraticXIntercepts,srQuadraticYIntercept,srQuadraticInteractiveElements}}const getQuadraticKeyboardConstraint=(coords,snapStep,pointMoved)=>{const newCoords=[coords[0],coords[1],coords[2]];const coordToBeMoved=newCoords[pointMoved];const movePointWithConstraint=moveFunc=>{let movedCoord=moveFunc(coordToBeMoved);newCoords[pointMoved]=movedCoord;if(areCoordsValid(newCoords)){return movedCoord}movedCoord=moveFunc(movedCoord);newCoords[pointMoved]=movedCoord;if(areCoordsValid(newCoords)){return movedCoord}return moveFunc(movedCoord)};return {up:vec.add(coordToBeMoved,[0,snapStep[1]]),down:vec.sub(coordToBeMoved,[0,snapStep[1]]),left:movePointWithConstraint(coord=>vec.sub(coord,[snapStep[0],0])),right:movePointWithConstraint(coord=>vec.add(coord,[snapStep[0],0]))}};const areCoordsValid=coords=>{const p1=coords[0];const p2=coords[1];const p3=coords[2];if(p1[0]===p2[0]||p2[0]===p3[0]||p1[0]===p3[0]){return false}return true};
1973
1973
 
1974
- function renderRayGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(RayGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getRayGraphDescription(state,i18n)}}const RayGraph=props=>{const{dispatch}=props;const{coords:line}=props.graphState;const handleMoveLine=delta=>dispatch(actions.ray.moveRay(delta));const handleMovePoint=(pointIndex,newPoint)=>dispatch(actions.ray.movePoint(pointIndex,newPoint));const{strings,locale}=usePerseusI18n();const id=React.useId();const pointsDescriptionId=id+"-points";const{srRayGraph,srRayPoints,srRayEndpoint,srRayTerminalPoint,srRayGrabHandle}=describeRayGraph(props.graphState,{strings,locale});return jsxRuntimeExports.jsxs("g",{"aria-label":srRayGraph,"aria-describedby":pointsDescriptionId,children:[jsxRuntimeExports.jsx(MovableLine,{points:line,ariaLabels:{point1AriaLabel:srRayEndpoint,point2AriaLabel:srRayTerminalPoint,grabHandleAriaLabel:srRayGrabHandle},onMoveLine:handleMoveLine,onMovePoint:handleMovePoint,extend:{start:false,end:true}}),jsxRuntimeExports.jsx(SRDescInSVG,{id:pointsDescriptionId,children:srRayPoints})]})};function getRayGraphDescription(state,i18n){const strings=describeRayGraph(state,i18n);return strings.srRayInteractiveElement}function describeRayGraph(state,i18n){const{coords:line}=state;const{strings,locale}=i18n;const srRayGraph=strings.srRayGraph;const srRayPoints=strings.srRayPoints({point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)});const srRayEndpoint=strings.srRayEndpoint({x:srFormatNumber(line[0][0],locale),y:srFormatNumber(line[0][1],locale)});const srRayTerminalPoint=strings.srRayTerminalPoint({x:srFormatNumber(line[1][0],locale),y:srFormatNumber(line[1][1],locale)});const srRayGrabHandle=strings.srRayGrabHandle({point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)});const srRayInteractiveElement=strings.srInteractiveElements({elements:[srRayGraph,srRayPoints].join(" ")});return {srRayGraph,srRayPoints,srRayEndpoint,srRayTerminalPoint,srRayGrabHandle,srRayInteractiveElement}}
1974
+ function renderRayGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(RayGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getRayGraphDescription(state,i18n)}}const RayGraph=props=>{const{dispatch}=props;const{coords:line}=props.graphState;const handleMoveLine=newStart=>dispatch(actions.ray.moveRay(newStart));const handleMovePoint=(pointIndex,newPoint)=>dispatch(actions.ray.movePoint(pointIndex,newPoint));const{strings,locale}=usePerseusI18n();const id=React.useId();const pointsDescriptionId=id+"-points";const{srRayGraph,srRayPoints,srRayEndpoint,srRayTerminalPoint,srRayGrabHandle}=describeRayGraph(props.graphState,{strings,locale});return jsxRuntimeExports.jsxs("g",{"aria-label":srRayGraph,"aria-describedby":pointsDescriptionId,children:[jsxRuntimeExports.jsx(MovableLine,{points:line,ariaLabels:{point1AriaLabel:srRayEndpoint,point2AriaLabel:srRayTerminalPoint,grabHandleAriaLabel:srRayGrabHandle},onMoveLine:handleMoveLine,onMovePoint:handleMovePoint,extend:{start:false,end:true}}),jsxRuntimeExports.jsx(SRDescInSVG,{id:pointsDescriptionId,children:srRayPoints})]})};function getRayGraphDescription(state,i18n){const strings=describeRayGraph(state,i18n);return strings.srRayInteractiveElement}function describeRayGraph(state,i18n){const{coords:line}=state;const{strings,locale}=i18n;const srRayGraph=strings.srRayGraph;const srRayPoints=strings.srRayPoints({point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)});const srRayEndpoint=strings.srRayEndpoint({x:srFormatNumber(line[0][0],locale),y:srFormatNumber(line[0][1],locale)});const srRayTerminalPoint=strings.srRayTerminalPoint({x:srFormatNumber(line[1][0],locale),y:srFormatNumber(line[1][1],locale)});const srRayGrabHandle=strings.srRayGrabHandle({point1X:srFormatNumber(line[0][0],locale),point1Y:srFormatNumber(line[0][1],locale),point2X:srFormatNumber(line[1][0],locale),point2Y:srFormatNumber(line[1][1],locale)});const srRayInteractiveElement=strings.srInteractiveElements({elements:[srRayGraph,srRayPoints].join(" ")});return {srRayGraph,srRayPoints,srRayEndpoint,srRayTerminalPoint,srRayGrabHandle,srRayInteractiveElement}}
1975
1975
 
1976
- function renderSegmentGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(SegmentGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getSegmentGraphDescription(state,i18n)}}const SegmentGraph=({dispatch,graphState})=>{const{coords:segments}=graphState;const{strings,locale}=usePerseusI18n();const segmentUniqueId=React.useId();const lengthDescriptionId=segmentUniqueId+"-length";const wholeGraphDescriptionId=segmentUniqueId+"-whole-graph";function getWholeSegmentGraphAriaLabel(){return segments?.length>1?strings.srMultipleSegmentGraphAriaLabel({countOfSegments:segments.length}):strings.srSingleSegmentGraphAriaLabel}const wholeSegmentGraphAriaLabel=getWholeSegmentGraphAriaLabel();function getIndividualSegmentAriaLabel(segment,index){if(segments.length===1){return strings.srSingleSegmentLabel({point1X:srFormatNumber(segments[0][0][X],locale),point1Y:srFormatNumber(segments[0][0][Y],locale),point2X:srFormatNumber(segments[0][1][X],locale),point2Y:srFormatNumber(segments[0][1][Y],locale)})}return strings.srMultipleSegmentIndividualLabel({point1X:srFormatNumber(segment[0][X],locale),point1Y:srFormatNumber(segment[0][Y],locale),point2X:srFormatNumber(segment[1][X],locale),point2Y:srFormatNumber(segment[1][Y],locale),indexOfSegment:index+1})}function getWholeSegmentGraphAriaDescription(){return segments.map((segment,index)=>getIndividualSegmentAriaLabel(segment,index)).join(" ")}function formatSegment(endpointNumber,x,y,index){const segObj={endpointNumber:endpointNumber,x:srFormatNumber(x,locale),y:srFormatNumber(y,locale)};return segments.length>1?strings.srMultipleSegmentGraphEndpointAriaLabel({...segObj,indexOfSegment:index}):strings.srSingleSegmentGraphEndpointAriaLabel(segObj)}return jsxRuntimeExports.jsxs("g",{"aria-label":wholeSegmentGraphAriaLabel,"aria-describedby":`${wholeGraphDescriptionId} ${segments.length===1&&lengthDescriptionId}`,children:[segments?.map((segment,i)=>jsxRuntimeExports.jsxs("g",{"aria-label":segments.length===1?undefined:getIndividualSegmentAriaLabel(segment,i),"aria-describedby":segments.length===1?undefined:lengthDescriptionId,children:[jsxRuntimeExports.jsx(MovableLine,{points:segment,onMoveLine:delta=>{dispatch(actions.segment.moveLine(i,delta));},onMovePoint:(endpointIndex,destination)=>{dispatch(actions.segment.movePointInFigure(i,endpointIndex,destination));},ariaLabels:{point1AriaLabel:formatSegment(1,segment[0][X],segment[0][Y],i+1),point2AriaLabel:formatSegment(2,segment[1][X],segment[1][Y],i+1),grabHandleAriaLabel:strings.srSegmentGrabHandle({point1X:srFormatNumber(segment[0][X],locale),point1Y:srFormatNumber(segment[0][Y],locale),point2X:srFormatNumber(segment[1][X],locale),point2Y:srFormatNumber(segment[1][Y],locale)})}},i),jsxRuntimeExports.jsx(SRDescInSVG,{id:lengthDescriptionId,children:strings.srSegmentLength({length:srFormatNumber(getLengthOfSegment(segment),locale)})})]},`${segmentUniqueId}-${i}`)),jsxRuntimeExports.jsx(SRDescInSVG,{id:wholeGraphDescriptionId,children:getWholeSegmentGraphAriaDescription()})]})};function getLengthOfSegment(segment){return point.distanceToPoint(...segment)}function getSegmentGraphDescription(state,i18n){const{strings,locale}=i18n;const segmentDescriptions=state.coords.map(([point1,point2],index)=>strings.srMultipleSegmentIndividualLabel({point1X:srFormatNumber(point1[X],locale),point1Y:srFormatNumber(point1[Y],locale),point2X:srFormatNumber(point2[X],locale),point2Y:srFormatNumber(point2[Y],locale),indexOfSegment:index+1}));return strings.srInteractiveElements({elements:segmentDescriptions.join(" ")})}
1976
+ function renderSegmentGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(SegmentGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getSegmentGraphDescription(state,i18n)}}const SegmentGraph=({dispatch,graphState})=>{const{coords:segments}=graphState;const{strings,locale}=usePerseusI18n();const segmentUniqueId=React.useId();const lengthDescriptionId=segmentUniqueId+"-length";const wholeGraphDescriptionId=segmentUniqueId+"-whole-graph";function getWholeSegmentGraphAriaLabel(){return segments?.length>1?strings.srMultipleSegmentGraphAriaLabel({countOfSegments:segments.length}):strings.srSingleSegmentGraphAriaLabel}const wholeSegmentGraphAriaLabel=getWholeSegmentGraphAriaLabel();function getIndividualSegmentAriaLabel(segment,index){if(segments.length===1){return strings.srSingleSegmentLabel({point1X:srFormatNumber(segments[0][0][X],locale),point1Y:srFormatNumber(segments[0][0][Y],locale),point2X:srFormatNumber(segments[0][1][X],locale),point2Y:srFormatNumber(segments[0][1][Y],locale)})}return strings.srMultipleSegmentIndividualLabel({point1X:srFormatNumber(segment[0][X],locale),point1Y:srFormatNumber(segment[0][Y],locale),point2X:srFormatNumber(segment[1][X],locale),point2Y:srFormatNumber(segment[1][Y],locale),indexOfSegment:index+1})}function getWholeSegmentGraphAriaDescription(){return segments.map((segment,index)=>getIndividualSegmentAriaLabel(segment,index)).join(" ")}function formatSegment(endpointNumber,x,y,index){const segObj={endpointNumber:endpointNumber,x:srFormatNumber(x,locale),y:srFormatNumber(y,locale)};return segments.length>1?strings.srMultipleSegmentGraphEndpointAriaLabel({...segObj,indexOfSegment:index}):strings.srSingleSegmentGraphEndpointAriaLabel(segObj)}return jsxRuntimeExports.jsxs("g",{"aria-label":wholeSegmentGraphAriaLabel,"aria-describedby":`${wholeGraphDescriptionId} ${segments.length===1&&lengthDescriptionId}`,children:[segments?.map((segment,i)=>jsxRuntimeExports.jsxs("g",{"aria-label":segments.length===1?undefined:getIndividualSegmentAriaLabel(segment,i),"aria-describedby":segments.length===1?undefined:lengthDescriptionId,children:[jsxRuntimeExports.jsx(MovableLine,{points:segment,onMoveLine:newStart=>{dispatch(actions.segment.moveLine(i,newStart));},onMovePoint:(endpointIndex,destination)=>{dispatch(actions.segment.movePointInFigure(i,endpointIndex,destination));},ariaLabels:{point1AriaLabel:formatSegment(1,segment[0][X],segment[0][Y],i+1),point2AriaLabel:formatSegment(2,segment[1][X],segment[1][Y],i+1),grabHandleAriaLabel:strings.srSegmentGrabHandle({point1X:srFormatNumber(segment[0][X],locale),point1Y:srFormatNumber(segment[0][Y],locale),point2X:srFormatNumber(segment[1][X],locale),point2Y:srFormatNumber(segment[1][Y],locale)})}},i),jsxRuntimeExports.jsx(SRDescInSVG,{id:lengthDescriptionId,children:strings.srSegmentLength({length:srFormatNumber(getLengthOfSegment(segment),locale)})})]},`${segmentUniqueId}-${i}`)),jsxRuntimeExports.jsx(SRDescInSVG,{id:wholeGraphDescriptionId,children:getWholeSegmentGraphAriaDescription()})]})};function getLengthOfSegment(segment){return point.distanceToPoint(...segment)}function getSegmentGraphDescription(state,i18n){const{strings,locale}=i18n;const segmentDescriptions=state.coords.map(([point1,point2],index)=>strings.srMultipleSegmentIndividualLabel({point1X:srFormatNumber(point1[X],locale),point1Y:srFormatNumber(point1[Y],locale),point2X:srFormatNumber(point2[X],locale),point2Y:srFormatNumber(point2[Y],locale),indexOfSegment:index+1}));return strings.srInteractiveElements({elements:segmentDescriptions.join(" ")})}
1977
1977
 
1978
1978
  function renderSinusoidGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(SinusoidGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getSinusoidDescription(state,i18n)}}function SinusoidGraph(props){const{dispatch,graphState}=props;const{interactiveColor}=useGraphConfig();const i18n=usePerseusI18n();const id=React.useId();const descriptionId=id+"-description";const{coords,snapStep}=graphState;const coeffRef=React.useRef({amplitude:1,angularFrequency:1,phase:1,verticalOffset:0});const coeffs=getSinusoidCoefficients$1(coords);if(coeffs!==undefined){coeffRef.current=coeffs;}const{srSinusoidGraph,srSinusoidDescription,srSinusoidRootPoint,srSinusoidPeakPoint}=describeSinusoidGraph(graphState,i18n);return jsxRuntimeExports.jsxs("g",{"aria-label":srSinusoidGraph,"aria-describedby":descriptionId,children:[jsxRuntimeExports.jsx(Plot$2.OfX,{y:x=>computeSine(x,coeffRef.current),color:interactiveColor,svgPathProps:{"aria-hidden":true}}),coords.map((coord,i)=>jsxRuntimeExports.jsx(MovablePoint$1,{ariaLabel:i===0?srSinusoidRootPoint:srSinusoidPeakPoint,point:coord,sequenceNumber:i+1,constrain:getSinusoidKeyboardConstraint(coords,snapStep,i),onMove:destination=>dispatch(actions.sinusoid.movePoint(i,destination))},"point-"+i)),jsxRuntimeExports.jsx(SRDescInSVG,{id:descriptionId,children:srSinusoidDescription})]})}const getSinusoidKeyboardConstraint=(coords,snapStep,pointIndex)=>{const coordToBeMoved=coords[pointIndex];const otherPoint=coords[1-pointIndex];const movePointWithConstraint=moveFunc=>{let movedCoord=moveFunc(coordToBeMoved);if(movedCoord[X]===otherPoint[X]){movedCoord=moveFunc(movedCoord);}return movedCoord};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,snapStep[1]])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,snapStep[1]])),left:movePointWithConstraint(coord=>vec.sub(coord,[snapStep[0],0])),right:movePointWithConstraint(coord=>vec.add(coord,[snapStep[0],0]))}};const computeSine=function(x,sinusoidCoefficients){const{amplitude:a,angularFrequency:b,phase:c,verticalOffset:d}=sinusoidCoefficients;return a*Math.sin(b*x-c)+d};const getSinusoidCoefficients$1=coords=>{const p1=coords[0];const p2=coords[1];if(p2[X]===p1[X]){return}const amplitude=p2[Y]-p1[Y];const angularFrequency=Math.PI/(2*(p2[X]-p1[X]));const phase=p1[X]*angularFrequency;const verticalOffset=p1[Y];return {amplitude,angularFrequency,phase,verticalOffset}};function getSinusoidDescription(state,i18n){const strings=describeSinusoidGraph(state,i18n);return strings.srSinusoidInteractiveElements}function describeSinusoidGraph(state,i18n){const{strings,locale}=i18n;const{coords}=state;const[root,peak]=coords;const diffX=Math.abs(peak[X]-root[X]);const diffY=Math.abs(peak[Y]-root[Y]);const formattedRoot={x:srFormatNumber(root[X],locale),y:srFormatNumber(root[Y],locale)};const formattedPeak={x:srFormatNumber(peak[X],locale),y:srFormatNumber(peak[Y],locale)};const srSinusoidGraph=strings.srSinusoidGraph;const srSinusoidDescription=strings.srSinusoidDescription({minValue:srFormatNumber(root[Y]-diffY,locale),maxValue:srFormatNumber(root[Y]+diffY,locale),cycleStart:srFormatNumber(root[X]-2*diffX,locale),cycleEnd:srFormatNumber(root[X]+2*diffX,locale)});const srSinusoidRootPoint=strings.srSinusoidRootPoint(formattedRoot);const srSinusoidPeakPoint=peak[Y]===root[Y]?strings.srSinusoidFlatPoint(formattedPeak):peak[Y]>root[Y]?strings.srSinusoidMaxPoint(formattedPeak):strings.srSinusoidMinPoint(formattedPeak);const srSinusoidInteractiveElements=strings.srInteractiveElements({elements:strings.srSinusoidInteractiveElements({point1X:srFormatNumber(root[X],locale),point1Y:srFormatNumber(root[Y],locale),point2X:srFormatNumber(peak[X],locale),point2Y:srFormatNumber(peak[Y],locale)})});return {srSinusoidGraph,srSinusoidDescription,srSinusoidRootPoint,srSinusoidPeakPoint,srSinusoidInteractiveElements}}
1979
1979
 
1980
1980
  function renderTangentGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(TangentGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getTangentDescription(state,i18n)}}function TangentGraph(props){const{dispatch,graphState}=props;const{interactiveColor,range}=useGraphConfig();const i18n=usePerseusI18n();const id=React.useId();const descriptionId=id+"-description";const{coords,snapStep}=graphState;const coeffRef=React.useRef({amplitude:1,angularFrequency:1,phase:1,verticalOffset:0});const coeffs=getTangentCoefficients$1(coords);if(coeffs!==undefined){coeffRef.current=coeffs;}const xRange=[range[0][0],range[0][1]];const segments=getPlotSegments(coeffRef.current,xRange);const{srTangentGraph,srTangentDescription,srTangentInflectionPoint,srTangentSecondPoint}=describeTangentGraph(graphState,i18n);return jsxRuntimeExports.jsxs("g",{"aria-label":srTangentGraph,"aria-describedby":descriptionId,children:[segments.map(([segStart,segEnd],i)=>jsxRuntimeExports.jsx(Plot$2.OfX,{y:x=>computeTangent(x,coeffRef.current),domain:[segStart,segEnd],color:interactiveColor,svgPathProps:{"aria-hidden":true}},`tangent-segment-${i}`)),coords.map((coord,i)=>jsxRuntimeExports.jsx(MovablePoint$1,{ariaLabel:i===0?srTangentInflectionPoint:srTangentSecondPoint,point:coord,sequenceNumber:i+1,constrain:getTangentKeyboardConstraint(coords,snapStep,i),onMove:destination=>dispatch(actions.tangent.movePoint(i,destination))},"point-"+i)),jsxRuntimeExports.jsx(SRDescInSVG,{id:descriptionId,children:srTangentDescription})]})}const getTangentKeyboardConstraint=(coords,snapStep,pointIndex)=>{const coordToBeMoved=coords[pointIndex];const otherPoint=coords[1-pointIndex];const movePointWithConstraint=moveFunc=>{let movedCoord=moveFunc(coordToBeMoved);if(movedCoord[X]===otherPoint[X]){movedCoord=moveFunc(movedCoord);}return movedCoord};return {up:movePointWithConstraint(coord=>vec.add(coord,[0,snapStep[1]])),down:movePointWithConstraint(coord=>vec.sub(coord,[0,snapStep[1]])),left:movePointWithConstraint(coord=>vec.sub(coord,[snapStep[0],0])),right:movePointWithConstraint(coord=>vec.add(coord,[snapStep[0],0]))}};const computeTangent=function(x,tangentCoefficients){const{amplitude:a,angularFrequency:b,phase:c,verticalOffset:d}=tangentCoefficients;const arg=b*x-c;const normalized=(arg-Math.PI/2)/Math.PI%1;const distToAsymptote=Math.abs(normalized>.5?normalized-1:normalized<-0.5?normalized+1:normalized);if(distToAsymptote<.001){return NaN}return a*Math.tan(arg)+d};const getTangentCoefficients$1=coords=>{const p1=coords[0];const p2=coords[1];if(p2[X]===p1[X]){return}const amplitude=p2[Y]-p1[Y];const angularFrequency=Math.PI/(4*(p2[X]-p1[X]));const phase=p1[X]*angularFrequency;const verticalOffset=p1[Y];return {amplitude,angularFrequency,phase,verticalOffset}};function getAsymptotePositions(coeffs,xRange){const{angularFrequency:b,phase:c}=coeffs;if(b===0){return []}const period=Math.PI/Math.abs(b);const referenceAsymptote=(c+Math.PI/2)/b;const asymptotes=[];let x=referenceAsymptote;while(x>xRange[0]-period){if(x>xRange[0]&&x<xRange[1]){asymptotes.push(x);}x-=period;}x=referenceAsymptote+period;while(x<xRange[1]+period){if(x>xRange[0]&&x<xRange[1]){asymptotes.push(x);}x+=period;}return asymptotes.sort((a,b)=>a-b)}function getPlotSegments(coeffs,xRange){const asymptotes=getAsymptotePositions(coeffs,xRange);const eps=.01;const segments=[];let start=xRange[0];for(const asymptote of asymptotes){segments.push([start,asymptote-eps]);start=asymptote+eps;}segments.push([start,xRange[1]]);return segments}function getTangentDescription(state,i18n){return describeTangentGraph(state,i18n).srTangentInteractiveElements}function describeTangentGraph(state,i18n){const{strings,locale}=i18n;const{coords}=state;const[inflection,secondPoint]=coords;const formattedInflection={x:srFormatNumber(inflection[X],locale),y:srFormatNumber(inflection[Y],locale)};const formattedSecondPoint={x:srFormatNumber(secondPoint[X],locale),y:srFormatNumber(secondPoint[Y],locale)};const srTangentGraph=strings.srTangentGraph;const srTangentDescription=strings.srTangentDescription({inflectionX:srFormatNumber(inflection[X],locale),inflectionY:srFormatNumber(inflection[Y],locale)});const srTangentInflectionPoint=strings.srTangentInflectionPoint(formattedInflection);const srTangentSecondPoint=strings.srTangentSecondPoint(formattedSecondPoint);const srTangentInteractiveElements=strings.srInteractiveElements({elements:strings.srTangentInteractiveElements({point1X:srFormatNumber(inflection[X],locale),point1Y:srFormatNumber(inflection[Y],locale),point2X:srFormatNumber(secondPoint[X],locale),point2Y:srFormatNumber(secondPoint[Y],locale)})});return {srTangentGraph,srTangentDescription,srTangentInflectionPoint,srTangentSecondPoint,srTangentInteractiveElements}}
1981
1981
 
1982
+ const hitboxSizePx=48;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,disableKeyboardInteraction,snapStep}=useGraphConfig();const{point,angle,dragging,cursor,showFocusRing,onClick=()=>{}}=props;const wbColorName=disableKeyboardInteraction?"fadedOffBlack64":"blue";const classes=classNames("movable-arrowhead",dragging&&"movable-arrowhead--dragging",showFocusRing&&"movable-arrowhead--focus");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.jsxs("g",{"aria-hidden":true,ref:hitboxRef,className:classes,style:{"--movable-arrowhead-color":interactiveColor,cursor},"data-testid":"movable-arrowhead",onClick:onClick,children:[jsxRuntimeExports.jsx("circle",{className:"movable-arrowhead-hitbox",r:hitboxSizePx/2,cx:x,cy:y}),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,backgroundColor:wbColorName,content:tooltipContent,contentStyle:{color:"white"},children:svgForArrowhead}):svgForArrowhead})});function classNames(...names){return names.filter(Boolean).join(" ")}
1983
+
1984
+ function useControlArrowhead(params){const{snapStep,disableKeyboardInteraction}=useGraphConfig();const{point,angle,ariaDescribedBy,ariaLabel,ariaLive="polite",constrain=p=>snap(snapStep,p),sequenceNumber=1,onMove=noop,onDragEnd=noop}=params;const{strings,locale}=usePerseusI18n();const[focused,setFocused]=useState(false);const focusableHandleRef=useRef(null);useDraggable({gestureTarget:focusableHandleRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const visibleRef=useRef(null);const{dragging}=useDraggable({gestureTarget:visibleRef,point,onMove,onDragEnd,constrainKeyboardMovement:constrain});const pointAriaLabel=ariaLabel||strings.srPointAtCoordinates({num: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-live":ariaLive,"aria-disabled":disableKeyboardInteraction,onFocus:()=>setFocused(true),onBlur:()=>setFocused(false)});const visibleArrowhead=jsxRuntimeExports.jsx(MovableArrowheadView,{point:point,angle:angle,dragging:dragging,focused:focused,ref:visibleRef,showFocusRing:focused,onClick:()=>{focusableHandleRef.current?.focus();}});return {focusableHandle,visibleArrowhead,dragging,focused}}const noop=()=>{};
1985
+
1986
+ const{calculateAngleInDegrees: calculateAngleInDegrees$1}=angles;const LINE_PULLBACK_PX=4;const TAIL_DOT_RADIUS=6;function renderVectorGraph(state,dispatch,i18n){return {graph:jsxRuntimeExports.jsx(VectorGraph,{graphState:state,dispatch:dispatch}),interactiveElementsDescription:getVectorGraphDescription(state,i18n)}}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,srVectorPoints,srVectorTipPoint,srVectorGrabHandle}=describeVectorGraph(props.graphState,{strings,locale});const tipArrowhead=useTipArrowhead({tail,tip,ariaLabel:srVectorTipPoint,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:srVectorPoints})]})};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{dragging}=useDraggable({gestureTarget:bodyRef,point:tail,onMove,onDragEnd:()=>{bodyRef.current?.blur();},constrainKeyboardMovement:p=>snap(snapStep,p)});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("g",{ref:bodyRef,tabIndex:disableKeyboardInteraction?-1:0,"aria-label":ariaLabel,"aria-describedby":ariaDescribedBy,"aria-disabled":disableKeyboardInteraction,"aria-live":"polite",className:"movable-line","data-testid":"movable-vector",style:{cursor:dragging?"grabbing":"grab"},role:"button",onMouseEnter:()=>setHovered(true),onMouseLeave:()=>setHovered(false),onFocus:()=>setFocused(true),onBlur:()=>setFocused(false),children:[jsxRuntimeExports.jsx(SVGLine,{start:tailPx,end:lineEndPx,style:{stroke:"transparent",strokeWidth:TARGET_SIZE}}),jsxRuntimeExports.jsx(SVGLine,{start:tailPx,end:lineEndPx,className:`movable-vector-line ${active?"movable-dragging":""}`,testId:"movable-vector__line"}),jsxRuntimeExports.jsx("circle",{cx:tailPx[X],cy:tailPx[Y],r:TAIL_DOT_RADIUS,fill:interactiveColor,"data-testid":"vector-tail-dot"}),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]))}};function getVectorGraphDescription(state,i18n){const strings=describeVectorGraph(state,i18n);return strings.srVectorInteractiveElement}function describeVectorGraph(state,i18n){const{coords}=state;const[tail,tip]=coords;const{strings,locale}=i18n;const srVectorGraph=strings.srVectorGraph;const srVectorPoints=strings.srVectorPoints({tailX:srFormatNumber(tail[X],locale),tailY:srFormatNumber(tail[Y],locale),tipX:srFormatNumber(tip[X],locale),tipY:srFormatNumber(tip[Y],locale)});const srVectorTipPoint=strings.srVectorTipPoint({x:srFormatNumber(tip[X],locale),y:srFormatNumber(tip[Y],locale)});const srVectorGrabHandle=strings.srVectorGrabHandle({tailX:srFormatNumber(tail[X],locale),tailY:srFormatNumber(tail[Y],locale),tipX:srFormatNumber(tip[X],locale),tipY:srFormatNumber(tip[Y],locale)});const srVectorInteractiveElement=strings.srInteractiveElements({elements:[srVectorGraph,srVectorPoints].join(" ")});return {srVectorGraph,srVectorPoints,srVectorTipPoint,srVectorGrabHandle,srVectorInteractiveElement}}
1987
+
1982
1988
  const{calculateAngleInDegrees,convertDegreesToRadians}=angles;const protractorImage="https://cdn.kastatic.org/images/perseus/protractor.svg";const centerToTopLeft=[-195,-190];const centerToRotationHandle=[-201,-15];function Protractor(){const staticUrl=getDependencies().staticUrl;const{range,snapStep}=useGraphConfig();const[[xMin,xMax],[yMin,yMax]]=range;const initialCenter=[lerp(xMin,xMax,.5),lerp(yMin,yMax,.25)];const[center,setCenter]=useState(initialCenter);const[rotationHandleOffset,setRotationHandleOffset]=useState(centerToRotationHandle);const draggableRef=useRef(null);const{dragging}=useDraggable({gestureTarget:draggableRef,onMove:setCenter,point:center,constrainKeyboardMovement:point=>bound$1({snapStep,range,point})});const rotationHandleRef=useRef(null);useDraggablePx({gestureTarget:rotationHandleRef,onMove:setRotationHandleOffset,point:rotationHandleOffset,constrain:constrainToCircle});const[centerPx]=useTransformVectorsToPixels(center);const topLeftPx=vec.add(centerPx,centerToTopLeft);const angle=calculateAngleInDegrees(rotationHandleOffset)-calculateAngleInDegrees(centerToRotationHandle);return jsxRuntimeExports.jsxs("g",{ref:draggableRef,transform:`translate(${topLeftPx[X]}, ${topLeftPx[Y]}), rotate(${angle})`,style:{transformOrigin:`${-centerToTopLeft[X]}px ${-centerToTopLeft[Y]}px`,cursor:dragging?"grabbing":"grab"},children:[jsxRuntimeExports.jsx("image",{href:staticUrl(protractorImage)}),jsxRuntimeExports.jsx("g",{transform:`translate(5, ${-centerToTopLeft[1]})`,ref:rotationHandleRef,children:jsxRuntimeExports.jsx(RotationArrow,{})})]})}function RotationArrow(){const radius=175;const angleDeg=10;const angleRad=convertDegreesToRadians(angleDeg);const endX=radius*(1-Math.cos(angleRad));const endY=radius*-Math.sin(angleRad);const rotationArrow=pathBuilder().move(0,0).circularArc(radius,endX,endY,{sweep:true}).build();const arrowhead=pathBuilder().move(-8,0).line(0,10).line(8,0).build();const targetRadius=TARGET_SIZE/2;return jsxRuntimeExports.jsxs("g",{className:"protractor-rotation-handle",children:[jsxRuntimeExports.jsx("path",{className:"protractor-rotation-handle-arrow-arc",d:rotationArrow}),jsxRuntimeExports.jsx("path",{className:"protractor-rotation-handle-arrowhead",d:arrowhead}),jsxRuntimeExports.jsx("path",{className:"protractor-rotation-handle-arrowhead",d:arrowhead,transform:`translate(${endX}, ${endY}), rotate(${180+angleDeg})`}),jsxRuntimeExports.jsx("ellipse",{cx:"0px",cy:"-15px",rx:targetRadius,ry:targetRadius,fill:"none"})]})}const protractorRadius=vec.mag(centerToRotationHandle);function constrainToCircle(edgePoint){return vec.withMag(edgePoint,protractorRadius)}function useDraggablePx(args){const{gestureTarget:target,onMove,point,constrain=p=>p}=args;const pickupPx=React.useRef([0,0]);useDrag(state=>{const{event,first,movement:pixelMovement}=state;event?.stopPropagation();if(first){pickupPx.current=point;}if(vec.mag(pixelMovement)===0){return}onMove?.(constrain(vec.add(pickupPx.current,pixelMovement)));},{target,eventOptions:{passive:false}});}
1983
1989
 
1984
- const GRAPH_LEFT_MARGIN=20;const MafsGraph=props=>{const{state,dispatch,labels,labelLocation,readOnly,fullGraphAriaLabel,fullGraphAriaDescription,widgetId}=props;const{type}=state;const[width,height]=props.box;const tickStep=props.step;const uniqueId=React.useId();const descriptionId=`interactive-graph-description-${uniqueId}`;const interactiveElementsDescriptionId=`interactive-graph-interactive-elements-description-${uniqueId}`;const unlimitedGraphKeyboardPromptId=`unlimited-graph-keyboard-prompt-${uniqueId}`;const instructionsId=`instructions-${uniqueId}`;const graphRef=React.useRef(null);const{analytics}=useDependencies();const{viewboxX,viewboxY}=calculateNestedSVGCoords(state.range,width,height);const viewBox=`${viewboxX} ${viewboxY} ${width} ${height}`;const nestedSVGAttributes={width,height,viewBox,preserveAspectRatio:"xMidYMin",x:viewboxX,y:viewboxY};const i18n=usePerseusI18n();const{strings}=i18n;const interactionPrompt=isUnlimitedGraphState(state)&&state.showKeyboardInteractionInvitation;useOnMountEffect(()=>{analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:type,widgetType:"interactive-graph",widgetId:widgetId}});});const{graph,interactiveElementsDescription}=renderGraphElements({state,dispatch,i18n,markings:props.markings});const disableInteraction=readOnly||!!props.static;const graphInfo={range:state.range,width,height};const[xAxisLabelLocation,yAxisLabelLocation]=getLabelPosition(graphInfo,labelLocation,tickStep);const needsExtraMargin=labelLocation==="alongEdge"&&yAxisLabelLocation[0]<-14*fontSizeYAxisLabelMultiplier;const marginLabelDiff=GRAPH_LEFT_MARGIN-fontSize*fontSizeYAxisLabelMultiplier;const marginWithExtraOffset=-1*(yAxisLabelLocation[X]-marginLabelDiff);return jsxRuntimeExports.jsx(GraphConfigContext.Provider,{value:{range:state.range,snapStep:state.snapStep,markings:props.markings,tickStep:tickStep,gridStep:props.gridStep,showTooltips:!!props.showTooltips,showAxisArrows:props.showAxisArrows,graphDimensionsInPixels:props.box,width,height,labels,labelLocation,disableKeyboardInteraction:disableInteraction,interactiveColor:disableInteraction?"var(--static-gray)":"var(--mafs-blue)"},children:jsxRuntimeExports.jsxs(View,{className:"mafs-graph-container",children:[jsxRuntimeExports.jsxs(View,{className:"mafs-graph",style:{position:"relative",padding:"25px 25px 0 0",boxSizing:"content-box",marginLeft:needsExtraMargin?`${marginWithExtraOffset}px`:`${GRAPH_LEFT_MARGIN}px`,marginBottom:"30px",pointerEvents:props.static?"none":"auto",userSelect:"none",width,height},onKeyUp:event=>{handleKeyboardEvent(event,state,dispatch);},"aria-label":fullGraphAriaLabel,"aria-describedby":describedByIds(fullGraphAriaDescription&&descriptionId,interactiveElementsDescription&&interactiveElementsDescriptionId,isUnlimitedGraphState(state)&&unlimitedGraphKeyboardPromptId,state.type!=="none"&&!disableInteraction&&instructionsId),ref:graphRef,tabIndex:0,onFocus:event=>{handleFocusEvent(event,state,dispatch);},onBlur:event=>{handleBlurEvent(event,state,dispatch);},children:[fullGraphAriaDescription&&jsxRuntimeExports.jsx(View,{id:descriptionId,tabIndex:-1,className:"mafs-sr-only",children:fullGraphAriaDescription}),interactiveElementsDescription&&jsxRuntimeExports.jsx(View,{id:interactiveElementsDescriptionId,tabIndex:-1,className:"mafs-sr-only",children:interactiveElementsDescription}),state.type!=="none"&&jsxRuntimeExports.jsx(View,{id:instructionsId,tabIndex:-1,className:"mafs-sr-only",children:isUnlimitedGraphState(state)?strings.srUnlimitedGraphInstructions:strings.srGraphInstructions}),jsxRuntimeExports.jsx(LegacyGrid,{box:props.box,backgroundImage:props.backgroundImage}),jsxRuntimeExports.jsxs(View,{style:{position:"absolute",bottom:0,left:0},children:[(props.markings==="graph"||props.markings==="axes")&&jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:jsxRuntimeExports.jsx(AxisLabels,{i18n:i18n,xAxisLabelLocation:xAxisLabelLocation,yAxisLabelLocation:yAxisLabelLocation})}),jsxRuntimeExports.jsx(View,{"aria-hidden":props.lockedFigures.length===0,children:jsxRuntimeExports.jsxs(Mafs,{preserveAspectRatio:false,viewBox:{x:state.range[X],y:state.range[Y],padding:0},pan:false,zoom:false,width:width,height:height,children:[jsxRuntimeExports.jsx(SvgDefs,{}),jsxRuntimeExports.jsx("svg",{...nestedSVGAttributes,children:jsxRuntimeExports.jsx(Grid,{gridStep:props.gridStep,range:state.range,containerSizeClass:props.containerSizeClass,markings:props.markings,width:width,height:height})}),(props.markings==="graph"||props.markings==="axes")&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(AxisTicks,{}),jsxRuntimeExports.jsx(AxisArrows,{})]}),props.lockedFigures.length>0&&jsxRuntimeExports.jsx("svg",{...nestedSVGAttributes,children:jsxRuntimeExports.jsx(GraphLockedLayer,{lockedFigures:props.lockedFigures,range:state.range})})]})}),jsxRuntimeExports.jsx(GraphLockedLabelsLayer,{lockedFigures:props.lockedFigures}),jsxRuntimeExports.jsx(View,{style:{position:"absolute"},children:jsxRuntimeExports.jsx(Mafs,{preserveAspectRatio:false,viewBox:{x:state.range[X],y:state.range[Y],padding:0},pan:false,zoom:false,width:width,height:height,children:jsxRuntimeExports.jsxs("svg",{...nestedSVGAttributes,style:{overflow:type==="point"?"visible":"hidden"},children:[props.showProtractor&&jsxRuntimeExports.jsx(Protractor,{}),graph]})})})]}),interactionPrompt&&jsxRuntimeExports.jsx(View,{style:{display:interactionPrompt?undefined:"hidden",textAlign:"center",backgroundColor:"white",border:"1px solid #21242C52",padding:"16px 0",boxShadow:"0px 8px 8px 0px #21242C14",top:"50%",transform:"translateY(-50%)"},children:jsxRuntimeExports.jsx(BodyText,{id:unlimitedGraphKeyboardPromptId,children:strings.graphKeyboardPrompt})})]}),renderGraphControls({state,dispatch,width,perseusStrings:strings})]})})};const renderPointGraphControls=props=>{const{interactionMode,showRemovePointButton,focusedPointIndex}=props.state;const{perseusStrings}=props;const shouldShowRemoveButton=showRemovePointButton&&focusedPointIndex!==null;return jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row",width:props.width},children:[interactionMode==="keyboard"&&jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},tabIndex:0,onClick:()=>{props.dispatch(actions.pointGraph.addPoint([0,0]));},children:perseusStrings.addPoint}),interactionMode==="mouse"&&jsxRuntimeExports.jsx(Button,{id:REMOVE_BUTTON_ID,kind:"secondary",actionType:"destructive",tabIndex:-1,style:{width:"100%",marginLeft:"20px",visibility:shouldShowRemoveButton?"visible":"hidden"},onClick:_event=>{props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));},children:perseusStrings.removePoint})]})};const renderPolygonGraphControls=props=>{const{interactionMode,showRemovePointButton,focusedPointIndex,closedPolygon,coords}=props.state;const{perseusStrings}=props;const shouldShowRemoveButton=showRemovePointButton&&focusedPointIndex!==null;const disableCloseButton=getArrayWithoutDuplicates(coords).length<3;const polygonButton=closedPolygon?jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},tabIndex:0,onClick:()=>{props.dispatch(actions.polygon.openPolygon());},children:perseusStrings.openPolygon}):jsxRuntimeExports.jsx(Button,{kind:"secondary",disabled:disableCloseButton,style:{width:"100%",marginLeft:"20px"},tabIndex:disableCloseButton?-1:0,onClick:()=>{props.dispatch(actions.polygon.closePolygon());},children:perseusStrings.closePolygon});return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row",width:props.width},children:[interactionMode==="keyboard"&&jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},disabled:closedPolygon,tabIndex:closedPolygon?-1:0,onClick:()=>{props.dispatch(actions.polygon.addPoint([0,0]));},children:perseusStrings.addPoint}),interactionMode==="mouse"&&jsxRuntimeExports.jsx(Button,{id:REMOVE_BUTTON_ID,kind:"secondary",actionType:"destructive",disabled:closedPolygon||!shouldShowRemoveButton,tabIndex:-1,style:{width:"100%",marginLeft:"20px"},onClick:_event=>{props.dispatch(actions.polygon.removePoint(props.state.focusedPointIndex));},children:perseusStrings.removePoint}),polygonButton]})})};const renderGraphControls=props=>{const{state,dispatch,width,perseusStrings}=props;const{type}=state;switch(type){case "point":if(state.numPoints==="unlimited"){return renderPointGraphControls({state,dispatch,width,perseusStrings})}return null;case "polygon":if(state.numSides==="unlimited"){return renderPolygonGraphControls({state,dispatch,width,perseusStrings})}return null;default:return null}};function handleFocusEvent(event,state,dispatch){if(isUnlimitedGraphState(state)){if(event.target.classList.contains("mafs-graph")&&state.interactionMode==="mouse"){dispatch(actions.global.changeKeyboardInvitationVisibility(true));}}}function handleBlurEvent(_event,state,dispatch){if(isUnlimitedGraphState(state)){dispatch(actions.global.changeKeyboardInvitationVisibility(false));}}function handleKeyboardEvent(event,state,dispatch){if(isUnlimitedGraphState(state)){if(event.key==="Backspace"||event.key==="Delete"){if(document.activeElement?.classList.contains("movable-point__focusable-handle")){if(state.type==="point"||state.type==="polygon"&&!state.closedPolygon){dispatch(actions.global.deleteIntent());}}document.activeElement.blur();}else if(event.shiftKey&&event.key==="Enter"){dispatch(actions.global.changeInteractionMode("keyboard"));}else if(state.interactionMode==="keyboard"&&event.key==="a"){dispatch(actions.pointGraph.addPoint([0,0]));}}}const renderGraphElements=props=>{const{state,dispatch,i18n,markings}=props;const{type}=state;switch(type){case "angle":return renderAngleGraph(state,dispatch,i18n);case "segment":return renderSegmentGraph(state,dispatch,i18n);case "linear-system":return renderLinearSystemGraph(state,dispatch,i18n);case "linear":return renderLinearGraph(state,dispatch,i18n);case "ray":return renderRayGraph(state,dispatch,i18n);case "polygon":return renderPolygonGraph(state,dispatch,i18n,markings);case "point":return renderPointGraph(state,dispatch,i18n);case "circle":return renderCircleGraph(state,dispatch,i18n);case "quadratic":return renderQuadraticGraph(state,dispatch,i18n);case "sinusoid":return renderSinusoidGraph(state,dispatch,i18n);case "exponential":return renderExponentialGraph(state,dispatch,i18n);case "none":return {graph:null,interactiveElementsDescription:null};case "absolute-value":return renderAbsoluteValueGraph(state,dispatch,i18n);case "tangent":return renderTangentGraph(state,dispatch,i18n);case "logarithm":return renderLogarithmGraph(state,dispatch,i18n);default:throw new UnreachableCaseError(type)}};function describedByIds(...args){return args.filter(Boolean).join(" ")||undefined}
1990
+ const GRAPH_LEFT_MARGIN=20;const MafsGraph=props=>{const{state,dispatch,labels,labelLocation,readOnly,fullGraphAriaLabel,fullGraphAriaDescription,widgetId}=props;const{type}=state;const[width,height]=props.box;const tickStep=props.step;const uniqueId=React.useId();const descriptionId=`interactive-graph-description-${uniqueId}`;const interactiveElementsDescriptionId=`interactive-graph-interactive-elements-description-${uniqueId}`;const unlimitedGraphKeyboardPromptId=`unlimited-graph-keyboard-prompt-${uniqueId}`;const instructionsId=`instructions-${uniqueId}`;const graphRef=React.useRef(null);const{analytics}=useDependencies();const{viewboxX,viewboxY}=calculateNestedSVGCoords(state.range,width,height);const viewBox=`${viewboxX} ${viewboxY} ${width} ${height}`;const nestedSVGAttributes={width,height,viewBox,preserveAspectRatio:"xMidYMin",x:viewboxX,y:viewboxY};const i18n=usePerseusI18n();const{strings}=i18n;const interactionPrompt=isUnlimitedGraphState(state)&&state.showKeyboardInteractionInvitation;useOnMountEffect(()=>{analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:type,widgetType:"interactive-graph",widgetId:widgetId}});});const{graph,interactiveElementsDescription}=renderGraphElements({state,dispatch,i18n,markings:props.markings});const disableInteraction=readOnly||!!props.static;const graphInfo={range:state.range,width,height};const[xAxisLabelLocation,yAxisLabelLocation]=getLabelPosition(graphInfo,labelLocation,tickStep);const needsExtraMargin=labelLocation==="alongEdge"&&yAxisLabelLocation[0]<-14*fontSizeYAxisLabelMultiplier;const marginLabelDiff=GRAPH_LEFT_MARGIN-fontSize*fontSizeYAxisLabelMultiplier;const marginWithExtraOffset=-1*(yAxisLabelLocation[X]-marginLabelDiff);const showsAxisLabels=props.markings==="graph"||props.markings==="axes";const hasXAxisLabel=!!(labels[0]&&labels[0].trim());const graphMarginBottom=getGraphBottomMargin(xAxisLabelLocation[Y],height,hasXAxisLabel,showsAxisLabels);return jsxRuntimeExports.jsx(GraphConfigContext.Provider,{value:{range:state.range,snapStep:state.snapStep,markings:props.markings,tickStep:tickStep,gridStep:props.gridStep,showTooltips:!!props.showTooltips,showAxisArrows:props.showAxisArrows,graphDimensionsInPixels:props.box,width,height,labels,labelLocation,disableKeyboardInteraction:disableInteraction,interactiveColor:disableInteraction?"var(--static-gray)":"var(--mafs-blue)"},children:jsxRuntimeExports.jsxs(View,{className:"mafs-graph-container",children:[jsxRuntimeExports.jsxs(View,{className:"mafs-graph",style:{position:"relative",padding:"25px 25px 0 0",boxSizing:"content-box",marginLeft:needsExtraMargin?`${marginWithExtraOffset}px`:`${GRAPH_LEFT_MARGIN}px`,marginBottom:graphMarginBottom,pointerEvents:props.static?"none":"auto",userSelect:"none",width,height},onKeyUp:event=>{handleKeyboardEvent(event,state,dispatch);},"aria-label":fullGraphAriaLabel,"aria-describedby":describedByIds(fullGraphAriaDescription&&descriptionId,interactiveElementsDescription&&interactiveElementsDescriptionId,isUnlimitedGraphState(state)&&unlimitedGraphKeyboardPromptId,state.type!=="none"&&!disableInteraction&&instructionsId),ref:graphRef,tabIndex:0,onFocus:event=>{handleFocusEvent(event,state,dispatch);},onBlur:event=>{handleBlurEvent(event,state,dispatch);},children:[fullGraphAriaDescription&&jsxRuntimeExports.jsx(View,{id:descriptionId,tabIndex:-1,className:"mafs-sr-only",children:fullGraphAriaDescription}),interactiveElementsDescription&&jsxRuntimeExports.jsx(View,{id:interactiveElementsDescriptionId,tabIndex:-1,className:"mafs-sr-only",children:interactiveElementsDescription}),state.type!=="none"&&jsxRuntimeExports.jsx(View,{id:instructionsId,tabIndex:-1,className:"mafs-sr-only",children:isUnlimitedGraphState(state)?strings.srUnlimitedGraphInstructions:strings.srGraphInstructions}),jsxRuntimeExports.jsx(LegacyGrid,{box:props.box,backgroundImage:props.backgroundImage}),jsxRuntimeExports.jsxs(View,{style:{position:"absolute",bottom:0,left:0},children:[(props.markings==="graph"||props.markings==="axes")&&jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:jsxRuntimeExports.jsx(AxisLabels,{i18n:i18n,xAxisLabelLocation:xAxisLabelLocation,yAxisLabelLocation:yAxisLabelLocation})}),jsxRuntimeExports.jsx(View,{"aria-hidden":props.lockedFigures.length===0,children:jsxRuntimeExports.jsxs(Mafs,{preserveAspectRatio:false,viewBox:{x:state.range[X],y:state.range[Y],padding:0},pan:false,zoom:false,width:width,height:height,children:[jsxRuntimeExports.jsx(SvgDefs,{}),jsxRuntimeExports.jsx("svg",{...nestedSVGAttributes,children:jsxRuntimeExports.jsx(Grid,{gridStep:props.gridStep,range:state.range,containerSizeClass:props.containerSizeClass,markings:props.markings,width:width,height:height})}),(props.markings==="graph"||props.markings==="axes")&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(AxisTicks,{}),jsxRuntimeExports.jsx(AxisArrows,{})]}),props.lockedFigures.length>0&&jsxRuntimeExports.jsx("svg",{...nestedSVGAttributes,children:jsxRuntimeExports.jsx(GraphLockedLayer,{lockedFigures:props.lockedFigures,range:state.range})})]})}),jsxRuntimeExports.jsx(GraphLockedLabelsLayer,{lockedFigures:props.lockedFigures}),jsxRuntimeExports.jsx(View,{style:{position:"absolute"},children:jsxRuntimeExports.jsx(Mafs,{preserveAspectRatio:false,viewBox:{x:state.range[X],y:state.range[Y],padding:0},pan:false,zoom:false,width:width,height:height,children:jsxRuntimeExports.jsxs("svg",{...nestedSVGAttributes,style:{overflow:type==="point"?"visible":"hidden"},children:[props.showProtractor&&jsxRuntimeExports.jsx(Protractor,{}),graph]})})})]}),interactionPrompt&&jsxRuntimeExports.jsx(View,{style:{display:interactionPrompt?undefined:"hidden",textAlign:"center",backgroundColor:"white",border:"1px solid #21242C52",padding:"16px 0",boxShadow:"0px 8px 8px 0px #21242C14",top:"50%",transform:"translateY(-50%)"},children:jsxRuntimeExports.jsx(BodyText,{id:unlimitedGraphKeyboardPromptId,children:strings.graphKeyboardPrompt})})]}),renderGraphControls({state,dispatch,width,perseusStrings:strings})]})})};const renderPointGraphControls=props=>{const{interactionMode,showRemovePointButton,focusedPointIndex}=props.state;const{perseusStrings}=props;const shouldShowRemoveButton=showRemovePointButton&&focusedPointIndex!==null;return jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row",width:props.width},children:[interactionMode==="keyboard"&&jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},tabIndex:0,onClick:()=>{props.dispatch(actions.pointGraph.addPoint([0,0]));},children:perseusStrings.addPoint}),interactionMode==="mouse"&&jsxRuntimeExports.jsx(Button,{id:REMOVE_BUTTON_ID,kind:"secondary",actionType:"destructive",tabIndex:-1,style:{width:"100%",marginLeft:"20px",visibility:shouldShowRemoveButton?"visible":"hidden"},onClick:_event=>{props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));},children:perseusStrings.removePoint})]})};const renderPolygonGraphControls=props=>{const{interactionMode,showRemovePointButton,focusedPointIndex,closedPolygon,coords}=props.state;const{perseusStrings}=props;const shouldShowRemoveButton=showRemovePointButton&&focusedPointIndex!==null;const disableCloseButton=getArrayWithoutDuplicates(coords).length<3;const polygonButton=closedPolygon?jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},tabIndex:0,onClick:()=>{props.dispatch(actions.polygon.openPolygon());},children:perseusStrings.openPolygon}):jsxRuntimeExports.jsx(Button,{kind:"secondary",disabled:disableCloseButton,style:{width:"100%",marginLeft:"20px"},tabIndex:disableCloseButton?-1:0,onClick:()=>{props.dispatch(actions.polygon.closePolygon());},children:perseusStrings.closePolygon});return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row",width:props.width},children:[interactionMode==="keyboard"&&jsxRuntimeExports.jsx(Button,{kind:"secondary",style:{width:"100%",marginLeft:"20px"},disabled:closedPolygon,tabIndex:closedPolygon?-1:0,onClick:()=>{props.dispatch(actions.polygon.addPoint([0,0]));},children:perseusStrings.addPoint}),interactionMode==="mouse"&&jsxRuntimeExports.jsx(Button,{id:REMOVE_BUTTON_ID,kind:"secondary",actionType:"destructive",disabled:closedPolygon||!shouldShowRemoveButton,tabIndex:-1,style:{width:"100%",marginLeft:"20px"},onClick:_event=>{props.dispatch(actions.polygon.removePoint(props.state.focusedPointIndex));},children:perseusStrings.removePoint}),polygonButton]})})};const renderGraphControls=props=>{const{state,dispatch,width,perseusStrings}=props;const{type}=state;switch(type){case "point":if(state.numPoints==="unlimited"){return renderPointGraphControls({state,dispatch,width,perseusStrings})}return null;case "polygon":if(state.numSides==="unlimited"){return renderPolygonGraphControls({state,dispatch,width,perseusStrings})}return null;default:return null}};function handleFocusEvent(event,state,dispatch){if(isUnlimitedGraphState(state)){if(event.target.classList.contains("mafs-graph")&&state.interactionMode==="mouse"){dispatch(actions.global.changeKeyboardInvitationVisibility(true));}}}function handleBlurEvent(_event,state,dispatch){if(isUnlimitedGraphState(state)){dispatch(actions.global.changeKeyboardInvitationVisibility(false));}}function handleKeyboardEvent(event,state,dispatch){if(isUnlimitedGraphState(state)){if(event.key==="Backspace"||event.key==="Delete"){if(document.activeElement?.classList.contains("movable-point__focusable-handle")){if(state.type==="point"||state.type==="polygon"&&!state.closedPolygon){dispatch(actions.global.deleteIntent());}}document.activeElement.blur();}else if(event.shiftKey&&event.key==="Enter"){dispatch(actions.global.changeInteractionMode("keyboard"));}else if(state.interactionMode==="keyboard"&&event.key==="a"){dispatch(actions.pointGraph.addPoint([0,0]));}}}const renderGraphElements=props=>{const{state,dispatch,i18n,markings}=props;const{type}=state;switch(type){case "angle":return renderAngleGraph(state,dispatch,i18n);case "segment":return renderSegmentGraph(state,dispatch,i18n);case "linear-system":return renderLinearSystemGraph(state,dispatch,i18n);case "linear":return renderLinearGraph(state,dispatch,i18n);case "ray":return renderRayGraph(state,dispatch,i18n);case "polygon":return renderPolygonGraph(state,dispatch,i18n,markings);case "point":return renderPointGraph(state,dispatch,i18n);case "circle":return renderCircleGraph(state,dispatch,i18n);case "quadratic":return renderQuadraticGraph(state,dispatch,i18n);case "sinusoid":return renderSinusoidGraph(state,dispatch,i18n);case "exponential":return renderExponentialGraph(state,dispatch,i18n);case "none":return {graph:null,interactiveElementsDescription:null};case "absolute-value":return renderAbsoluteValueGraph(state,dispatch,i18n);case "tangent":return renderTangentGraph(state,dispatch,i18n);case "logarithm":return renderLogarithmGraph(state,dispatch,i18n);case "vector":return renderVectorGraph(state,dispatch,i18n);default:throw new UnreachableCaseError(type)}};function describedByIds(...args){return args.filter(Boolean).join(" ")||undefined}
1985
1991
 
1986
- function mafsStateToInteractiveGraph(state,originalGraph){switch(state.type){case "angle":invariant(originalGraph.type==="angle");return {...originalGraph,coords:state.coords};case "quadratic":invariant(originalGraph.type==="quadratic");return {...originalGraph,coords:state.coords};case "circle":invariant(originalGraph.type==="circle");return {...originalGraph,center:state.center,radius:getRadius(state)};case "linear":invariant(originalGraph.type==="linear");return {...originalGraph,coords:state.coords};case "ray":invariant(originalGraph.type==="ray");return {...originalGraph,coords:state.coords};case "sinusoid":invariant(originalGraph.type==="sinusoid");return {...originalGraph,coords:state.coords};case "segment":invariant(originalGraph.type==="segment");return {...originalGraph,coords:state.coords};case "linear-system":invariant(originalGraph.type==="linear-system");return {...originalGraph,coords:state.coords};case "polygon":invariant(originalGraph.type==="polygon");return {...originalGraph,coords:state.coords};case "point":invariant(originalGraph.type==="point");return {...originalGraph,coords:state.coords};case "exponential":invariant(originalGraph.type==="exponential");return {...originalGraph,coords:state.coords,asymptote:state.asymptote};case "none":invariant(originalGraph.type==="none");return {...originalGraph};case "absolute-value":invariant(originalGraph.type==="absolute-value");return {...originalGraph,coords:state.coords};case "tangent":invariant(originalGraph.type==="tangent");return {...originalGraph,coords:state.coords};case "logarithm":invariant(originalGraph.type==="logarithm");return {...originalGraph,coords:state.coords,asymptote:state.asymptote};default:throw new UnreachableCaseError(state)}}
1992
+ function mafsStateToInteractiveGraph(state,originalGraph){switch(state.type){case "angle":invariant(originalGraph.type==="angle");return {...originalGraph,coords:state.coords};case "quadratic":invariant(originalGraph.type==="quadratic");return {...originalGraph,coords:state.coords};case "circle":invariant(originalGraph.type==="circle");return {...originalGraph,center:state.center,radius:getRadius(state)};case "linear":invariant(originalGraph.type==="linear");return {...originalGraph,coords:state.coords};case "ray":invariant(originalGraph.type==="ray");return {...originalGraph,coords:state.coords};case "sinusoid":invariant(originalGraph.type==="sinusoid");return {...originalGraph,coords:state.coords};case "segment":invariant(originalGraph.type==="segment");return {...originalGraph,coords:state.coords};case "linear-system":invariant(originalGraph.type==="linear-system");return {...originalGraph,coords:state.coords};case "polygon":invariant(originalGraph.type==="polygon");return {...originalGraph,coords:state.coords};case "point":invariant(originalGraph.type==="point");return {...originalGraph,coords:state.coords};case "exponential":invariant(originalGraph.type==="exponential");return {...originalGraph,coords:state.coords,asymptote:state.asymptote};case "none":invariant(originalGraph.type==="none");return {...originalGraph};case "absolute-value":invariant(originalGraph.type==="absolute-value");return {...originalGraph,coords:state.coords};case "tangent":invariant(originalGraph.type==="tangent");return {...originalGraph,coords:state.coords};case "logarithm":invariant(originalGraph.type==="logarithm");return {...originalGraph,coords:state.coords,asymptote:state.asymptote};case "vector":invariant(originalGraph.type==="vector");return {...originalGraph,coords:state.coords};default:throw new UnreachableCaseError(state)}}
1987
1993
 
1988
1994
  const StatefulMafsGraph=React.forwardRef(function StatefulMafsGraphWithRef(props,ref){const{onChange,graph}=props;const[state,dispatch]=React.useReducer(interactiveGraphReducer,props,initializeGraphState);useImperativeHandle(ref,()=>({getUserInput:()=>getGradableGraph(state,graph)}));const prevState=useRef(state);useEffect(()=>{if(prevState.current!==state){onChange(mafsStateToInteractiveGraph(state,graph));}prevState.current=state;},[onChange,state,graph]);const[xSnap,ySnap]=props.snapStep;useEffect(()=>{dispatch(changeSnapStep([xSnap,ySnap]));},[dispatch,xSnap,ySnap]);const[[xMinRange,xMaxRange],[yMinRange,yMaxRange]]=props.range;useEffect(()=>{dispatch(changeRange([[xMinRange,xMaxRange],[yMinRange,yMaxRange]]));},[dispatch,xMinRange,xMaxRange,yMinRange,yMaxRange]);const numSegments=graph.type==="segment"?graph.numSegments:null;const numPoints=graph.type==="point"?graph.numPoints:null;const numSides=graph.type==="polygon"?graph.numSides:null;const snapTo=graph.type==="polygon"?graph.snapTo:null;const showAngles=graph.type==="polygon"||graph.type==="angle"?graph.showAngles:null;const allowReflexAngles=graph.type==="angle"?graph.allowReflexAngles:null;const showSides=graph.type==="polygon"?graph.showSides:null;const startCoords="startCoords"in graph?graph.startCoords:undefined;const originalPropsRef=useRef(props);const latestPropsRef=useLatestRef(props);useEffect(()=>{if(latestPropsRef.current!==originalPropsRef.current){dispatch(reinitialize(latestPropsRef.current));}},[graph.type,numPoints,numSegments,numSides,snapTo,showAngles,showSides,latestPropsRef,startCoords,allowReflexAngles]);if(props.static&&props.correct){return jsxRuntimeExports.jsx(MafsGraph,{...props,state:initializeGraphState({...props,graph:props.correct}),dispatch:dispatch})}return jsxRuntimeExports.jsx(MafsGraph,{...props,state:state,dispatch:dispatch})});
1989
1995
 
1990
- const{getClockwiseAngle}=angles;const{getSinusoidCoefficients,getTangentCoefficients,getQuadraticCoefficients,getExponentialCoefficients,getLogarithmCoefficients}=coefficients;const{getLineEquation,getLineIntersectionString,magnitude,vector}=geometry;const defaultBackgroundImage={url:null};const UNLIMITED="unlimited";function numSteps(range,step){return Math.floor((range[1]-range[0])/step)}const makeInvalidTypeError=(functionName,graphType)=>{return new PerseusError(`${functionName} called but current graph type is not a '${graphType}'`,Errors.NotAllowed,{metadata:{graphType}})};class InteractiveGraph extends React.Component{getUserInput(){if(this.mafsRef.current?.getUserInput){return this.mafsRef.current.getUserInput()}throw new PerseusError("Cannot getUserInput from a graph that has never rendered",Errors.NotAllowed)}getPromptJSON(){return getPromptJSON$b(this.props,this.getUserInput())}getSerializedState(){const{userInput:_,...rest}=this.props;return {...rest,graph:this.props.userInput}}render(){const box=getInteractiveBoxFromSizeClass(this.props.containerSizeClass);const gridStep=this.props.gridStep||Util.getGridStep(this.props.range,this.props.step,box[0]);const snapStep=this.props.snapStep||Util.snapStepFromGridStep(gridStep);const mafsProps={...this.props,graph:this.props.userInput,onChange:()=>this.props.handleUserInput(this.mafsRef.current?.getUserInput())};return jsxRuntimeExports.jsx(StatefulMafsGraph,{...mafsProps,ref:this.mafsRef,gridStep:gridStep,snapStep:snapStep,box:box,showTooltips:!!this.props.showTooltips,readOnly:this.props.apiOptions?.readOnly,widgetId:this.props.widgetId})}static getLineCoords(graph,props){return graph.coords||InteractiveGraph.pointsFromNormalized(props,[[.25,.75],[.75,.75]])}static getPointCoords(graph,props){const numPoints=graph.numPoints||1;let coords=graph.coords;if(coords){return coords}switch(numPoints){case 1:coords=[graph.coord||[0,0]];break;case 2:coords=[[-5,0],[5,0]];break;case 3:coords=[[-5,0],[0,0],[5,0]];break;case 4:coords=[[-6,0],[-2,0],[2,0],[6,0]];break;case 5:coords=[[-6,0],[-3,0],[0,0],[3,0],[6,0]];break;case 6:coords=[[-5,0],[-3,0],[-1,0],[1,0],[3,0],[5,0]];break;case UNLIMITED:coords=[];break}const range=[[-10,10],[-10,10]];const newCoords=InteractiveGraph.normalizeCoords(coords,range);return InteractiveGraph.pointsFromNormalized(props,newCoords)}static getLinearSystemCoords(graph,props){return graph.coords||_.map([[[.25,.75],[.75,.75]],[[.25,.25],[.75,.25]]],coords=>{return InteractiveGraph.pointsFromNormalized(props,coords)})}static getPolygonCoords(graph,props){if(graph.type!=="polygon"){throw makeInvalidTypeError("toggleShowSides","polygon")}let coords=graph.coords;if(coords){return coords}const n=graph.numSides||3;if(n===UNLIMITED){coords=[];}else {const angle=2*Math.PI/n;const offset=(1/n-1/2)*Math.PI;const radius=graph.snapTo==="sides"?Math.sqrt(3)/3*7:4;coords=_.times(n,function(i){return [radius*Math.cos(i*angle+offset),radius*Math.sin(i*angle+offset)]});}const ranges=[[-10,10],[-10,10]];coords=InteractiveGraph.normalizeCoords(coords,ranges);const snapToGrid=!_.contains(["angles","sides"],graph.snapTo);coords=InteractiveGraph.pointsFromNormalized(props,coords,!snapToGrid);return coords}static getSegmentCoords(graph,props){const coords=graph.coords;if(coords){return coords}const n=graph.numSegments||1;const ys={1:[5],2:[5,-5],3:[5,0,-5],4:[6,2,-2,-6],5:[6,3,0,-3,-6],6:[5,3,1,-1,-3,-5]}[n];const range=[[-10,10],[-10,10]];return ys.map(function(y){let segment=[[-5,y],[5,y]];segment=InteractiveGraph.normalizeCoords(segment,range);segment=InteractiveGraph.pointsFromNormalized(props,segment);return segment})}static getAngleCoords(graph,props){let coords=graph.coords;if(coords){return coords}const snap=graph.snapDegrees||1;let angle=snap;while(angle<20){angle+=snap;}angle=angle*Math.PI/180;const offset=(graph.angleOffsetDeg||0)*Math.PI/180;coords=InteractiveGraph.pointsFromNormalized(props,[[.85,.5],[.5,.5]]);const radius=magnitude(vector(...coords));coords[0]=[coords[1][0]+radius*Math.cos(offset),coords[1][1]+radius*Math.sin(offset)];coords[2]=[coords[1][0]+radius*Math.cos(angle+offset),coords[1][1]+radius*Math.sin(angle+offset)];return coords}static normalizeCoords(coordsList,ranges){return _.map(coordsList,function(coords){return _.map(coords,function(coord,i){const extent=ranges[i][1]-ranges[i][0];return (coord+ranges[i][1])/extent})})}static getEquationString(props){const type=props.userInput.type;switch(type){case "none":return InteractiveGraph.getNoneEquationString();case "linear":return InteractiveGraph.getLinearEquationString(props);case "quadratic":return InteractiveGraph.getQuadraticEquationString(props);case "sinusoid":return InteractiveGraph.getSinusoidEquationString(props);case "circle":return InteractiveGraph.getCircleEquationString(props);case "linear-system":return InteractiveGraph.getLinearSystemEquationString(props);case "point":return InteractiveGraph.getPointEquationString(props);case "segment":return InteractiveGraph.getSegmentEquationString(props);case "ray":return InteractiveGraph.getRayEquationString(props);case "polygon":return InteractiveGraph.getPolygonEquationString(props);case "angle":return InteractiveGraph.getAngleEquationString(props);case "absolute-value":return InteractiveGraph.getAbsoluteValueEquationString(props);case "exponential":return InteractiveGraph.getExponentialEquationString(props);case "tangent":return InteractiveGraph.getTangentEquationString(props);case "logarithm":return InteractiveGraph.getLogarithmEquationString(props);default:throw new UnreachableCaseError(type)}}static pointsFromNormalized(props,coordsList,noSnap){return _.map(coordsList,function(coords){return _.map(coords,function(coord,i){const range=props.range[i];if(noSnap){return range[0]+(range[1]-range[0])*coord}const step=props.step[i];const nSteps=numSteps(range,step);const tick=Math.round(coord*nSteps);return range[0]+step*tick})})}static getNoneEquationString(){return ""}static getLinearEquationString(props){const coords=InteractiveGraph.getLineCoords(props.userInput,props);if(approximateEqual(coords[0][0],coords[1][0])){return "x = "+coords[0][0].toFixed(3)}const m=(coords[1][1]-coords[0][1])/(coords[1][0]-coords[0][0]);const b=coords[0][1]-m*coords[0][0];if(approximateEqual(m,0)){return "y = "+b.toFixed(3)}return "y = "+m.toFixed(3)+"x + "+b.toFixed(3)}static getCurrentQuadraticCoefficients(props){const coords=props.userInput.coords||InteractiveGraph.defaultQuadraticCoords(props);return getQuadraticCoefficients(coords)}static defaultQuadraticCoords(props){const coords=[[.25,.75],[.5,.25],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getQuadraticEquationString(props){const coeffs=InteractiveGraph.getCurrentQuadraticCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"x^2 + "+coeffs[1].toFixed(3)+"x + "+coeffs[2].toFixed(3)}static getCurrentSinusoidCoefficients(props){const coords=props.userInput.coords||InteractiveGraph.defaultSinusoidCoords(props);return getSinusoidCoefficients(coords)}static defaultSinusoidCoords(props){const coords=[[.5,.5],[.65,.6]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getSinusoidEquationString(props){const coeffs=InteractiveGraph.getCurrentSinusoidCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"sin("+coeffs[1].toFixed(3)+"x - "+coeffs[2].toFixed(3)+") + "+coeffs[3].toFixed(3)}static defaultExponentialCoords(props){const coords=[[.5,.55],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords,true)}static getExponentialEquationString(props){const coords=props.userInput.coords||InteractiveGraph.defaultExponentialCoords(props);const asymptote=props.userInput.asymptote??0;const coeffs=getExponentialCoefficients(coords,asymptote);if(coeffs==null){return "y = e^x"}return "y = "+coeffs.a.toFixed(3)+"e^("+coeffs.b.toFixed(3)+"x) + "+coeffs.c.toFixed(3)}static defaultLogarithmCoords(props){const coords=[[.55,.55],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords,true)}static getLogarithmEquationString(props){const coords=props.userInput.coords||InteractiveGraph.defaultLogarithmCoords(props);const asymptote=props.userInput.asymptote??0;const coeffs=getLogarithmCoefficients(coords,asymptote);if(coeffs==null){return "y = ln(x)"}const cStr=coeffs.c===0?"x":coeffs.c<0?"x - "+Math.abs(coeffs.c).toFixed(3):"x + "+coeffs.c.toFixed(3);return "y = "+coeffs.a.toFixed(3)+"ln("+coeffs.b.toFixed(3)+cStr+")"}static getAbsoluteValueEquationString(props){const userInput=props.userInput;if(userInput.type!=="absolute-value"||!userInput.coords){return ""}const coeffs=getAbsoluteValueCoefficients(userInput.coords);if(coeffs===undefined){return ""}const{m,h,v}=coeffs;return "y = "+m.toFixed(3)+"|x - "+h.toFixed(3)+"| + "+v.toFixed(3)}static getCurrentTangentCoefficients(props){const coords=props.userInput.coords||InteractiveGraph.defaultTangentCoords(props);return getTangentCoefficients(coords)}static defaultTangentCoords(props){const coords=[[.5,.5],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getTangentEquationString(props){const coeffs=InteractiveGraph.getCurrentTangentCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"tan("+coeffs[1].toFixed(3)+"x - "+coeffs[2].toFixed(3)+") + "+coeffs[3].toFixed(3)}static getCircleEquationString(props){const graph=props.userInput;const center=graph.center||[0,0];const radius=graph.radius||2;return "center ("+center[0]+", "+center[1]+"), radius "+radius}static getLinearSystemEquationString(props){const coords=InteractiveGraph.getLinearSystemCoords(props.userInput,props);return "\n"+getLineEquation(coords[0][0],coords[0][1])+"\n"+getLineEquation(coords[1][0],coords[1][1])+"\n"+getLineIntersectionString(coords[0],coords[1])}static getPointEquationString(props){if(props.userInput.type!=="point"){throw makeInvalidTypeError("getPointEquationString","point")}const coords=InteractiveGraph.getPointCoords(props.userInput,props);return coords.map(function(coord){return "("+coord[0]+", "+coord[1]+")"}).join(", ")}static getSegmentEquationString(props){if(props.userInput.type!=="segment"){throw makeInvalidTypeError("getSegmentEquationString","segment")}const segments=InteractiveGraph.getSegmentCoords(props.userInput,props);return _.map(segments,function(segment){return "["+_.map(segment,function(coord){return "("+coord.join(", ")+")"}).join(" ")+"]"}).join(" ")}static getRayEquationString(props){if(props.userInput.type!=="ray"){throw makeInvalidTypeError("createPointForPolygonType","ray")}const coords=InteractiveGraph.getLineCoords(props.userInput,props);const a=coords[0];const b=coords[1];let eq=InteractiveGraph.getLinearEquationString(props);if(a[0]>b[0]){eq+=" (for x <= "+a[0].toFixed(3)+")";}else if(a[0]<b[0]){eq+=" (for x >= "+a[0].toFixed(3)+")";}else if(a[1]>b[1]){eq+=" (for y <= "+a[1].toFixed(3)+")";}else {eq+=" (for y >= "+a[1].toFixed(3)+")";}return eq}static getPolygonEquationString(props){if(props.userInput.type!=="polygon"){throw makeInvalidTypeError("getPolygonEquationString","polygon")}const coords=InteractiveGraph.getPolygonCoords(props.userInput,props);return _.map(coords,function(coord){return "("+coord.join(", ")+")"}).join(" ")}static getAngleEquationString(props){if(props.userInput.type!=="angle"){throw makeInvalidTypeError("getAngleEquationString","angle")}const coords=InteractiveGraph.getAngleCoords(props.userInput,props);const allowReflexAngles=props.userInput.allowReflexAngles;const angle=getClockwiseAngle(coords,allowReflexAngles);return angle.toFixed(0)+"° angle"+" at ("+coords[1].join(", ")+")"}constructor(...args){super(...args),this.mafsRef=React.createRef();}}InteractiveGraph.defaultProps={labels:["$x$","$y$"],labelLocation:"onAxis",range:[[-10,10],[-10,10]],showAxisArrows:{xMin:true,xMax:true,yMin:true,yMax:true},step:[1,1],backgroundImage:defaultBackgroundImage,markings:"graph",showTooltips:false,showProtractor:false,userInput:{type:"linear"}};function getUserInputFromSerializedState$8(serializedState){return serializedState.graph}function getStartUserInput$8(options){return options.graph}function getCorrectUserInput$4(options){return options.correct}var InteractiveGraph$1 = {name:"interactive-graph",displayName:"Interactive graph",widget:InteractiveGraph,getStartUserInput: getStartUserInput$8,getCorrectUserInput: getCorrectUserInput$4,getUserInputFromSerializedState: getUserInputFromSerializedState$8};
1996
+ const{getClockwiseAngle}=angles;const{getSinusoidCoefficients,getTangentCoefficients,getQuadraticCoefficients,getExponentialCoefficients,getLogarithmCoefficients}=coefficients;const{getLineEquation,getLineIntersectionString,magnitude,vector}=geometry;const defaultBackgroundImage={url:null};const UNLIMITED="unlimited";function numSteps(range,step){return Math.floor((range[1]-range[0])/step)}const makeInvalidTypeError=(functionName,graphType)=>{return new PerseusError(`${functionName} called but current graph type is not a '${graphType}'`,Errors.NotAllowed,{metadata:{graphType}})};class InteractiveGraph extends React.Component{getUserInput(){if(this.mafsRef.current?.getUserInput){return this.mafsRef.current.getUserInput()}throw new PerseusError("Cannot getUserInput from a graph that has never rendered",Errors.NotAllowed)}getPromptJSON(){return getPromptJSON$b(this.props,this.getUserInput())}getSerializedState(){const{userInput:_,...rest}=this.props;return {...rest,graph:this.props.userInput}}render(){const box=getInteractiveBoxFromSizeClass(this.props.containerSizeClass);const gridStep=this.props.gridStep||Util.getGridStep(this.props.range,this.props.step,box[0]);const snapStep=this.props.snapStep||Util.snapStepFromGridStep(gridStep);const mafsProps={...this.props,graph:this.props.userInput,onChange:()=>this.props.handleUserInput(this.mafsRef.current?.getUserInput())};return jsxRuntimeExports.jsx(StatefulMafsGraph,{...mafsProps,ref:this.mafsRef,gridStep:gridStep,snapStep:snapStep,box:box,showTooltips:!!this.props.showTooltips,readOnly:this.props.apiOptions?.readOnly,widgetId:this.props.widgetId})}static getLineCoords(graph,props){return graph.coords||InteractiveGraph.pointsFromNormalized(props,[[.25,.75],[.75,.75]])}static getPointCoords(graph,props){const numPoints=graph.numPoints||1;let coords=graph.coords;if(coords){return coords}switch(numPoints){case 1:coords=[graph.coord||[0,0]];break;case 2:coords=[[-5,0],[5,0]];break;case 3:coords=[[-5,0],[0,0],[5,0]];break;case 4:coords=[[-6,0],[-2,0],[2,0],[6,0]];break;case 5:coords=[[-6,0],[-3,0],[0,0],[3,0],[6,0]];break;case 6:coords=[[-5,0],[-3,0],[-1,0],[1,0],[3,0],[5,0]];break;case UNLIMITED:coords=[];break}const range=[[-10,10],[-10,10]];const newCoords=InteractiveGraph.normalizeCoords(coords,range);return InteractiveGraph.pointsFromNormalized(props,newCoords)}static getLinearSystemCoords(graph,props){return graph.coords||_.map([[[.25,.75],[.75,.75]],[[.25,.25],[.75,.25]]],coords=>{return InteractiveGraph.pointsFromNormalized(props,coords)})}static getPolygonCoords(graph,props){if(graph.type!=="polygon"){throw makeInvalidTypeError("toggleShowSides","polygon")}let coords=graph.coords;if(coords){return coords}const n=graph.numSides||3;if(n===UNLIMITED){coords=[];}else {const angle=2*Math.PI/n;const offset=(1/n-1/2)*Math.PI;const radius=graph.snapTo==="sides"?Math.sqrt(3)/3*7:4;coords=_.times(n,function(i){return [radius*Math.cos(i*angle+offset),radius*Math.sin(i*angle+offset)]});}const ranges=[[-10,10],[-10,10]];coords=InteractiveGraph.normalizeCoords(coords,ranges);const snapToGrid=!_.contains(["angles","sides"],graph.snapTo);coords=InteractiveGraph.pointsFromNormalized(props,coords,!snapToGrid);return coords}static getSegmentCoords(graph,props){const coords=graph.coords;if(coords){return coords}const n=graph.numSegments||1;const ys={1:[5],2:[5,-5],3:[5,0,-5],4:[6,2,-2,-6],5:[6,3,0,-3,-6],6:[5,3,1,-1,-3,-5]}[n];const range=[[-10,10],[-10,10]];return ys.map(function(y){let segment=[[-5,y],[5,y]];segment=InteractiveGraph.normalizeCoords(segment,range);segment=InteractiveGraph.pointsFromNormalized(props,segment);return segment})}static getAngleCoords(graph,props){let coords=graph.coords;if(coords){return coords}const snap=graph.snapDegrees||1;let angle=snap;while(angle<20){angle+=snap;}angle=angle*Math.PI/180;const offset=(graph.angleOffsetDeg||0)*Math.PI/180;coords=InteractiveGraph.pointsFromNormalized(props,[[.85,.5],[.5,.5]]);const radius=magnitude(vector(...coords));coords[0]=[coords[1][0]+radius*Math.cos(offset),coords[1][1]+radius*Math.sin(offset)];coords[2]=[coords[1][0]+radius*Math.cos(angle+offset),coords[1][1]+radius*Math.sin(angle+offset)];return coords}static normalizeCoords(coordsList,ranges){return _.map(coordsList,function(coords){return _.map(coords,function(coord,i){const extent=ranges[i][1]-ranges[i][0];return (coord+ranges[i][1])/extent})})}static getEquationString(props){const type=props.userInput.type;switch(type){case "none":return InteractiveGraph.getNoneEquationString();case "linear":return InteractiveGraph.getLinearEquationString(props);case "quadratic":return InteractiveGraph.getQuadraticEquationString(props);case "sinusoid":return InteractiveGraph.getSinusoidEquationString(props);case "circle":return InteractiveGraph.getCircleEquationString(props);case "linear-system":return InteractiveGraph.getLinearSystemEquationString(props);case "point":return InteractiveGraph.getPointEquationString(props);case "segment":return InteractiveGraph.getSegmentEquationString(props);case "ray":return InteractiveGraph.getRayEquationString(props);case "polygon":return InteractiveGraph.getPolygonEquationString(props);case "angle":return InteractiveGraph.getAngleEquationString(props);case "absolute-value":return InteractiveGraph.getAbsoluteValueEquationString(props);case "exponential":return InteractiveGraph.getExponentialEquationString(props);case "tangent":return InteractiveGraph.getTangentEquationString(props);case "logarithm":return InteractiveGraph.getLogarithmEquationString(props);case "vector":return InteractiveGraph.getVectorEquationString(props);default:throw new UnreachableCaseError(type)}}static pointsFromNormalized(props,coordsList,noSnap){return _.map(coordsList,function(coords){return _.map(coords,function(coord,i){const range=props.range[i];if(noSnap){return range[0]+(range[1]-range[0])*coord}const step=props.step[i];const nSteps=numSteps(range,step);const tick=Math.round(coord*nSteps);return range[0]+step*tick})})}static getNoneEquationString(){return ""}static getLinearEquationString(props){const coords=InteractiveGraph.getLineCoords(props.userInput,props);if(approximateEqual(coords[0][0],coords[1][0])){return "x = "+coords[0][0].toFixed(3)}const m=(coords[1][1]-coords[0][1])/(coords[1][0]-coords[0][0]);const b=coords[0][1]-m*coords[0][0];if(approximateEqual(m,0)){return "y = "+b.toFixed(3)}return "y = "+m.toFixed(3)+"x + "+b.toFixed(3)}static getCurrentQuadraticCoefficients(props){const coords=props.userInput.coords||InteractiveGraph.defaultQuadraticCoords(props);return getQuadraticCoefficients(coords)}static defaultQuadraticCoords(props){const coords=[[.25,.75],[.5,.25],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getQuadraticEquationString(props){const coeffs=InteractiveGraph.getCurrentQuadraticCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"x^2 + "+coeffs[1].toFixed(3)+"x + "+coeffs[2].toFixed(3)}static getCurrentSinusoidCoefficients(props){const coords=props.userInput.coords||InteractiveGraph.defaultSinusoidCoords(props);return getSinusoidCoefficients(coords)}static defaultSinusoidCoords(props){const coords=[[.5,.5],[.65,.6]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getSinusoidEquationString(props){const coeffs=InteractiveGraph.getCurrentSinusoidCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"sin("+coeffs[1].toFixed(3)+"x - "+coeffs[2].toFixed(3)+") + "+coeffs[3].toFixed(3)}static defaultExponentialCoords(props){const coords=[[.5,.55],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords,true)}static getExponentialEquationString(props){const coords=props.userInput.coords||InteractiveGraph.defaultExponentialCoords(props);const asymptote=props.userInput.asymptote??0;const coeffs=getExponentialCoefficients(coords,asymptote);if(coeffs==null){return "y = e^x"}return "y = "+coeffs.a.toFixed(3)+"e^("+coeffs.b.toFixed(3)+"x) + "+coeffs.c.toFixed(3)}static defaultLogarithmCoords(props){const coords=[[.55,.55],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords,true)}static getLogarithmEquationString(props){const coords=props.userInput.coords||InteractiveGraph.defaultLogarithmCoords(props);const asymptote=props.userInput.asymptote??0;const coeffs=getLogarithmCoefficients(coords,asymptote);if(coeffs==null){return "y = ln(x)"}const cStr=coeffs.c===0?"x":coeffs.c<0?"x - "+Math.abs(coeffs.c).toFixed(3):"x + "+coeffs.c.toFixed(3);return "y = "+coeffs.a.toFixed(3)+"ln("+coeffs.b.toFixed(3)+cStr+")"}static getAbsoluteValueEquationString(props){const userInput=props.userInput;if(userInput.type!=="absolute-value"||!userInput.coords){return ""}const coeffs=getAbsoluteValueCoefficients(userInput.coords);if(coeffs===undefined){return ""}const{m,h,v}=coeffs;return "y = "+m.toFixed(3)+"|x - "+h.toFixed(3)+"| + "+v.toFixed(3)}static getCurrentTangentCoefficients(props){const coords=props.userInput.coords||InteractiveGraph.defaultTangentCoords(props);return getTangentCoefficients(coords)}static defaultTangentCoords(props){const coords=[[.5,.5],[.75,.75]];return InteractiveGraph.pointsFromNormalized(props,coords)}static getTangentEquationString(props){const coeffs=InteractiveGraph.getCurrentTangentCoefficients(props);return "y = "+coeffs[0].toFixed(3)+"tan("+coeffs[1].toFixed(3)+"x - "+coeffs[2].toFixed(3)+") + "+coeffs[3].toFixed(3)}static getCircleEquationString(props){const graph=props.userInput;const center=graph.center||[0,0];const radius=graph.radius||2;return "center ("+center[0]+", "+center[1]+"), radius "+radius}static getLinearSystemEquationString(props){const coords=InteractiveGraph.getLinearSystemCoords(props.userInput,props);return "\n"+getLineEquation(coords[0][0],coords[0][1])+"\n"+getLineEquation(coords[1][0],coords[1][1])+"\n"+getLineIntersectionString(coords[0],coords[1])}static getPointEquationString(props){if(props.userInput.type!=="point"){throw makeInvalidTypeError("getPointEquationString","point")}const coords=InteractiveGraph.getPointCoords(props.userInput,props);return coords.map(function(coord){return "("+coord[0]+", "+coord[1]+")"}).join(", ")}static getSegmentEquationString(props){if(props.userInput.type!=="segment"){throw makeInvalidTypeError("getSegmentEquationString","segment")}const segments=InteractiveGraph.getSegmentCoords(props.userInput,props);return _.map(segments,function(segment){return "["+_.map(segment,function(coord){return "("+coord.join(", ")+")"}).join(" ")+"]"}).join(" ")}static getRayEquationString(props){if(props.userInput.type!=="ray"){throw makeInvalidTypeError("createPointForPolygonType","ray")}const coords=InteractiveGraph.getLineCoords(props.userInput,props);const a=coords[0];const b=coords[1];let eq=InteractiveGraph.getLinearEquationString(props);if(a[0]>b[0]){eq+=" (for x <= "+a[0].toFixed(3)+")";}else if(a[0]<b[0]){eq+=" (for x >= "+a[0].toFixed(3)+")";}else if(a[1]>b[1]){eq+=" (for y <= "+a[1].toFixed(3)+")";}else {eq+=" (for y >= "+a[1].toFixed(3)+")";}return eq}static getPolygonEquationString(props){if(props.userInput.type!=="polygon"){throw makeInvalidTypeError("getPolygonEquationString","polygon")}const coords=InteractiveGraph.getPolygonCoords(props.userInput,props);return _.map(coords,function(coord){return "("+coord.join(", ")+")"}).join(" ")}static getAngleEquationString(props){if(props.userInput.type!=="angle"){throw makeInvalidTypeError("getAngleEquationString","angle")}const coords=InteractiveGraph.getAngleCoords(props.userInput,props);const allowReflexAngles=props.userInput.allowReflexAngles;const angle=getClockwiseAngle(coords,allowReflexAngles);return angle.toFixed(0)+"° angle"+" at ("+coords[1].join(", ")+")"}static getVectorEquationString(props){if(props.userInput.type!=="vector"){throw makeInvalidTypeError("getVectorEquationString","vector")}const coords=props.userInput.coords;if(!coords){return ""}const[tail,tip]=coords;const dx=tip[0]-tail[0];const dy=tip[1]-tail[1];return `\u27E8${dx.toFixed(3)}, ${dy.toFixed(3)}\u27E9`}constructor(...args){super(...args),this.mafsRef=React.createRef();}}InteractiveGraph.defaultProps={labels:["$x$","$y$"],labelLocation:"onAxis",range:[[-10,10],[-10,10]],showAxisArrows:{xMin:true,xMax:true,yMin:true,yMax:true},step:[1,1],backgroundImage:defaultBackgroundImage,markings:"graph",showTooltips:false,showProtractor:false,userInput:{type:"linear"}};function getUserInputFromSerializedState$8(serializedState){return serializedState.graph}function getStartUserInput$8(options){return options.graph}function getCorrectUserInput$4(options){return options.correct}var InteractiveGraph$1 = {name:"interactive-graph",displayName:"Interactive graph",widget:InteractiveGraph,getStartUserInput: getStartUserInput$8,getCorrectUserInput: getCorrectUserInput$4,getUserInputFromSerializedState: getUserInputFromSerializedState$8};
1991
1997
 
1992
1998
  const bodyXsmallBold={fontFamily:"inherit",fontSize:15,fontWeight:"bold",lineHeight:"22px"};
1993
1999
 
@@ -1997,11 +2003,11 @@ const AnswerChoices=props=>{const{strings}=usePerseusI18n();const onAnswerChange
1997
2003
 
1998
2004
  const HideAnswersToggle=props=>{const switchId=useId();const labelId=useId();const{strings}=usePerseusI18n();return jsxRuntimeExports.jsxs(View,{style:styles$c.switchWrapper,children:[jsxRuntimeExports.jsx(Switch,{id:switchId,checked:props.areAnswersHidden,onChange:props.onChange,"aria-labelledby":labelId}),jsxRuntimeExports.jsx(BodyText,{id:labelId,htmlFor:switchId,tag:"label",children:strings.hideAnswersToggleLabel})]})};const styles$c=StyleSheet.create({switchWrapper:{display:"flex",flexDirection:"row",flexWrap:"wrap-reverse",alignItems:"center",gap:"0.5em",marginTop:"1em"}});
1999
2005
 
2000
- const BringToFront={boxShadow:`0 8px 8px ${semanticColor.core.border.neutral.default}`,zIndex:1e3};const AnswerPill=props=>{const{selectedAnswers,showCorrectness,markerRef,side,onClick,style,focused,hovered}=props;const pillId=useId();const{strings}=usePerseusI18n();const answerString=selectedAnswers.length>1?strings.answers({num:selectedAnswers.length}):selectedAnswers[0];const correct=showCorrectness==="correct";const incorrect=showCorrectness==="incorrect";return jsxRuntimeExports.jsx(Popper,{placement:side,referenceElement:markerRef,modifiers:[{name:"preventOverflow",options:{rootBoundary:"viewport"}}],children:({ref,style:popperStyle})=>jsxRuntimeExports.jsx(Pill,{size:"large",kind:"accent",id:pillId,onClick:correct?undefined:onClick,ref:ref,style:[style,popperStyle,styles$b.pill,correct&&styles$b.correct,incorrect&&styles$b.incorrect,(focused||hovered)&&BringToFront],children:jsxRuntimeExports.jsx(Renderer,{content:answerString,strings:strings,inline:true})})})};const styles$b=StyleSheet.create({correct:{backgroundColor:"#00880b"},incorrect:{backgroundColor:semanticColor.core.background.neutral.default},pill:{height:"auto"}});
2006
+ const BringToFront={boxShadow:`0 8px 8px ${semanticColor.core.border.neutral.default}`,zIndex:1e3};const AnswerPill=props=>{const{selectedAnswers,showCorrectness,markerRef,side,onClick,style,focused,hovered}=props;const pillId=useId();const{strings}=usePerseusI18n();const answerString=selectedAnswers.length>1?strings.answers({num:selectedAnswers.length}):selectedAnswers[0];const correct=showCorrectness==="correct";const incorrect=showCorrectness==="incorrect";return jsxRuntimeExports.jsx(Popper,{placement:side,referenceElement:markerRef,modifiers:[{name:"preventOverflow",options:{rootBoundary:"viewport"}}],children:({ref,style:popperStyle})=>jsxRuntimeExports.jsx(Pill,{size:"large",kind:"accent",id:pillId,onClick:correct?undefined:onClick,ref:ref,style:[style,popperStyle,styles$b.pill,correct&&styles$b.correct,incorrect&&styles$b.incorrect,(focused||hovered)&&BringToFront],children:jsxRuntimeExports.jsx(Renderer,{content:answerString,strings:strings,inline:true})})})};const styles$b=StyleSheet.create({correct:{backgroundColor:semanticColor.core.background.success.strong},incorrect:{backgroundColor:semanticColor.core.background.neutral.default},pill:{height:"auto"}});
2001
2007
 
2002
- function shouldReduceMotion(){if(typeof window.matchMedia!=="function"){return true}const mediaQuery=window.matchMedia("(prefers-reduced-motion: reduce)");return !mediaQuery||mediaQuery.matches}const MARKER_SIZE=24;class Marker extends React.Component{componentDidMount(){this._mounted=true;}componentWillUnmount(){this._mounted=false;}renderIcon(){const{selected,showCorrectness,showSelected,showPulsate}=this.props;const isOpen=showSelected;const selectedAnswers=selected;let iconStyles;const iconNull={path:"",height:1,width:1};let args={size:MARKER_SIZE,color:semanticColor.core.foreground.knockout.default,icon:iconNull};if(showCorrectness){iconStyles=[styles$a.markerGraded,showCorrectness==="correct"?styles$a.markerCorrect:styles$a.markerIncorrect,isOpen&&styles$a.markerSelected];args={...args,icon:showCorrectness==="correct"?iconCheck:iconMinus};}else if(selectedAnswers&&selectedAnswers.length>0){iconStyles=[styles$a.markerFilled,isOpen&&styles$a.markerSelected];}else if(isOpen){iconStyles=[styles$a.markerSelected];args={...args,icon:iconChevronDown,size:8};}else if(showPulsate){iconStyles=[styles$a.markerPulsateBase,this._mounted&&shouldReduceMotion()?showPulsate&&styles$a.markerUnfilledPulsateOnce:showPulsate&&styles$a.markerUnfilledPulsateInfinite];}return jsxRuntimeExports.jsx(View,{style:[styles$a.markerIcon,iconStyles],ref:node=>this._icon=node,children:jsxRuntimeExports.jsx(Icon,{...args})})}render(){const{showCorrectness,selected,showAnswer,answerSide,answerStyles,hovered,focused,label}=this.props;const markerDisabled=showCorrectness==="correct";const active=hovered||focused;return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(View,{style:[styles$a.marker,active&&!markerDisabled&&styles$a.markerActive],"aria-label":markerDisabled?this.context.strings.correctExcited:label,children:this.renderIcon()}),!!selected&&showAnswer&&jsxRuntimeExports.jsx(AnswerPill,{selectedAnswers:selected,showCorrectness:showCorrectness,side:answerSide,style:answerStyles,markerRef:this._icon??undefined,hovered:hovered,focused:focused})]})}constructor(...args){super(...args),this._mounted=false;}}Marker.contextType=PerseusI18nContext;Marker.defaultProps={selected:[]};const styles$a=StyleSheet.create({marker:{position:"absolute",backgroundColor:semanticColor.core.background.base.default,borderRadius:MARKER_SIZE,width:MARKER_SIZE,height:MARKER_SIZE,marginLeft:MARKER_SIZE/-2,marginTop:MARKER_SIZE/-2,boxShadow:boxShadow.mid},markerIcon:{display:"flex",alignItems:"center",justifyContent:"center",boxSizing:"border-box",width:MARKER_SIZE,height:MARKER_SIZE,border:`2px solid ${semanticColor.core.border.neutral.default}`,borderRadius:MARKER_SIZE},markerPulsateBase:{animationName:{"0%":{transform:"scale(1)",backgroundColor:semanticColor.core.background.instructive.default},"100%":{transform:"scale(1.3)",backgroundColor:semanticColor.core.background.instructive.default}},animationDirection:"alternate",animationDuration:"0.8s",animationTimingFunction:"ease-in",transformOrigin:"50% 50%",animationIterationCount:"0"},markerUnfilledPulsateInfinite:{animationIterationCount:"infinite"},markerUnfilledPulsateOnce:{animationIterationCount:"2"},markerActive:{outline:`2px solid ${semanticColor.core.border.instructive.default}`,outlineOffset:2},markerSelected:{boxShadow:boxShadow.mid,border:`solid 4px ${semanticColor.core.border.knockout.default}`,backgroundColor:semanticColor.core.background.instructive.default,borderRadius:MARKER_SIZE,transform:"rotate(180deg)"},markerFilled:{backgroundColor:"#ECF3FE",border:`4px solid ${semanticColor.core.border.instructive.default}`},markerGraded:{width:MARKER_SIZE,height:MARKER_SIZE,justifyContent:"center",alignItems:"center",border:`2px solid ${semanticColor.core.border.knockout.default}`},markerCorrect:{background:"#00880b"},markerIncorrect:{background:semanticColor.core.background.neutral.default}});
2008
+ function shouldReduceMotion(){if(typeof window.matchMedia!=="function"){return true}const mediaQuery=window.matchMedia("(prefers-reduced-motion: reduce)");return !mediaQuery||mediaQuery.matches}const MARKER_SIZE=24;class Marker extends React.Component{componentDidMount(){this._mounted=true;}componentWillUnmount(){this._mounted=false;}renderIcon(){const{selected,showCorrectness,showSelected,showPulsate}=this.props;const isOpen=showSelected;const selectedAnswers=selected;let iconStyles;const iconNull={path:"",height:1,width:1};let args={size:MARKER_SIZE,color:semanticColor.core.foreground.knockout.default,icon:iconNull};if(showCorrectness){iconStyles=[styles$a.markerGraded,showCorrectness==="correct"?styles$a.markerCorrect:styles$a.markerIncorrect,isOpen&&styles$a.markerSelected];args={...args,icon:showCorrectness==="correct"?iconCheck:iconMinus};}else if(selectedAnswers&&selectedAnswers.length>0){iconStyles=[styles$a.markerFilled,isOpen&&styles$a.markerSelected];}else if(isOpen){iconStyles=[styles$a.markerSelected];args={...args,icon:iconChevronDown,size:8};}else if(showPulsate){iconStyles=[styles$a.markerPulsateBase,this._mounted&&shouldReduceMotion()?showPulsate&&styles$a.markerUnfilledPulsateOnce:showPulsate&&styles$a.markerUnfilledPulsateInfinite];}return jsxRuntimeExports.jsx(View,{style:[styles$a.markerIcon,iconStyles],ref:node=>this._icon=node,children:jsxRuntimeExports.jsx(Icon,{...args})})}render(){const{showCorrectness,selected,showAnswer,answerSide,answerStyles,hovered,focused,label}=this.props;const markerDisabled=showCorrectness==="correct";const active=hovered||focused;return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(View,{style:[styles$a.marker,active&&!markerDisabled&&styles$a.markerActive],"aria-label":markerDisabled?this.context.strings.correctExcited:label,children:this.renderIcon()}),!!selected&&showAnswer&&jsxRuntimeExports.jsx(AnswerPill,{selectedAnswers:selected,showCorrectness:showCorrectness,side:answerSide,style:answerStyles,markerRef:this._icon??undefined,hovered:hovered,focused:focused})]})}constructor(...args){super(...args),this._mounted=false;}}Marker.contextType=PerseusI18nContext;Marker.defaultProps={selected:[]};const styles$a=StyleSheet.create({marker:{position:"absolute",backgroundColor:semanticColor.core.background.base.default,borderRadius:MARKER_SIZE,width:MARKER_SIZE,height:MARKER_SIZE,marginLeft:MARKER_SIZE/-2,marginTop:MARKER_SIZE/-2,boxShadow:boxShadow.mid},markerIcon:{display:"flex",alignItems:"center",justifyContent:"center",boxSizing:"border-box",width:MARKER_SIZE,height:MARKER_SIZE,border:`2px solid ${semanticColor.core.border.neutral.default}`,borderRadius:MARKER_SIZE},markerPulsateBase:{animationName:{"0%":{transform:"scale(1)",backgroundColor:semanticColor.core.background.instructive.default},"100%":{transform:"scale(1.3)",backgroundColor:semanticColor.core.background.instructive.default}},animationDirection:"alternate",animationDuration:"0.8s",animationTimingFunction:"ease-in",transformOrigin:"50% 50%",animationIterationCount:"0"},markerUnfilledPulsateInfinite:{animationIterationCount:"infinite"},markerUnfilledPulsateOnce:{animationIterationCount:"2"},markerActive:{outline:`2px solid ${semanticColor.core.border.instructive.default}`,outlineOffset:2},markerSelected:{boxShadow:boxShadow.mid,border:`solid 4px ${semanticColor.core.border.knockout.default}`,backgroundColor:semanticColor.core.background.instructive.default,borderRadius:MARKER_SIZE,transform:"rotate(180deg)"},markerFilled:{backgroundColor:semanticColor.core.background.instructive.subtle,border:`4px solid ${semanticColor.core.border.instructive.default}`},markerGraded:{width:MARKER_SIZE,height:MARKER_SIZE,justifyContent:"center",alignItems:"center",border:`2px solid ${semanticColor.core.border.knockout.default}`},markerCorrect:{background:semanticColor.core.background.success.strong},markerIncorrect:{background:semanticColor.core.background.neutral.default}});
2003
2009
 
2004
- function isAnswerful(marker){return marker.answers!=null}function getComputedSelectedState(marker,userInputMarker,reviewMode,showSolutions){const shouldShowFeedback=showSolutions==="all"||reviewMode;if(!shouldShowFeedback){return userInputMarker}if(isAnswerful(marker)){return {...userInputMarker,selected:marker.answers}}else {return {...userInputMarker,selected:undefined}}}class LabelImage extends React.Component{static pointInTriangle(p,a,b,c){const sign=(p1,p2,p3)=>(p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y);const b1=sign(p,a,b)<0;const b2=sign(p,b,c)<0;const b3=sign(p,c,a)<0;return b1===b2&&b2===b3}static imageSideForMarkerPosition(x,y,preferredDirection){if(preferredDirection&&preferredDirection!=="NONE"){if(preferredDirection==="LEFT"&&x>20){return "right"}else if(preferredDirection==="RIGHT"&&x<80){return "left"}else if(preferredDirection==="UP"&&y>20){return "bottom"}else if(preferredDirection==="DOWN"&&y<80){return "top"}}if(x<20){return "left"}if(x>80){return "right"}const tl={x:20,y:0};const tr={x:80,y:0};const br={x:80,y:100};const bl={x:20,y:100};const cp={x:50,y:50};const sides=["top","right","bottom","left"];const triangles={top:[tl,tr,cp],right:[cp,tr,br],bottom:[bl,cp,br],left:[tl,cp,bl]};const p={x,y};for(const side of sides){const corners=triangles[side];if(LabelImage.pointInTriangle(p,...corners)){return side}}return "center"}static navigateToMarkerIndex(navigateDirection,markers,thisIndex){const thisMarker=markers[thisIndex];const sortedMarkers=markers.map((otherMarker,index)=>{const x=otherMarker.x-thisMarker.x;const y=otherMarker.y-thisMarker.y;const dist=Math.sqrt(x**2+y**2);return {index,dist,dir:{x:dist!==0?x/dist:0,y:dist!==0?y/dist:0}}}).filter(marker=>{if(marker.index===thisIndex){return false}return markers[marker.index].showCorrectness!=="correct"}).sort((a,b)=>{const distA=Math.round(a.dist*(navigateDirection.x*a.dir.x+navigateDirection.y*a.dir.y));const distB=Math.round(b.dist*(navigateDirection.x*b.dir.x+navigateDirection.y*b.dir.y));let dirA;let dirB;if(navigateDirection.x>0){dirA=a.dir.x>0&&distA!==0;dirB=b.dir.x>0&&distB!==0;}else if(navigateDirection.x<0){dirA=a.dir.x<0&&distA!==0;dirB=b.dir.x<0&&distB!==0;}else if(navigateDirection.y>0){dirA=a.dir.y>0&&distA!==0;dirB=b.dir.y>0&&distB!==0;}else if(navigateDirection.y<0){dirA=a.dir.y<0&&distA!==0;dirB=b.dir.y<0&&distB!==0;}if(dirA!==dirB){if(dirA){return -1}return 1}return distA-distB});return sortedMarkers.length>0?sortedMarkers[0].index:thisIndex}componentDidMount(){this.props.analytics?.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:"null",widgetType:"label-image",widgetId:this.props.widgetId}});this._mounted=true;}componentWillUnmount(){this._mounted=false;}getPromptJSON(){return getPromptJSON$a(this.props)}handleMarkerChange(index,marker){const{userInput,handleUserInput}=this.props;const updatedUserInput=[...userInput.markers.slice(0,index),{label:marker.label,selected:marker.selected},...userInput.markers.slice(index+1)];handleUserInput({markers:updatedUserInput});}activateMarker(index,opened){this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:marker-interacted-with",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:marker-interacted-with:ti",payload:null});const{activeMarkerIndex}=this.state;if(activeMarkerIndex!==index&&opened){this.setState({activeMarkerIndex:index,markersInteracted:true});}else {this.setState({activeMarkerIndex:-1});}}handleMarkerKeyDown(index,e){const{markers}=this.props;if(markers.length<2){return}const directions={ArrowUp:{x:0,y:-1},ArrowRight:{x:1,y:0},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0}};if(!(e.key in directions)){return}const navigateDirection=directions[e.key];e.preventDefault();const marker=this._markers[LabelImage.navigateToMarkerIndex(navigateDirection,markers,index)];if(marker){ReactDOM.findDOMNode(marker).focus();}}handleAnswerChoicesChangeForMarker(index,selection){const{choices,markers}=this.props;const selected=choices.filter((_,index)=>selection[index]);this.handleMarkerChange(index,{...markers[index],selected:selected.length>0?selected:undefined});}renderMarkers(){const{markers,preferredPopoverDirection,userInput}=this.props;const{markersInteracted,activeMarkerIndex}=this.state;const isNarrowPage=this._mounted&&window.matchMedia(mediaQueries.xsOrSmaller.replace("@media ","")).matches;const isWideImage=this.props.imageWidth/2>this.props.imageHeight;return markers.map((marker,index)=>{const userInputMarker=userInput.markers[index];let side;let markerPosition;if(isNarrowPage||isWideImage){side=marker.y>50?"top":"bottom";markerPosition=marker.y>50?"bottom":"top";}else {markerPosition=LabelImage.imageSideForMarkerPosition(marker.x,marker.y,preferredPopoverDirection);if(markerPosition==="center"){markerPosition="bottom";}side=({left:"right",top:"bottom",right:"left",bottom:"top"})[markerPosition];}const computedSelectedState=getComputedSelectedState(marker,userInputMarker,this.props.reviewMode,this.props.showSolutions);let score;if(isAnswerful(marker)){score=scoreLabelImageMarker(computedSelectedState.selected,marker.answers);}else {score={hasAnswers:false,isCorrect:false};}const shouldShowFeedback=this.props.showSolutions==="all"||this.props.reviewMode;const showCorrectness=shouldShowFeedback&&score.isCorrect?"correct":marker.showCorrectness;const disabled=shouldShowFeedback;const isActiveAnswerChoice=activeMarkerIndex===index;const showAnswerChoice=computedSelectedState.selected&&!this.state.hideAnswers&&!isActiveAnswerChoice;const adjustPillDistance={[`margin${markerPosition.charAt(0).toUpperCase()+markerPosition.slice(1)}`]:10};return jsxRuntimeExports.jsx(View,{style:{position:"absolute",left:`${marker.x}%`,top:`${marker.y}%`,zIndex:"unset"},children:jsxRuntimeExports.jsx(AnswerChoices,{choices:this.props.choices.map(choice=>({content:choice,checked:computedSelectedState.selected?computedSelectedState.selected.includes(choice):false})),multipleSelect:this.props.multipleAnswers,onChange:selection=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:choiced-interacted-with",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:choiced-interacted-with:ti",payload:null});this.handleAnswerChoicesChangeForMarker(index,selection);},onToggle:opened=>this.activateMarker(index,opened),disabled:disabled,opener:({opened})=>jsxRuntimeExports.jsx(Clickable,{role:"button","aria-expanded":opened,children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx(Marker,{label:marker.label,showCorrectness:showCorrectness,showSelected:opened,showPulsate:!markersInteracted,ref:node=>this._markers[index]=node,showAnswer:showAnswerChoice,answerSide:side,answerStyles:adjustPillDistance,focused:focused||pressed,hovered:hovered,selected:computedSelectedState.selected})},`marker-${marker.x}.${marker.y}`)},`answers-${marker.x}.${marker.y}`)},index)})}renderInstructions(){const{apiOptions:{isMobile},choices,multipleAnswers,hideChoicesFromInstructions:hideChoices}=this.props;const{strings}=this.context;const promptString=isMobile?multipleAnswers?strings.tapMultiple:strings.tapSingle:multipleAnswers?strings.clickMultiple:strings.clickSingle;const choicesString=strings.choices;return jsxRuntimeExports.jsxs("div",{className:classNames$1("perseus-label-image-widget-instructions",css(styles$9.instructions)),children:[jsxRuntimeExports.jsxs("div",{className:css(styles$9.instructionsCaption),children:[promptString," ",!hideChoices&&choicesString]}),!hideChoices&&jsxRuntimeExports.jsx("div",{className:css(styles$9.instructionsChoices),children:choices.map((choice,index)=>jsxRuntimeExports.jsx("div",{className:css(styles$9.instructionsChoice),children:jsxRuntimeExports.jsx(Renderer,{content:choice,strings:strings})},index))})]})}getSerializedState(){const{userInput,markers,...rest}=this.props;return {...rest,markers:markers.map((marker,index)=>({...marker,selected:userInput.markers[index].selected}))}}render(){const{imageAlt,imageUrl,imageWidth,imageHeight}=this.props;const{activeMarkerIndex}=this.state;return jsxRuntimeExports.jsxs("div",{children:[this.renderInstructions(),jsxRuntimeExports.jsxs("div",{className:css(styles$9.markersCanvas),style:{maxWidth:imageWidth,maxHeight:imageHeight},children:[jsxRuntimeExports.jsx("div",{className:css(styles$9.imageContainer,activeMarkerIndex!==-1&&styles$9.imageInteractionDisabled),children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{alt:imageAlt,src:imageUrl,width:imageWidth,height:imageHeight,setAssetStatus:setAssetStatus,allowZoom:true})})}),this.renderMarkers()]}),jsxRuntimeExports.jsx(HideAnswersToggle,{areAnswersHidden:this.state.hideAnswers,onChange:hideAnswers=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:toggle-answers-hidden",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:toggle-answers-hidden:ti",payload:null});this.setState({hideAnswers});}})]})}constructor(props){super(props),this._mounted=false;this._markers=[];this.state={activeMarkerIndex:-1,markersInteracted:false,hideAnswers:false};}}LabelImage.contextType=PerseusI18nContext;const LabelImageWithDependencies=React.forwardRef((props,ref)=>{const deps=useDependencies();return jsxRuntimeExports.jsx(LabelImage,{ref:ref,analytics:deps.analytics,...props})});function getStartUserInput$7(options){return {markers:options.markers.map(m=>({label:m.label}))}}function getUserInputFromSerializedState$7(serializedState){return {markers:serializedState.markers.map(m=>({label:m.label,selected:m.selected}))}}function getCorrectUserInput$3(options){return {markers:options.markers.map(marker=>({label:marker.label,selected:marker.answers}))}}var LabelImage$1 = {name:"label-image",displayName:"Label Image",widget:LabelImageWithDependencies,isLintable:true,getStartUserInput: getStartUserInput$7,getCorrectUserInput: getCorrectUserInput$3,getUserInputFromSerializedState: getUserInputFromSerializedState$7};const styles$9=StyleSheet.create({instructions:{paddingBottom:16},instructionsCaption:{...bodyXsmallBold,paddingBottom:16},instructionsChoices:{display:"flex",flexWrap:"wrap",margin:"-8px 0"},instructionsChoice:{display:"flex",alignItems:"center",margin:"8px 0",":not(:last-child)":{"::after":{content:"''",display:"inline-block",position:"relative",width:2,height:2,marginLeft:5,marginRight:5,background:"rgba(33, 36, 44, 0.32)",borderRadius:2}}},markersCanvas:{position:"relative"},imageContainer:{display:"flex"},imageInteractionDisabled:{pointerEvents:"none"}});
2010
+ function isAnswerful(marker){return marker.answers!=null}function getComputedSelectedState(marker,userInputMarker,reviewMode,showSolutions){const shouldShowFeedback=showSolutions==="all"||reviewMode;if(!shouldShowFeedback){return userInputMarker}if(isAnswerful(marker)){return {...userInputMarker,selected:marker.answers}}else {return {...userInputMarker,selected:undefined}}}class LabelImage extends React.Component{static pointInTriangle(p,a,b,c){const sign=(p1,p2,p3)=>(p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y);const b1=sign(p,a,b)<0;const b2=sign(p,b,c)<0;const b3=sign(p,c,a)<0;return b1===b2&&b2===b3}static imageSideForMarkerPosition(x,y,preferredDirection){if(preferredDirection&&preferredDirection!=="NONE"){if(preferredDirection==="LEFT"&&x>20){return "right"}else if(preferredDirection==="RIGHT"&&x<80){return "left"}else if(preferredDirection==="UP"&&y>20){return "bottom"}else if(preferredDirection==="DOWN"&&y<80){return "top"}}if(x<20){return "left"}if(x>80){return "right"}const tl={x:20,y:0};const tr={x:80,y:0};const br={x:80,y:100};const bl={x:20,y:100};const cp={x:50,y:50};const sides=["top","right","bottom","left"];const triangles={top:[tl,tr,cp],right:[cp,tr,br],bottom:[bl,cp,br],left:[tl,cp,bl]};const p={x,y};for(const side of sides){const corners=triangles[side];if(LabelImage.pointInTriangle(p,...corners)){return side}}return "center"}static navigateToMarkerIndex(navigateDirection,markers,thisIndex){const thisMarker=markers[thisIndex];const sortedMarkers=markers.map((otherMarker,index)=>{const x=otherMarker.x-thisMarker.x;const y=otherMarker.y-thisMarker.y;const dist=Math.sqrt(x**2+y**2);return {index,dist,dir:{x:dist!==0?x/dist:0,y:dist!==0?y/dist:0}}}).filter(marker=>{if(marker.index===thisIndex){return false}return markers[marker.index].showCorrectness!=="correct"}).sort((a,b)=>{const distA=Math.round(a.dist*(navigateDirection.x*a.dir.x+navigateDirection.y*a.dir.y));const distB=Math.round(b.dist*(navigateDirection.x*b.dir.x+navigateDirection.y*b.dir.y));let dirA;let dirB;if(navigateDirection.x>0){dirA=a.dir.x>0&&distA!==0;dirB=b.dir.x>0&&distB!==0;}else if(navigateDirection.x<0){dirA=a.dir.x<0&&distA!==0;dirB=b.dir.x<0&&distB!==0;}else if(navigateDirection.y>0){dirA=a.dir.y>0&&distA!==0;dirB=b.dir.y>0&&distB!==0;}else if(navigateDirection.y<0){dirA=a.dir.y<0&&distA!==0;dirB=b.dir.y<0&&distB!==0;}if(dirA!==dirB){if(dirA){return -1}return 1}return distA-distB});return sortedMarkers.length>0?sortedMarkers[0].index:thisIndex}componentDidMount(){this.props.analytics?.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:"null",widgetType:"label-image",widgetId:this.props.widgetId}});this._mounted=true;}componentWillUnmount(){this._mounted=false;}getPromptJSON(){return getPromptJSON$a(this.props)}handleMarkerChange(index,marker){const{userInput,handleUserInput}=this.props;const updatedUserInput=[...userInput.markers.slice(0,index),{label:marker.label,selected:marker.selected},...userInput.markers.slice(index+1)];handleUserInput({markers:updatedUserInput});}activateMarker(index,opened){this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:marker-interacted-with",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:marker-interacted-with:ti",payload:null});const{activeMarkerIndex}=this.state;if(activeMarkerIndex!==index&&opened){this.setState({activeMarkerIndex:index,markersInteracted:true});}else {this.setState({activeMarkerIndex:-1});}}handleMarkerKeyDown(index,e){const{markers}=this.props;if(markers.length<2){return}const directions={ArrowUp:{x:0,y:-1},ArrowRight:{x:1,y:0},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0}};if(!(e.key in directions)){return}const navigateDirection=directions[e.key];e.preventDefault();const marker=this._markers[LabelImage.navigateToMarkerIndex(navigateDirection,markers,index)];if(marker){ReactDOM.findDOMNode(marker).focus();}}handleAnswerChoicesChangeForMarker(index,selection){const{choices,markers}=this.props;const selected=choices.filter((_,index)=>selection[index]);this.handleMarkerChange(index,{...markers[index],selected:selected.length>0?selected:undefined});}renderMarkers(){const{markers,preferredPopoverDirection,userInput}=this.props;const{markersInteracted,activeMarkerIndex}=this.state;const isNarrowPage=this._mounted&&window.matchMedia(mediaQueries.xsOrSmaller.replace("@media ","")).matches;const isWideImage=this.props.imageWidth/2>this.props.imageHeight;return markers.map((marker,index)=>{const userInputMarker=userInput.markers[index];let side;let markerPosition;if(isNarrowPage||isWideImage){side=marker.y>50?"top":"bottom";markerPosition=marker.y>50?"bottom":"top";}else {markerPosition=LabelImage.imageSideForMarkerPosition(marker.x,marker.y,preferredPopoverDirection);if(markerPosition==="center"){markerPosition="bottom";}side=({left:"right",top:"bottom",right:"left",bottom:"top"})[markerPosition];}const computedSelectedState=getComputedSelectedState(marker,userInputMarker,this.props.reviewMode,this.props.showSolutions);let score;if(isAnswerful(marker)){score=scoreLabelImageMarker(computedSelectedState.selected,marker.answers);}else {score={hasAnswers:false,isCorrect:false};}const shouldShowFeedback=this.props.showSolutions==="all"||this.props.reviewMode;const showCorrectness=shouldShowFeedback&&score.isCorrect?"correct":marker.showCorrectness;const disabled=shouldShowFeedback;const isActiveAnswerChoice=activeMarkerIndex===index;const showAnswerChoice=computedSelectedState.selected&&!this.state.hideAnswers&&!isActiveAnswerChoice;const adjustPillDistance={[`margin${markerPosition.charAt(0).toUpperCase()+markerPosition.slice(1)}`]:10};return jsxRuntimeExports.jsx(View,{style:{position:"absolute",left:`${marker.x}%`,top:`${marker.y}%`,zIndex:"unset"},children:jsxRuntimeExports.jsx(AnswerChoices,{choices:this.props.choices.map(choice=>({content:choice,checked:computedSelectedState.selected?computedSelectedState.selected.includes(choice):false})),multipleSelect:this.props.multipleAnswers,onChange:selection=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:choiced-interacted-with",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:choiced-interacted-with:ti",payload:null});this.handleAnswerChoicesChangeForMarker(index,selection);},onToggle:opened=>this.activateMarker(index,opened),disabled:disabled,opener:({opened})=>jsxRuntimeExports.jsx(Clickable,{role:"button","aria-expanded":opened,children:({hovered,focused,pressed})=>jsxRuntimeExports.jsx(Marker,{label:marker.label,showCorrectness:showCorrectness,showSelected:opened,showPulsate:!markersInteracted,ref:node=>this._markers[index]=node,showAnswer:showAnswerChoice,answerSide:side,answerStyles:adjustPillDistance,focused:focused||pressed,hovered:hovered,selected:computedSelectedState.selected})},`marker-${marker.x}.${marker.y}`)},`answers-${marker.x}.${marker.y}`)},index)})}renderInstructions(){const{apiOptions:{isMobile},choices,multipleAnswers,hideChoicesFromInstructions:hideChoices}=this.props;const{strings}=this.context;const promptString=isMobile?multipleAnswers?strings.tapMultiple:strings.tapSingle:multipleAnswers?strings.clickMultiple:strings.clickSingle;const choicesString=strings.choices;return jsxRuntimeExports.jsxs("div",{className:classNames$2("perseus-label-image-widget-instructions",css(styles$9.instructions)),children:[jsxRuntimeExports.jsxs("div",{className:css(styles$9.instructionsCaption),children:[promptString," ",!hideChoices&&choicesString]}),!hideChoices&&jsxRuntimeExports.jsx("div",{className:css(styles$9.instructionsChoices),children:choices.map((choice,index)=>jsxRuntimeExports.jsx("div",{className:css(styles$9.instructionsChoice),children:jsxRuntimeExports.jsx(Renderer,{content:choice,strings:strings})},index))})]})}getSerializedState(){const{userInput,markers,...rest}=this.props;return {...rest,markers:markers.map((marker,index)=>({...marker,selected:userInput.markers[index].selected}))}}render(){const{imageAlt,imageUrl,imageWidth,imageHeight}=this.props;const{activeMarkerIndex}=this.state;return jsxRuntimeExports.jsxs("div",{children:[this.renderInstructions(),jsxRuntimeExports.jsxs("div",{className:css(styles$9.markersCanvas),style:{maxWidth:imageWidth,maxHeight:imageHeight},children:[jsxRuntimeExports.jsx("div",{className:css(styles$9.imageContainer,activeMarkerIndex!==-1&&styles$9.imageInteractionDisabled),children:jsxRuntimeExports.jsx(context$1.Consumer,{children:({setAssetStatus})=>jsxRuntimeExports.jsx(SvgImage,{alt:imageAlt,src:imageUrl,width:imageWidth,height:imageHeight,setAssetStatus:setAssetStatus,allowZoom:true})})}),this.renderMarkers()]}),jsxRuntimeExports.jsx(HideAnswersToggle,{areAnswersHidden:this.state.hideAnswers,onChange:hideAnswers=>{this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:toggle-answers-hidden",payload:null});this.props.analytics?.onAnalyticsEvent({type:"perseus:label-image:toggle-answers-hidden:ti",payload:null});this.setState({hideAnswers});}})]})}constructor(props){super(props),this._mounted=false;this._markers=[];this.state={activeMarkerIndex:-1,markersInteracted:false,hideAnswers:false};}}LabelImage.contextType=PerseusI18nContext;const LabelImageWithDependencies=React.forwardRef((props,ref)=>{const deps=useDependencies();return jsxRuntimeExports.jsx(LabelImage,{ref:ref,analytics:deps.analytics,...props})});function getStartUserInput$7(options){return {markers:options.markers.map(m=>({label:m.label}))}}function getUserInputFromSerializedState$7(serializedState){return {markers:serializedState.markers.map(m=>({label:m.label,selected:m.selected}))}}function getCorrectUserInput$3(options){return {markers:options.markers.map(marker=>({label:marker.label,selected:marker.answers}))}}var LabelImage$1 = {name:"label-image",displayName:"Label Image",widget:LabelImageWithDependencies,isLintable:true,getStartUserInput: getStartUserInput$7,getCorrectUserInput: getCorrectUserInput$3,getUserInputFromSerializedState: getUserInputFromSerializedState$7};const styles$9=StyleSheet.create({instructions:{paddingBottom:16},instructionsCaption:{...bodyXsmallBold,paddingBottom:16},instructionsChoices:{display:"flex",flexWrap:"wrap",margin:"-8px 0"},instructionsChoice:{display:"flex",alignItems:"center",margin:"8px 0",":not(:last-child)":{"::after":{content:"''",display:"inline-block",position:"relative",width:2,height:2,marginLeft:5,marginRight:5,background:semanticColor.core.border.neutral.default,borderRadius:2}}},markersCanvas:{position:"relative"},imageContainer:{display:"flex"},imageInteractionDisabled:{pointerEvents:"none"}});
2005
2011
 
2006
2012
  const addOffsetParentScroll=($el,position)=>{const $offsetParent=$el.offsetParent();return {top:position.top+$offsetParent.scrollTop(),left:position.left+$offsetParent.scrollLeft()}};class Placeholder extends React.Component{render(){const{layout}=this.props;const className=css(styles$8.card,styles$8.placeholder,layout==="horizontal"&&styles$8.horizontalCard);const style={width:this.props.width,height:this.props.height};if(this.props.margin!=null){style.margin=this.props.margin;}return jsxRuntimeExports.jsx("li",{className:className,style:style})}}class Draggable extends React.Component{componentDidMount(){this._mounted=true;this.isMouseMoveUpBound=false;document.addEventListener("touchmove",this.onMouseMove,Util.supportsPassiveEvents()?{passive:false}:false);}componentDidUpdate(prevProps){if(this.props.state===prevProps.state){return}if(this.props.state==="animating"&&this.props.endPosition){const current=this.getCurrentPosition();const duration=15*Math.sqrt(Math.sqrt(Math.pow(this.props.endPosition.left-current.left,2)+Math.pow(this.props.endPosition.top-current.top,2)));$(ReactDOM__default.findDOMNode(this)).animate(this.props.endPosition,{duration:Math.max(duration,1),complete:this.props.onAnimationEnd});}else if(this.props.state==="static"){$(ReactDOM__default.findDOMNode(this)).finish();}}componentWillUnmount(){this._mounted=false;if(this.isMouseMoveUpBound){this.unbindMouseMoveUp();}if(this.animationFrameRequest){cancelAnimationFrame(this.animationFrameRequest);}document.removeEventListener("touchmove",this.onMouseMove);}render(){const{includePadding,layout,state:type}=this.props;let className=css(styles$8.card,styles$8.draggable,layout==="horizontal"&&styles$8.horizontalCard,layout==="vertical"&&styles$8.verticalCard,type==="dragging"&&styles$8.dragging,type==="disabled"&&styles$8.disabled,!includePadding&&styles$8.unpaddedCard)+" "+"perseus-sortable-draggable";if(!includePadding){className+=" perseus-sortable-draggable-unpadded";}const style={position:"static"};if(this.props.state==="dragging"||this.props.state==="animating"){_.extend(style,{position:"absolute"},this.getCurrentPosition());}if(this.props.width){style.width=this.props.width+1;}if(this.props.height){style.height=this.props.height;}if(this.props.margin!=null){style.margin=this.props.margin;}return jsxRuntimeExports.jsx("li",{className:className,style:style,onMouseDown:this.onMouseDown,onTouchStart:this.onMouseDown,onTouchMove:this.onMouseMove,onTouchEnd:this.onMouseUp,onTouchCancel:this.onMouseUp,children:jsxRuntimeExports.jsx(Renderer,{content:this.props.content,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"draggable"),onRender:this.props.onRender,strings:this.context.strings})})}constructor(...args){super(...args),this.animationFrameRequest=null,this.state={startPosition:{left:0,top:0},startMouse:{left:0,top:0},mouse:{left:0,top:0},dragging:false},this.getCurrentPosition=()=>{return {left:this.state.startPosition.left+this.state.mouse.left-this.state.startMouse.left,top:this.state.startPosition.top+this.state.mouse.top-this.state.startMouse.top}},this.bindMouseMoveUp=()=>{this.isMouseMoveUpBound=true;$(document).on("mousemove",this.onMouseMove);$(document).on("mouseup",this.onMouseUp);},this.unbindMouseMoveUp=()=>{this.isMouseMoveUpBound=false;$(document).off("mousemove",this.onMouseMove);$(document).off("mouseup",this.onMouseUp);},this.onMouseDown=event=>{if(this.props.state!=="static"){return}if(!(event.button===0||event.touches!=null&&event.touches.length===1)){return}event.preventDefault();const loc=Util.extractPointerLocation(event);this.animationFrameRequest=requestAnimationFrame(()=>{const $el=$(ReactDOM__default.findDOMNode(this));const position=$el.position();const startPosition=addOffsetParentScroll($el,position);if(loc&&this._mounted){this.setState({startPosition,startMouse:loc,mouse:loc,dragging:true},function(){this.bindMouseMoveUp();this.props.onMouseDown();});}});},this.onMouseMove=event=>{const notDragging=this.props.state!=="dragging"||!this.state.dragging;if(notDragging){return}event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.animationFrameRequest=requestAnimationFrame(()=>{this.setState({mouse:loc},this.props.onMouseMove);});}},this.onMouseUp=event=>{const notDragging=this.props.state!=="dragging"||!this.state.dragging;if(notDragging){return}event.preventDefault();const loc=Util.extractPointerLocation(event);if(loc){this.setState({dragging:false});this.unbindMouseMoveUp();this.props.onMouseUp();}};}}Draggable.contextType=PerseusI18nContext;Draggable.defaultProps={includePadding:true,type:"static",linterContext:PerseusLinter.linterContextDefault};class Sortable extends React.Component{static itemsFromProps(props){const state=props.disabled?"disabled":"static";return props.options.map((option,i)=>{return {option:option,key:i,state,width:0,height:0}})}static clearItemMeasurements(items){return items.map(item=>{return {...item,width:0,height:0}})}UNSAFE_componentWillReceiveProps(nextProps){const prevProps=this.props;if(!_.isEqual(nextProps.options,prevProps.options)){this.setState({items:Sortable.itemsFromProps(nextProps)});}else if(nextProps.layout!==prevProps.layout||nextProps.padding!==prevProps.padding||nextProps.disabled!==prevProps.disabled||!_.isEqual(nextProps.constraints,prevProps.constraints)){this.setState({items:Sortable.clearItemMeasurements(this.state.items)});}}componentDidUpdate(){if(this.state.items.length&&!this.state.items[0].width&&!this.state.items[0].height){setTimeout(()=>{this.measureItems();},0);}}measureItems(){let items=[...this.state.items];const $items=_.map(items,function(item){return $(ReactDOM__default.findDOMNode(this.refs[item.key]))},this);const widths=_.invoke($items,"outerWidth");const heights=_.invoke($items,"outerHeight");const{constraints,layout}=this.props;let syncWidth=null;if(constraints?.width){syncWidth=_.max(widths.concat(constraints.width));}else if(layout==="vertical"){syncWidth=_.max(widths);}let syncHeight=null;if(constraints?.height){syncHeight=_.max(heights.concat(constraints.height));}else if(layout==="horizontal"){syncHeight=_.max(heights);}items=_.map(items,function(item,i){item.width=syncWidth||widths[i];item.height=syncHeight||heights[i];return item});this.setState({items},()=>{this.props.onMeasure?.({widths:widths,heights:heights});});}onMouseDown(key){const items=_.map(this.state.items,function(item){if(item.key===key){item.state="dragging";}return item});this.setState({items:items});}moveOptionToIndex(option,index){const{items}=this.state;if(index<0||index>items.length){throw new Error(`index ${index} out of bounds`)}const nextItems=_.clone(items);const item=items.filter(item=>{return item.option===option})[0];if(item==null){throw new Error(`option ${option} not found`)}const currentIndex=items.findIndex(i=>{return i.key===item.key});nextItems.splice(currentIndex,1);nextItems.splice(index,0,item);this.setState({items:nextItems},()=>{this.props.onChange?.({});});}onMouseMove(key){const $draggable=$(ReactDOM__default.findDOMNode(this.refs[key]));const $sortable=$(ReactDOM__default.findDOMNode(this));const items=_.clone(this.state.items);const item=_.findWhere(this.state.items,{key:key});const margin=this.props.margin||0;const currentIndex=_.indexOf(items,item);let newIndex=0;items.splice(currentIndex,1);if(this.props.layout==="horizontal"){const midWidth=$draggable.offset().left-$sortable.offset().left;let sumWidth=0;let cardWidth;_.each(items,function(item){cardWidth=item.width;if(midWidth>sumWidth+cardWidth/2){newIndex+=1;}sumWidth+=cardWidth+margin;});}else {const midHeight=$draggable.offset().top-$sortable.offset().top;let sumHeight=0;let cardHeight;_.each(items,function(item){cardHeight=item.height;if(midHeight>sumHeight+cardHeight/2){newIndex+=1;}sumHeight+=cardHeight+margin;});}if(newIndex!==currentIndex){items.splice(newIndex,0,item);this.setState({items:items});}}onMouseUp(key){const nextAnimationFrame=requestAnimationFrame(()=>{const items=_.map(this.state.items,function(item){if(item.key===key){item.state="animating";const $placeholder=$(ReactDOM__default.findDOMNode(this.refs["placeholder_"+key]));const position=$placeholder.position();const endPosition=addOffsetParentScroll($placeholder,position);item.endPosition=endPosition;}return item},this);this.setState({items:items},()=>{this.props.onChange?.({});});});this.animationFrameRequest=nextAnimationFrame;}onAnimationEnd(key){const items=_.map(this.state.items,function(item){if(item.key===key){item.state="static";}return item});this.setState({items:items});}getOptions(){return _.pluck(this.state.items,"option")}render(){if(this.props.waitForTexRendererToLoad&&!this.state.texRendererLoaded){const{TeX}=getDependencies();return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(CircularSpinner,{}),jsxRuntimeExports.jsx("div",{style:{display:"none"},children:jsxRuntimeExports.jsx(TeX,{onRender:()=>this.setState({texRendererLoaded:true}),children:"1"})})]})}const cards=[];const{layout}=this.props;const className=css(styles$8.sortable)+" perseus-sortable";const syncWidth=this.props.constraints?.width||layout==="vertical";const syncHeight=this.props.constraints?.height||layout==="horizontal";_.each(this.state.items,function(item,i,items){const isLast=i===items.length-1;const isStatic=item.state==="static"||item.state==="disabled";let margin;if(this.props.layout==="horizontal"){margin="0 "+this.props.margin+"px 0 0";}else if(this.props.layout==="vertical"){margin="0 0 "+this.props.margin+"px 0";}cards.push(jsxRuntimeExports.jsx(Draggable,{content:item.option,state:item.state,ref:item.key,width:syncWidth?item.width:undefined,height:syncHeight?item.height:undefined,layout:layout,includePadding:this.props.padding,margin:isLast&&isStatic?0:margin,endPosition:item.endPosition,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"sortable"),onRender:this.remeasureItems,onMouseDown:this.onMouseDown.bind(this,item.key),onMouseMove:this.onMouseMove.bind(this,item.key),onMouseUp:this.onMouseUp.bind(this,item.key),onTouchMove:this.onMouseMove.bind(this,item.key),onTouchEnd:this.onMouseUp.bind(this,item.key),onTouchCancel:this.onMouseUp.bind(this,item.key),onAnimationEnd:this.onAnimationEnd.bind(this,item.key)},item.key));if(item.state==="dragging"||item.state==="animating"){cards.push(jsxRuntimeExports.jsx(Placeholder,{ref:"placeholder_"+item.key,width:item.width,height:item.height,layout:layout,margin:isLast?0:margin},"placeholder_"+item.key));}},this);return jsxRuntimeExports.jsx("ul",{className:className,children:cards})}constructor(props){super(props),this.remeasureItems=_.debounce(()=>{this.setState({items:Sortable.clearItemMeasurements(this.state.items)});},20);this.state={items:Sortable.itemsFromProps(this.props),texRendererLoaded:false};}}Sortable.defaultProps={layout:"horizontal",padding:true,disabled:false,constraints:{},onMeasure:function(){},margin:5,onChange:function(){},linterContext:PerseusLinter.linterContextDefault,waitForTexRendererToLoad:true};const styles$8=StyleSheet.create({sortable:{boxSizing:"border-box",float:"left",padding:0,margin:0},card:{boxSizing:"border-box",background:"#fff",border:"1px solid #ddd",borderRadius:4,cursor:"pointer",minWidth:25,minHeight:44,padding:10,listStyleType:"none",userSelect:"none",touchAction:"none"},placeholder:{background:"#ddd",border:"1px solid #ccc"},draggable:{textAlign:"center"},horizontalCard:{float:"left",cursor:"ew-resize"},verticalCard:{maxWidth:"100%",cursor:"ns-resize"},unpaddedCard:{padding:0},dragging:{background:"#ffedcd",opacity:.8},disabled:{backgroundColor:"inherit",border:"1px solid transparent",cursor:"default"}});
2007
2013
 
@@ -2011,7 +2017,7 @@ const HACKY_CSS_CLASSNAME="perseus-widget-matcher";class Matcher extends React.C
2011
2017
 
2012
2018
  const getPromptJSON$8=widgetData=>{return {type:"matrix",options:{height:widgetData.matrixBoardSize[0],width:widgetData.matrixBoardSize[1]},userInput:{answerRows:widgetData.userInput.answers}}};
2013
2019
 
2014
- const{assert: assert$2}=InteractiveUtil;const NORMAL_DIMENSIONS={INPUT_MARGIN:3,INPUT_HEIGHT:30,INPUT_WIDTH:40};const KEYPAD_INPUT_DIMENSIONS={INPUT_MARGIN:4,INPUT_HEIGHT:36,INPUT_WIDTH:64};function getInputPath$1(row,column){return [""+row,""+column]}const getDefaultPath$1=function(){return getInputPath$1(0,0)};const getRowFromPath$1=function(path){assert$2(Array.isArray(path)&&path.length===2);return +path[0]};const getColumnFromPath$1=function(path){assert$2(Array.isArray(path)&&path.length===2);return +path[1]};const getRefForPath$1=function(path){const row=getRowFromPath$1(path);const column=getColumnFromPath$1(path);return "answer"+row+","+column};class Matrix extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"matrix",widgetSubType:"null",widgetId:this.props.widgetId}});this.cursorPosition=[0,0];}getDOMNodeForPath(path){const inputID=getRefForPath$1(path);return ReactDOM__default.findDOMNode(this.refs[inputID])}getPromptJSON(){return getPromptJSON$8(this.props)}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,answers:userInput.answers,cursorPosition:this.state.cursorPosition}}render(){let dimensions;if(this.props.apiOptions.customKeypad){dimensions=KEYPAD_INPUT_DIMENSIONS;}else {dimensions=NORMAL_DIMENSIONS;}const{INPUT_MARGIN,INPUT_HEIGHT,INPUT_WIDTH}=dimensions;const matrixSize=getMatrixSize(this.props.userInput.answers);const maxRows=this.props.matrixBoardSize[0];const maxCols=this.props.matrixBoardSize[1];const cursorRow=this.state.cursorPosition[0];const cursorCol=this.state.cursorPosition[1];const highlightedRow=Math.max(cursorRow,matrixSize[0]-1);const highlightedCol=Math.max(cursorCol,matrixSize[1]-1);const bracketHeight=(highlightedRow+1)*(INPUT_HEIGHT+2*INPUT_MARGIN);const bracketOffset=(highlightedCol+1)*(INPUT_WIDTH+2*INPUT_MARGIN);const className=classNames$1({"perseus-matrix":true,"static-mode":this.props.static,"the-matrix":this.state.enterTheMatrix>=5});return jsxRuntimeExports.jsxs("div",{className:className,children:[this.props.prefix&&jsxRuntimeExports.jsx("div",{className:"matrix-prefix",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.prefix,linterContext:this.props.linterContext,strings:this.context.strings})}),jsxRuntimeExports.jsxs("div",{className:"matrix-input",children:[jsxRuntimeExports.jsx("div",{className:"matrix-bracket bracket-left",style:{height:bracketHeight}}),jsxRuntimeExports.jsx("div",{className:"matrix-bracket bracket-right",style:{height:bracketHeight,left:bracketOffset}}),_(maxRows).times(row=>{const rowVals=this.props.userInput.answers[row];return jsxRuntimeExports.jsx("div",{className:"matrix-row",children:_(maxCols).times(col=>{const outside=row>highlightedRow||col>highlightedCol;const inputProps={className:outside?"outside":"inside",ref:getRefForPath$1(getInputPath$1(row,col)),value:rowVals?rowVals[col]:null,style:{height:INPUT_HEIGHT,width:INPUT_WIDTH,margin:INPUT_MARGIN},disabled:this.props.apiOptions.readOnly||this.props.apiOptions.editingDisabled,onFocus:()=>{this.cursorPosition=[row,col];this.setState({cursorPosition:[row,col]});this._handleFocus(row,col);},onBlur:()=>{if(row===this.cursorPosition[0]&&col===this.cursorPosition[1]){this.setState({cursorPosition:[0,0]});}this._handleBlur(row,col);},onKeyDown:e=>{this.handleKeyDown(row,col,e);},onChange:(value,cb)=>{this.onValueChange(row,col,value,cb);}};let MatrixInput;if(this.props.apiOptions.customKeypad){const style={margin:INPUT_MARGIN,minWidth:INPUT_WIDTH,minHeight:INPUT_HEIGHT,boxSizing:"border-box",backgroundColor:outside?"#f3f3f3":"#fff"};MatrixInput=jsxRuntimeExports.jsx(SimpleKeypadInput,{...inputProps,style:style,scrollable:true,keypadElement:this.props.keypadElement});}else {const updatedProps={...inputProps,style:StyleSheet.create({input:{...inputProps.style,display:"inline-block",padding:0,backgroundColor:outside?"#f3f3f3":"#fff"}}).input};MatrixInput=jsxRuntimeExports.jsx(TextInput,{...updatedProps});}return jsxRuntimeExports.jsx("span",{className:"matrix-input-field",children:MatrixInput},col)})},row)})]}),this.props.suffix&&jsxRuntimeExports.jsx("div",{className:"matrix-suffix",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.suffix,linterContext:this.props.linterContext,strings:this.context.strings})})]})}constructor(...args){super(...args),this.state={cursorPosition:[0,0],enterTheMatrix:0},this.getInputPaths=()=>{const inputPaths=[];const maxRows=this.props.matrixBoardSize[0];const maxCols=this.props.matrixBoardSize[1];for(let row=0;row<maxRows;row++){for(let col=0;col<maxCols;col++){const inputPath=getInputPath$1(row,col);inputPaths.push(inputPath);}}return inputPaths},this._handleFocus=(row,col)=>{this.props.onFocus(getInputPath$1(row,col));},this._handleBlur=(row,col)=>{this.props.onBlur(getInputPath$1(row,col));},this.focus=()=>{this.focusInputPath(getDefaultPath$1());return true},this.focusInputPath=path=>{const inputID=getRefForPath$1(path);this.refs[inputID].focus();},this.blurInputPath=path=>{if(path.length===0){path=getDefaultPath$1();}const inputID=getRefForPath$1(path);this.refs[inputID].blur();},this.handleKeyDown=(row,col,e)=>{const maxRow=this.props.matrixBoardSize[0];const maxCol=this.props.matrixBoardSize[1];let enterTheMatrix=null;const curInput=this.refs[getRefForPath$1(getInputPath$1(row,col))];const curValueString=curInput.getStringValue();const cursorStartPosition=curInput.getSelectionStart();const cursorEndPosition=curInput.getSelectionEnd();let nextPath=null;if(e.key==="ArrowUp"&&row>0){nextPath=getInputPath$1(row-1,col);}else if(e.key==="ArrowDown"&&row+1<maxRow){nextPath=getInputPath$1(row+1,col);}else if(e.key==="ArrowLeft"&&col>0){if(cursorStartPosition===0&&cursorEndPosition===0){nextPath=getInputPath$1(row,col-1);}}else if(e.key==="ArrowRight"&&col+1<maxCol){if(cursorStartPosition===curValueString.length){nextPath=getInputPath$1(row,col+1);}}else if(e.key==="Enter"){enterTheMatrix=this.state.enterTheMatrix+1;}else if(e.key==="Escape"){enterTheMatrix=0;}if(nextPath){e.preventDefault();const input=this.refs[getRefForPath$1(nextPath)];const inputValString=input.getStringValue();const valueLength=inputValString.length*2;input.focus();if(e.key==="ArrowRight"){input.setSelectionRange(0,0);}else {input.setSelectionRange(valueLength,valueLength);}}if(enterTheMatrix!=null){this.setState({enterTheMatrix:enterTheMatrix});}},this.onValueChange=(row,column,value,cb)=>{const answers=this.props.userInput.answers.map(answer=>[...answer]);if(!answers[row]){answers[row]=[];}answers[row][column]=value;this.props.handleUserInput({answers},cb);this.props.trackInteraction();};}}Matrix.contextType=PerseusI18nContext;Matrix.defaultProps={matrixBoardSize:[3,3],prefix:"",suffix:"",apiOptions:ApiOptions.defaults,linterContext:linterContextDefault,userInput:{answers:[[]]}};function getStartUserInput$5(){return {answers:[[]]}}function getCorrectUserInput$2(options){return {answers:options.answers.map(row=>row.map(num=>String(num)))}}function getUserInputFromSerializedState$5(serializedState){return {answers:serializedState.answers}}const WrappedMatrix=withDependencies(Matrix);var Matrix$1 = {name:"matrix",displayName:"Matrix",hidden:true,widget:WrappedMatrix,isLintable:true,getStartUserInput: getStartUserInput$5,getCorrectUserInput: getCorrectUserInput$2,getUserInputFromSerializedState: getUserInputFromSerializedState$5};
2020
+ const{assert: assert$2}=InteractiveUtil;const NORMAL_DIMENSIONS={INPUT_MARGIN:3,INPUT_HEIGHT:30,INPUT_WIDTH:40};const KEYPAD_INPUT_DIMENSIONS={INPUT_MARGIN:4,INPUT_HEIGHT:36,INPUT_WIDTH:64};function getInputPath$1(row,column){return [""+row,""+column]}const getDefaultPath$1=function(){return getInputPath$1(0,0)};const getRowFromPath$1=function(path){assert$2(Array.isArray(path)&&path.length===2);return +path[0]};const getColumnFromPath$1=function(path){assert$2(Array.isArray(path)&&path.length===2);return +path[1]};const getRefForPath$1=function(path){const row=getRowFromPath$1(path);const column=getColumnFromPath$1(path);return "answer"+row+","+column};class Matrix extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetType:"matrix",widgetSubType:"null",widgetId:this.props.widgetId}});this.cursorPosition=[0,0];}getDOMNodeForPath(path){const inputID=getRefForPath$1(path);return ReactDOM__default.findDOMNode(this.refs[inputID])}getPromptJSON(){return getPromptJSON$8(this.props)}getSerializedState(){const{userInput,...rest}=this.props;return {...rest,answers:userInput.answers,cursorPosition:this.state.cursorPosition}}render(){let dimensions;if(this.props.apiOptions.customKeypad){dimensions=KEYPAD_INPUT_DIMENSIONS;}else {dimensions=NORMAL_DIMENSIONS;}const{INPUT_MARGIN,INPUT_HEIGHT,INPUT_WIDTH}=dimensions;const matrixSize=getMatrixSize(this.props.userInput.answers);const maxRows=this.props.matrixBoardSize[0];const maxCols=this.props.matrixBoardSize[1];const cursorRow=this.state.cursorPosition[0];const cursorCol=this.state.cursorPosition[1];const highlightedRow=Math.max(cursorRow,matrixSize[0]-1);const highlightedCol=Math.max(cursorCol,matrixSize[1]-1);const bracketHeight=(highlightedRow+1)*(INPUT_HEIGHT+2*INPUT_MARGIN);const bracketOffset=(highlightedCol+1)*(INPUT_WIDTH+2*INPUT_MARGIN);const className=classNames$2({"perseus-matrix":true,"static-mode":this.props.static,"the-matrix":this.state.enterTheMatrix>=5});return jsxRuntimeExports.jsxs("div",{className:className,children:[this.props.prefix&&jsxRuntimeExports.jsx("div",{className:"matrix-prefix",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.prefix,linterContext:this.props.linterContext,strings:this.context.strings})}),jsxRuntimeExports.jsxs("div",{className:"matrix-input",children:[jsxRuntimeExports.jsx("div",{className:"matrix-bracket bracket-left",style:{height:bracketHeight}}),jsxRuntimeExports.jsx("div",{className:"matrix-bracket bracket-right",style:{height:bracketHeight,left:bracketOffset}}),_(maxRows).times(row=>{const rowVals=this.props.userInput.answers[row];return jsxRuntimeExports.jsx("div",{className:"matrix-row",children:_(maxCols).times(col=>{const outside=row>highlightedRow||col>highlightedCol;const inputProps={className:outside?"outside":"inside",ref:getRefForPath$1(getInputPath$1(row,col)),value:rowVals?rowVals[col]:null,style:{height:INPUT_HEIGHT,width:INPUT_WIDTH,margin:INPUT_MARGIN},disabled:this.props.apiOptions.readOnly||this.props.apiOptions.editingDisabled,onFocus:()=>{this.cursorPosition=[row,col];this.setState({cursorPosition:[row,col]});this._handleFocus(row,col);},onBlur:()=>{if(row===this.cursorPosition[0]&&col===this.cursorPosition[1]){this.setState({cursorPosition:[0,0]});}this._handleBlur(row,col);},onKeyDown:e=>{this.handleKeyDown(row,col,e);},onChange:(value,cb)=>{this.onValueChange(row,col,value,cb);}};let MatrixInput;if(this.props.apiOptions.customKeypad){const style={margin:INPUT_MARGIN,minWidth:INPUT_WIDTH,minHeight:INPUT_HEIGHT,boxSizing:"border-box",backgroundColor:outside?"#f3f3f3":"#fff"};MatrixInput=jsxRuntimeExports.jsx(SimpleKeypadInput,{...inputProps,style:style,scrollable:true,keypadElement:this.props.keypadElement});}else {const updatedProps={...inputProps,style:StyleSheet.create({input:{...inputProps.style,display:"inline-block",padding:0,backgroundColor:outside?"#f3f3f3":"#fff"}}).input};MatrixInput=jsxRuntimeExports.jsx(TextInput,{...updatedProps});}return jsxRuntimeExports.jsx("span",{className:"matrix-input-field",children:MatrixInput},col)})},row)})]}),this.props.suffix&&jsxRuntimeExports.jsx("div",{className:"matrix-suffix",children:jsxRuntimeExports.jsx(Renderer,{content:this.props.suffix,linterContext:this.props.linterContext,strings:this.context.strings})})]})}constructor(...args){super(...args),this.state={cursorPosition:[0,0],enterTheMatrix:0},this.getInputPaths=()=>{const inputPaths=[];const maxRows=this.props.matrixBoardSize[0];const maxCols=this.props.matrixBoardSize[1];for(let row=0;row<maxRows;row++){for(let col=0;col<maxCols;col++){const inputPath=getInputPath$1(row,col);inputPaths.push(inputPath);}}return inputPaths},this._handleFocus=(row,col)=>{this.props.onFocus(getInputPath$1(row,col));},this._handleBlur=(row,col)=>{this.props.onBlur(getInputPath$1(row,col));},this.focus=()=>{this.focusInputPath(getDefaultPath$1());return true},this.focusInputPath=path=>{const inputID=getRefForPath$1(path);this.refs[inputID].focus();},this.blurInputPath=path=>{if(path.length===0){path=getDefaultPath$1();}const inputID=getRefForPath$1(path);this.refs[inputID].blur();},this.handleKeyDown=(row,col,e)=>{const maxRow=this.props.matrixBoardSize[0];const maxCol=this.props.matrixBoardSize[1];let enterTheMatrix=null;const curInput=this.refs[getRefForPath$1(getInputPath$1(row,col))];const curValueString=curInput.getStringValue();const cursorStartPosition=curInput.getSelectionStart();const cursorEndPosition=curInput.getSelectionEnd();let nextPath=null;if(e.key==="ArrowUp"&&row>0){nextPath=getInputPath$1(row-1,col);}else if(e.key==="ArrowDown"&&row+1<maxRow){nextPath=getInputPath$1(row+1,col);}else if(e.key==="ArrowLeft"&&col>0){if(cursorStartPosition===0&&cursorEndPosition===0){nextPath=getInputPath$1(row,col-1);}}else if(e.key==="ArrowRight"&&col+1<maxCol){if(cursorStartPosition===curValueString.length){nextPath=getInputPath$1(row,col+1);}}else if(e.key==="Enter"){enterTheMatrix=this.state.enterTheMatrix+1;}else if(e.key==="Escape"){enterTheMatrix=0;}if(nextPath){e.preventDefault();const input=this.refs[getRefForPath$1(nextPath)];const inputValString=input.getStringValue();const valueLength=inputValString.length*2;input.focus();if(e.key==="ArrowRight"){input.setSelectionRange(0,0);}else {input.setSelectionRange(valueLength,valueLength);}}if(enterTheMatrix!=null){this.setState({enterTheMatrix:enterTheMatrix});}},this.onValueChange=(row,column,value,cb)=>{const answers=this.props.userInput.answers.map(answer=>[...answer]);if(!answers[row]){answers[row]=[];}answers[row][column]=value;this.props.handleUserInput({answers},cb);this.props.trackInteraction();};}}Matrix.contextType=PerseusI18nContext;Matrix.defaultProps={matrixBoardSize:[3,3],prefix:"",suffix:"",apiOptions:ApiOptions.defaults,linterContext:linterContextDefault,userInput:{answers:[[]]}};function getStartUserInput$5(){return {answers:[[]]}}function getCorrectUserInput$2(options){return {answers:options.answers.map(row=>row.map(num=>String(num)))}}function getUserInputFromSerializedState$5(serializedState){return {answers:serializedState.answers}}const WrappedMatrix=withDependencies(Matrix);var Matrix$1 = {name:"matrix",displayName:"Matrix",hidden:true,widget:WrappedMatrix,isLintable:true,getStartUserInput: getStartUserInput$5,getCorrectUserInput: getCorrectUserInput$2,getUserInputFromSerializedState: getUserInputFromSerializedState$5};
2015
2021
 
2016
2022
  const getPromptJSON$7=()=>{return getUnsupportedPromptJSON("measurer")};
2017
2023
 
@@ -2053,7 +2059,7 @@ const{assert}=InteractiveUtil;function getInputPath(row,column){return [""+row,"
2053
2059
 
2054
2060
  const getPromptJSON=()=>{return getUnsupportedPromptJSON("video")};
2055
2061
 
2056
- const IS_URL$1=/^https?:\/\//;const getYoutubeId=url=>{const regExp=/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;const match=url.match(regExp);if(match&&match[7].length===11){return match[7]}return "videoNotFound"};const VideoTranscriptLink=props=>{const{location}=props;const{useVideo}=useDependencies();const[id,kind]=IS_URL$1.test(location)?[getYoutubeId(location),"YOUTUBE_ID"]:[location,"READABLE_ID"];const result=useVideo(id,kind);const{strings}=usePerseusI18n();switch(result.status){case "loading":return jsxRuntimeExports.jsx(View,{children:strings.loading});case "success":{const video=result.data?.video;return jsxRuntimeExports.jsxs(View,{style:styles$3.transcriptLink,children:[jsxRuntimeExports.jsx(Text$1,{children:video?.title}),jsxRuntimeExports.jsx(Strut,{size:10}),jsxRuntimeExports.jsx(Link,{href:"/transcript/"+(video?.contentId||"videoNotFound"),target:"_blank",className:"visited-no-recolor",children:strings.videoTranscript})]})}case "error":return jsxRuntimeExports.jsx(View,{children:strings.somethingWrong});case "aborted":return jsxRuntimeExports.jsx(View,{children:strings.somethingWrong});default:return jsxRuntimeExports.jsx(View,{children:strings.somethingWrong})}};const styles$3=StyleSheet.create({transcriptLink:{flexDirection:"row",width:"100%",justifyContent:"center"}});
2062
+ const IS_URL$1=/^https?:\/\//;const getYoutubeId=url=>{const regExp=/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;const match=url.match(regExp);if(match&&match[7].length===11){return match[7]}return "videoNotFound"};const VideoTranscriptLink=props=>{const{location}=props;const{useVideo}=useDependencies();const[id,kind]=IS_URL$1.test(location)?[getYoutubeId(location),"YOUTUBE_ID"]:[location,"READABLE_ID"];const result=useVideo(id,kind);const{strings}=usePerseusI18n();switch(result.status){case "loading":return jsxRuntimeExports.jsx(View,{children:strings.loading});case "success":{const video=result.data?.video;return jsxRuntimeExports.jsxs(View,{style:styles$3.transcriptLink,children:[jsxRuntimeExports.jsx(Text$2,{children:video?.title}),jsxRuntimeExports.jsx(Strut,{size:10}),jsxRuntimeExports.jsx(Link,{href:"/transcript/"+(video?.contentId||"videoNotFound"),target:"_blank",className:"visited-no-recolor",children:strings.videoTranscript})]})}case "error":return jsxRuntimeExports.jsx(View,{children:strings.somethingWrong});case "aborted":return jsxRuntimeExports.jsx(View,{children:strings.somethingWrong});default:return jsxRuntimeExports.jsx(View,{children:strings.somethingWrong})}};const styles$3=StyleSheet.create({transcriptLink:{flexDirection:"row",width:"100%",justifyContent:"center"}});
2057
2063
 
2058
2064
  const DEFAULT_WIDTH=1280;const DEFAULT_HEIGHT=720;const KA_EMBED="{host}/embed_video?slug={slug}"+"&internal_video_only=1";const IS_URL=/^https?:\/\//;const IS_KA_SITE=/(khanacademy\.org|localhost)/;const IS_VIMEO=/(vimeo\.com)/;class Video extends React.Component{componentDidMount(){this.props.dependencies.analytics.onAnalyticsEvent({type:"perseus:widget:rendered:ti",payload:{widgetSubType:"null",widgetType:"video",widgetId:this.props.widgetId}});}getPromptJSON(){return getPromptJSON()}render(){const{InitialRequestUrl}=getDependencies();const location=this.props.location;if(!location){return jsxRuntimeExports.jsx("div",{})}let url;if(IS_URL.test(location)){url=location;if(IS_VIMEO.test(url)){if(url.indexOf("?")===-1){url+="?dnt=1";}else {url+="&dnt=1";}}}else {url=KA_EMBED.replace("{slug}",location);let embedHostname="https://www.khanacademy.org";if(IS_KA_SITE.test(InitialRequestUrl.host)){embedHostname=InitialRequestUrl.origin;}url=url.replace("{host}",embedHostname);}url=this.props.dependencies.generateUrl({url,context:"video:video_url",kaLocale:this.context.locale});return jsxRuntimeExports.jsxs(View,{children:[jsxRuntimeExports.jsxs(FixedToResponsive,{width:DEFAULT_WIDTH,height:DEFAULT_HEIGHT,children:[jsxRuntimeExports.jsx(View,{style:a11y.srOnly,children:this.context.strings.videoWrapper}),jsxRuntimeExports.jsx("iframe",{className:"perseus-video-widget",sandbox:"allow-same-origin allow-scripts",width:DEFAULT_WIDTH,height:DEFAULT_HEIGHT,src:url,allowFullScreen:true,allow:"autoplay"})]},location+this.props.alignment),jsxRuntimeExports.jsx(VideoTranscriptLink,{location:location})]})}constructor(...args){super(...args),this.isWidget=true;}}Video.contextType=PerseusI18nContext;const WrappedVideo=withDependencies(Video);var Video$1 = {name:"video",displayName:"Video",widget:WrappedVideo};
2059
2065
 
@@ -2061,7 +2067,7 @@ var extraWidgets = [CSProgram$1,Categorizer$1,Definition$1,DeprecatedStandin$1,D
2061
2067
 
2062
2068
  const init=function(){registerWidgets(basicWidgets);registerWidgets(extraWidgets);replaceDeprecatedWidgets();};
2063
2069
 
2064
- const libName="@khanacademy/perseus";const libVersion="77.2.2";addLibraryVersionToPerseusDebug(libName,libVersion);
2070
+ const libName="@khanacademy/perseus";const libVersion="77.3.2";addLibraryVersionToPerseusDebug(libName,libVersion);
2065
2071
 
2066
2072
  const apiVersion={major:12,minor:0};
2067
2073
 
@@ -2069,11 +2075,11 @@ var allWidgets = [...basicWidgets,...extraWidgets];
2069
2075
 
2070
2076
  registerWidgets(allWidgets);const ItemVersion=getVersionVector();
2071
2077
 
2072
- class ArticleRenderer extends React.Component{componentDidMount(){this._currentFocus=null;}shouldComponentUpdate(nextProps){return nextProps!==this.props}render(){const apiOptions={...ApiOptions.defaults,...this.props.apiOptions,isArticle:true};const classes=classNames$1({"framework-perseus":true,"perseus-article":true,[ClassNames.MOBILE]:apiOptions.isMobile});const sections=this._sections().map((section,sectionIndex)=>{return jsxRuntimeExports.jsx("div",{className:"clearfix",children:jsxRuntimeExports.jsx(UserInputManager,{widgets:section.widgets,problemNum:0,children:({userInput,handleUserInput,initializeUserInput})=>createElement(Renderer,{...section,userInput:userInput,handleUserInput:handleUserInput,initializeUserInput:initializeUserInput,ref:elem=>{if(elem){this.sectionRenderers[sectionIndex]=elem;}},key:sectionIndex,keypadElement:this.props.keypadElement,apiOptions:{...apiOptions,onFocusChange:(newFocusPath,oldFocusPath)=>{this._handleFocusChange(newFocusPath&&[sectionIndex].concat(newFocusPath),oldFocusPath&&[sectionIndex].concat(oldFocusPath));}},linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"article"),legacyPerseusLint:this.props.legacyPerseusLint,strings:this.context.strings})})},sectionIndex)});return jsxRuntimeExports.jsx("div",{className:classes,children:jsxRuntimeExports.jsx(DependenciesContext.Provider,{value:this.props.dependencies,children:sections})})}constructor(props){super(props),this.sectionRenderers=[],this._handleFocusChange=(newFocusPath,oldFocusPath)=>{if(newFocusPath){this._setCurrentFocus(newFocusPath);}else {this._onRendererBlur(oldFocusPath);}},this._setCurrentFocus=newFocusPath=>{const{keypadElement,apiOptions}=this.props;const{isMobile}=apiOptions;const prevFocusPath=this._currentFocus;this._currentFocus=newFocusPath;let didFocusInput=false;let focusedInput;if(this._currentFocus){const[sectionIndex,...focusPath]=this._currentFocus;const inputPaths=this.sectionRenderers[sectionIndex].getInputPaths();didFocusInput=inputPaths.some(inputPath=>{return Util.inputPathsEqual(inputPath,focusPath)});focusedInput=this.sectionRenderers[sectionIndex].getDOMNodeForPath(focusPath);}const{onFocusChange}=this.props.apiOptions;if(onFocusChange){setTimeout(()=>{const keypadDomNode=keypadElement?.getDOMNode();const keypadHeight=keypadDomNode&&didFocusInput?keypadDomNode.getBoundingClientRect().height:0;onFocusChange(this._currentFocus,prevFocusPath,keypadHeight,didFocusInput?focusedInput:null);},0);}if(keypadElement&&isMobile){if(didFocusInput){keypadElement.activate();}else {keypadElement.dismiss();}}},this._onRendererBlur=blurPath=>{const blurringFocusPath=this._currentFocus;if(!Util.inputPathsEqual(blurPath,blurringFocusPath)){return}setTimeout(()=>{if(Util.inputPathsEqual(this._currentFocus,blurringFocusPath)){this._setCurrentFocus(null);}});},this.blur=()=>{if(this._currentFocus){const[sectionIndex,...inputPath]=this._currentFocus;this.sectionRenderers[sectionIndex].blurPath(inputPath);}},this._sections=()=>{const sections=Array.isArray(this.props.json)?this.props.json:[this.props.json];if(getDependencies().JIPT.useJIPT){const paragraphs=[];for(const section of sections){JiptParagraphs.parseToArray(section.content).forEach(paragraph=>{paragraphs.push({...section,content:paragraph});});}return paragraphs}return sections};}}ArticleRenderer.contextType=PerseusI18nContext;ArticleRenderer.defaultProps={apiOptions:ApiOptions.defaults,linterContext:PerseusLinter.linterContextDefault};
2078
+ class ArticleRenderer extends React.Component{componentDidMount(){this._currentFocus=null;}shouldComponentUpdate(nextProps){return nextProps!==this.props}render(){const apiOptions={...ApiOptions.defaults,...this.props.apiOptions,isArticle:true};const classes=classNames$2({"framework-perseus":true,"perseus-article":true,[ClassNames.MOBILE]:apiOptions.isMobile});const sections=this._sections().map((section,sectionIndex)=>{return jsxRuntimeExports.jsx("div",{className:"clearfix",children:jsxRuntimeExports.jsx(UserInputManager,{widgets:section.widgets,problemNum:0,children:({userInput,handleUserInput,initializeUserInput})=>createElement(Renderer,{...section,userInput:userInput,handleUserInput:handleUserInput,initializeUserInput:initializeUserInput,ref:elem=>{if(elem){this.sectionRenderers[sectionIndex]=elem;}},key:sectionIndex,keypadElement:this.props.keypadElement,apiOptions:{...apiOptions,onFocusChange:(newFocusPath,oldFocusPath)=>{this._handleFocusChange(newFocusPath&&[sectionIndex].concat(newFocusPath),oldFocusPath&&[sectionIndex].concat(oldFocusPath));}},linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"article"),legacyPerseusLint:this.props.legacyPerseusLint,strings:this.context.strings})})},sectionIndex)});return jsxRuntimeExports.jsx("div",{className:classes,children:jsxRuntimeExports.jsx(DependenciesContext.Provider,{value:this.props.dependencies,children:sections})})}constructor(props){super(props),this.sectionRenderers=[],this._handleFocusChange=(newFocusPath,oldFocusPath)=>{if(newFocusPath){this._setCurrentFocus(newFocusPath);}else {this._onRendererBlur(oldFocusPath);}},this._setCurrentFocus=newFocusPath=>{const{keypadElement,apiOptions}=this.props;const{isMobile}=apiOptions;const prevFocusPath=this._currentFocus;this._currentFocus=newFocusPath;let didFocusInput=false;let focusedInput;if(this._currentFocus){const[sectionIndex,...focusPath]=this._currentFocus;const inputPaths=this.sectionRenderers[sectionIndex].getInputPaths();didFocusInput=inputPaths.some(inputPath=>{return Util.inputPathsEqual(inputPath,focusPath)});focusedInput=this.sectionRenderers[sectionIndex].getDOMNodeForPath(focusPath);}const{onFocusChange}=this.props.apiOptions;if(onFocusChange){setTimeout(()=>{const keypadDomNode=keypadElement?.getDOMNode();const keypadHeight=keypadDomNode&&didFocusInput?keypadDomNode.getBoundingClientRect().height:0;onFocusChange(this._currentFocus,prevFocusPath,keypadHeight,didFocusInput?focusedInput:null);},0);}if(keypadElement&&isMobile){if(didFocusInput){keypadElement.activate();}else {keypadElement.dismiss();}}},this._onRendererBlur=blurPath=>{const blurringFocusPath=this._currentFocus;if(!Util.inputPathsEqual(blurPath,blurringFocusPath)){return}setTimeout(()=>{if(Util.inputPathsEqual(this._currentFocus,blurringFocusPath)){this._setCurrentFocus(null);}});},this.blur=()=>{if(this._currentFocus){const[sectionIndex,...inputPath]=this._currentFocus;this.sectionRenderers[sectionIndex].blurPath(inputPath);}},this._sections=()=>{const sections=Array.isArray(this.props.json)?this.props.json:[this.props.json];if(getDependencies().JIPT.useJIPT){const paragraphs=[];for(const section of sections){JiptParagraphs.parseToArray(section.content).forEach(paragraph=>{paragraphs.push({...section,content:paragraph});});}return paragraphs}return sections};}}ArticleRenderer.contextType=PerseusI18nContext;ArticleRenderer.defaultProps={apiOptions:ApiOptions.defaults,linterContext:PerseusLinter.linterContextDefault};
2073
2079
 
2074
- class HintRenderer extends React.Component{render(){if(this.props.hint.placeholder){return jsxRuntimeExports.jsx("span",{})}const{apiOptions,className,hint,lastHint,lastRendered,pos,totalHints}=this.props;const{isMobile}=apiOptions;const classNames=classNames$1("hint",!isMobile&&"perseus-hint-renderer",isMobile&&css(styles$2.newHint),isMobile&&lastRendered&&css(styles$2.lastRenderedNewHint),lastHint&&"last-hint",lastRendered&&"last-rendered",className);const rendererApiOptions={...apiOptions,customKeypad:false};return jsxRuntimeExports.jsx(DependenciesContext.Provider,{value:this.props.dependencies,children:jsxRuntimeExports.jsxs("div",{className:classNames,tabIndex:"-1",children:[!apiOptions.isMobile&&jsxRuntimeExports.jsx("span",{className:"perseus-sr-only",children:this.context.strings.hintPos({pos:pos+1})}),!apiOptions.isMobile&&totalHints!=null&&pos!=null&&jsxRuntimeExports.jsx("span",{className:"perseus-hint-label",style:{display:"block",color:apiOptions.hintProgressColor},children:`${pos+1} / ${totalHints}`}),jsxRuntimeExports.jsx(UserInputManager,{widgets:hint.widgets,problemNum:0,children:({userInput,handleUserInput,initializeUserInput})=>jsxRuntimeExports.jsx(Renderer,{ref:this.rendererRef,userInput:userInput,handleUserInput:handleUserInput,initializeUserInput:initializeUserInput,widgets:hint.widgets,content:hint.content||"",images:hint.images,apiOptions:rendererApiOptions,findExternalWidgets:this.props.findExternalWidgets,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"hint"),strings:this.context.strings})})]})})}constructor(...args){super(...args),this.rendererRef=React.createRef(),this.getSerializedState=()=>{return this.rendererRef.current?.getSerializedState()};}}HintRenderer.contextType=PerseusI18nContext;HintRenderer.defaultProps={linterContext:PerseusLinter.linterContextDefault};const styles$2=StyleSheet.create({newHint:{marginBottom:1.5*baseUnitPx,borderLeftColor:gray97,borderLeftStyle:"solid",borderLeftWidth:hintBorderWidth,[mediaQueries.lgOrSmaller]:{paddingLeft:baseUnitPx},[mediaQueries.smOrSmaller]:{paddingLeft:0},":focus":{outline:"none"}},lastRenderedNewHint:{marginBottom:0,borderLeftColor:kaGreen}});
2080
+ class HintRenderer extends React.Component{render(){if(this.props.hint.placeholder){return jsxRuntimeExports.jsx("span",{})}const{apiOptions,className,hint,lastHint,lastRendered,pos,totalHints}=this.props;const{isMobile}=apiOptions;const classNames=classNames$2("hint",!isMobile&&"perseus-hint-renderer",isMobile&&css(styles$2.newHint),isMobile&&lastRendered&&css(styles$2.lastRenderedNewHint),lastHint&&"last-hint",lastRendered&&"last-rendered",className);const rendererApiOptions={...apiOptions,customKeypad:false};return jsxRuntimeExports.jsx(DependenciesContext.Provider,{value:this.props.dependencies,children:jsxRuntimeExports.jsxs("div",{className:classNames,tabIndex:"-1",children:[!apiOptions.isMobile&&jsxRuntimeExports.jsx("span",{className:"perseus-sr-only",children:this.context.strings.hintPos({pos:pos+1})}),!apiOptions.isMobile&&totalHints!=null&&pos!=null&&jsxRuntimeExports.jsx("span",{className:"perseus-hint-label",style:{display:"block",color:apiOptions.hintProgressColor},children:`${pos+1} / ${totalHints}`}),jsxRuntimeExports.jsx(UserInputManager,{widgets:hint.widgets,problemNum:0,children:({userInput,handleUserInput,initializeUserInput})=>jsxRuntimeExports.jsx(Renderer,{ref:this.rendererRef,userInput:userInput,handleUserInput:handleUserInput,initializeUserInput:initializeUserInput,widgets:hint.widgets,content:hint.content||"",images:hint.images,apiOptions:rendererApiOptions,findExternalWidgets:this.props.findExternalWidgets,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"hint"),strings:this.context.strings})})]})})}constructor(...args){super(...args),this.rendererRef=React.createRef(),this.getSerializedState=()=>{return this.rendererRef.current?.getSerializedState()};}}HintRenderer.contextType=PerseusI18nContext;HintRenderer.defaultProps={linterContext:PerseusLinter.linterContextDefault};const styles$2=StyleSheet.create({newHint:{marginBottom:1.5*baseUnitPx,borderLeftColor:gray97,borderLeftStyle:"solid",borderLeftWidth:hintBorderWidth,[mediaQueries.lgOrSmaller]:{paddingLeft:baseUnitPx},[mediaQueries.smOrSmaller]:{paddingLeft:0},":focus":{outline:"none"}},lastRenderedNewHint:{marginBottom:0,borderLeftColor:kaGreen}});
2075
2081
 
2076
- class HintsRenderer extends React.Component{componentDidMount(){this._cacheHintImages();}UNSAFE_componentWillReceiveProps(nextProps){if(nextProps.hintsVisible-this.props.hintsVisible>1){this.setState({isFinalHelpPage:true});}}componentDidUpdate(prevProps,prevState){if(!_.isEqual(prevProps.hints,this.props.hints)||prevProps.hintsVisible!==this.props.hintsVisible){this._cacheHintImages();}const prevHintsVisible=prevProps.hintsVisible||0;const nextHintsVisible=this.props.hintsVisible||0;if(prevHintsVisible<nextHintsVisible&&nextHintsVisible<=this.props.hints.length&&nextHintsVisible>=0){const pos=nextHintsVisible-1;const hint=this.props.hints[pos];if(hint?.placeholder){return}try{const ref=this.refs["hintRenderer"+pos];if(ref==null){return}const domNode=ReactDOM__default.findDOMNode(ref);if(domNode&&typeof domNode.focus==="function"){domNode.focus();}}catch(e){}}}render(){const apiOptions=this.getApiOptions();const hintsVisible=this._hintsVisible();const hints=[];const isFinalHelpPage=this.state.isFinalHelpPage;this.props.hints.slice(0,hintsVisible).forEach((hint,i)=>{const lastHint=i===this.props.hints.length-1&&!/\*\*/.test(hint.content);const lastRendered=i===hintsVisible-1;const renderer=jsxRuntimeExports.jsx(HintRenderer,{dependencies:this.props.dependencies,lastHint:lastHint,lastRendered:lastRendered,hint:hint,pos:i,totalHints:this.props.hints.length,ref:"hintRenderer"+i,apiOptions:apiOptions,findExternalWidgets:this.props.findExternalWidgets,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"hints["+i+"]")},"hintRenderer"+i);if(isFinalHelpPage){hints.push(renderer);}else {if(hint.replace&&hints.length>0){hints[hints.length-1]=renderer;}else {hints.push(renderer);}}});const{getAnotherHint}=apiOptions;const showGetAnotherHint=getAnotherHint&&hintsVisible>0&&hintsVisible<this.props.hints.length;const hintRatioCopy=`(${hintsVisible}/${this.props.hints.length})`;const classNames=classNames$1(this.props.className,apiOptions.isMobile&&hintsVisible>0&&css(styles$1.mobileHintStylesHintsRenderer));return jsxRuntimeExports.jsxs("div",{className:classNames,children:[apiOptions.isMobile&&hintsVisible>0&&jsxRuntimeExports.jsx("div",{className:css(styles$1.mobileHintStylesHintTitle,sharedStyles.responsiveLabel),children:this.context.strings.hints}),hints,showGetAnotherHint&&jsxRuntimeExports.jsxs("button",{rel:"button",className:css(styles$1.linkButton,styles$1.getAnotherHintButton,apiOptions.isMobile&&styles$1.mobileHintStylesGetAnotherHintButton),onClick:evt=>{evt.preventDefault();evt.stopPropagation();if(getAnotherHint){getAnotherHint();}},children:[jsxRuntimeExports.jsx("span",{className:css(styles$1.plusText,apiOptions.isMobile&&styles$1.mobileHintStylesPlusText),children:"+"}),jsxRuntimeExports.jsxs("span",{className:css(styles$1.getAnotherHintText),children:[this.context.strings.getAnotherHint," ",hintRatioCopy]})]})]})}constructor(...args){super(...args),this.state={isFinalHelpPage:false},this._hintsVisible=()=>{if(this.props.hintsVisible==null||this.props.hintsVisible===-1){return this.props.hints.length}return this.props.hintsVisible},this._cacheImagesInHint=hint=>{_.each(hint.images,(data,src)=>{const image=new Image;image.src=Util.getRealImageUrl(src);});},this._cacheHintImages=()=>{if(this._hintsVisible()>0){_.each(this.props.hints,this._cacheImagesInHint);}else if(this.props.hints.length>0){this._cacheImagesInHint(this.props.hints[0]);}},this.getApiOptions=()=>{return {...ApiOptions.defaults,...this.props.apiOptions,readOnly:false}},this.getSerializedState=()=>{return _.times(this._hintsVisible(),i=>{return this.refs["hintRenderer"+i].getSerializedState()})};}}HintsRenderer.contextType=PerseusI18nContext;HintsRenderer.defaultProps={linterContext:PerseusLinter.linterContextDefault};const hintIndentation=baseUnitPx+hintBorderWidth;const styles$1=StyleSheet.create({rendererMargins:{marginTop:baseUnitPx},linkButton:{cursor:"pointer",border:"none",backgroundColor:"transparent",fontSize:"100%",fontFamily:"inherit",fontWeight:"bold",color:kaGreen,padding:0,position:"relative"},plusText:{fontSize:20,position:"absolute",top:-3,left:0},getAnotherHintText:{marginLeft:16},mobileHintStylesHintsRenderer:{marginTop:4*baseUnitPx,border:`solid ${gray85}`,borderWidth:"1px 0 0 0",position:"relative",":before":{content:'""',display:"table",clear:"both"},":after":{content:'""',display:"table",clear:"both"}},mobileHintStylesHintTitle:{fontFamily:"inherit",fontStyle:"normal",fontWeight:"bold",color:gray17,paddingTop:baseUnitPx,paddingBottom:1.5*baseUnitPx,[mediaQueries.lgOrSmaller]:{paddingLeft:0},[mediaQueries.smOrSmaller]:{paddingLeft:hintIndentation}},getAnotherHintButton:{marginTop:1.5*baseUnitPx},mobileHintStylesGetAnotherHintButton:{[mediaQueries.lgOrSmaller]:{paddingLeft:0},[mediaQueries.smOrSmaller]:{paddingLeft:hintIndentation}},mobileHintStylesPlusText:{[mediaQueries.lgOrSmaller]:{left:0},[mediaQueries.smOrSmaller]:{left:hintIndentation}}});
2082
+ class HintsRenderer extends React.Component{componentDidMount(){this._cacheHintImages();}UNSAFE_componentWillReceiveProps(nextProps){if(nextProps.hintsVisible-this.props.hintsVisible>1){this.setState({isFinalHelpPage:true});}}componentDidUpdate(prevProps,prevState){if(!_.isEqual(prevProps.hints,this.props.hints)||prevProps.hintsVisible!==this.props.hintsVisible){this._cacheHintImages();}const prevHintsVisible=prevProps.hintsVisible||0;const nextHintsVisible=this.props.hintsVisible||0;if(prevHintsVisible<nextHintsVisible&&nextHintsVisible<=this.props.hints.length&&nextHintsVisible>=0){const pos=nextHintsVisible-1;const hint=this.props.hints[pos];if(hint?.placeholder){return}try{const ref=this.refs["hintRenderer"+pos];if(ref==null){return}const domNode=ReactDOM__default.findDOMNode(ref);if(domNode&&typeof domNode.focus==="function"){domNode.focus();}}catch(e){}}}render(){const apiOptions=this.getApiOptions();const hintsVisible=this._hintsVisible();const hints=[];const isFinalHelpPage=this.state.isFinalHelpPage;this.props.hints.slice(0,hintsVisible).forEach((hint,i)=>{const lastHint=i===this.props.hints.length-1&&!/\*\*/.test(hint.content);const lastRendered=i===hintsVisible-1;const renderer=jsxRuntimeExports.jsx(HintRenderer,{dependencies:this.props.dependencies,lastHint:lastHint,lastRendered:lastRendered,hint:hint,pos:i,totalHints:this.props.hints.length,ref:"hintRenderer"+i,apiOptions:apiOptions,findExternalWidgets:this.props.findExternalWidgets,linterContext:PerseusLinter.pushContextStack(this.props.linterContext,"hints["+i+"]")},"hintRenderer"+i);if(isFinalHelpPage){hints.push(renderer);}else {if(hint.replace&&hints.length>0){hints[hints.length-1]=renderer;}else {hints.push(renderer);}}});const{getAnotherHint}=apiOptions;const showGetAnotherHint=getAnotherHint&&hintsVisible>0&&hintsVisible<this.props.hints.length;const hintRatioCopy=`(${hintsVisible}/${this.props.hints.length})`;const classNames=classNames$2(this.props.className,apiOptions.isMobile&&hintsVisible>0&&css(styles$1.mobileHintStylesHintsRenderer));return jsxRuntimeExports.jsxs("div",{className:classNames,children:[apiOptions.isMobile&&hintsVisible>0&&jsxRuntimeExports.jsx("div",{className:css(styles$1.mobileHintStylesHintTitle,sharedStyles.responsiveLabel),children:this.context.strings.hints}),hints,showGetAnotherHint&&jsxRuntimeExports.jsxs("button",{rel:"button",className:css(styles$1.linkButton,styles$1.getAnotherHintButton,apiOptions.isMobile&&styles$1.mobileHintStylesGetAnotherHintButton),onClick:evt=>{evt.preventDefault();evt.stopPropagation();if(getAnotherHint){getAnotherHint();}},children:[jsxRuntimeExports.jsx("span",{className:css(styles$1.plusText,apiOptions.isMobile&&styles$1.mobileHintStylesPlusText),children:"+"}),jsxRuntimeExports.jsxs("span",{className:css(styles$1.getAnotherHintText),children:[this.context.strings.getAnotherHint," ",hintRatioCopy]})]})]})}constructor(...args){super(...args),this.state={isFinalHelpPage:false},this._hintsVisible=()=>{if(this.props.hintsVisible==null||this.props.hintsVisible===-1){return this.props.hints.length}return this.props.hintsVisible},this._cacheImagesInHint=hint=>{_.each(hint.images,(data,src)=>{const image=new Image;image.src=Util.getRealImageUrl(src);});},this._cacheHintImages=()=>{if(this._hintsVisible()>0){_.each(this.props.hints,this._cacheImagesInHint);}else if(this.props.hints.length>0){this._cacheImagesInHint(this.props.hints[0]);}},this.getApiOptions=()=>{return {...ApiOptions.defaults,...this.props.apiOptions,readOnly:false}},this.getSerializedState=()=>{return _.times(this._hintsVisible(),i=>{return this.refs["hintRenderer"+i].getSerializedState()})};}}HintsRenderer.contextType=PerseusI18nContext;HintsRenderer.defaultProps={linterContext:PerseusLinter.linterContextDefault};const hintIndentation=baseUnitPx+hintBorderWidth;const styles$1=StyleSheet.create({rendererMargins:{marginTop:baseUnitPx},linkButton:{cursor:"pointer",border:"none",backgroundColor:"transparent",fontSize:"100%",fontFamily:"inherit",fontWeight:"bold",color:kaGreen,padding:0,position:"relative"},plusText:{fontSize:20,position:"absolute",top:-3,left:0},getAnotherHintText:{marginLeft:16},mobileHintStylesHintsRenderer:{marginTop:4*baseUnitPx,border:`solid ${gray85}`,borderWidth:"1px 0 0 0",position:"relative",":before":{content:'""',display:"table",clear:"both"},":after":{content:'""',display:"table",clear:"both"}},mobileHintStylesHintTitle:{fontFamily:"inherit",fontStyle:"normal",fontWeight:"bold",color:gray17,paddingTop:baseUnitPx,paddingBottom:1.5*baseUnitPx,[mediaQueries.lgOrSmaller]:{paddingLeft:0},[mediaQueries.smOrSmaller]:{paddingLeft:hintIndentation}},getAnotherHintButton:{marginTop:1.5*baseUnitPx},mobileHintStylesGetAnotherHintButton:{[mediaQueries.lgOrSmaller]:{paddingLeft:0},[mediaQueries.smOrSmaller]:{paddingLeft:hintIndentation}},mobileHintStylesPlusText:{[mediaQueries.lgOrSmaller]:{left:0},[mediaQueries.smOrSmaller]:{left:hintIndentation}}});
2077
2083
 
2078
2084
  const defaultContext={onRendered:isRendered=>{}};const context=React.createContext(defaultContext);
2079
2085
 
@@ -2103,5 +2109,5 @@ const EditorJsonify={serialize:function(){return excludeDenylistKeys(this.props)
2103
2109
 
2104
2110
  const GrapherUtil={DEFAULT_GRAPHER_PROPS,chooseType,defaultPlotProps,getEquationString,typeToButton};
2105
2111
 
2106
- export { ApiOptions, ArticleRenderer, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, DependenciesContext, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, context as LoadingContext, Log, MathRenderingContext, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, serverItemRenderer as ServerItemRenderer, Table$1 as TableWidget, UserInputManager, Util, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, deriveUserInputFromSerializedState, displaySigFigs, excludeDenylistKeys, extractWidgetIds, generateTestCategorizerWidget, getAbsoluteValueCoords, getAngleCoords, getAnswerFromUserInput, getCircleCoords, getCorrectAnswerForWidgetId, getExponentialCoords, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getLogarithmCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getTangentCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetSubTypeByWidgetId, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, iconChevronDown, iconTrash, init, interactiveSizes$1 as interactiveSizes, isWidgetIdInContent, isWrongAnswerSupported, ItemVersion as itemVersion, libVersion, mathOnlyParser, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting, shouldHaveIndividualAnswer, usePerseusI18n, allWidgets as widgets };
2112
+ export { ApiOptions, ArticleRenderer, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, dependencies as Dependencies, DependenciesContext, EditorJsonify, Expression, GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, context as LoadingContext, Log, MathRenderingContext, Matrix$1 as MatrixWidget, NumericInput$1 as NumericInput, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, serverItemRenderer as ServerItemRenderer, Table$1 as TableWidget, UserInputManager, Util, widgets$1 as Widgets, apiVersion, bodyXsmallBold, components, containerSizeClass, contentHasWidgetType, convertWidgetNameToEnum, deriveUserInputFromSerializedState, displaySigFigs, excludeDenylistKeys, extractWidgetIds, generateTestCategorizerWidget, getAbsoluteValueCoords, getAngleCoords, getAnswerFromUserInput, getCircleCoords, getCorrectAnswerForWidgetId, getExponentialCoords, getImagesWithoutAltData, getInteractiveBoxFromSizeClass, getLineCoords, getLinearSystemCoords, getLogarithmCoords, getPointCoords, getPolygonCoords, getQuadraticCoords, getSegmentCoords, getSinusoidCoords, getTangentCoords, getValidWidgetIds, getVectorCoords, getWidgetFromWidgetMap, getWidgetSubTypeByWidgetId, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, iconChevronDown, iconTrash, init, interactiveSizes$1 as interactiveSizes, isWidgetIdInContent, isWrongAnswerSupported, ItemVersion as itemVersion, libVersion, mathOnlyParser, parseDataFromJSONP, preprocessTex, registerAllWidgetsForTesting, shouldHaveIndividualAnswer, usePerseusI18n, allWidgets as widgets };
2107
2113
  //# sourceMappingURL=index.js.map