@khanacademy/perseus 3.0.0 → 4.0.1

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 (1209) hide show
  1. package/.babelrc.js +1 -1
  2. package/.eslintrc.js +1 -0
  3. package/CHANGELOG.md +32 -0
  4. package/dist/__tests__/mock-asset-loading-widget.d.ts +59 -0
  5. package/dist/__tests__/mock-asset-loading-widget.js.flow +70 -0
  6. package/dist/__tests__/mock-widget.d.ts +30 -0
  7. package/dist/__tests__/mock-widget.js.flow +39 -0
  8. package/dist/__tests__/test-items/image-item.d.ts +14 -0
  9. package/dist/__tests__/test-items/image-item.js.flow +20 -0
  10. package/dist/__tests__/test-items/input-number-1-item.d.ts +9 -0
  11. package/dist/__tests__/test-items/input-number-1-item.js.flow +15 -0
  12. package/dist/__tests__/test-items/input-number-2-item.d.ts +9 -0
  13. package/dist/__tests__/test-items/input-number-2-item.js.flow +15 -0
  14. package/dist/__tests__/test-items/table-item.d.ts +9 -0
  15. package/dist/__tests__/test-items/table-item.js.flow +15 -0
  16. package/dist/a11y.d.ts +5 -0
  17. package/dist/a11y.js.flow +11 -0
  18. package/dist/all-widgets.d.ts +22 -0
  19. package/dist/all-widgets.js.flow +33 -0
  20. package/dist/article-renderer.d.ts +54 -0
  21. package/dist/article-renderer.js.flow +69 -0
  22. package/dist/asset-context.d.ts +14 -0
  23. package/dist/asset-context.js.flow +15 -0
  24. package/dist/basic-widgets.d.ts +22 -0
  25. package/dist/basic-widgets.js.flow +33 -0
  26. package/dist/components/button-group.d.ts +27 -0
  27. package/dist/components/button-group.js.flow +33 -0
  28. package/dist/components/constants.d.ts +7 -0
  29. package/dist/components/constants.js.flow +13 -0
  30. package/dist/components/fixed-to-responsive.d.ts +2 -0
  31. package/dist/components/fixed-to-responsive.js.flow +8 -0
  32. package/dist/components/graph.d.ts +44 -0
  33. package/dist/components/graph.js.flow +52 -0
  34. package/dist/components/graphie-classes.d.ts +13 -0
  35. package/dist/components/graphie-classes.js.flow +19 -0
  36. package/dist/components/graphie-movables.d.ts +16 -0
  37. package/dist/components/graphie-movables.js.flow +22 -0
  38. package/dist/components/graphie.d.ts +44 -0
  39. package/dist/components/graphie.js.flow +56 -0
  40. package/dist/components/highlighting/highlightable-content.d.ts +39 -0
  41. package/dist/components/highlighting/highlightable-content.js.flow +59 -0
  42. package/dist/components/highlighting/highlights.d.ts +30 -0
  43. package/dist/components/highlighting/highlights.js.flow +54 -0
  44. package/dist/components/highlighting/ranges.d.ts +42 -0
  45. package/dist/components/highlighting/ranges.js.flow +60 -0
  46. package/dist/components/highlighting/types.d.ts +64 -0
  47. package/dist/components/highlighting/types.js.flow +70 -0
  48. package/dist/components/highlighting/ui/highlight-renderer.d.ts +34 -0
  49. package/dist/components/highlighting/ui/highlight-renderer.js.flow +45 -0
  50. package/dist/components/highlighting/ui/highlight-set-renderer.d.ts +34 -0
  51. package/dist/components/highlighting/ui/highlight-set-renderer.js.flow +44 -0
  52. package/dist/components/highlighting/ui/highlight-tooltip.d.ts +16 -0
  53. package/dist/components/highlighting/ui/highlight-tooltip.js.flow +24 -0
  54. package/dist/components/highlighting/ui/highlighting-ui.d.ts +19 -0
  55. package/dist/components/highlighting/ui/highlighting-ui.js.flow +30 -0
  56. package/dist/components/highlighting/ui/selection-tracker.d.ts +47 -0
  57. package/dist/components/highlighting/ui/selection-tracker.js.flow +56 -0
  58. package/dist/components/highlighting/ui/types.d.ts +13 -0
  59. package/dist/components/highlighting/ui/types.js.flow +22 -0
  60. package/dist/components/highlighting/ui/util.d.ts +28 -0
  61. package/dist/components/highlighting/ui/util.js.flow +42 -0
  62. package/dist/components/highlighting/word-indexer.d.ts +28 -0
  63. package/dist/components/highlighting/word-indexer.js.flow +21 -0
  64. package/dist/components/hud.d.ts +15 -0
  65. package/dist/components/hud.js.flow +21 -0
  66. package/dist/components/icon.d.ts +56 -0
  67. package/dist/components/icon.js.flow +31 -0
  68. package/dist/components/image-loader.d.ts +52 -0
  69. package/dist/components/image-loader.js.flow +49 -0
  70. package/dist/components/info-tip/info-tip.d.ts +14 -0
  71. package/dist/components/info-tip/info-tip.js.flow +20 -0
  72. package/dist/components/info-tip.d.ts +18 -0
  73. package/dist/components/info-tip.js.flow +18 -0
  74. package/dist/components/inline-icon.d.ts +10 -0
  75. package/dist/components/inline-icon.js.flow +16 -0
  76. package/dist/components/input-with-examples.d.ts +52 -0
  77. package/dist/components/input-with-examples.js.flow +58 -0
  78. package/dist/components/lint.d.ts +23 -0
  79. package/dist/components/lint.js.flow +29 -0
  80. package/dist/components/math-input.d.ts +40 -0
  81. package/dist/components/math-input.js.flow +46 -0
  82. package/dist/components/math-output.d.ts +23 -0
  83. package/dist/components/math-output.js.flow +29 -0
  84. package/dist/components/multi-button-group.d.ts +29 -0
  85. package/dist/components/multi-button-group.js.flow +35 -0
  86. package/dist/components/number-input.d.ts +34 -0
  87. package/dist/components/number-input.js.flow +42 -0
  88. package/dist/components/prop-check-box.d.ts +2 -0
  89. package/dist/components/prop-check-box.js.flow +8 -0
  90. package/dist/components/range-input.d.ts +14 -0
  91. package/dist/components/range-input.js.flow +20 -0
  92. package/dist/components/simple-keypad-input.d.ts +19 -0
  93. package/dist/components/simple-keypad-input.js.flow +16 -0
  94. package/dist/components/sortable.d.ts +68 -0
  95. package/dist/components/sortable.js.flow +78 -0
  96. package/dist/components/stub-tag-editor.d.ts +25 -0
  97. package/dist/components/stub-tag-editor.js.flow +18 -0
  98. package/dist/components/svg-image.d.ts +74 -0
  99. package/dist/components/svg-image.js.flow +83 -0
  100. package/dist/components/tex-buttons.d.ts +20 -0
  101. package/dist/components/tex-buttons.js.flow +26 -0
  102. package/dist/components/tex.d.ts +25 -0
  103. package/dist/components/tex.js.flow +25 -0
  104. package/dist/components/text-input.d.ts +33 -0
  105. package/dist/components/text-input.js.flow +39 -0
  106. package/dist/components/text-list-editor.d.ts +16 -0
  107. package/dist/components/text-list-editor.js.flow +22 -0
  108. package/dist/components/tooltip.d.ts +32 -0
  109. package/dist/components/tooltip.js.flow +38 -0
  110. package/dist/components/visibility-observer/create-visibility-observer.d.ts +34 -0
  111. package/dist/components/visibility-observer/create-visibility-observer.js.flow +23 -0
  112. package/dist/components/visibility-observer/get-scroll-parent.d.ts +1 -0
  113. package/dist/components/visibility-observer/get-scroll-parent.js.flow +7 -0
  114. package/dist/components/visibility-observer/visibility-observer.d.ts +31 -0
  115. package/dist/components/visibility-observer/visibility-observer.js.flow +36 -0
  116. package/dist/components/zoomable-tex.d.ts +18 -0
  117. package/dist/components/zoomable-tex.js.flow +18 -0
  118. package/dist/components/zoomable.d.ts +64 -0
  119. package/dist/components/zoomable.js.flow +70 -0
  120. package/dist/components.d.ts +16 -0
  121. package/dist/components.js.flow +22 -0
  122. package/dist/definition-context.d.ts +21 -0
  123. package/dist/definition-context.js.flow +25 -0
  124. package/dist/dependencies.d.ts +3 -0
  125. package/dist/dependencies.js.flow +9 -0
  126. package/dist/error-boundary.d.ts +13 -0
  127. package/dist/error-boundary.js.flow +21 -0
  128. package/dist/es/index.js +17390 -21159
  129. package/dist/es/index.js.map +1 -1
  130. package/dist/extra-widgets.d.ts +22 -0
  131. package/dist/extra-widgets.js.flow +33 -0
  132. package/dist/hint-renderer.d.ts +23 -0
  133. package/dist/hint-renderer.js.flow +32 -0
  134. package/dist/hints-renderer.d.ts +29 -0
  135. package/dist/hints-renderer.js.flow +41 -0
  136. package/dist/icon-paths.d.ts +117 -0
  137. package/dist/icon-paths.js.flow +123 -0
  138. package/dist/index.d.ts +103 -2
  139. package/dist/index.js +12443 -15871
  140. package/dist/index.js.flow +133 -2
  141. package/dist/index.js.map +1 -1
  142. package/dist/init.d.ts +14 -0
  143. package/dist/init.js.flow +20 -0
  144. package/dist/interaction-tracker.d.ts +23 -0
  145. package/dist/interaction-tracker.js.flow +33 -0
  146. package/dist/interactive2/__tests__/movable-mock.d.ts +5 -0
  147. package/dist/interactive2/__tests__/movable-mock.js.flow +11 -0
  148. package/dist/interactive2/interactive-util.d.ts +31 -0
  149. package/dist/interactive2/interactive-util.js.flow +50 -0
  150. package/dist/interactive2/movable-helper-methods.d.ts +9 -0
  151. package/dist/interactive2/movable-helper-methods.js.flow +15 -0
  152. package/dist/interactive2/movable-line-options.d.ts +34 -0
  153. package/dist/interactive2/movable-line-options.js.flow +44 -0
  154. package/dist/interactive2/movable-line.d.ts +2 -0
  155. package/dist/interactive2/movable-line.js.flow +8 -0
  156. package/dist/interactive2/movable-point-options.d.ts +34 -0
  157. package/dist/interactive2/movable-point-options.js.flow +44 -0
  158. package/dist/interactive2/movable-point.d.ts +2 -0
  159. package/dist/interactive2/movable-point.js.flow +8 -0
  160. package/dist/interactive2/movable-polygon-options.d.ts +40 -0
  161. package/dist/interactive2/movable-polygon-options.js.flow +47 -0
  162. package/dist/interactive2/movable-polygon.d.ts +2 -0
  163. package/dist/interactive2/movable-polygon.js.flow +8 -0
  164. package/dist/interactive2/movable.d.ts +2 -0
  165. package/dist/interactive2/movable.js.flow +8 -0
  166. package/dist/interactive2/objective_.d.ts +13 -0
  167. package/dist/interactive2/objective_.js.flow +25 -0
  168. package/dist/interactive2/types.d.ts +2 -0
  169. package/dist/interactive2/types.js.flow +8 -0
  170. package/dist/interactive2/wrapped-defaults.d.ts +5 -0
  171. package/dist/interactive2/wrapped-defaults.js.flow +11 -0
  172. package/dist/interactive2/wrapped-ellipse.d.ts +3 -0
  173. package/dist/interactive2/wrapped-ellipse.js.flow +14 -0
  174. package/dist/interactive2/wrapped-line.d.ts +3 -0
  175. package/dist/interactive2/wrapped-line.js.flow +14 -0
  176. package/dist/interactive2/wrapped-path.d.ts +3 -0
  177. package/dist/interactive2/wrapped-path.js.flow +13 -0
  178. package/dist/interactive2.d.ts +10 -0
  179. package/dist/interactive2.js.flow +16 -0
  180. package/dist/item-renderer.d.ts +117 -0
  181. package/dist/item-renderer.js.flow +136 -0
  182. package/dist/item-version.d.ts +4 -0
  183. package/dist/item-version.js.flow +11 -0
  184. package/dist/jipt-paragraphs.d.ts +8 -0
  185. package/dist/jipt-paragraphs.js.flow +14 -0
  186. package/dist/keypad-context.d.ts +20 -0
  187. package/dist/keypad-context.js.flow +18 -0
  188. package/dist/loading-context.d.ts +14 -0
  189. package/dist/loading-context.js.flow +12 -0
  190. package/dist/logging/log.d.ts +55 -0
  191. package/dist/logging/log.js.flow +62 -0
  192. package/dist/mixins/changeable.d.ts +30 -0
  193. package/dist/mixins/changeable.js.flow +33 -0
  194. package/dist/mixins/editor-jsonify.d.ts +4 -0
  195. package/dist/mixins/editor-jsonify.js.flow +10 -0
  196. package/dist/mixins/provide-keypad.d.ts +36 -0
  197. package/dist/mixins/provide-keypad.js.flow +33 -0
  198. package/dist/mixins/widget-jsonify-deprecated.d.ts +5 -0
  199. package/dist/mixins/widget-jsonify-deprecated.js.flow +11 -0
  200. package/dist/mixins/widget-prop-denylist.d.ts +14 -0
  201. package/dist/mixins/widget-prop-denylist.js.flow +20 -0
  202. package/dist/multi-items/item-types.d.ts +32 -0
  203. package/dist/multi-items/items.d.ts +53 -0
  204. package/dist/multi-items/items.js.flow +66 -0
  205. package/dist/multi-items/multi-renderer.d.ts +152 -0
  206. package/dist/multi-items/multi-renderer.js.flow +213 -0
  207. package/dist/multi-items/prop-type-builders.d.ts +20 -0
  208. package/dist/multi-items/prop-type-builders.js.flow +22 -0
  209. package/dist/multi-items/shape-types.d.ts +47 -0
  210. package/dist/multi-items/shape-types.js.flow +60 -0
  211. package/dist/multi-items/shapes.d.ts +23 -0
  212. package/dist/multi-items/shapes.js.flow +25 -0
  213. package/dist/multi-items/tree-types.d.ts +34 -0
  214. package/dist/multi-items/tree-types.js.flow +45 -0
  215. package/dist/multi-items/trees.d.ts +146 -0
  216. package/dist/multi-items/trees.js.flow +147 -0
  217. package/dist/multi-items.d.ts +73 -0
  218. package/dist/multi-items.js.flow +36 -0
  219. package/dist/perseus-api.d.ts +128 -0
  220. package/dist/perseus-api.js.flow +139 -0
  221. package/dist/perseus-env.d.ts +3 -0
  222. package/dist/perseus-env.js.flow +6 -0
  223. package/dist/perseus-error.d.ts +11 -0
  224. package/dist/perseus-error.js.flow +16 -0
  225. package/dist/perseus-markdown.d.ts +11 -0
  226. package/dist/perseus-markdown.js.flow +20 -0
  227. package/dist/perseus-types.d.ts +776 -0
  228. package/dist/perseus-types.js.flow +1032 -0
  229. package/dist/question-paragraph.d.ts +11 -0
  230. package/dist/question-paragraph.js.flow +17 -0
  231. package/dist/renderability.d.ts +10 -0
  232. package/dist/renderability.js.flow +19 -0
  233. package/dist/renderer.d.ts +254 -0
  234. package/dist/renderer.js.flow +327 -0
  235. package/dist/server-item-renderer.d.ts +113 -0
  236. package/dist/server-item-renderer.js.flow +136 -0
  237. package/dist/sigfigs.d.ts +32 -0
  238. package/dist/sigfigs.js.flow +41 -0
  239. package/dist/styles/constants.d.ts +56 -0
  240. package/dist/styles/constants.js.flow +62 -0
  241. package/dist/styles/global-constants.d.ts +342 -0
  242. package/dist/styles/global-constants.js.flow +348 -0
  243. package/dist/styles/global-styles.d.ts +47 -0
  244. package/dist/styles/global-styles.js.flow +53 -0
  245. package/dist/styles/media-queries.d.ts +29 -0
  246. package/dist/styles/media-queries.js.flow +35 -0
  247. package/dist/styles/shared.d.ts +3 -0
  248. package/dist/styles/shared.js.flow +9 -0
  249. package/dist/tex-wrangler.d.ts +7 -0
  250. package/dist/tex-wrangler.js.flow +13 -0
  251. package/dist/translation-linter.d.ts +21 -0
  252. package/dist/translation-linter.js.flow +32 -0
  253. package/dist/traversal.d.ts +15 -0
  254. package/dist/traversal.js.flow +26 -0
  255. package/dist/types.d.ts +262 -0
  256. package/dist/types.js.flow +337 -0
  257. package/dist/util/a11y.d.ts +3 -0
  258. package/dist/util/a11y.js.flow +9 -0
  259. package/dist/util/answer-types.d.ts +34 -0
  260. package/dist/util/answer-types.js.flow +49 -0
  261. package/dist/util/colors.d.ts +89 -0
  262. package/dist/util/colors.js.flow +95 -0
  263. package/dist/util/fix-passage-refs.d.ts +2 -0
  264. package/dist/util/fix-passage-refs.js.flow +8 -0
  265. package/dist/util/graph-utils.d.ts +12 -0
  266. package/dist/util/graph-utils.js.flow +9 -0
  267. package/dist/util/graphie.d.ts +2 -0
  268. package/dist/util/graphie.js.flow +8 -0
  269. package/dist/util/interactive.d.ts +9 -0
  270. package/dist/util/interactive.js.flow +9 -0
  271. package/dist/util/katex-preprocess.d.ts +6 -0
  272. package/dist/util/katex-preprocess.js.flow +12 -0
  273. package/dist/util/math.d.ts +28 -0
  274. package/dist/util/math.js.flow +62 -0
  275. package/dist/util/mobile-native-utils.d.ts +7 -0
  276. package/dist/util/mobile-native-utils.js.flow +12 -0
  277. package/dist/util/react-render.d.ts +2 -0
  278. package/dist/util/react-render.js.flow +12 -0
  279. package/dist/util/register-all-widgets-for-testing.d.ts +1 -0
  280. package/dist/util/register-all-widgets-for-testing.js.flow +7 -0
  281. package/dist/util/scroll-utils.d.ts +10 -0
  282. package/dist/util/scroll-utils.js.flow +19 -0
  283. package/dist/util/sizing-utils.d.ts +9 -0
  284. package/dist/util/sizing-utils.js.flow +20 -0
  285. package/dist/util/tex.d.ts +6 -0
  286. package/dist/util/tex.js.flow +17 -0
  287. package/dist/util/url-utils.d.ts +6 -0
  288. package/dist/util/url-utils.js.flow +11 -0
  289. package/dist/util.d.ts +247 -0
  290. package/dist/util.js.flow +320 -0
  291. package/dist/version.d.ts +11 -0
  292. package/dist/version.js.flow +17 -0
  293. package/dist/widget-container.d.ts +27 -0
  294. package/dist/widget-container.js.flow +33 -0
  295. package/dist/widgets/__tests__/renderQuestion.d.ts +13 -0
  296. package/dist/widgets/__tests__/renderQuestion.js.flow +28 -0
  297. package/dist/widgets/categorizer.d.ts +55 -0
  298. package/dist/widgets/categorizer.js.flow +70 -0
  299. package/dist/widgets/cs-program.d.ts +48 -0
  300. package/dist/widgets/cs-program.js.flow +54 -0
  301. package/dist/widgets/definition.d.ts +42 -0
  302. package/dist/widgets/definition.js.flow +57 -0
  303. package/dist/widgets/dropdown.d.ts +51 -0
  304. package/dist/widgets/dropdown.js.flow +63 -0
  305. package/dist/widgets/explanation.d.ts +49 -0
  306. package/dist/widgets/explanation.js.flow +58 -0
  307. package/dist/widgets/expression.d.ts +90 -0
  308. package/dist/widgets/expression.js.flow +115 -0
  309. package/dist/widgets/graded-group-answer-bar.d.ts +13 -0
  310. package/dist/widgets/graded-group-answer-bar.js.flow +24 -0
  311. package/dist/widgets/graded-group-set.d.ts +52 -0
  312. package/dist/widgets/graded-group-set.js.flow +65 -0
  313. package/dist/widgets/graded-group.d.ts +66 -0
  314. package/dist/widgets/graded-group.js.flow +78 -0
  315. package/dist/widgets/grapher/util.d.ts +19 -0
  316. package/dist/widgets/grapher/util.js.flow +38 -0
  317. package/dist/widgets/grapher.d.ts +63 -0
  318. package/dist/widgets/grapher.js.flow +93 -0
  319. package/dist/widgets/group.d.ts +51 -0
  320. package/dist/widgets/group.js.flow +60 -0
  321. package/dist/widgets/iframe.d.ts +52 -0
  322. package/dist/widgets/iframe.js.flow +53 -0
  323. package/dist/widgets/image.d.ts +59 -0
  324. package/dist/widgets/image.js.flow +73 -0
  325. package/dist/widgets/input-number.d.ts +75 -0
  326. package/dist/widgets/input-number.js.flow +94 -0
  327. package/dist/widgets/interaction/arrow-picker.d.ts +12 -0
  328. package/dist/widgets/interaction/arrow-picker.js.flow +21 -0
  329. package/dist/widgets/interaction/color-picker.d.ts +14 -0
  330. package/dist/widgets/interaction/color-picker.js.flow +23 -0
  331. package/dist/widgets/interaction/dash-picker.d.ts +12 -0
  332. package/dist/widgets/interaction/dash-picker.js.flow +21 -0
  333. package/dist/widgets/interaction/element-container.d.ts +21 -0
  334. package/dist/widgets/interaction/element-container.js.flow +33 -0
  335. package/dist/widgets/interaction.d.ts +51 -0
  336. package/dist/widgets/interaction.js.flow +60 -0
  337. package/dist/widgets/interactive-graph.d.ts +180 -0
  338. package/dist/widgets/interactive-graph.js.flow +242 -0
  339. package/dist/widgets/label-image/answer-choices.d.ts +51 -0
  340. package/dist/widgets/label-image/answer-choices.js.flow +55 -0
  341. package/dist/widgets/label-image/marker.d.ts +28 -0
  342. package/dist/widgets/label-image/marker.js.flow +33 -0
  343. package/dist/widgets/label-image/types.d.ts +14 -0
  344. package/dist/widgets/label-image/types.js.flow +23 -0
  345. package/dist/widgets/label-image.d.ts +113 -0
  346. package/dist/widgets/label-image.js.flow +142 -0
  347. package/dist/widgets/lights-puzzle.d.ts +47 -0
  348. package/dist/widgets/lights-puzzle.js.flow +58 -0
  349. package/dist/widgets/matcher.d.ts +82 -0
  350. package/dist/widgets/matcher.js.flow +101 -0
  351. package/dist/widgets/matrix.d.ts +65 -0
  352. package/dist/widgets/matrix.js.flow +80 -0
  353. package/dist/widgets/measurer.d.ts +21 -0
  354. package/dist/widgets/measurer.js.flow +30 -0
  355. package/dist/widgets/molecule/molecule-drawing.d.ts +5 -0
  356. package/dist/widgets/molecule/molecule-drawing.js.flow +11 -0
  357. package/dist/widgets/molecule/molecule-layout.d.ts +82 -0
  358. package/dist/widgets/molecule/molecule-layout.js.flow +96 -0
  359. package/dist/widgets/molecule/smiles-parser.d.ts +19 -0
  360. package/dist/widgets/molecule/smiles-parser.js.flow +26 -0
  361. package/dist/widgets/molecule.d.ts +51 -0
  362. package/dist/widgets/molecule.js.flow +60 -0
  363. package/dist/widgets/number-line.d.ts +90 -0
  364. package/dist/widgets/number-line.js.flow +102 -0
  365. package/dist/widgets/numeric-input.d.ts +80 -0
  366. package/dist/widgets/numeric-input.js.flow +109 -0
  367. package/dist/widgets/orderer.d.ts +69 -0
  368. package/dist/widgets/orderer.js.flow +81 -0
  369. package/dist/widgets/passage/passage-markdown.d.ts +22 -0
  370. package/dist/widgets/passage/passage-markdown.js.flow +33 -0
  371. package/dist/widgets/passage-ref-target.d.ts +41 -0
  372. package/dist/widgets/passage-ref-target.js.flow +52 -0
  373. package/dist/widgets/passage-ref.d.ts +65 -0
  374. package/dist/widgets/passage-ref.js.flow +85 -0
  375. package/dist/widgets/passage.d.ts +125 -0
  376. package/dist/widgets/passage.js.flow +157 -0
  377. package/dist/widgets/plotter.d.ts +68 -0
  378. package/dist/widgets/plotter.js.flow +78 -0
  379. package/dist/widgets/radio/base-radio.d.ts +38 -0
  380. package/dist/widgets/radio/base-radio.js.flow +44 -0
  381. package/dist/widgets/radio/choice-a11y-text.d.ts +2 -0
  382. package/dist/widgets/radio/choice-a11y-text.js.flow +14 -0
  383. package/dist/widgets/radio/choice-icon/choice-icon.d.ts +16 -0
  384. package/dist/widgets/radio/choice-icon/choice-icon.js.flow +22 -0
  385. package/dist/widgets/radio/choice-icon/cross-out-line.d.ts +9 -0
  386. package/dist/widgets/radio/choice-icon/cross-out-line.js.flow +16 -0
  387. package/dist/widgets/radio/choice-icon/shared-styles.d.ts +5 -0
  388. package/dist/widgets/radio/choice-icon/shared-styles.js.flow +11 -0
  389. package/dist/widgets/radio/choice-none-above.d.ts +6 -0
  390. package/dist/widgets/radio/choice-none-above.js.flow +16 -0
  391. package/dist/widgets/radio/choice.d.ts +23 -0
  392. package/dist/widgets/radio/choice.js.flow +32 -0
  393. package/dist/widgets/radio/choice.jsx.fixture.d.ts +1 -0
  394. package/dist/widgets/radio/choice.jsx.fixture.js.flow +7 -0
  395. package/dist/widgets/radio/focus-ring.d.ts +12 -0
  396. package/dist/widgets/radio/focus-ring.js.flow +15 -0
  397. package/dist/widgets/radio/option-status.d.ts +17 -0
  398. package/dist/widgets/radio/option-status.js.flow +16 -0
  399. package/dist/widgets/radio/util.d.ts +6 -0
  400. package/dist/widgets/radio/util.js.flow +12 -0
  401. package/dist/widgets/radio/widget.d.ts +62 -0
  402. package/dist/widgets/radio/widget.js.flow +80 -0
  403. package/dist/widgets/radio.d.ts +22 -0
  404. package/dist/widgets/radio.js.flow +31 -0
  405. package/dist/widgets/reaction-diagram.d.ts +37 -0
  406. package/dist/widgets/reaction-diagram.js.flow +46 -0
  407. package/dist/widgets/sequence.d.ts +79 -0
  408. package/dist/widgets/sequence.js.flow +100 -0
  409. package/dist/widgets/simulator.d.ts +89 -0
  410. package/dist/widgets/simulator.js.flow +106 -0
  411. package/dist/widgets/sorter.d.ts +82 -0
  412. package/dist/widgets/sorter.js.flow +101 -0
  413. package/dist/widgets/table.d.ts +56 -0
  414. package/dist/widgets/table.js.flow +67 -0
  415. package/dist/widgets/transformer.d.ts +94 -0
  416. package/dist/widgets/transformer.js.flow +127 -0
  417. package/dist/widgets/unit.d.ts +77 -0
  418. package/dist/widgets/unit.js.flow +93 -0
  419. package/dist/widgets/video-transcript-link.d.ts +12 -0
  420. package/dist/widgets/video-transcript-link.js.flow +15 -0
  421. package/dist/widgets/video.d.ts +48 -0
  422. package/dist/widgets/video.js.flow +57 -0
  423. package/dist/widgets.d.ts +73 -0
  424. package/dist/widgets.js.flow +108 -0
  425. package/dist/zoom.d.ts +1 -0
  426. package/dist/zoom.js.flow +7 -0
  427. package/package.json +9 -9
  428. package/src/__stories__/hints-renderer.stories.tsx +58 -0
  429. package/src/__stories__/item-renderer.stories.tsx +29 -0
  430. package/src/__stories__/server-item-renderer.stories.tsx +29 -0
  431. package/src/__testdata__/graphie.testdata.ts +51 -0
  432. package/src/__testdata__/item-renderer.testdata.ts +160 -0
  433. package/src/__testdata__/renderer.testdata.ts +122 -0
  434. package/src/__tests__/a11y.test.ts +303 -0
  435. package/src/__tests__/item-renderer.test.tsx +891 -0
  436. package/src/__tests__/jipt-paragraphs.test.ts +32 -0
  437. package/src/__tests__/mock-asset-loading-widget.tsx +56 -0
  438. package/src/__tests__/mock-widget.tsx +33 -0
  439. package/src/__tests__/perseus-markdown.test.ts +764 -0
  440. package/src/__tests__/renderability.test.ts +480 -0
  441. package/src/__tests__/renderer-api.test.tsx +328 -0
  442. package/src/__tests__/renderer.test.tsx +1802 -0
  443. package/src/__tests__/server-item-renderer.test.tsx +518 -0
  444. package/src/__tests__/test-items/image-item.ts +24 -0
  445. package/src/__tests__/test-items/input-number-1-item.ts +26 -0
  446. package/src/__tests__/test-items/input-number-2-item.ts +38 -0
  447. package/src/__tests__/test-items/table-item.ts +29 -0
  448. package/src/__tests__/tex-wrangler.test.ts +82 -0
  449. package/src/__tests__/util.test.ts +40 -0
  450. package/src/a11y.ts +45 -0
  451. package/src/all-widgets.ts +9 -0
  452. package/src/article-renderer.tsx +215 -0
  453. package/src/asset-context.ts +30 -0
  454. package/src/basic-widgets.ts +16 -0
  455. package/src/components/__stories__/button-group.stories.tsx +59 -0
  456. package/src/components/__stories__/fixed-to-responsive.stories.tsx +143 -0
  457. package/src/components/__stories__/graph.stories.tsx +30 -0
  458. package/src/components/__stories__/graphie.stories.tsx +35 -0
  459. package/src/components/__stories__/hud.stories.tsx +39 -0
  460. package/src/components/__stories__/icon.stories.tsx +63 -0
  461. package/src/components/__stories__/image-loader.stories.tsx +62 -0
  462. package/src/components/__stories__/info-tip.stories.tsx +35 -0
  463. package/src/components/__stories__/inline-icon.stories.tsx +44 -0
  464. package/src/components/__stories__/input-with-examples.stories.tsx +57 -0
  465. package/src/components/__stories__/lint.stories.tsx +51 -0
  466. package/src/components/__stories__/math-input.stories.tsx +34 -0
  467. package/src/components/__stories__/math-output.stories.tsx +25 -0
  468. package/src/components/__stories__/multi-button-group.stories.tsx +70 -0
  469. package/src/components/__stories__/number-input.stories.tsx +43 -0
  470. package/src/components/__stories__/prop-check-box.stories.tsx +65 -0
  471. package/src/components/__stories__/range-input.stories.tsx +31 -0
  472. package/src/components/__stories__/simple-keypad-input.stories.tsx +29 -0
  473. package/src/components/__stories__/sortable.stories.tsx +77 -0
  474. package/src/components/__stories__/stub-tag-editor.stories.tsx +49 -0
  475. package/src/components/__stories__/svg-image.stories.tsx +82 -0
  476. package/src/components/__stories__/tex.stories.tsx +25 -0
  477. package/src/components/__stories__/text-buttons.stories.tsx +66 -0
  478. package/src/components/__stories__/text-input.stories.tsx +39 -0
  479. package/src/components/__stories__/text-list-editor.stories.tsx +34 -0
  480. package/src/components/__stories__/zoomable-tex.stories.tsx +39 -0
  481. package/src/components/__stories__/zoomable.stories.tsx +43 -0
  482. package/src/components/__testdata__/sorter.testdata.ts +19 -0
  483. package/src/components/__tests__/number-input.test.tsx +128 -0
  484. package/src/components/__tests__/sortable.test.tsx +234 -0
  485. package/src/components/__tests__/sorter.test.tsx +111 -0
  486. package/src/components/__tests__/zoomable.test.tsx +399 -0
  487. package/src/components/button-group.tsx +130 -0
  488. package/src/components/constants.ts +14 -0
  489. package/src/components/fixed-to-responsive.tsx +165 -0
  490. package/src/components/graph.tsx +421 -0
  491. package/src/components/graphie-classes.ts +153 -0
  492. package/src/components/graphie-movables.ts +338 -0
  493. package/src/components/graphie.tsx +357 -0
  494. package/src/components/highlighting/highlightable-content.tsx +243 -0
  495. package/src/components/highlighting/highlights.ts +207 -0
  496. package/src/components/highlighting/ranges.ts +304 -0
  497. package/src/components/highlighting/types.ts +73 -0
  498. package/src/components/highlighting/ui/highlight-renderer.tsx +175 -0
  499. package/src/components/highlighting/ui/highlight-set-renderer.tsx +188 -0
  500. package/src/components/highlighting/ui/highlight-tooltip.tsx +141 -0
  501. package/src/components/highlighting/ui/highlighting-ui.tsx +153 -0
  502. package/src/components/highlighting/ui/selection-tracker.tsx +189 -0
  503. package/src/components/highlighting/ui/types.ts +21 -0
  504. package/src/components/highlighting/ui/util.ts +165 -0
  505. package/src/components/highlighting/word-indexer.tsx +156 -0
  506. package/src/components/hud.tsx +178 -0
  507. package/src/components/icon.tsx +140 -0
  508. package/src/components/image-loader.tsx +176 -0
  509. package/src/components/info-tip/info-tip.tsx +144 -0
  510. package/src/components/info-tip.tsx +40 -0
  511. package/src/components/inline-icon.tsx +77 -0
  512. package/src/components/input-with-examples.tsx +249 -0
  513. package/src/components/lint.tsx +453 -0
  514. package/src/components/math-input.tsx +299 -0
  515. package/src/components/math-output.tsx +128 -0
  516. package/src/components/multi-button-group.tsx +135 -0
  517. package/src/components/number-input.tsx +250 -0
  518. package/src/components/prop-check-box.tsx +80 -0
  519. package/src/components/range-input.tsx +60 -0
  520. package/src/components/simple-keypad-input.tsx +88 -0
  521. package/src/components/sortable.tsx +914 -0
  522. package/src/components/stub-tag-editor.tsx +53 -0
  523. package/src/components/svg-image.tsx +777 -0
  524. package/src/components/tex-buttons.tsx +321 -0
  525. package/src/components/tex.tsx +67 -0
  526. package/src/components/text-input.tsx +128 -0
  527. package/src/components/text-list-editor.tsx +169 -0
  528. package/src/components/tooltip.tsx +392 -0
  529. package/src/components/visibility-observer/__stories__/visibility-observer.stories.tsx +143 -0
  530. package/src/components/visibility-observer/create-visibility-observer.ts +108 -0
  531. package/src/components/visibility-observer/get-scroll-parent.ts +46 -0
  532. package/src/components/visibility-observer/visibility-observer.tsx +124 -0
  533. package/src/components/zoomable-tex.tsx +78 -0
  534. package/src/components/zoomable.tsx +300 -0
  535. package/src/components.ts +16 -0
  536. package/src/definition-context.tsx +60 -0
  537. package/src/dependencies.ts +21 -0
  538. package/src/error-boundary.tsx +50 -0
  539. package/src/extra-widgets.ts +75 -0
  540. package/src/hint-renderer.tsx +149 -0
  541. package/src/hints-renderer.tsx +377 -0
  542. package/src/icon-paths.ts +119 -0
  543. package/src/index.ts +158 -0
  544. package/src/init.ts +54 -0
  545. package/src/interaction-tracker.ts +61 -0
  546. package/src/interactive2/__tests__/movable-mock.ts +43 -0
  547. package/src/interactive2/__tests__/movable-point.test.ts +145 -0
  548. package/src/interactive2/interactive-util.ts +155 -0
  549. package/src/interactive2/movable-helper-methods.ts +161 -0
  550. package/src/interactive2/movable-line-options.ts +419 -0
  551. package/src/interactive2/movable-line.ts +318 -0
  552. package/src/interactive2/movable-point-options.ts +212 -0
  553. package/src/interactive2/movable-point.tsx +593 -0
  554. package/src/interactive2/movable-polygon-options.ts +390 -0
  555. package/src/interactive2/movable-polygon.ts +368 -0
  556. package/src/interactive2/movable.ts +275 -0
  557. package/src/interactive2/objective_.ts +79 -0
  558. package/src/interactive2/types.ts +2 -0
  559. package/src/interactive2/wrapped-defaults.ts +87 -0
  560. package/src/interactive2/wrapped-ellipse.ts +94 -0
  561. package/src/interactive2/wrapped-line.ts +116 -0
  562. package/src/interactive2/wrapped-path.ts +51 -0
  563. package/src/interactive2.ts +73 -0
  564. package/src/item-renderer.tsx +514 -0
  565. package/src/item-version.ts +10 -0
  566. package/src/jipt-paragraphs.ts +49 -0
  567. package/src/jquery.mobile.vmouse.js +0 -1
  568. package/src/keypad-context.ts +34 -0
  569. package/src/loading-context.ts +21 -0
  570. package/src/logging/log.test.ts +39 -0
  571. package/src/logging/log.ts +97 -0
  572. package/src/mixins/__tests__/widget-prop-denylist.test.ts +21 -0
  573. package/src/mixins/changeable.ts +133 -0
  574. package/src/mixins/editor-jsonify.ts +13 -0
  575. package/src/mixins/provide-keypad.tsx +144 -0
  576. package/src/mixins/widget-jsonify-deprecated.ts +23 -0
  577. package/src/mixins/widget-prop-denylist.ts +48 -0
  578. package/src/multi-items/__stories__/multi-renderer.stories.tsx +87 -0
  579. package/src/multi-items/__testdata__/multi-renderer.testdata.ts +172 -0
  580. package/src/multi-items/__tests__/items.test.ts +250 -0
  581. package/src/multi-items/__tests__/multi-renderer.test.tsx +910 -0
  582. package/src/multi-items/__tests__/prop-type-builders.test.ts +133 -0
  583. package/src/multi-items/__tests__/shapes.test.ts +43 -0
  584. package/src/multi-items/__tests__/trees.test.ts +325 -0
  585. package/src/multi-items/item-types.js.flow +55 -0
  586. package/src/multi-items/item-types.ts +56 -0
  587. package/src/multi-items/items.ts +176 -0
  588. package/src/multi-items/multi-renderer.tsx +564 -0
  589. package/src/multi-items/prop-type-builders.ts +106 -0
  590. package/src/multi-items/shape-types.ts +52 -0
  591. package/src/multi-items/shapes.ts +52 -0
  592. package/src/multi-items/tree-types.ts +47 -0
  593. package/src/multi-items/trees.ts +360 -0
  594. package/src/multi-items.ts +75 -0
  595. package/src/perseus-api.tsx +188 -0
  596. package/src/perseus-env.ts +27 -0
  597. package/src/perseus-error.ts +17 -0
  598. package/src/perseus-markdown.tsx +407 -0
  599. package/src/perseus-types.ts +1474 -0
  600. package/src/question-paragraph.tsx +29 -0
  601. package/src/renderability.ts +109 -0
  602. package/src/renderer.tsx +1992 -0
  603. package/src/server-item-renderer.tsx +465 -0
  604. package/src/sigfigs.ts +490 -0
  605. package/src/styles/constants.ts +94 -0
  606. package/src/styles/global-constants.ts +343 -0
  607. package/src/styles/global-styles.ts +306 -0
  608. package/src/styles/media-queries.ts +47 -0
  609. package/src/styles/shared.ts +130 -0
  610. package/src/tex-wrangler.ts +182 -0
  611. package/src/translation-linter.ts +126 -0
  612. package/src/traversal.ts +138 -0
  613. package/src/types.ts +468 -0
  614. package/src/util/a11y.ts +23 -0
  615. package/src/util/answer-types.test.ts +114 -0
  616. package/src/util/answer-types.ts +876 -0
  617. package/src/util/colors.ts +93 -0
  618. package/src/util/fix-passage-refs.ts +121 -0
  619. package/src/util/graph-utils.ts +15 -0
  620. package/src/util/graphie.ts +1617 -0
  621. package/src/util/interactive.ts +4191 -0
  622. package/src/util/katex-preprocess.ts +14 -0
  623. package/src/util/math.ts +289 -0
  624. package/src/util/mobile-native-utils.test.ts +22 -0
  625. package/src/util/mobile-native-utils.ts +12 -0
  626. package/src/util/react-render.tsx +27 -0
  627. package/src/util/register-all-widgets-for-testing.ts +11 -0
  628. package/src/util/scroll-utils.ts +155 -0
  629. package/src/util/sizing-utils.ts +46 -0
  630. package/src/util/tex.ts +208 -0
  631. package/src/util/url-utils.ts +34 -0
  632. package/src/util.ts +1003 -0
  633. package/src/version.ts +11 -0
  634. package/src/widget-container.tsx +175 -0
  635. package/src/widgets/__stories__/categorizer.stories.tsx +14 -0
  636. package/src/widgets/__stories__/cs-program.stories.tsx +14 -0
  637. package/src/widgets/__stories__/definition.stories.tsx +111 -0
  638. package/src/widgets/__stories__/dropdown.stories.tsx +14 -0
  639. package/src/widgets/__stories__/explanation.stories.tsx +18 -0
  640. package/src/widgets/__stories__/expression.stories.tsx +29 -0
  641. package/src/widgets/__stories__/graded-group-set.stories.tsx +31 -0
  642. package/src/widgets/__stories__/graded-group.stories.tsx +31 -0
  643. package/src/widgets/__stories__/grapher.stories.tsx +60 -0
  644. package/src/widgets/__stories__/group.stories.tsx +14 -0
  645. package/src/widgets/__stories__/iframe.stories.tsx +14 -0
  646. package/src/widgets/__stories__/image.stories.tsx +61 -0
  647. package/src/widgets/__stories__/input-number.stories.tsx +110 -0
  648. package/src/widgets/__stories__/interaction.stories.tsx +14 -0
  649. package/src/widgets/__stories__/interactive-graph.stories.tsx +60 -0
  650. package/src/widgets/__stories__/matcher.stories.tsx +14 -0
  651. package/src/widgets/__stories__/matrix.stories.tsx +14 -0
  652. package/src/widgets/__stories__/number-line.stories.tsx +14 -0
  653. package/src/widgets/__stories__/numeric-input.stories.tsx +107 -0
  654. package/src/widgets/__stories__/on-screen-keypad.stories.tsx +120 -0
  655. package/src/widgets/__stories__/orderer.stories.tsx +14 -0
  656. package/src/widgets/__stories__/passage-ref.stories.tsx +22 -0
  657. package/src/widgets/__stories__/passage.stories.tsx +32 -0
  658. package/src/widgets/__stories__/radio.stories.tsx +88 -0
  659. package/src/widgets/__stories__/sorter.stories.tsx +14 -0
  660. package/src/widgets/__stories__/transformer.stories.tsx +14 -0
  661. package/src/widgets/__stories__/unit.stories.tsx +30 -0
  662. package/src/widgets/__stories__/video-transcript-link.stories.tsx +27 -0
  663. package/src/widgets/__stories__/video.stories.tsx +14 -0
  664. package/src/widgets/__testdata__/base-radio.testdata.ts +21 -0
  665. package/src/widgets/__testdata__/categorizer.testdata.ts +49 -0
  666. package/src/widgets/__testdata__/cs-program.testdata.ts +27 -0
  667. package/src/widgets/__testdata__/dropdown.testdata.ts +33 -0
  668. package/src/widgets/__testdata__/explanation.testdata.ts +55 -0
  669. package/src/widgets/__testdata__/expression.testdata.ts +94 -0
  670. package/src/widgets/__testdata__/graded-group-set.testdata.ts +356 -0
  671. package/src/widgets/__testdata__/graded-group.testdata.ts +77 -0
  672. package/src/widgets/__testdata__/grapher.testdata.ts +374 -0
  673. package/src/widgets/__testdata__/group.testdata.ts +155 -0
  674. package/src/widgets/__testdata__/iframe.testdata.ts +35 -0
  675. package/src/widgets/__testdata__/image.testdata.ts +36 -0
  676. package/src/widgets/__testdata__/input-number.testdata.ts +72 -0
  677. package/src/widgets/__testdata__/interaction.testdata.ts +262 -0
  678. package/src/widgets/__testdata__/interactive-graph.testdata.ts +644 -0
  679. package/src/widgets/__testdata__/matcher.testdata.ts +33 -0
  680. package/src/widgets/__testdata__/matrix.testdata.ts +28 -0
  681. package/src/widgets/__testdata__/number-line.testdata.ts +30 -0
  682. package/src/widgets/__testdata__/numeric-input.testdata.ts +266 -0
  683. package/src/widgets/__testdata__/orderer.testdata.ts +57 -0
  684. package/src/widgets/__testdata__/passage-ref.testdata.ts +101 -0
  685. package/src/widgets/__testdata__/passage.testdata.ts +95 -0
  686. package/src/widgets/__testdata__/radio.testdata.ts +261 -0
  687. package/src/widgets/__testdata__/sorter.testdata.ts +19 -0
  688. package/src/widgets/__testdata__/transformer.testdata.ts +108 -0
  689. package/src/widgets/__testdata__/video.testdata.ts +21 -0
  690. package/src/widgets/__tests__/categorizer.test.ts +203 -0
  691. package/src/widgets/__tests__/cs-program.test.ts +43 -0
  692. package/src/widgets/__tests__/definition.test.ts +136 -0
  693. package/src/widgets/__tests__/dropdown.test.ts +105 -0
  694. package/src/widgets/__tests__/explanation.test.ts +198 -0
  695. package/src/widgets/__tests__/expression.test.ts +431 -0
  696. package/src/widgets/__tests__/graded-group-set-jipt.test.ts +30 -0
  697. package/src/widgets/__tests__/graded-group-set.test.ts +288 -0
  698. package/src/widgets/__tests__/graded-group.test.ts +232 -0
  699. package/src/widgets/__tests__/grapher.cypress.ts +576 -0
  700. package/src/widgets/__tests__/grapher.test.ts +33 -0
  701. package/src/widgets/__tests__/group.test.tsx +512 -0
  702. package/src/widgets/__tests__/iframe.test.ts +45 -0
  703. package/src/widgets/__tests__/image.test.ts +62 -0
  704. package/src/widgets/__tests__/input-number.test.ts +354 -0
  705. package/src/widgets/__tests__/interaction.test.ts +31 -0
  706. package/src/widgets/__tests__/interactive-graph.cypress.ts +815 -0
  707. package/src/widgets/__tests__/interactive-graph.test.ts +106 -0
  708. package/src/widgets/__tests__/matcher.test.tsx +152 -0
  709. package/src/widgets/__tests__/matrix.test.ts +82 -0
  710. package/src/widgets/__tests__/number-line.test.ts +79 -0
  711. package/src/widgets/__tests__/numeric-input.test.ts +502 -0
  712. package/src/widgets/__tests__/orderer.test.ts +73 -0
  713. package/src/widgets/__tests__/passage-ref.test.ts +168 -0
  714. package/src/widgets/__tests__/passage.test.tsx +224 -0
  715. package/src/widgets/__tests__/radio/base-radio.test.tsx +470 -0
  716. package/src/widgets/__tests__/radio/choice-icon.test.tsx +90 -0
  717. package/src/widgets/__tests__/radio/choice.test.tsx +310 -0
  718. package/src/widgets/__tests__/radio/focus-ring.test.tsx +23 -0
  719. package/src/widgets/__tests__/radio/option-status.test.tsx +77 -0
  720. package/src/widgets/__tests__/radio.test.ts +807 -0
  721. package/src/widgets/__tests__/renderQuestion.tsx +77 -0
  722. package/src/widgets/__tests__/unit.test.tsx +168 -0
  723. package/src/widgets/__tests__/video.test.ts +53 -0
  724. package/src/widgets/categorizer.tsx +343 -0
  725. package/src/widgets/cs-program.tsx +227 -0
  726. package/src/widgets/definition.tsx +121 -0
  727. package/src/widgets/dropdown.tsx +142 -0
  728. package/src/widgets/explanation.tsx +317 -0
  729. package/src/widgets/expression.tsx +647 -0
  730. package/src/widgets/graded-group-answer-bar.tsx +142 -0
  731. package/src/widgets/graded-group-set.tsx +296 -0
  732. package/src/widgets/graded-group.tsx +466 -0
  733. package/src/widgets/grapher/util.tsx +803 -0
  734. package/src/widgets/grapher.tsx +674 -0
  735. package/src/widgets/group.tsx +196 -0
  736. package/src/widgets/iframe.tsx +195 -0
  737. package/src/widgets/image.tsx +309 -0
  738. package/src/widgets/input-number.tsx +413 -0
  739. package/src/widgets/interaction/arrow-picker.tsx +38 -0
  740. package/src/widgets/interaction/color-picker.tsx +69 -0
  741. package/src/widgets/interaction/dash-picker.tsx +45 -0
  742. package/src/widgets/interaction/element-container.tsx +101 -0
  743. package/src/widgets/interaction.tsx +826 -0
  744. package/src/widgets/interactive-graph.tsx +2921 -0
  745. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +98 -0
  746. package/src/widgets/label-image/__stories__/marker.stories.tsx +181 -0
  747. package/src/widgets/label-image/answer-choices.tsx +300 -0
  748. package/src/widgets/label-image/marker.tsx +364 -0
  749. package/src/widgets/label-image/types.ts +26 -0
  750. package/src/widgets/label-image.test.ts +840 -0
  751. package/src/widgets/label-image.tsx +849 -0
  752. package/src/widgets/lights-puzzle.tsx +359 -0
  753. package/src/widgets/matcher.tsx +278 -0
  754. package/src/widgets/matrix.tsx +637 -0
  755. package/src/widgets/measurer.tsx +217 -0
  756. package/src/widgets/molecule/__tests__/layout.test.ts +162 -0
  757. package/src/widgets/molecule/__tests__/parser.test.ts +155 -0
  758. package/src/widgets/molecule/molecule-drawing.ts +173 -0
  759. package/src/widgets/molecule/molecule-layout.ts +362 -0
  760. package/src/widgets/molecule/smiles-parser.ts +296 -0
  761. package/src/widgets/molecule.tsx +172 -0
  762. package/src/widgets/number-line.tsx +864 -0
  763. package/src/widgets/numeric-input.tsx +563 -0
  764. package/src/widgets/orderer.tsx +788 -0
  765. package/src/widgets/passage/__tests__/passage-markdown.test.ts +376 -0
  766. package/src/widgets/passage/passage-markdown.tsx +475 -0
  767. package/src/widgets/passage-ref-target.tsx +73 -0
  768. package/src/widgets/passage-ref.tsx +215 -0
  769. package/src/widgets/passage.tsx +584 -0
  770. package/src/widgets/plotter.tsx +1204 -0
  771. package/src/widgets/radio/__stories__/base-radio.stories.tsx +204 -0
  772. package/src/widgets/radio/__stories__/choice-icon.stories.tsx +168 -0
  773. package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +40 -0
  774. package/src/widgets/radio/__stories__/choice.stories.tsx +137 -0
  775. package/src/widgets/radio/__stories__/focus-ring.stories.tsx +43 -0
  776. package/src/widgets/radio/__stories__/option-status.stories.tsx +105 -0
  777. package/src/widgets/radio/base-radio.tsx +509 -0
  778. package/src/widgets/radio/choice-a11y-text.ts +61 -0
  779. package/src/widgets/radio/choice-icon/choice-icon.tsx +217 -0
  780. package/src/widgets/radio/choice-icon/cross-out-line.tsx +53 -0
  781. package/src/widgets/radio/choice-icon/shared-styles.ts +15 -0
  782. package/src/widgets/radio/choice-none-above.tsx +56 -0
  783. package/src/widgets/radio/choice.jsx.fixture.ts +45 -0
  784. package/src/widgets/radio/choice.tsx +358 -0
  785. package/src/widgets/radio/focus-ring.tsx +57 -0
  786. package/src/widgets/radio/option-status.tsx +100 -0
  787. package/src/widgets/radio/util.test.ts +18 -0
  788. package/src/widgets/radio/util.ts +31 -0
  789. package/src/widgets/radio/widget.tsx +521 -0
  790. package/src/widgets/radio.ts +157 -0
  791. package/src/widgets/reaction-diagram.tsx +144 -0
  792. package/src/widgets/sequence.tsx +137 -0
  793. package/src/widgets/simulator.tsx +831 -0
  794. package/src/widgets/sorter.tsx +154 -0
  795. package/src/widgets/table.tsx +393 -0
  796. package/src/widgets/transformer.tsx +2951 -0
  797. package/src/widgets/unit.tsx +374 -0
  798. package/src/widgets/video-transcript-link.test.tsx +213 -0
  799. package/src/widgets/video-transcript-link.tsx +84 -0
  800. package/src/widgets/video.tsx +124 -0
  801. package/src/widgets.ts +444 -0
  802. package/src/zoom.ts +558 -0
  803. package/tsconfig.json +25 -0
  804. package/tsconfig.tsbuildinfo +1 -0
  805. package/src/__stories__/hints-renderer.stories.jsx +0 -60
  806. package/src/__stories__/item-renderer.stories.jsx +0 -27
  807. package/src/__stories__/server-item-renderer.stories.jsx +0 -27
  808. package/src/__testdata__/graphie_testdata.js +0 -53
  809. package/src/__testdata__/item-renderer_testdata.js +0 -161
  810. package/src/__testdata__/renderer_testdata.js +0 -123
  811. package/src/__tests__/a11y_test.jsx +0 -304
  812. package/src/__tests__/item-renderer_test.jsx +0 -892
  813. package/src/__tests__/jipt-paragraphs_test.js +0 -34
  814. package/src/__tests__/mock-asset-loading-widget.jsx +0 -57
  815. package/src/__tests__/mock-widget.jsx +0 -35
  816. package/src/__tests__/perseus-markdown_test.jsx +0 -747
  817. package/src/__tests__/renderability_test.jsx +0 -481
  818. package/src/__tests__/renderer-api_test.jsx +0 -309
  819. package/src/__tests__/renderer_test.jsx +0 -1792
  820. package/src/__tests__/server-item-renderer_test.jsx +0 -516
  821. package/src/__tests__/test-items/image-item.js +0 -25
  822. package/src/__tests__/test-items/input-number-1-item.js +0 -27
  823. package/src/__tests__/test-items/input-number-2-item.js +0 -39
  824. package/src/__tests__/test-items/table-item.js +0 -30
  825. package/src/__tests__/tex-wrangler_test.js +0 -83
  826. package/src/__tests__/util_test.js +0 -41
  827. package/src/a11y.js +0 -46
  828. package/src/all-widgets.js +0 -9
  829. package/src/article-renderer.jsx +0 -214
  830. package/src/asset-context.js +0 -30
  831. package/src/basic-widgets.js +0 -13
  832. package/src/components/__stories__/button-group.stories.jsx +0 -61
  833. package/src/components/__stories__/fixed-to-responsive.stories.jsx +0 -132
  834. package/src/components/__stories__/graph.stories.jsx +0 -27
  835. package/src/components/__stories__/graphie.stories.jsx +0 -32
  836. package/src/components/__stories__/hud.stories.jsx +0 -36
  837. package/src/components/__stories__/icon.stories.jsx +0 -56
  838. package/src/components/__stories__/image-loader.stories.jsx +0 -60
  839. package/src/components/__stories__/info-tip.stories.jsx +0 -33
  840. package/src/components/__stories__/inline-icon.stories.jsx +0 -39
  841. package/src/components/__stories__/input-with-examples.stories.jsx +0 -52
  842. package/src/components/__stories__/lint.stories.jsx +0 -40
  843. package/src/components/__stories__/math-input.stories.jsx +0 -29
  844. package/src/components/__stories__/math-output.stories.jsx +0 -24
  845. package/src/components/__stories__/multi-button-group.stories.jsx +0 -69
  846. package/src/components/__stories__/number-input.stories.jsx +0 -42
  847. package/src/components/__stories__/prop-check-box.stories.jsx +0 -62
  848. package/src/components/__stories__/range-input.stories.jsx +0 -28
  849. package/src/components/__stories__/simple-keypad-input.stories.jsx +0 -28
  850. package/src/components/__stories__/sortable.stories.jsx +0 -73
  851. package/src/components/__stories__/stub-tag-editor.stories.jsx +0 -44
  852. package/src/components/__stories__/svg-image.stories.jsx +0 -71
  853. package/src/components/__stories__/tex.stories.jsx +0 -24
  854. package/src/components/__stories__/text-buttons.stories.jsx +0 -51
  855. package/src/components/__stories__/text-input.stories.jsx +0 -34
  856. package/src/components/__stories__/text-list-editor.stories.jsx +0 -33
  857. package/src/components/__stories__/zoomable-tex.stories.jsx +0 -39
  858. package/src/components/__stories__/zoomable.stories.jsx +0 -40
  859. package/src/components/__testdata__/sorter_testdata.js +0 -20
  860. package/src/components/__tests__/number-input_test.jsx +0 -112
  861. package/src/components/__tests__/sortable_test.jsx +0 -234
  862. package/src/components/__tests__/sorter_test.jsx +0 -111
  863. package/src/components/__tests__/zoomable_test.jsx +0 -388
  864. package/src/components/button-group.jsx +0 -131
  865. package/src/components/constants.js +0 -15
  866. package/src/components/fixed-to-responsive.jsx +0 -165
  867. package/src/components/graph.jsx +0 -413
  868. package/src/components/graphie-classes.jsx +0 -148
  869. package/src/components/graphie-movables.jsx +0 -342
  870. package/src/components/graphie.jsx +0 -353
  871. package/src/components/highlighting/highlightable-content.jsx +0 -245
  872. package/src/components/highlighting/highlights.js +0 -208
  873. package/src/components/highlighting/ranges.js +0 -299
  874. package/src/components/highlighting/types.js +0 -73
  875. package/src/components/highlighting/ui/highlight-renderer.jsx +0 -179
  876. package/src/components/highlighting/ui/highlight-set-renderer.jsx +0 -190
  877. package/src/components/highlighting/ui/highlight-tooltip.jsx +0 -143
  878. package/src/components/highlighting/ui/highlighting-ui.jsx +0 -163
  879. package/src/components/highlighting/ui/selection-tracker.jsx +0 -184
  880. package/src/components/highlighting/ui/types.js +0 -21
  881. package/src/components/highlighting/ui/util.js +0 -166
  882. package/src/components/highlighting/word-indexer.jsx +0 -155
  883. package/src/components/hud.jsx +0 -179
  884. package/src/components/icon.jsx +0 -149
  885. package/src/components/image-loader.jsx +0 -175
  886. package/src/components/info-tip/info-tip.jsx +0 -146
  887. package/src/components/info-tip.jsx +0 -38
  888. package/src/components/inline-icon.jsx +0 -81
  889. package/src/components/input-with-examples.jsx +0 -248
  890. package/src/components/lint.jsx +0 -455
  891. package/src/components/math-input.jsx +0 -293
  892. package/src/components/math-output.jsx +0 -128
  893. package/src/components/multi-button-group.jsx +0 -137
  894. package/src/components/number-input.jsx +0 -253
  895. package/src/components/prop-check-box.jsx +0 -80
  896. package/src/components/range-input.jsx +0 -61
  897. package/src/components/simple-keypad-input.jsx +0 -88
  898. package/src/components/sortable.jsx +0 -872
  899. package/src/components/stub-tag-editor.jsx +0 -54
  900. package/src/components/svg-image.jsx +0 -765
  901. package/src/components/tex-buttons.jsx +0 -320
  902. package/src/components/tex.jsx +0 -70
  903. package/src/components/text-input.jsx +0 -133
  904. package/src/components/text-list-editor.jsx +0 -157
  905. package/src/components/tooltip.jsx +0 -392
  906. package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +0 -140
  907. package/src/components/visibility-observer/get-scroll-parent.js +0 -45
  908. package/src/components/visibility-observer/visibility-observer.js +0 -109
  909. package/src/components/visibility-observer/visibility-observer.jsx +0 -123
  910. package/src/components/zoomable-tex.jsx +0 -70
  911. package/src/components/zoomable.jsx +0 -294
  912. package/src/components.js +0 -17
  913. package/src/definition-context.js +0 -59
  914. package/src/dependencies.js +0 -22
  915. package/src/error-boundary.jsx +0 -48
  916. package/src/extra-widgets.js +0 -75
  917. package/src/hint-renderer.jsx +0 -152
  918. package/src/hints-renderer.jsx +0 -367
  919. package/src/icon-paths.js +0 -120
  920. package/src/index.js +0 -159
  921. package/src/init.js +0 -49
  922. package/src/interaction-tracker.js +0 -60
  923. package/src/interactive2/__tests__/movable-mock.js +0 -48
  924. package/src/interactive2/__tests__/movable-point_test.js +0 -146
  925. package/src/interactive2/interactive-util.js +0 -152
  926. package/src/interactive2/movable-helper-methods.js +0 -160
  927. package/src/interactive2/movable-line-options.js +0 -392
  928. package/src/interactive2/movable-line.js +0 -322
  929. package/src/interactive2/movable-point-options.js +0 -190
  930. package/src/interactive2/movable-point.jsx +0 -597
  931. package/src/interactive2/movable-polygon-options.js +0 -351
  932. package/src/interactive2/movable-polygon.js +0 -368
  933. package/src/interactive2/movable.js +0 -273
  934. package/src/interactive2/objective_.js +0 -79
  935. package/src/interactive2/types.js +0 -3
  936. package/src/interactive2/wrapped-defaults.js +0 -86
  937. package/src/interactive2/wrapped-ellipse.js +0 -86
  938. package/src/interactive2/wrapped-line.js +0 -109
  939. package/src/interactive2/wrapped-path.js +0 -47
  940. package/src/interactive2.js +0 -91
  941. package/src/item-renderer.jsx +0 -514
  942. package/src/item-version.js +0 -12
  943. package/src/jipt-paragraphs.js +0 -52
  944. package/src/keypad-context.js +0 -33
  945. package/src/loading-context.js +0 -20
  946. package/src/logging/log.js +0 -107
  947. package/src/logging/log_test.js +0 -41
  948. package/src/mixins/__tests__/widget-prop-denylist_test.js +0 -23
  949. package/src/mixins/changeable.jsx +0 -115
  950. package/src/mixins/editor-jsonify.jsx +0 -13
  951. package/src/mixins/provide-keypad.jsx +0 -131
  952. package/src/mixins/widget-jsonify-deprecated.jsx +0 -23
  953. package/src/mixins/widget-prop-denylist.js +0 -49
  954. package/src/multi-items/__stories__/multi-renderer.stories.js +0 -88
  955. package/src/multi-items/__testdata__/multi-renderer_testdata.js +0 -174
  956. package/src/multi-items/__tests__/items_test.js +0 -245
  957. package/src/multi-items/__tests__/multi-renderer_test.js +0 -903
  958. package/src/multi-items/__tests__/prop-type-builders_test.js +0 -134
  959. package/src/multi-items/__tests__/shapes_test.js +0 -44
  960. package/src/multi-items/__tests__/trees_test.js +0 -316
  961. package/src/multi-items/items.js +0 -176
  962. package/src/multi-items/multi-renderer.jsx +0 -543
  963. package/src/multi-items/prop-type-builders.js +0 -105
  964. package/src/multi-items/shape-types.js +0 -47
  965. package/src/multi-items/shapes.js +0 -53
  966. package/src/multi-items/tree-types.js +0 -42
  967. package/src/multi-items/trees.js +0 -345
  968. package/src/multi-items.js +0 -76
  969. package/src/perseus-api.jsx +0 -214
  970. package/src/perseus-env.js +0 -26
  971. package/src/perseus-error.js +0 -19
  972. package/src/perseus-markdown.jsx +0 -408
  973. package/src/perseus-types.js +0 -1471
  974. package/src/question-paragraph.jsx +0 -30
  975. package/src/renderability.jsx +0 -108
  976. package/src/renderer.jsx +0 -1937
  977. package/src/server-item-renderer.jsx +0 -467
  978. package/src/sigfigs.jsx +0 -492
  979. package/src/styles/constants.js +0 -96
  980. package/src/styles/global-constants.js +0 -344
  981. package/src/styles/global-styles.js +0 -307
  982. package/src/styles/media-queries.js +0 -48
  983. package/src/styles/shared.js +0 -132
  984. package/src/tex-wrangler.js +0 -176
  985. package/src/translation-linter.js +0 -125
  986. package/src/traversal.jsx +0 -141
  987. package/src/types.js +0 -482
  988. package/src/util/a11y.js +0 -24
  989. package/src/util/answer-types.js +0 -861
  990. package/src/util/answer-types_test.js +0 -115
  991. package/src/util/colors.js +0 -94
  992. package/src/util/fix-passage-refs.jsx +0 -122
  993. package/src/util/graph-utils.js +0 -16
  994. package/src/util/graphie.js +0 -1586
  995. package/src/util/interactive.js +0 -4078
  996. package/src/util/katex-preprocess.js +0 -16
  997. package/src/util/math.js +0 -289
  998. package/src/util/mobile-native-utils.js +0 -14
  999. package/src/util/mobile-native-utils_test.js +0 -24
  1000. package/src/util/react-render.js +0 -27
  1001. package/src/util/register-all-widgets-for-testing.js +0 -12
  1002. package/src/util/scroll-utils.js +0 -162
  1003. package/src/util/sizing-utils.js +0 -46
  1004. package/src/util/tex.js +0 -203
  1005. package/src/util/url-utils.js +0 -36
  1006. package/src/util.js +0 -984
  1007. package/src/version.js +0 -11
  1008. package/src/widget-container.jsx +0 -178
  1009. package/src/widgets/__stories__/categorizer.stories.jsx +0 -15
  1010. package/src/widgets/__stories__/cs-program.stories.jsx +0 -15
  1011. package/src/widgets/__stories__/definition.stories.jsx +0 -108
  1012. package/src/widgets/__stories__/dropdown.stories.jsx +0 -15
  1013. package/src/widgets/__stories__/explanation.stories.jsx +0 -19
  1014. package/src/widgets/__stories__/expression.stories.jsx +0 -26
  1015. package/src/widgets/__stories__/graded-group-set.stories.jsx +0 -32
  1016. package/src/widgets/__stories__/graded-group.stories.jsx +0 -32
  1017. package/src/widgets/__stories__/grapher.stories.jsx +0 -47
  1018. package/src/widgets/__stories__/group.stories.jsx +0 -15
  1019. package/src/widgets/__stories__/iframe.stories.jsx +0 -15
  1020. package/src/widgets/__stories__/image.stories.jsx +0 -62
  1021. package/src/widgets/__stories__/input-number.stories.jsx +0 -102
  1022. package/src/widgets/__stories__/interaction.stories.jsx +0 -15
  1023. package/src/widgets/__stories__/interactive-graph.stories.jsx +0 -61
  1024. package/src/widgets/__stories__/matcher.stories.jsx +0 -15
  1025. package/src/widgets/__stories__/matrix.stories.jsx +0 -15
  1026. package/src/widgets/__stories__/number-line.stories.jsx +0 -15
  1027. package/src/widgets/__stories__/numeric-input.stories.jsx +0 -106
  1028. package/src/widgets/__stories__/on-screen-keypad.stories.jsx +0 -118
  1029. package/src/widgets/__stories__/orderer.stories.jsx +0 -15
  1030. package/src/widgets/__stories__/passage-ref.stories.jsx +0 -23
  1031. package/src/widgets/__stories__/passage.stories.jsx +0 -27
  1032. package/src/widgets/__stories__/radio.stories.jsx +0 -90
  1033. package/src/widgets/__stories__/sorter.stories.jsx +0 -15
  1034. package/src/widgets/__stories__/transformer.stories.jsx +0 -15
  1035. package/src/widgets/__stories__/unit.stories.jsx +0 -29
  1036. package/src/widgets/__stories__/video-transcript-link.stories.jsx +0 -24
  1037. package/src/widgets/__stories__/video.stories.jsx +0 -15
  1038. package/src/widgets/__testdata__/base-radio_testdata.js +0 -22
  1039. package/src/widgets/__testdata__/categorizer_testdata.js +0 -50
  1040. package/src/widgets/__testdata__/cs-program_testdata.js +0 -28
  1041. package/src/widgets/__testdata__/dropdown_testdata.js +0 -34
  1042. package/src/widgets/__testdata__/explanation_testdata.js +0 -56
  1043. package/src/widgets/__testdata__/expression_testdata.js +0 -95
  1044. package/src/widgets/__testdata__/graded-group-set_testdata.js +0 -358
  1045. package/src/widgets/__testdata__/graded-group_testdata.js +0 -79
  1046. package/src/widgets/__testdata__/grapher_testdata.js +0 -376
  1047. package/src/widgets/__testdata__/group_testdata.js +0 -156
  1048. package/src/widgets/__testdata__/iframe_testdata.js +0 -36
  1049. package/src/widgets/__testdata__/image_testdata.js +0 -38
  1050. package/src/widgets/__testdata__/input-number_testdata.js +0 -73
  1051. package/src/widgets/__testdata__/interaction_testdata.js +0 -264
  1052. package/src/widgets/__testdata__/interactive-graph_testdata.js +0 -646
  1053. package/src/widgets/__testdata__/matcher_testdata.js +0 -34
  1054. package/src/widgets/__testdata__/matrix_testdata.js +0 -29
  1055. package/src/widgets/__testdata__/number-line_testdata.js +0 -31
  1056. package/src/widgets/__testdata__/numeric-input_testdata.js +0 -266
  1057. package/src/widgets/__testdata__/orderer_testdata.js +0 -58
  1058. package/src/widgets/__testdata__/passage-ref_testdata.js +0 -103
  1059. package/src/widgets/__testdata__/passage_testdata.js +0 -96
  1060. package/src/widgets/__testdata__/radio_testdata.js +0 -262
  1061. package/src/widgets/__testdata__/sorter_testdata.js +0 -20
  1062. package/src/widgets/__testdata__/transformer_testdata.js +0 -109
  1063. package/src/widgets/__testdata__/video_testdata.js +0 -22
  1064. package/src/widgets/__tests__/categorizer_test.jsx +0 -205
  1065. package/src/widgets/__tests__/cs-program_test.jsx +0 -45
  1066. package/src/widgets/__tests__/definition_test.js +0 -137
  1067. package/src/widgets/__tests__/dropdown_test.jsx +0 -106
  1068. package/src/widgets/__tests__/explanation_test.js +0 -199
  1069. package/src/widgets/__tests__/expression_test.jsx +0 -429
  1070. package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +0 -32
  1071. package/src/widgets/__tests__/graded-group-set_test.jsx +0 -290
  1072. package/src/widgets/__tests__/graded-group_test.jsx +0 -233
  1073. package/src/widgets/__tests__/grapher.cypress.jsx +0 -546
  1074. package/src/widgets/__tests__/grapher_test.jsx +0 -35
  1075. package/src/widgets/__tests__/group_test.jsx +0 -512
  1076. package/src/widgets/__tests__/iframe_test.jsx +0 -47
  1077. package/src/widgets/__tests__/image_test.jsx +0 -65
  1078. package/src/widgets/__tests__/input-number_test.jsx +0 -352
  1079. package/src/widgets/__tests__/interaction_test.jsx +0 -33
  1080. package/src/widgets/__tests__/interactive-graph.cypress.jsx +0 -777
  1081. package/src/widgets/__tests__/interactive-graph_test.jsx +0 -105
  1082. package/src/widgets/__tests__/matcher_test.jsx +0 -157
  1083. package/src/widgets/__tests__/matrix_test.jsx +0 -84
  1084. package/src/widgets/__tests__/number-line_test.jsx +0 -81
  1085. package/src/widgets/__tests__/numeric-input_test.jsx +0 -503
  1086. package/src/widgets/__tests__/orderer_test.jsx +0 -75
  1087. package/src/widgets/__tests__/passage-ref_test.jsx +0 -166
  1088. package/src/widgets/__tests__/passage_test.jsx +0 -212
  1089. package/src/widgets/__tests__/radio/base-radio_test.jsx +0 -443
  1090. package/src/widgets/__tests__/radio/choice-icon_test.jsx +0 -91
  1091. package/src/widgets/__tests__/radio/choice_test.jsx +0 -311
  1092. package/src/widgets/__tests__/radio/focus-ring_test.jsx +0 -23
  1093. package/src/widgets/__tests__/radio/option-status_test.jsx +0 -65
  1094. package/src/widgets/__tests__/radio_test.jsx +0 -810
  1095. package/src/widgets/__tests__/renderQuestion.jsx +0 -66
  1096. package/src/widgets/__tests__/unit_test.jsx +0 -169
  1097. package/src/widgets/__tests__/video_test.jsx +0 -55
  1098. package/src/widgets/categorizer.jsx +0 -343
  1099. package/src/widgets/cs-program.jsx +0 -227
  1100. package/src/widgets/definition.jsx +0 -121
  1101. package/src/widgets/dropdown.jsx +0 -141
  1102. package/src/widgets/explanation.jsx +0 -315
  1103. package/src/widgets/expression.jsx +0 -629
  1104. package/src/widgets/graded-group-answer-bar.jsx +0 -152
  1105. package/src/widgets/graded-group-set.jsx +0 -296
  1106. package/src/widgets/graded-group.jsx +0 -453
  1107. package/src/widgets/grapher/util.jsx +0 -806
  1108. package/src/widgets/grapher.jsx +0 -663
  1109. package/src/widgets/group.jsx +0 -195
  1110. package/src/widgets/iframe.jsx +0 -198
  1111. package/src/widgets/image.jsx +0 -310
  1112. package/src/widgets/input-number.jsx +0 -402
  1113. package/src/widgets/interaction/arrow-picker.jsx +0 -39
  1114. package/src/widgets/interaction/color-picker.jsx +0 -69
  1115. package/src/widgets/interaction/dash-picker.jsx +0 -46
  1116. package/src/widgets/interaction/element-container.jsx +0 -97
  1117. package/src/widgets/interaction.jsx +0 -742
  1118. package/src/widgets/interactive-graph.jsx +0 -2820
  1119. package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +0 -93
  1120. package/src/widgets/label-image/__stories__/marker.stories.jsx +0 -172
  1121. package/src/widgets/label-image/answer-choices.jsx +0 -311
  1122. package/src/widgets/label-image/marker.jsx +0 -365
  1123. package/src/widgets/label-image/types.js +0 -33
  1124. package/src/widgets/label-image.jsx +0 -841
  1125. package/src/widgets/label-image_test.js +0 -840
  1126. package/src/widgets/lights-puzzle.jsx +0 -356
  1127. package/src/widgets/matcher.jsx +0 -274
  1128. package/src/widgets/matrix.jsx +0 -616
  1129. package/src/widgets/measurer.jsx +0 -215
  1130. package/src/widgets/molecule/__tests__/layout_test.jsx +0 -159
  1131. package/src/widgets/molecule/__tests__/parser_test.jsx +0 -142
  1132. package/src/widgets/molecule/molecule-drawing.jsx +0 -175
  1133. package/src/widgets/molecule/molecule-layout.jsx +0 -367
  1134. package/src/widgets/molecule/smiles-parser.jsx +0 -300
  1135. package/src/widgets/molecule.jsx +0 -172
  1136. package/src/widgets/number-line.jsx +0 -855
  1137. package/src/widgets/numeric-input.jsx +0 -546
  1138. package/src/widgets/orderer.jsx +0 -756
  1139. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -373
  1140. package/src/widgets/passage/passage-markdown.jsx +0 -472
  1141. package/src/widgets/passage-ref-target.jsx +0 -73
  1142. package/src/widgets/passage-ref.jsx +0 -214
  1143. package/src/widgets/passage.jsx +0 -576
  1144. package/src/widgets/plotter.jsx +0 -1198
  1145. package/src/widgets/radio/__stories__/base-radio.stories.jsx +0 -189
  1146. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +0 -166
  1147. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +0 -41
  1148. package/src/widgets/radio/__stories__/choice.stories.jsx +0 -138
  1149. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +0 -44
  1150. package/src/widgets/radio/__stories__/option-status.stories.jsx +0 -104
  1151. package/src/widgets/radio/base-radio.jsx +0 -494
  1152. package/src/widgets/radio/choice-a11y-text.js +0 -62
  1153. package/src/widgets/radio/choice-icon/choice-icon.jsx +0 -214
  1154. package/src/widgets/radio/choice-icon/cross-out-line.jsx +0 -52
  1155. package/src/widgets/radio/choice-icon/shared-styles.js +0 -16
  1156. package/src/widgets/radio/choice-none-above.jsx +0 -63
  1157. package/src/widgets/radio/choice.jsx +0 -373
  1158. package/src/widgets/radio/choice.jsx.fixture.js +0 -46
  1159. package/src/widgets/radio/focus-ring.jsx +0 -58
  1160. package/src/widgets/radio/option-status.jsx +0 -98
  1161. package/src/widgets/radio/util.js +0 -32
  1162. package/src/widgets/radio/util_test.js +0 -19
  1163. package/src/widgets/radio/widget.jsx +0 -515
  1164. package/src/widgets/radio.jsx +0 -155
  1165. package/src/widgets/reaction-diagram.jsx +0 -137
  1166. package/src/widgets/sequence.jsx +0 -143
  1167. package/src/widgets/simulator.jsx +0 -802
  1168. package/src/widgets/sorter.jsx +0 -149
  1169. package/src/widgets/table.jsx +0 -389
  1170. package/src/widgets/transformer.jsx +0 -2872
  1171. package/src/widgets/unit.jsx +0 -357
  1172. package/src/widgets/video-transcript-link.jsx +0 -84
  1173. package/src/widgets/video-transcript-link_test.js +0 -211
  1174. package/src/widgets/video.jsx +0 -126
  1175. package/src/widgets.js +0 -431
  1176. package/src/zoom.js +0 -539
  1177. /package/{src/multi-items/item-types.js → dist/multi-items/item-types.js.flow} +0 -0
  1178. /package/src/__tests__/__snapshots__/{item-renderer_test.jsx.snap → item-renderer.test.tsx.snap} +0 -0
  1179. /package/src/__tests__/__snapshots__/{perseus-markdown_test.jsx.snap → perseus-markdown.test.ts.snap} +0 -0
  1180. /package/src/__tests__/__snapshots__/{renderer_test.jsx.snap → renderer.test.tsx.snap} +0 -0
  1181. /package/src/__tests__/__snapshots__/{server-item-renderer_test.jsx.snap → server-item-renderer.test.tsx.snap} +0 -0
  1182. /package/src/components/__tests__/__snapshots__/{sortable_test.jsx.snap → sortable.test.tsx.snap} +0 -0
  1183. /package/src/components/__tests__/__snapshots__/{sorter_test.jsx.snap → sorter.test.tsx.snap} +0 -0
  1184. /package/src/multi-items/__tests__/__snapshots__/{multi-renderer_test.js.snap → multi-renderer.test.tsx.snap} +0 -0
  1185. /package/src/widgets/__tests__/__snapshots__/{categorizer_test.jsx.snap → categorizer.test.ts.snap} +0 -0
  1186. /package/src/widgets/__tests__/__snapshots__/{cs-program_test.jsx.snap → cs-program.test.ts.snap} +0 -0
  1187. /package/src/widgets/__tests__/__snapshots__/{definition_test.js.snap → definition.test.ts.snap} +0 -0
  1188. /package/src/widgets/__tests__/__snapshots__/{dropdown_test.jsx.snap → dropdown.test.ts.snap} +0 -0
  1189. /package/src/widgets/__tests__/__snapshots__/{explanation_test.js.snap → explanation.test.ts.snap} +0 -0
  1190. /package/src/widgets/__tests__/__snapshots__/{graded-group-set-jipt_test.jsx.snap → graded-group-set-jipt.test.ts.snap} +0 -0
  1191. /package/src/widgets/__tests__/__snapshots__/{graded-group-set_test.jsx.snap → graded-group-set.test.ts.snap} +0 -0
  1192. /package/src/widgets/__tests__/__snapshots__/{graded-group_test.jsx.snap → graded-group.test.ts.snap} +0 -0
  1193. /package/src/widgets/__tests__/__snapshots__/{grapher_test.jsx.snap → grapher.test.ts.snap} +0 -0
  1194. /package/src/widgets/__tests__/__snapshots__/{group_test.jsx.snap → group.test.tsx.snap} +0 -0
  1195. /package/src/widgets/__tests__/__snapshots__/{iframe_test.jsx.snap → iframe.test.ts.snap} +0 -0
  1196. /package/src/widgets/__tests__/__snapshots__/{image_test.jsx.snap → image.test.ts.snap} +0 -0
  1197. /package/src/widgets/__tests__/__snapshots__/{input-number_test.jsx.snap → input-number.test.ts.snap} +0 -0
  1198. /package/src/widgets/__tests__/__snapshots__/{interaction_test.jsx.snap → interaction.test.ts.snap} +0 -0
  1199. /package/src/widgets/__tests__/__snapshots__/{interactive-graph_test.jsx.snap → interactive-graph.test.ts.snap} +0 -0
  1200. /package/src/widgets/__tests__/__snapshots__/{matcher_test.jsx.snap → matcher.test.tsx.snap} +0 -0
  1201. /package/src/widgets/__tests__/__snapshots__/{matrix_test.jsx.snap → matrix.test.ts.snap} +0 -0
  1202. /package/src/widgets/__tests__/__snapshots__/{number-line_test.jsx.snap → number-line.test.ts.snap} +0 -0
  1203. /package/src/widgets/__tests__/__snapshots__/{numeric-input_test.jsx.snap → numeric-input.test.ts.snap} +0 -0
  1204. /package/src/widgets/__tests__/__snapshots__/{orderer_test.jsx.snap → orderer.test.ts.snap} +0 -0
  1205. /package/src/widgets/__tests__/__snapshots__/{passage-ref_test.jsx.snap → passage-ref.test.ts.snap} +0 -0
  1206. /package/src/widgets/__tests__/__snapshots__/{passage_test.jsx.snap → passage.test.tsx.snap} +0 -0
  1207. /package/src/widgets/__tests__/__snapshots__/{radio_test.jsx.snap → radio.test.ts.snap} +0 -0
  1208. /package/src/widgets/__tests__/__snapshots__/{unit_test.jsx.snap → unit.test.tsx.snap} +0 -0
  1209. /package/src/widgets/__tests__/__snapshots__/{video_test.jsx.snap → video.test.ts.snap} +0 -0
@@ -0,0 +1,2921 @@
1
+ /* eslint-disable @babel/no-invalid-this, react/no-unsafe, react/sort-comp */
2
+ import {number as knumber, point as kpoint} from "@khanacademy/kmath";
3
+ import * as i18n from "@khanacademy/wonder-blocks-i18n";
4
+ import $ from "jquery";
5
+ import debounce from "lodash.debounce";
6
+ import * as React from "react";
7
+ import _ from "underscore";
8
+
9
+ import Graph from "../components/graph";
10
+ import Interactive2 from "../interactive2";
11
+ import WrappedLine from "../interactive2/wrapped-line";
12
+ import {Errors} from "../logging/log";
13
+ import {PerseusError} from "../perseus-error";
14
+ import Util from "../util";
15
+ import KhanColors from "../util/colors";
16
+ import GraphUtils from "../util/graph-utils";
17
+ import {getInteractiveBoxFromSizeClass} from "../util/sizing-utils";
18
+
19
+ import type {Coord, Line} from "../interactive2/types";
20
+ import type {
21
+ PerseusGraphType,
22
+ PerseusGraphTypeAngle,
23
+ PerseusGraphTypePoint,
24
+ PerseusGraphTypePolygon,
25
+ PerseusGraphTypeSegment,
26
+ PerseusInteractiveGraphWidgetOptions,
27
+ } from "../perseus-types";
28
+ import type {PerseusScore, WidgetExports, WidgetProps} from "../types";
29
+
30
+ type Range = ReadonlyArray<number>; // This should really be a readonly tuple of [number, number]
31
+ type SineCoefficient = [
32
+ /* amplitude */ number,
33
+ /* angularFrequency */ number,
34
+ /* phase */ number,
35
+ /* verticalOffset */ number,
36
+ ];
37
+ type QuadraticCoefficient = [/* a */ number, /* b */ number, /* c */ number];
38
+
39
+ const {DeprecationMixin} = Util;
40
+
41
+ const TRASH_ICON_URI =
42
+ "https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
43
+
44
+ const defaultBackgroundImage = {
45
+ url: null,
46
+ } as const;
47
+
48
+ const eq = Util.eq;
49
+ const deepEq = Util.deepEq;
50
+
51
+ const UNLIMITED = "unlimited" as const;
52
+
53
+ // Sample background image:
54
+ // https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
55
+
56
+ function ccw(a: Coord, b: Coord, c: Coord): number {
57
+ return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
58
+ }
59
+
60
+ function collinear(a: Coord, b: Coord, c: Coord): boolean {
61
+ return eq(ccw(a, b, c), 0);
62
+ }
63
+
64
+ function sign(val: number): 0 | 1 | -1 {
65
+ if (eq(val, 0)) {
66
+ return 0;
67
+ }
68
+ return val > 0 ? 1 : -1;
69
+ }
70
+
71
+ // default to defaultValue if actual is null or undefined
72
+ function defaultVal<T>(actual: T | null | undefined, defaultValue: T): T {
73
+ return actual == null ? defaultValue : actual;
74
+ }
75
+
76
+ // Given rect bounding points A and B, whether point C is inside the rect
77
+ function pointInRect(a: Coord, b: Coord, c: Coord): boolean {
78
+ return (
79
+ c[0] <= Math.max(a[0], b[0]) &&
80
+ c[0] >= Math.min(a[0], b[0]) &&
81
+ c[1] <= Math.max(a[1], b[1]) &&
82
+ c[1] >= Math.min(a[1], b[1])
83
+ );
84
+ }
85
+
86
+ // Whether line segment AB intersects line segment CD
87
+ // http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
88
+ function intersects(ab: Line, cd: Line): boolean {
89
+ const triplets = [
90
+ [ab[0], ab[1], cd[0]],
91
+ [ab[0], ab[1], cd[1]],
92
+ [cd[0], cd[1], ab[0]],
93
+ [cd[0], cd[1], ab[1]],
94
+ ];
95
+
96
+ const orientations = _.map(triplets, function (triplet) {
97
+ // @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
98
+ return sign(ccw(...triplet));
99
+ });
100
+
101
+ if (
102
+ orientations[0] !== orientations[1] &&
103
+ orientations[2] !== orientations[3]
104
+ ) {
105
+ return true;
106
+ }
107
+
108
+ for (let i = 0; i < 4; i++) {
109
+ // @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
110
+ if (orientations[i] === 0 && pointInRect(...triplets[i])) {
111
+ return true;
112
+ }
113
+ }
114
+
115
+ return false;
116
+ }
117
+
118
+ function vector(a, b) {
119
+ return _.map(_.zip(a, b), function (pair) {
120
+ return pair[0] - pair[1];
121
+ });
122
+ }
123
+
124
+ function magnitude(v: ReadonlyArray<Coord>): number {
125
+ return Math.sqrt(
126
+ _.reduce(
127
+ v,
128
+ function (memo, el) {
129
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'Coord' is not assignable to parameter of type 'number'.
130
+ return memo + Math.pow(el, 2);
131
+ },
132
+ 0,
133
+ ),
134
+ );
135
+ }
136
+
137
+ function dotProduct(a: Coord, b: Coord): number {
138
+ return _.reduce(
139
+ _.zip(a, b),
140
+ function (memo, pair) {
141
+ return memo + pair[0] * pair[1];
142
+ },
143
+ 0,
144
+ );
145
+ }
146
+
147
+ function sideLengths(coords: ReadonlyArray<Coord>): ReadonlyArray<number> {
148
+ const segments = _.zip(coords, rotate(coords));
149
+ return segments.map(function (segment) {
150
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
151
+ return magnitude(vector(...segment));
152
+ });
153
+ }
154
+
155
+ // Based on http://math.stackexchange.com/a/151149
156
+ function angleMeasures(coords: ReadonlyArray<Coord>): ReadonlyArray<number> {
157
+ const triplets = _.zip(rotate(coords, -1), coords, rotate(coords, 1));
158
+
159
+ const offsets = _.map(triplets, function (triplet) {
160
+ const p = vector(triplet[1], triplet[0]);
161
+ const q = vector(triplet[2], triplet[1]);
162
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'Coord'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
163
+ const raw = Math.acos(dotProduct(p, q) / (magnitude(p) * magnitude(q)));
164
+ // @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
165
+ return sign(ccw(...triplet)) > 0 ? raw : -raw;
166
+ });
167
+
168
+ const sum = _.reduce(
169
+ offsets,
170
+ function (memo, arg) {
171
+ return memo + arg;
172
+ },
173
+ 0,
174
+ );
175
+
176
+ return _.map(offsets, function (offset) {
177
+ return sum > 0 ? Math.PI - offset : Math.PI + offset;
178
+ });
179
+ }
180
+
181
+ // Whether two polygons are similar (or if specified, congruent)
182
+ function similar(
183
+ coords1: ReadonlyArray<Coord>,
184
+ coords2: ReadonlyArray<Coord>,
185
+ tolerance: number,
186
+ ): boolean {
187
+ if (coords1.length !== coords2.length) {
188
+ return false;
189
+ }
190
+
191
+ const n = coords1.length;
192
+
193
+ const angles1 = angleMeasures(coords1);
194
+ const angles2 = angleMeasures(coords2);
195
+
196
+ const sides1 = sideLengths(coords1);
197
+ const sides2 = sideLengths(coords2);
198
+
199
+ for (let i = 0; i < 2 * n; i++) {
200
+ let angles = angles2.slice();
201
+ let sides = sides2.slice();
202
+
203
+ // Reverse angles and sides to allow matching reflected polygons
204
+ if (i >= n) {
205
+ angles.reverse();
206
+ sides.reverse();
207
+ // Since sides are calculated from two coordinates,
208
+ // simply reversing results in an off by one error
209
+ // @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
210
+ sides = rotate(sides, 1);
211
+ }
212
+
213
+ // @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
214
+ angles = rotate(angles, i);
215
+ // @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
216
+ sides = rotate(sides, i);
217
+
218
+ if (deepEq(angles1, angles)) {
219
+ const sidePairs = _.zip(sides1, sides);
220
+
221
+ const factors = _.map(sidePairs, function (pair) {
222
+ return pair[0] / pair[1];
223
+ });
224
+
225
+ const same = _.all(factors, function (factor) {
226
+ return eq(factors[0], factor);
227
+ });
228
+
229
+ const congruentEnough = _.all(sidePairs, function (pair) {
230
+ return knumber.equal(pair[0], pair[1], tolerance);
231
+ });
232
+
233
+ if (same && congruentEnough) {
234
+ return true;
235
+ }
236
+ }
237
+ }
238
+
239
+ return false;
240
+ }
241
+
242
+ // Less than or approximately equal
243
+ function leq(a: any, b) {
244
+ return a < b || eq(a, b);
245
+ }
246
+
247
+ // Given triangle with sides ABC return angle opposite side C in degrees
248
+ function lawOfCosines(a: number, b: number, c: number): number {
249
+ return (Math.acos((a * a + b * b - c * c) / (2 * a * b)) * 180) / Math.PI;
250
+ }
251
+
252
+ function canonicalSineCoefficients([
253
+ amplitude,
254
+ angularFrequency,
255
+ phase,
256
+ verticalOffset,
257
+ ]: [any, any, any, any]) {
258
+ // For a curve of the form f(x) = a * Sin(b * x - c) + d,
259
+ // this function ensures that a, b > 0, and c is its
260
+ // smallest possible positive value.
261
+
262
+ // Guarantee a > 0
263
+ if (amplitude < 0) {
264
+ amplitude *= -1;
265
+ angularFrequency *= -1;
266
+ phase *= -1;
267
+ }
268
+
269
+ const period = 2 * Math.PI;
270
+ // Guarantee b > 0
271
+ if (angularFrequency < 0) {
272
+ angularFrequency *= -1;
273
+ phase *= -1;
274
+ phase += period / 2;
275
+ }
276
+
277
+ // Guarantee c is smallest possible positive value
278
+ while (phase > 0) {
279
+ phase -= period;
280
+ }
281
+ while (phase < 0) {
282
+ phase += period;
283
+ }
284
+
285
+ return [amplitude, angularFrequency, phase, verticalOffset];
286
+ }
287
+
288
+ // e.g. rotate([1, 2, 3]) -> [2, 3, 1]
289
+ function rotate<T>(array: ReadonlyArray<T>, n?: number): ReadonlyArray<T> {
290
+ n = typeof n === "undefined" ? 1 : n % array.length;
291
+ return array.slice(n).concat(array.slice(0, n));
292
+ }
293
+
294
+ function capitalize(str) {
295
+ return str.replace(/(?:^|-)(.)/g, function (match, letter) {
296
+ return letter.toUpperCase();
297
+ });
298
+ }
299
+
300
+ function getLineEquation(first: Coord, second: Coord): string {
301
+ if (eq(first[0], second[0])) {
302
+ return "x = " + first[0].toFixed(3);
303
+ }
304
+ const m = (second[1] - first[1]) / (second[0] - first[0]);
305
+ const b = first[1] - m * first[0];
306
+ return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
307
+ }
308
+
309
+ // Stolen from the wikipedia article
310
+ // http://en.wikipedia.org/wiki/Line-line_intersection
311
+ function getLineIntersection(
312
+ // TODO(LP-10725): update these to be 2-tuples
313
+ firstPoints: ReadonlyArray<Coord>,
314
+ secondPoints: ReadonlyArray<Coord>,
315
+ ): string {
316
+ const x1 = firstPoints[0][0];
317
+ const y1 = firstPoints[0][1];
318
+ const x2 = firstPoints[1][0];
319
+ const y2 = firstPoints[1][1];
320
+ const x3 = secondPoints[0][0];
321
+ const y3 = secondPoints[0][1];
322
+ const x4 = secondPoints[1][0];
323
+ const y4 = secondPoints[1][1];
324
+
325
+ const determinant = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
326
+
327
+ if (Math.abs(determinant) < 1e-9) {
328
+ return "Lines are parallel";
329
+ }
330
+ const x =
331
+ ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) /
332
+ determinant;
333
+ const y =
334
+ ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) /
335
+ determinant;
336
+ return "Intersection: (" + x.toFixed(3) + ", " + y.toFixed(3) + ")";
337
+ }
338
+
339
+ function numSteps(range: Range, step: number) {
340
+ return Math.floor((range[1] - range[0]) / step);
341
+ }
342
+
343
+ const deprecatedProps = {
344
+ showGraph: function (props) {
345
+ return {markings: props.showGraph ? "graph" : "none"};
346
+ },
347
+ } as const;
348
+
349
+ const _getShouldShowInstructions: (arg1: Props) => boolean = (props) => {
350
+ return (
351
+ _isClickToAddPoints(props) &&
352
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
353
+ (props.graph.coords == null || props.graph.coords.length === 0)
354
+ );
355
+ };
356
+
357
+ const _isClickToAddPoints: (arg1: Props) => boolean = (props) => {
358
+ return (
359
+ (props.graph.type === "point" && props.graph.numPoints === UNLIMITED) ||
360
+ (props.graph.type === "polygon" && props.graph.numSides === UNLIMITED)
361
+ );
362
+ };
363
+
364
+ const makeInvalidTypeError = (
365
+ functionName: string,
366
+ graphType: string,
367
+ ): PerseusError => {
368
+ return new PerseusError(
369
+ `${functionName} called but current graph type is not a '${graphType}'`,
370
+ Errors.NotAllowed,
371
+ {metadata: {graphType}},
372
+ );
373
+ };
374
+
375
+ type RenderProps = PerseusInteractiveGraphWidgetOptions; // There's no transform function in exports
376
+ type Rubric = PerseusInteractiveGraphWidgetOptions;
377
+ type Props = WidgetProps<RenderProps, Rubric>;
378
+ type State = any;
379
+ type DefaultProps = {
380
+ labels: Props["labels"];
381
+ range: Props["range"];
382
+ step: Props["step"];
383
+ backgroundImage: Props["backgroundImage"];
384
+ markings: Props["markings"];
385
+ showTooltips: Props["showTooltips"];
386
+ showProtractor: Props["showProtractor"];
387
+ showRuler: Props["showRuler"];
388
+ rulerLabel: Props["rulerLabel"];
389
+ rulerTicks: Props["rulerTicks"];
390
+ graph: Props["graph"];
391
+ };
392
+
393
+ class InteractiveGraph extends React.Component<Props, State> {
394
+ angle: any | null | undefined;
395
+ circle: any | null | undefined;
396
+ graphie: any | null | undefined;
397
+ horizHairline: any | null | undefined;
398
+ line: any | null | undefined;
399
+ lines: ReadonlyArray<ReadonlyArray<Coord>> | null | undefined;
400
+ parabola: any | null | undefined;
401
+ pointA: any | null | undefined;
402
+ pointB: any | null | undefined;
403
+ pointC: any | null | undefined;
404
+ // @ts-expect-error [FEI-5003] - TS2564 - Property 'points' has no initializer and is not definitely assigned in the constructor.
405
+ points: Array<any>;
406
+ polygon: any | null | undefined;
407
+ // @ts-expect-error [FEI-5003] - TS2564 - Property 'shouldResetGraphie' has no initializer and is not definitely assigned in the constructor.
408
+ shouldResetGraphie: boolean;
409
+ sinusoid: any | null | undefined;
410
+ trashCan: any | null | undefined;
411
+ vertHairline: any | null | undefined;
412
+
413
+ static defaultProps: DefaultProps = {
414
+ labels: ["x", "y"],
415
+ range: [
416
+ [-10, 10],
417
+ [-10, 10],
418
+ ],
419
+ step: [1, 1],
420
+ // $FlowFixMe[prop-missing]
421
+ // @ts-expect-error [FEI-5003] - TS2739 - Type '{ readonly url: null; }' is missing the following properties from type 'PerseusImageBackground': width, height
422
+ backgroundImage: defaultBackgroundImage,
423
+ markings: "graph",
424
+ showTooltips: false,
425
+ showProtractor: false,
426
+ showRuler: false,
427
+ rulerLabel: "",
428
+ rulerTicks: 10,
429
+ graph: {
430
+ type: "linear",
431
+ },
432
+ };
433
+
434
+ state: State = {
435
+ shouldShowInstructions: _getShouldShowInstructions(this.props),
436
+ };
437
+
438
+ // TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
439
+ UNSAFE_componentWillMount() {
440
+ DeprecationMixin.UNSAFE_componentWillMount.call(this);
441
+ }
442
+
443
+ componentDidMount() {
444
+ // eslint-disable-next-line react/no-string-refs
445
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
446
+ this.setGraphie(this.refs.graph.graphie());
447
+ }
448
+
449
+ UNSAFE_componentWillReceiveProps(nextProps: Props) {
450
+ if (this.isClickToAddPoints() !== this.isClickToAddPoints(nextProps)) {
451
+ this.shouldResetGraphie = true;
452
+ this.setState({
453
+ shouldShowInstructions: _getShouldShowInstructions(nextProps),
454
+ });
455
+ }
456
+
457
+ if (
458
+ this.props.backgroundImage?.url !==
459
+ nextProps.backgroundImage?.url ||
460
+ this.props.backgroundImage !== nextProps.backgroundImage ||
461
+ this.props.containerSizeClass !== nextProps.containerSizeClass
462
+ ) {
463
+ this.shouldResetGraphie = true;
464
+ }
465
+ }
466
+
467
+ componentDidUpdate(prevProps: Props, prevState: State) {
468
+ // TODO(jeremy): This feels underspecified. For example, if the props
469
+ // change the coords of a segment graph, but not the numSegments, we'd
470
+ // not count that as an update.
471
+ const oldType = prevProps.graph.type;
472
+ const newType = this.props.graph.type;
473
+ if (
474
+ oldType !== newType ||
475
+ (prevProps.graph.type === "angle" &&
476
+ this.props.graph.type === "angle" &&
477
+ this.areAngleGraphsEqual(prevProps.graph, this.props.graph)) ||
478
+ (prevProps.graph.type === "point" &&
479
+ this.props.graph.type === "point" &&
480
+ this.arePointGraphsEqual(prevProps.graph, this.props.graph)) ||
481
+ (prevProps.graph.type === "polygon" &&
482
+ this.props.graph.type === "polygon" &&
483
+ this.arePolygonGraphsEqual(
484
+ prevProps.graph,
485
+ this.props.graph,
486
+ )) ||
487
+ (prevProps.graph.type === "segment" &&
488
+ this.props.graph.type === "segment" &&
489
+ this.areSegmentGraphsEqual(prevProps.graph, this.props.graph))
490
+ ) {
491
+ this["remove" + capitalize(oldType) + "Controls"]();
492
+ this["add" + capitalize(newType) + "Controls"]();
493
+ }
494
+ if (this.shouldResetGraphie) {
495
+ this.resetGraphie();
496
+ }
497
+ }
498
+
499
+ _getShouldShowInstructions: (arg1?: Props | null | undefined) => boolean = (
500
+ props,
501
+ ) => {
502
+ props = props || this.props;
503
+ return (
504
+ this.isClickToAddPoints(props) &&
505
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
506
+ (props.graph.coords == null || props.graph.coords.length === 0)
507
+ );
508
+ };
509
+
510
+ deprecatedProps: any = deprecatedProps;
511
+
512
+ setGraphie: (arg1: any) => void = (newGraphie) => {
513
+ this.graphie = newGraphie;
514
+ this.setupGraphie();
515
+ };
516
+
517
+ handleAddPointsMouseDown: (arg1: Coord) => void = (coord) => {
518
+ // This function should only be called when this.isClickToAddPoints()
519
+ // is true
520
+ if (!this.isClickToAddPoints()) {
521
+ throw new PerseusError(
522
+ "handleAddPointsClick should not be registered" +
523
+ "when isClickToAddPoints() is false",
524
+ Errors.NotAllowed,
525
+ );
526
+ }
527
+ if (!this.isCoordInTrash(coord)) {
528
+ let point;
529
+
530
+ if (this.props.graph.type === "point") {
531
+ point = this.createPointForPointsType(
532
+ coord,
533
+ this.points.length,
534
+ );
535
+ if (!point.constrain()) {
536
+ point.remove();
537
+ return;
538
+ }
539
+ this.points.push(point);
540
+
541
+ // interactive2 allows us to grab the point
542
+ const idx = this.points.length - 1;
543
+ this.points[idx].grab(coord);
544
+
545
+ this.updateCoordsFromPoints();
546
+ } else if (this.props.graph.type === "polygon") {
547
+ if (this.polygon?.closed()) {
548
+ return;
549
+ }
550
+ point = this.createPointForPolygonType(
551
+ coord,
552
+ this.points.length,
553
+ );
554
+ this.points.push(point);
555
+
556
+ const idx = this.points.length - 1;
557
+ this.points[idx].grab(coord);
558
+
559
+ // We don't call updateCoordsFromPoints for
560
+ // polygons, since the polygon won't be
561
+ // closed yet.
562
+ this.updatePolygon();
563
+ }
564
+
565
+ this.setState({
566
+ shouldShowInstructions: false,
567
+ });
568
+ }
569
+ };
570
+
571
+ resetGraphie: () => void = () => {
572
+ this.shouldResetGraphie = false;
573
+ this.parabola = null;
574
+ this.sinusoid = null;
575
+ // eslint-disable-next-line react/no-string-refs
576
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'reset' does not exist on type 'ReactInstance'.
577
+ this.refs.graph.reset();
578
+ };
579
+
580
+ setupGraphie: () => void = () => {
581
+ this.setTrashCanVisibility(0);
582
+ if (this.isClickToAddPoints()) {
583
+ this.setTrashCanVisibility(0.5);
584
+ }
585
+
586
+ if (this.props.apiOptions.isMobile) {
587
+ this.horizHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
588
+ normalStyle: {
589
+ strokeWidth: 1,
590
+ },
591
+ });
592
+ this.horizHairline.attr({
593
+ stroke: KhanColors.INTERACTIVE,
594
+ });
595
+ this.horizHairline?.hide();
596
+
597
+ this.vertHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
598
+ normalStyle: {
599
+ strokeWidth: 1,
600
+ },
601
+ });
602
+ this.vertHairline.attr({
603
+ stroke: KhanColors.INTERACTIVE,
604
+ });
605
+ this.vertHairline?.hide();
606
+ }
607
+
608
+ const type = this.props.graph.type;
609
+ this["add" + capitalize(type) + "Controls"]();
610
+ };
611
+
612
+ showHairlines: (arg1: Coord) => void = (point) => {
613
+ if (this.props.apiOptions.isMobile && this.props.markings !== "none") {
614
+ // Hairlines are already initialized when the graph is loaded, so
615
+ // here we just move them to the updated location and make them
616
+ // visible.
617
+ this.horizHairline?.moveTo(
618
+ [this.props.range[0][0], point[1]],
619
+ [this.props.range[0][1], point[1]],
620
+ );
621
+
622
+ this.horizHairline?.show();
623
+
624
+ this.vertHairline?.moveTo(
625
+ [point[0], this.props.range[1][0]],
626
+ [point[0], this.props.range[1][1]],
627
+ );
628
+
629
+ this.vertHairline?.show();
630
+ }
631
+ };
632
+
633
+ hideHairlines: () => void = () => {
634
+ if (this.props.apiOptions.isMobile) {
635
+ this.horizHairline?.hide();
636
+ this.vertHairline?.hide();
637
+ }
638
+ };
639
+
640
+ setTrashCanVisibility: (arg1: number) => void = (opacity) => {
641
+ const graphie = this.graphie;
642
+
643
+ if (knumber.equal(opacity, 0)) {
644
+ if (this.trashCan) {
645
+ this.trashCan.remove();
646
+ this.trashCan = null;
647
+ }
648
+ } else if (!this.props.apiOptions.isMobile) {
649
+ // Only if trash tooltips are not being used, we initialize the old
650
+ // trash can area.
651
+ if (!this.trashCan) {
652
+ this.trashCan = graphie?.raphael?.image(
653
+ TRASH_ICON_URI,
654
+ graphie.xpixels - 40,
655
+ graphie.ypixels - 40,
656
+ 40,
657
+ 40,
658
+ );
659
+ }
660
+ this.trashCan?.attr({
661
+ opacity: opacity,
662
+ });
663
+ }
664
+ };
665
+
666
+ isClickToAddPoints: (arg1?: Props | null | undefined) => boolean = (
667
+ props,
668
+ ) => {
669
+ props = props || this.props;
670
+ return _isClickToAddPoints(props);
671
+ };
672
+
673
+ areAngleGraphsEqual(
674
+ prevGraph: PerseusGraphTypeAngle,
675
+ currentGraph: PerseusGraphTypeAngle,
676
+ ): boolean {
677
+ return (
678
+ prevGraph.allowReflexAngles !== currentGraph.allowReflexAngles ||
679
+ prevGraph.angleOffsetDeg !== currentGraph.angleOffsetDeg ||
680
+ prevGraph.snapDegrees !== currentGraph.snapDegrees
681
+ );
682
+ }
683
+
684
+ arePointGraphsEqual(
685
+ prevGraph: PerseusGraphTypePoint,
686
+ currentGraph: PerseusGraphTypePoint,
687
+ ): boolean {
688
+ return prevGraph.numPoints !== currentGraph.numPoints;
689
+ }
690
+
691
+ arePolygonGraphsEqual(
692
+ prevGraph: PerseusGraphTypePolygon,
693
+ currentGraph: PerseusGraphTypePolygon,
694
+ ): boolean {
695
+ return (
696
+ prevGraph.numSides !== currentGraph.numSides ||
697
+ prevGraph.showAngles !== currentGraph.showAngles ||
698
+ prevGraph.showSides !== currentGraph.showSides ||
699
+ prevGraph.snapTo !== currentGraph.snapTo
700
+ );
701
+ }
702
+
703
+ areSegmentGraphsEqual(
704
+ prevGraph: PerseusGraphTypeSegment,
705
+ currentGraph: PerseusGraphTypeSegment,
706
+ ): boolean {
707
+ return prevGraph.numSegments !== currentGraph.numSegments;
708
+ }
709
+
710
+ _lineStroke: () => {
711
+ ["stroke-width"]?: number;
712
+ } = () => {
713
+ // This should probably use: this.props.apiOptions.isMobile
714
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'isMobile' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
715
+ return this.props.isMobile ? {"stroke-width": 3} : {};
716
+ };
717
+
718
+ addLine: (arg1: string) => void = (type) => {
719
+ const self = this;
720
+ const graphie = self.graphie;
721
+ const coords = InteractiveGraph.getLineCoords(
722
+ self.props.graph,
723
+ self.props,
724
+ );
725
+
726
+ const points = (self.points = _.map(coords, (coord) => {
727
+ return Interactive2.addMaybeMobileMovablePoint(this, {
728
+ coord: coord,
729
+ constraints: [
730
+ // $FlowFixMe[prop-missing]
731
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
732
+ Interactive2.MovablePoint.constraints.bound(),
733
+ // $FlowFixMe[prop-missing]
734
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
735
+ Interactive2.MovablePoint.constraints.snap(),
736
+ ],
737
+ onMove: () => {
738
+ const graph = _.extend({}, self.props.graph, {
739
+ coords: _.invoke(points, "coord"),
740
+ });
741
+ self.onChange({graph: graph});
742
+ },
743
+ });
744
+ }));
745
+
746
+ const lineConfig = {
747
+ points: points,
748
+ static: true,
749
+ normalStyle: {
750
+ stroke: this.props.apiOptions.isMobile
751
+ ? KhanColors.BLUE_C
752
+ : KhanColors.INTERACTIVE,
753
+ ...this._lineStroke(),
754
+ },
755
+ } as const;
756
+
757
+ if (type === "line") {
758
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'extendLine' does not exist on type '{ readonly points: any[]; readonly static: true; readonly normalStyle: { readonly "stroke-width"?: number | undefined; readonly stroke: "#71B307" | "#63D9EA"; }; }'.
759
+ lineConfig.extendLine = true;
760
+ } else if (type === "ray") {
761
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'extendRay' does not exist on type '{ readonly points: any[]; readonly static: true; readonly normalStyle: { readonly "stroke-width"?: number | undefined; readonly stroke: "#71B307" | "#63D9EA"; }; }'.
762
+ lineConfig.extendRay = true;
763
+ }
764
+
765
+ self.line = Interactive2.addMovableLine(graphie, lineConfig);
766
+
767
+ // A and B can't be in the same place
768
+ points[0].listen("constraints", "isLine", (coord) => {
769
+ return !kpoint.equal(coord, points[1].coord());
770
+ });
771
+ points[1].listen("constraints", "isLine", (coord) => {
772
+ return !kpoint.equal(coord, points[0].coord());
773
+ });
774
+ };
775
+
776
+ removeLine: () => void = () => {
777
+ _.invoke(this.points, "remove");
778
+ this.line?.remove();
779
+ };
780
+
781
+ addLinearControls: () => void = () => {
782
+ this.addLine("line");
783
+ };
784
+
785
+ removeLinearControls: () => void = () => {
786
+ this.removeLine();
787
+ };
788
+
789
+ addQuadraticControls: () => void = () => {
790
+ if (this.props.graph.type !== "quadratic") {
791
+ throw makeInvalidTypeError("addQuadraticControls", "quadratic");
792
+ }
793
+
794
+ let coords = this.props.graph.coords;
795
+ if (!coords) {
796
+ coords = InteractiveGraph.defaultQuadraticCoords(this.props);
797
+ }
798
+
799
+ const onMoveHandler = () => {
800
+ const graph = _.extend({}, this.props.graph, {
801
+ // $FlowFixMe[incompatible-use]
802
+ // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'.
803
+ coords: [pointA.coord(), pointB.coord(), pointC.coord()],
804
+ });
805
+ this.onChange({graph});
806
+ this.updateQuadratic();
807
+ };
808
+
809
+ // NOTE(jeresig): This code attempts to access the variable while it's
810
+ // still being defined, it should be refactored! The callbacks appear
811
+ // to be executed synchronously, which causes this issue.
812
+ let pointA = null;
813
+ pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
814
+ coord: coords[0],
815
+ constraints: [
816
+ // $FlowFixMe[prop-missing]
817
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
818
+ Interactive2.MovablePoint.constraints.bound(),
819
+ // $FlowFixMe[prop-missing]
820
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
821
+ Interactive2.MovablePoint.constraints.snap(),
822
+ (coord) => {
823
+ return (
824
+ !pointA ||
825
+ // $FlowFixMe[incompatible-use]
826
+ // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'.
827
+ (coord[0] !== pointB.coord()[0] &&
828
+ // $FlowFixMe[incompatible-use]
829
+ // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'.
830
+ coord[0] !== pointC.coord()[0])
831
+ );
832
+ },
833
+ ],
834
+ onMove: onMoveHandler,
835
+ });
836
+
837
+ // NOTE(jeresig): This code attempts to access the variable while it's
838
+ // still being defined, it should be refactored! The callbacks appear
839
+ // to be executed synchronously, which causes this issue.
840
+ let pointB = null;
841
+ pointB = this.pointB = Interactive2.addMaybeMobileMovablePoint(this, {
842
+ coord: coords[1],
843
+ constraints: [
844
+ // $FlowFixMe[prop-missing]
845
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
846
+ Interactive2.MovablePoint.constraints.bound(),
847
+ // $FlowFixMe[prop-missing]
848
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
849
+ Interactive2.MovablePoint.constraints.snap(),
850
+ (coord: any) => {
851
+ return (
852
+ !pointB ||
853
+ // $FlowFixMe[incompatible-use]
854
+ // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'.
855
+ (coord[0] !== pointA.coord()[0] &&
856
+ // $FlowFixMe[incompatible-use]
857
+ // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'.
858
+ coord[0] !== pointC.coord()[0])
859
+ );
860
+ },
861
+ ],
862
+ onMove: onMoveHandler,
863
+ });
864
+
865
+ // NOTE(jeresig): This code attempts to access the variable while it's
866
+ // still being defined, it should be refactored! The callbacks appear
867
+ // to be executed synchronously, which causes this issue.
868
+ let pointC = null;
869
+ pointC = this.pointC = Interactive2.addMaybeMobileMovablePoint(this, {
870
+ coord: coords[2],
871
+ constraints: [
872
+ // $FlowFixMe[prop-missing]
873
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
874
+ Interactive2.MovablePoint.constraints.bound(),
875
+ // $FlowFixMe[prop-missing]
876
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
877
+ Interactive2.MovablePoint.constraints.snap(),
878
+ (coord: any) => {
879
+ return (
880
+ !pointC ||
881
+ // $FlowFixMe[incompatible-use]
882
+ // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'.
883
+ (coord[0] !== pointA.coord()[0] &&
884
+ // $FlowFixMe[incompatible-use]
885
+ // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'.
886
+ coord[0] !== pointB.coord()[0])
887
+ );
888
+ },
889
+ ],
890
+ onMove: onMoveHandler,
891
+ });
892
+
893
+ this.updateQuadratic();
894
+ };
895
+
896
+ updateQuadratic: () => void = () => {
897
+ const coeffs = InteractiveGraph.getCurrentQuadraticCoefficients(
898
+ this.props,
899
+ );
900
+ if (!coeffs) {
901
+ return;
902
+ }
903
+
904
+ // Extract coefficients the parabola
905
+ const a = coeffs[0];
906
+ const b = coeffs[1];
907
+ const c = coeffs[2];
908
+
909
+ // Plot and style
910
+ if (this.parabola) {
911
+ const path = this.graphie?.svgParabolaPath(a, b, c);
912
+ this.parabola?.attr({path: path});
913
+ } else {
914
+ this.parabola = this.graphie?.parabola(a, b, c);
915
+ this.parabola?.attr({
916
+ stroke: this.props.apiOptions.isMobile
917
+ ? KhanColors.BLUE_C
918
+ : KhanColors.DYNAMIC,
919
+ ...this._lineStroke(),
920
+ });
921
+ this.parabola?.toBack();
922
+ }
923
+ };
924
+
925
+ removeQuadraticControls: () => void = () => {
926
+ this.pointA?.remove();
927
+ this.pointB?.remove();
928
+ this.pointC?.remove();
929
+ if (this.parabola) {
930
+ this.parabola.remove();
931
+ this.parabola = null;
932
+ }
933
+ };
934
+
935
+ addSinusoidControls: () => void = () => {
936
+ if (this.props.graph.type !== "sinusoid") {
937
+ throw makeInvalidTypeError("addSinusoidControls", "sinusoid");
938
+ }
939
+ let coords = this.props.graph.coords;
940
+ if (!coords) {
941
+ coords = InteractiveGraph.defaultSinusoidCoords(this.props);
942
+ }
943
+
944
+ const onMoveHandler = () => {
945
+ const graph = _.extend({}, this.props.graph, {
946
+ // $FlowFixMe[incompatible-use]
947
+ // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'.
948
+ coords: [pointA.coord(), pointB.coord()],
949
+ });
950
+ this.onChange({graph: graph});
951
+ this.updateSinusoid();
952
+ };
953
+
954
+ // NOTE(jeresig): This code attempts to access the variable while it's
955
+ // still being defined, it should be refactored! The callbacks appear
956
+ // to be executed synchronously, which causes this issue.
957
+ let pointA = null;
958
+ pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
959
+ coord: coords[0],
960
+ constraints: [
961
+ // $FlowFixMe[prop-missing]
962
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
963
+ Interactive2.MovablePoint.constraints.bound(),
964
+ // $FlowFixMe[prop-missing]
965
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
966
+ Interactive2.MovablePoint.constraints.snap(),
967
+ (coord: any) => {
968
+ return !pointA || coord[0] !== pointB.coord()[0];
969
+ },
970
+ ],
971
+ onMove: onMoveHandler,
972
+ });
973
+
974
+ const pointB = (this.pointB = Interactive2.addMaybeMobileMovablePoint(
975
+ this,
976
+ {
977
+ coord: coords[1],
978
+ constraints: [
979
+ // $FlowFixMe[prop-missing]
980
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
981
+ Interactive2.MovablePoint.constraints.bound(),
982
+ // $FlowFixMe[prop-missing]
983
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
984
+ Interactive2.MovablePoint.constraints.snap(),
985
+ (coord: any) => {
986
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'coord' does not exist on type 'never'.
987
+ return !pointA || coord[0] !== pointA.coord()[0];
988
+ },
989
+ ],
990
+ onMove: onMoveHandler,
991
+ },
992
+ ));
993
+
994
+ this.updateSinusoid();
995
+ };
996
+
997
+ updateSinusoid: () => void = () => {
998
+ const coeffs = InteractiveGraph.getCurrentSinusoidCoefficients(
999
+ this.props,
1000
+ );
1001
+ if (!coeffs) {
1002
+ return;
1003
+ }
1004
+
1005
+ const a = coeffs[0];
1006
+ const b = coeffs[1];
1007
+ const c = coeffs[2];
1008
+ const d = coeffs[3];
1009
+
1010
+ // Plot and style
1011
+ if (this.sinusoid) {
1012
+ const path = this.graphie?.svgSinusoidPath(a, b, c, d);
1013
+ this.sinusoid?.attr({path: path});
1014
+ } else {
1015
+ this.sinusoid = this.graphie?.sinusoid(a, b, c, d);
1016
+ this.sinusoid?.attr({
1017
+ stroke: this.props.apiOptions.isMobile
1018
+ ? KhanColors.BLUE_C
1019
+ : KhanColors.DYNAMIC,
1020
+ ...this._lineStroke(),
1021
+ });
1022
+ this.sinusoid?.toBack();
1023
+ }
1024
+ };
1025
+
1026
+ removeSinusoidControls: () => void = () => {
1027
+ this.pointA?.remove();
1028
+ this.pointB?.remove();
1029
+ if (this.sinusoid) {
1030
+ this.sinusoid.remove();
1031
+ this.sinusoid = null;
1032
+ }
1033
+ };
1034
+
1035
+ addCircleControls: () => void = () => {
1036
+ const graphie = this.graphie;
1037
+ const minSnap = _.min(graphie.snap);
1038
+
1039
+ const circle = (this.circle = graphie?.addCircleGraph({
1040
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'.
1041
+ center: this.props.graph.center || [0, 0],
1042
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
1043
+ radius: this.props.graph.radius || _.min(this.props.step),
1044
+ snapX: graphie.snap[0],
1045
+ snapY: graphie.snap[1],
1046
+ minRadius: minSnap,
1047
+ snapRadius: minSnap,
1048
+ }));
1049
+
1050
+ $(circle).on("move", () => {
1051
+ const graph = _.extend({}, this.props.graph, {
1052
+ // $FlowFixMe[incompatible-use]
1053
+ center: circle.center,
1054
+ // $FlowFixMe[incompatible-use]
1055
+ radius: circle.radius,
1056
+ });
1057
+ this.onChange({graph: graph});
1058
+ });
1059
+ };
1060
+
1061
+ removeCircleControls: () => void = () => {
1062
+ this.circle?.remove();
1063
+ };
1064
+
1065
+ addLinearSystemControls: () => void = () => {
1066
+ const graphie = this.graphie;
1067
+ const coords = InteractiveGraph.getLinearSystemCoords(
1068
+ this.props.graph,
1069
+ this.props,
1070
+ );
1071
+
1072
+ const segmentColors = [KhanColors.INTERACTIVE, KhanColors.GREEN];
1073
+ const points = (this.points = _.map(
1074
+ coords,
1075
+ (segmentCoords, segmentIndex) => {
1076
+ // NOTE(jeresig): This code attempts to access the variable
1077
+ // while it's still being defined, it should be refactored!
1078
+ // The callbacks appear to be executed synchronously, which
1079
+ // causes this issue.
1080
+ const segmentPoints: Array<any> = [];
1081
+
1082
+ for (let i = 0; i < segmentCoords.length; i += 1) {
1083
+ const coord = segmentCoords[i];
1084
+ segmentPoints.push(
1085
+ Interactive2.addMaybeMobileMovablePoint(this, {
1086
+ coord: coord,
1087
+ constraints: [
1088
+ // $FlowFixMe[prop-missing]
1089
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1090
+ Interactive2.MovablePoint.constraints.bound(),
1091
+ // $FlowFixMe[prop-missing]
1092
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1093
+ Interactive2.MovablePoint.constraints.snap(),
1094
+ (coord) => {
1095
+ const otherSegment = segmentPoints[1 - i];
1096
+ if (!otherSegment) {
1097
+ // segment hasn't been defined yet
1098
+ // because we're still creating them
1099
+ return;
1100
+ }
1101
+ return !kpoint.equal(
1102
+ coord,
1103
+ otherSegment.coord(),
1104
+ );
1105
+ },
1106
+ ],
1107
+ onMove: () => {
1108
+ const graph = _.extend({}, this.props.graph, {
1109
+ coords: _.map(this.points, (segment) =>
1110
+ _.invoke(segment, "coord"),
1111
+ ),
1112
+ });
1113
+ this.onChange({graph: graph});
1114
+ },
1115
+ normalStyle: {
1116
+ fill: segmentColors[segmentIndex],
1117
+ },
1118
+ highlightStyle: {
1119
+ fill: segmentColors[segmentIndex],
1120
+ },
1121
+ }),
1122
+ );
1123
+ }
1124
+
1125
+ return segmentPoints;
1126
+ },
1127
+ ));
1128
+
1129
+ this.lines = _.map(points, (segmentPoints, segmentIndex) => {
1130
+ return Interactive2.addMovableLine(graphie, {
1131
+ points: segmentPoints,
1132
+ static: true,
1133
+ extendLine: true,
1134
+ normalStyle: {
1135
+ stroke: segmentColors[segmentIndex],
1136
+ },
1137
+ });
1138
+ });
1139
+ };
1140
+
1141
+ removeLinearSystemControls: () => void = () => {
1142
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
1143
+ _.invoke(this.lines, "remove");
1144
+ _.map(this.points, (segment) => _.invoke(segment, "remove"));
1145
+ };
1146
+
1147
+ isCoordInTrash: (arg1: Coord) => boolean = (coord) => {
1148
+ if (this.props.apiOptions.isMobile) {
1149
+ return false;
1150
+ }
1151
+
1152
+ const graphie = this.graphie;
1153
+ const screenPoint = graphie?.scalePoint(coord);
1154
+ return (
1155
+ // $FlowFixMe[incompatible-use]
1156
+ screenPoint[0] >= graphie.xpixels - 40 &&
1157
+ // $FlowFixMe[incompatible-use]
1158
+ screenPoint[1] >= graphie.ypixels - 40
1159
+ );
1160
+ };
1161
+
1162
+ createPointForPointsType: (arg1: Coord, arg2: number) => any = (
1163
+ coord,
1164
+ i,
1165
+ ) => {
1166
+ const self = this;
1167
+
1168
+ const remove = () => {
1169
+ self.points = _.filter(self.points, function (pt) {
1170
+ return pt !== point;
1171
+ });
1172
+ // update the correct answer box
1173
+ self.updateCoordsFromPoints();
1174
+
1175
+ // remove this movablePoint from graphie.
1176
+ // we wait to do this until we're not inside of
1177
+ // said point's onMoveEnd method so its state is
1178
+ // consistent throughout this method call
1179
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
1180
+ // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'.
1181
+ setTimeout(point.remove.bind(point), 0); // eslint-disable-line no-restricted-syntax
1182
+ };
1183
+
1184
+ // NOTE(jeresig): This code attempts to access the variable while it's
1185
+ // still being defined, it should be refactored! The callbacks appear
1186
+ // to be executed synchronously, which causes this issue.
1187
+ let point = null;
1188
+ point = Interactive2.addMaybeMobileMovablePoint(this, {
1189
+ coord: coord,
1190
+ constraints: [
1191
+ // $FlowFixMe[prop-missing]
1192
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1193
+ Interactive2.MovablePoint.constraints.bound(),
1194
+ // $FlowFixMe[prop-missing]
1195
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1196
+ Interactive2.MovablePoint.constraints.snap(),
1197
+ function (coord) {
1198
+ // TODO(jack): There ought to be a
1199
+ // MovablePoint.constraints.avoid
1200
+ // default that lets you do things like this
1201
+ return _.all(self.points, function (pt) {
1202
+ return point === pt || !kpoint.equal(coord, pt.coord());
1203
+ });
1204
+ },
1205
+ ],
1206
+ onMoveStart: function () {
1207
+ if (self.isClickToAddPoints()) {
1208
+ self.setTrashCanVisibility(1);
1209
+ }
1210
+ },
1211
+ onMove: self.updateCoordsFromPoints,
1212
+ onMoveEnd: function (coord) {
1213
+ if (self.isClickToAddPoints()) {
1214
+ if (self.isCoordInTrash(coord)) {
1215
+ remove();
1216
+ }
1217
+ // In case we mouseup'd off the graphie and that
1218
+ // stopped the move (in which case, we might not
1219
+ // be in isCoordInTrash()
1220
+ self.setTrashCanVisibility(0.5);
1221
+ }
1222
+ },
1223
+ ...(this.props.apiOptions.isMobile && self.isClickToAddPoints()
1224
+ ? {onRemove: remove}
1225
+ : {}),
1226
+ });
1227
+
1228
+ return point;
1229
+ };
1230
+
1231
+ removePoint: (arg1: Coord) => number | null | undefined = (point) => {
1232
+ let index = null;
1233
+ this.points = _.filter(this.points, function (pt, i) {
1234
+ if (pt === point) {
1235
+ // @ts-expect-error [FEI-5003] - TS2322 - Type 'number' is not assignable to type 'null'.
1236
+ index = i;
1237
+ return false;
1238
+ }
1239
+ return true;
1240
+ });
1241
+ return index;
1242
+ };
1243
+
1244
+ createPointForPolygonType: (arg1: Coord, arg2: number) => any = (
1245
+ coord,
1246
+ i,
1247
+ ) => {
1248
+ if (this.props.graph.type !== "polygon") {
1249
+ throw makeInvalidTypeError("createPointForPolygonType", "polygon");
1250
+ }
1251
+
1252
+ // TODO(alex): check against "grid" instead, use constants
1253
+ const snapToGrid = !_.contains(
1254
+ ["angles", "sides"],
1255
+ this.props.graph.snapTo,
1256
+ );
1257
+
1258
+ // Index relative to current point -> absolute index
1259
+ // NOTE: This does not work when isClickToAddPoints() == true,
1260
+ // as `i` can be changed by dragging a point to the trash
1261
+ // Currently this function is only called when !isClickToAddPoints()
1262
+ const rel = (j) => {
1263
+ return (i + j + this.points.length) % this.points.length;
1264
+ };
1265
+
1266
+ const remove = () => {
1267
+ // remove this point from points
1268
+ const index = this.removePoint(point);
1269
+ if (this.polygon?.closed()) {
1270
+ // We should be checking if this.points is defined before rotating them.
1271
+ // @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly any[]' is 'readonly' and cannot be assigned to the mutable type 'any[]'. | TS2345 - Argument of type 'number | null | undefined' is not assignable to parameter of type 'number | undefined'.
1272
+ this.points = rotate(this.points, index);
1273
+ this.polygon?.update({closed: false});
1274
+ }
1275
+ this.updatePolygon();
1276
+ // the polygon is now unclosed, so we need to
1277
+ // remove any points props
1278
+ this.clearCoords();
1279
+
1280
+ // remove this movablePoint from graphie.
1281
+ // wait to do this until we're not inside of
1282
+ // said point's onMoveEnd method so state is
1283
+ // consistent throughout the method call
1284
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
1285
+ // eslint-disable-next-line no-restricted-syntax
1286
+ setTimeout(point.remove.bind(point), 0);
1287
+ };
1288
+
1289
+ const setInitialMove = debounce((point, bool) => {
1290
+ point.state.isInitialMove = bool;
1291
+ }, 1000);
1292
+
1293
+ const onMoveEndHandler = (coord) => {
1294
+ // If the user clicked and dragged a point over endpoint,
1295
+ // join the them
1296
+ const overlappedPoints =
1297
+ this.points.length > 1 &&
1298
+ ((point === this.points[0] &&
1299
+ kpoint.equal(coord, _.last(this.points).coord())) ||
1300
+ (point === _.last(this.points) &&
1301
+ kpoint.equal(coord, this.points[0].coord())));
1302
+
1303
+ if (this.isClickToAddPoints()) {
1304
+ if (this.isCoordInTrash(coord)) {
1305
+ remove();
1306
+ } else if (overlappedPoints) {
1307
+ const pointToRemove = this.points.pop();
1308
+
1309
+ if (this.points.length > 2) {
1310
+ this.polygon?.update({closed: true});
1311
+ this.updateCoordsFromPoints();
1312
+ } else {
1313
+ this.polygon?.update({closed: false});
1314
+ this.clearCoords();
1315
+ }
1316
+
1317
+ this.updatePolygon();
1318
+ // remove this movablePoint from graphie.
1319
+ // wait to do this until we're not inside of
1320
+ // said point's onMoveEnd method so state is
1321
+ // consistent throughout the method call
1322
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
1323
+ // eslint-disable-next-line no-restricted-syntax
1324
+ setTimeout(pointToRemove.remove.bind(pointToRemove), 0);
1325
+ } else {
1326
+ // If the user clicked and dragged a point over any other
1327
+ // existing point, fix shape
1328
+ const shouldRemove = _.any(this.points, function (pt) {
1329
+ return pt !== point && kpoint.equal(pt.coord(), coord);
1330
+ });
1331
+
1332
+ if (shouldRemove) {
1333
+ this.removePoint(point);
1334
+
1335
+ if (this.points.length < 3) {
1336
+ this.polygon?.update({closed: false});
1337
+ this.clearCoords();
1338
+ } else if (this.polygon?.closed()) {
1339
+ this.updateCoordsFromPoints();
1340
+ }
1341
+
1342
+ this.updatePolygon();
1343
+ // remove this movablePoint from graphie.
1344
+ // wait to do this until we're not inside
1345
+ // said point's onMoveEnd method so state
1346
+ // is consistent throughout the method call
1347
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
1348
+ // eslint-disable-next-line no-restricted-syntax
1349
+ setTimeout(point.remove.bind(point), 0);
1350
+ } else {
1351
+ // If this was
1352
+ // * a click on the first or last point
1353
+ // * and not a deletion
1354
+ // * and not a drag,
1355
+ // * and not a creation of a new point
1356
+ // (see !point.state.isInitialMove, below),
1357
+ // * and our polygon is not closed,
1358
+ // * and we can close it (we need at least the same
1359
+ // number of points as coordinates asked for in the
1360
+ // question),
1361
+ // then close it
1362
+ const isFirstOrLastPoint =
1363
+ point === _.head(this.points) ||
1364
+ point === _.last(this.points);
1365
+ const pointNotMoved = !point.hasMoved();
1366
+ // NOTE (jsatk): Currently different on mobile versus
1367
+ // destkop
1368
+ const notANewPoint = !point.state.isInitialMove;
1369
+ const polygonNotClosed = !this.polygon?.closed();
1370
+ const allPointsPlotted = this.points.length > 2;
1371
+
1372
+ const canCloseUnclosedPolygon =
1373
+ isFirstOrLastPoint &&
1374
+ pointNotMoved &&
1375
+ notANewPoint &&
1376
+ polygonNotClosed &&
1377
+ allPointsPlotted;
1378
+
1379
+ if (canCloseUnclosedPolygon) {
1380
+ this.polygon?.update({closed: true});
1381
+ this.updatePolygon();
1382
+
1383
+ // We finally have a closed polygon, so save our
1384
+ // points to props
1385
+ this.updateCoordsFromPoints();
1386
+ }
1387
+ }
1388
+ }
1389
+
1390
+ // In case we mouseup'd off the graphie and that
1391
+ // stopped the move
1392
+ this.setTrashCanVisibility(0.5);
1393
+ }
1394
+
1395
+ setInitialMove(point, false);
1396
+ };
1397
+
1398
+ const graphConstraint = (coord: any) => {
1399
+ // These constraints are all relative to the other points, so if
1400
+ // we're creating the initial points and haven't added any others
1401
+ // to the graph, we can't enforce them.
1402
+ if (this.points == null || this.points.length === 0) {
1403
+ return true;
1404
+ }
1405
+
1406
+ const coords: Array<Coord> = _.invoke(this.points, "coord");
1407
+ coords[i] = coord;
1408
+
1409
+ // Check for invalid positioning, but only if we aren't adding
1410
+ // points one click at a time, since those added points could
1411
+ // have already violated these constraints
1412
+ if (!this.isClickToAddPoints()) {
1413
+ // Polygons can't have consecutive collinear points
1414
+ if (
1415
+ collinear(coords[rel(-2)], coords[rel(-1)], coords[i]) ||
1416
+ collinear(coords[rel(-1)], coords[i], coords[rel(1)]) ||
1417
+ collinear(coords[i], coords[rel(1)], coords[rel(2)])
1418
+ ) {
1419
+ return false;
1420
+ }
1421
+
1422
+ const segments = _.zip(coords, rotate(coords));
1423
+
1424
+ if (this.points.length > 3) {
1425
+ // Constrain to simple (non self-intersecting) polygon by
1426
+ // testing whether adjacent segments intersect any others
1427
+ for (let j = -1; j <= 0; j++) {
1428
+ const segment = segments[rel(j)];
1429
+ const others = _.without(
1430
+ segments,
1431
+ segment,
1432
+ segments[rel(j - 1)],
1433
+ segments[rel(j + 1)],
1434
+ );
1435
+
1436
+ for (let k = 0; k < others.length; k++) {
1437
+ const other = others[k];
1438
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'Line'.
1439
+ if (intersects(segment, other)) {
1440
+ return false;
1441
+ }
1442
+ }
1443
+ }
1444
+ }
1445
+ }
1446
+
1447
+ if (
1448
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
1449
+ this.props.graph.snapTo === "angles" &&
1450
+ this.points.length > 2
1451
+ ) {
1452
+ // Snap to whole degree interior angles
1453
+
1454
+ const angles = _.map(angleMeasures(coords), function (rad) {
1455
+ return (rad * 180) / Math.PI;
1456
+ });
1457
+
1458
+ _.each([-1, 1], function (j) {
1459
+ angles[rel(j)] = Math.round(angles[rel(j)]);
1460
+ });
1461
+
1462
+ const getAngle = function (a: number, vertex, b: number) {
1463
+ const angle = GraphUtils.findAngle(
1464
+ coords[rel(a)],
1465
+ coords[rel(b)],
1466
+ coords[rel(vertex)],
1467
+ );
1468
+ return (angle + 360) % 360;
1469
+ };
1470
+
1471
+ const innerAngles = [
1472
+ angles[rel(-1)] - getAngle(-2, -1, 1),
1473
+ angles[rel(1)] - getAngle(-1, 1, 2),
1474
+ ];
1475
+ innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
1476
+
1477
+ // Avoid degenerate triangles
1478
+ if (
1479
+ _.any(innerAngles, function (angle) {
1480
+ return leq(angle, 1);
1481
+ })
1482
+ ) {
1483
+ return false;
1484
+ }
1485
+
1486
+ const knownSide = magnitude(
1487
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
1488
+ vector(coords[rel(-1)], coords[rel(1)]),
1489
+ );
1490
+
1491
+ const onLeft =
1492
+ sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
1493
+
1494
+ // Solve for side by using the law of sines
1495
+ const side =
1496
+ (Math.sin((innerAngles[1] * Math.PI) / 180) /
1497
+ Math.sin((innerAngles[2] * Math.PI) / 180)) *
1498
+ knownSide;
1499
+
1500
+ const outerAngle = GraphUtils.findAngle(
1501
+ coords[rel(1)],
1502
+ coords[rel(-1)],
1503
+ );
1504
+
1505
+ const offset = this.graphie?.polar(
1506
+ side,
1507
+ outerAngle + (onLeft ? 1 : -1) * innerAngles[0],
1508
+ );
1509
+
1510
+ return this.graphie?.addPoints(coords[rel(-1)], offset);
1511
+ }
1512
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
1513
+ if (this.props.graph.snapTo === "sides" && this.points.length > 1) {
1514
+ // Snap to whole unit side measures
1515
+
1516
+ const sides = _.map(
1517
+ [
1518
+ [coords[rel(-1)], coords[i]],
1519
+ [coords[i], coords[rel(1)]],
1520
+ [coords[rel(-1)], coords[rel(1)]],
1521
+ ],
1522
+ function (coords) {
1523
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
1524
+ return magnitude(vector(...coords));
1525
+ },
1526
+ );
1527
+
1528
+ _.each([0, 1], function (j) {
1529
+ sides[j] = Math.round(sides[j]);
1530
+ });
1531
+
1532
+ // Avoid degenerate triangles
1533
+ if (
1534
+ leq(sides[1] + sides[2], sides[0]) ||
1535
+ leq(sides[0] + sides[2], sides[1]) ||
1536
+ leq(sides[0] + sides[1], sides[2])
1537
+ ) {
1538
+ return false;
1539
+ }
1540
+
1541
+ // Solve for angle by using the law of cosines
1542
+ const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
1543
+
1544
+ const outerAngle = GraphUtils.findAngle(
1545
+ coords[rel(1)],
1546
+ coords[rel(-1)],
1547
+ );
1548
+
1549
+ const onLeft =
1550
+ sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
1551
+
1552
+ const offset = this.graphie?.polar(
1553
+ sides[0],
1554
+ outerAngle + (onLeft ? 1 : -1) * innerAngle,
1555
+ );
1556
+
1557
+ return this.graphie?.addPoints(coords[rel(-1)], offset);
1558
+ }
1559
+ // Snap to grid (already done)
1560
+ return true;
1561
+ };
1562
+
1563
+ const point = Interactive2.addMaybeMobileMovablePoint(this, {
1564
+ coord: coord,
1565
+ constraints: [
1566
+ // $FlowFixMe[prop-missing]
1567
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1568
+ Interactive2.MovablePoint.constraints.bound(),
1569
+ snapToGrid
1570
+ ? // $FlowFixMe[prop-missing]
1571
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1572
+ Interactive2.MovablePoint.constraints.snap()
1573
+ : null,
1574
+ graphConstraint,
1575
+ ],
1576
+ onMoveStart: () => {
1577
+ if (this.isClickToAddPoints()) {
1578
+ this.setTrashCanVisibility(1);
1579
+ }
1580
+ },
1581
+ onMove: () => {
1582
+ if (this.polygon?.closed()) {
1583
+ this.updateCoordsFromPoints();
1584
+ }
1585
+ },
1586
+ onMoveEnd: onMoveEndHandler,
1587
+ ...(this.props.apiOptions.isMobile && this.isClickToAddPoints()
1588
+ ? {onRemove: remove}
1589
+ : {}),
1590
+ });
1591
+
1592
+ point.state.isInitialMove = true;
1593
+
1594
+ return point;
1595
+ };
1596
+
1597
+ updateCoordsFromPoints: () => void = () => {
1598
+ const graph = _.extend({}, this.props.graph, {
1599
+ // Handle old movable points with .coord, or
1600
+ // Interactive2.MovablePoint's with .coord()
1601
+ coords: _.map(this.points, function (point) {
1602
+ return _.result(point, "coord");
1603
+ }),
1604
+ });
1605
+ this.onChange({graph: graph});
1606
+ };
1607
+
1608
+ clearCoords: () => void = () => {
1609
+ const graph = _.extend({}, this.props.graph, {
1610
+ coords: null,
1611
+ });
1612
+ this.onChange({graph: graph});
1613
+ };
1614
+
1615
+ onChange: (arg1: any) => void = (data) => {
1616
+ this.props.onChange(data);
1617
+ this.props.trackInteraction();
1618
+ };
1619
+
1620
+ addPointControls: () => void = () => {
1621
+ if (this.props.graph.type !== "point") {
1622
+ throw makeInvalidTypeError("addPointControls", "point");
1623
+ }
1624
+
1625
+ const coords = InteractiveGraph.getPointCoords(
1626
+ this.props.graph,
1627
+ this.props,
1628
+ );
1629
+ // Clear out our old points so that newly added points don't
1630
+ // "collide" with them and reposition when being added
1631
+ // Without this, when added, each point checks whether it is on top
1632
+ // of a point in this.points, which (a) shouldn't matter since
1633
+ // we're clearing out this.points anyways, and (b) can cause problems
1634
+ // if each of this.points is a MovablePoint instead of an
1635
+ // Interactive2.MovablePoint, since one has a .coord and the other
1636
+ // has .coord()
1637
+ // TODO(jack): Figure out a better way to do this
1638
+ this.points = [];
1639
+ this.points = _.map(coords, this.createPointForPointsType, this);
1640
+ };
1641
+
1642
+ removePointControls: () => void = () => {
1643
+ _.invoke(this.points, "remove");
1644
+ };
1645
+
1646
+ addSegmentControls: () => void = () => {
1647
+ if (this.props.graph.type !== "segment") {
1648
+ throw makeInvalidTypeError("addSegmentControls", "segment");
1649
+ }
1650
+
1651
+ const self = this;
1652
+ const graphie = this.graphie;
1653
+
1654
+ const coords = InteractiveGraph.getSegmentCoords(
1655
+ this.props.graph,
1656
+ this.props,
1657
+ );
1658
+
1659
+ const createPoint = (options: {
1660
+ constraints: Array<any | ((coord: never) => boolean)>;
1661
+ coord: any;
1662
+ onMove: () => void;
1663
+ }) => Interactive2.addMaybeMobileMovablePoint(this, options);
1664
+
1665
+ this.points = [];
1666
+ this.lines = _.map(
1667
+ coords,
1668
+ function (segment, i) {
1669
+ const updateCoordProps = function () {
1670
+ const graph = _.extend({}, self.props.graph, {
1671
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
1672
+ coords: _.invoke(self.lines, "coords"),
1673
+ });
1674
+ self.onChange({graph: graph});
1675
+ };
1676
+
1677
+ // NOTE(jeresig): This code attempts to access the variable
1678
+ // while it's still being defined, it should be refactored!
1679
+ // The callbacks appear to be executed synchronously, which
1680
+ // causes this issue.
1681
+ const points: Array<any> = [];
1682
+
1683
+ for (let i = 0; i < segment.length; i += 1) {
1684
+ const coord = segment[i];
1685
+ points.push(
1686
+ createPoint({
1687
+ coord: coord,
1688
+ constraints: [
1689
+ // $FlowFixMe[prop-missing]
1690
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1691
+ Interactive2.MovablePoint.constraints.bound(),
1692
+ // $FlowFixMe[prop-missing]
1693
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1694
+ Interactive2.MovablePoint.constraints.snap(),
1695
+ (coord: any) => {
1696
+ const otherPoint = points[1 - i];
1697
+ if (!otherPoint) {
1698
+ // point hasn't been defined yet
1699
+ // because we're still creating them
1700
+ // We return false as the points aren't
1701
+ // going to be in the same position at
1702
+ // this point.
1703
+ return false;
1704
+ }
1705
+ return !kpoint.equal(
1706
+ coord,
1707
+ otherPoint.coord(),
1708
+ );
1709
+ },
1710
+ ],
1711
+ onMove: updateCoordProps,
1712
+ }),
1713
+ );
1714
+ }
1715
+
1716
+ self.points = self.points.concat(points);
1717
+ const line = Interactive2.addMovableLine(graphie, {
1718
+ points: points,
1719
+ static: false,
1720
+ constraints: [
1721
+ // $FlowFixMe[prop-missing]
1722
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1723
+ Interactive2.MovableLine.constraints.bound(),
1724
+ // $FlowFixMe[prop-missing]
1725
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1726
+ Interactive2.MovableLine.constraints.snap(),
1727
+ ],
1728
+ onMove: [
1729
+ // $FlowFixMe[prop-missing]
1730
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => void'.
1731
+ Interactive2.MovableLine.onMove.updatePoints,
1732
+ updateCoordProps,
1733
+ ],
1734
+ normalStyle: {
1735
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1736
+ stroke: this.props.apiOptions.isMobile
1737
+ ? KhanColors.BLUE_C
1738
+ : KhanColors.INTERACTIVE,
1739
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1740
+ ...this._lineStroke(),
1741
+ },
1742
+ highlightStyle: {
1743
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1744
+ stroke: this.props.apiOptions.isMobile
1745
+ ? KhanColors.BLUE_C
1746
+ : KhanColors.INTERACTING,
1747
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1748
+ ...this._lineStroke(),
1749
+ },
1750
+ });
1751
+ _.invoke(points, "toFront");
1752
+
1753
+ return line;
1754
+ },
1755
+ this,
1756
+ );
1757
+ };
1758
+
1759
+ removeSegmentControls: () => void = () => {
1760
+ _.invoke(this.points, "remove");
1761
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
1762
+ _.invoke(this.lines, "remove");
1763
+ };
1764
+
1765
+ addRayControls: () => void = () => {
1766
+ this.addLine("ray");
1767
+ };
1768
+
1769
+ removeRayControls: () => void = () => {
1770
+ this.removeLine();
1771
+ };
1772
+
1773
+ addPolygonControls: () => void = () => {
1774
+ this.polygon = null;
1775
+ const coords = InteractiveGraph.getPolygonCoords(
1776
+ this.props.graph,
1777
+ this.props,
1778
+ );
1779
+ // Clear out our old points so that newly added points don't
1780
+ // "collide", as in `addPointControls`
1781
+ this.points = [];
1782
+ this.points = _.map(coords, this.createPointForPolygonType, this);
1783
+ this.updatePolygon();
1784
+ };
1785
+
1786
+ updatePolygon: () => void = () => {
1787
+ if (this.props.graph.type !== "polygon") {
1788
+ throw makeInvalidTypeError("updatePolygon", "polygon");
1789
+ }
1790
+
1791
+ let closed;
1792
+ if (this.polygon) {
1793
+ closed = this.polygon.closed();
1794
+ } else if (this.points.length >= 3) {
1795
+ closed = true;
1796
+ } else {
1797
+ // There will only be fewer than 3 points in click-to-add-vertices
1798
+ // mode, so we don't need to explicitly check for that here.
1799
+ closed = false;
1800
+ }
1801
+
1802
+ const graphie = this.graphie;
1803
+ const n = this.points.length;
1804
+
1805
+ // TODO(alex): check against "grid" instead, use constants
1806
+ const snapToGrid = !_.contains(
1807
+ ["angles", "sides"],
1808
+ this.props.graph.snapTo,
1809
+ );
1810
+
1811
+ const angleLabels = _.times(
1812
+ n,
1813
+ function (i) {
1814
+ if (
1815
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1816
+ !this.props.graph.showAngles ||
1817
+ (!closed && (i === 0 || i === n - 1))
1818
+ ) {
1819
+ return "";
1820
+ }
1821
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1822
+ if (this.props.graph.snapTo === "angles") {
1823
+ return "$deg0";
1824
+ }
1825
+ return "$deg1";
1826
+ },
1827
+ this,
1828
+ );
1829
+
1830
+ const showRightAngleMarkers = _.times(
1831
+ n,
1832
+ function (i) {
1833
+ return closed || (i !== 0 && i !== n - 1);
1834
+ },
1835
+ this,
1836
+ );
1837
+
1838
+ const numArcs = _.times(
1839
+ n,
1840
+ function (i) {
1841
+ if (
1842
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1843
+ this.props.graph.showAngles &&
1844
+ (closed || (i !== 0 && i !== n - 1))
1845
+ ) {
1846
+ return 1;
1847
+ }
1848
+ return 0;
1849
+ },
1850
+ this,
1851
+ );
1852
+
1853
+ const sideLabels = _.times(
1854
+ n,
1855
+ function (i) {
1856
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1857
+ if (!this.props.graph.showSides || (!closed && i === n - 1)) {
1858
+ return "";
1859
+ }
1860
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1861
+ if (this.props.graph.snapTo === "sides") {
1862
+ return "$len0";
1863
+ }
1864
+ return "$len1";
1865
+ },
1866
+ this,
1867
+ );
1868
+
1869
+ if (this.polygon == null) {
1870
+ const self = this;
1871
+ self.polygon = Interactive2.addMovablePolygon(graphie, {
1872
+ constraints: [
1873
+ // $FlowFixMe[prop-missing]
1874
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
1875
+ Interactive2.MovablePolygon.constraints.bound(),
1876
+ snapToGrid
1877
+ ? // $FlowFixMe[prop-missing]
1878
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
1879
+ Interactive2.MovablePolygon.constraints.snap()
1880
+ : null,
1881
+ ],
1882
+ closed: closed,
1883
+ points: self.points,
1884
+ angleLabels: angleLabels,
1885
+ showRightAngleMarkers: showRightAngleMarkers,
1886
+ numArcs: numArcs,
1887
+ sideLabels: sideLabels,
1888
+ onMove: [
1889
+ // $FlowFixMe[prop-missing]
1890
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
1891
+ Interactive2.MovablePolygon.onMove.updatePoints,
1892
+ function () {
1893
+ // @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
1894
+ if (this.closed()) {
1895
+ self.updateCoordsFromPoints();
1896
+ }
1897
+ },
1898
+ ],
1899
+ normalStyle: {
1900
+ stroke: this.props.apiOptions.isMobile
1901
+ ? KhanColors.BLUE_C
1902
+ : KhanColors.INTERACTIVE,
1903
+ ...this._lineStroke(),
1904
+ },
1905
+ });
1906
+ } else {
1907
+ // We only need to pass in the properties that might've changed
1908
+ this.polygon.update({
1909
+ closed: closed,
1910
+ points: this.points,
1911
+ angleLabels: angleLabels,
1912
+ showRightAngleMarkers: showRightAngleMarkers,
1913
+ numArcs: numArcs,
1914
+ sideLabels: sideLabels,
1915
+ });
1916
+ }
1917
+ };
1918
+
1919
+ removePolygonControls: () => void = () => {
1920
+ _.invoke(this.points, "remove");
1921
+ this.polygon?.remove();
1922
+ };
1923
+
1924
+ addAngleControls: () => void = () => {
1925
+ if (this.props.graph.type !== "angle") {
1926
+ throw makeInvalidTypeError("addAngleControls", "angle");
1927
+ }
1928
+
1929
+ const graph = this.props.graph;
1930
+ const graphie = this.graphie;
1931
+
1932
+ const coords = InteractiveGraph.getAngleCoords(
1933
+ this.props.graph,
1934
+ this.props,
1935
+ );
1936
+
1937
+ // The vertex snaps to the grid, but the rays don't...
1938
+ this.points = coords.map(function (coord, i) {
1939
+ return graphie?.addMovablePoint(
1940
+ _.extend(
1941
+ {
1942
+ coord: coord,
1943
+ normalStyle: {
1944
+ stroke: KhanColors.INTERACTIVE,
1945
+ fill: KhanColors.INTERACTIVE,
1946
+ },
1947
+ },
1948
+ i === 1
1949
+ ? {
1950
+ snapX: graphie.snap[0],
1951
+ snapY: graphie.snap[1],
1952
+ }
1953
+ : {},
1954
+ ),
1955
+ );
1956
+ });
1957
+
1958
+ // ...they snap to whole-degree angles from the vertex.
1959
+ this.angle = graphie?.addMovableAngle({
1960
+ points: this.points,
1961
+ snapDegrees: graph.snapDegrees || 1,
1962
+ snapOffsetDeg: graph.angleOffsetDeg || 0,
1963
+ angleLabel: graph.showAngles ? "$deg0" : "",
1964
+ pushOut: 2,
1965
+ allowReflex: defaultVal(graph.allowReflexAngles, true),
1966
+ });
1967
+
1968
+ $(this.angle).on("move", () => {
1969
+ this.onChange({
1970
+ graph: {...graph, coords: this.angle?.getClockwiseCoords()},
1971
+ });
1972
+ });
1973
+ };
1974
+
1975
+ removeAngleControls: () => void = () => {
1976
+ _.invoke(this.points, "remove");
1977
+ this.angle?.remove();
1978
+ };
1979
+
1980
+ toggleShowAngles: () => void = () => {
1981
+ if (this.props.graph.type !== "polygon") {
1982
+ throw makeInvalidTypeError("toggleShowAngles", "polygon");
1983
+ }
1984
+ const graph = _.extend({}, this.props.graph, {
1985
+ showAngles: !this.props.graph.showAngles,
1986
+ });
1987
+ this.onChange({graph: graph});
1988
+ };
1989
+
1990
+ toggleShowSides: () => void = () => {
1991
+ if (this.props.graph.type !== "polygon") {
1992
+ throw makeInvalidTypeError("toggleShowSides", "polygon");
1993
+ }
1994
+ const graph = _.extend({}, this.props.graph, {
1995
+ showSides: !this.props.graph.showSides,
1996
+ });
1997
+ this.onChange({graph: graph});
1998
+ };
1999
+
2000
+ getUserInput: () => PerseusGraphType = () => {
2001
+ return InteractiveGraph.getUserInputFromProps(this.props);
2002
+ };
2003
+
2004
+ simpleValidate: (arg1: Rubric) => PerseusScore = (rubric) => {
2005
+ return InteractiveGraph.validate(this.getUserInput(), rubric, this);
2006
+ };
2007
+
2008
+ focus: () => void = $.noop;
2009
+
2010
+ render(): React.ReactNode {
2011
+ const box = getInteractiveBoxFromSizeClass(
2012
+ this.props.containerSizeClass,
2013
+ );
2014
+
2015
+ let instructions;
2016
+ if (this.isClickToAddPoints() && this.state.shouldShowInstructions) {
2017
+ if (this.props.graph.type === "point") {
2018
+ instructions = i18n._("Click to add points");
2019
+ } else if (this.props.graph.type === "polygon") {
2020
+ instructions = i18n._("Click to add vertices");
2021
+ }
2022
+ } else {
2023
+ instructions = undefined;
2024
+ }
2025
+
2026
+ const onMouseDown = this.isClickToAddPoints()
2027
+ ? this.handleAddPointsMouseDown
2028
+ : null;
2029
+
2030
+ const gridStep =
2031
+ this.props.gridStep ||
2032
+ Util.getGridStep(this.props.range, this.props.step, box[0]);
2033
+ const snapStep =
2034
+ this.props.snapStep || Util.snapStepFromGridStep(gridStep);
2035
+
2036
+ const isMobile = this.props.apiOptions.isMobile;
2037
+
2038
+ return (
2039
+ <div
2040
+ className={
2041
+ "perseus-widget " + "perseus-widget-interactive-graph"
2042
+ }
2043
+ style={{
2044
+ width: box[0],
2045
+ height: box[1],
2046
+ }}
2047
+ >
2048
+ <Graph
2049
+ instructions={instructions}
2050
+ // eslint-disable-next-line react/no-string-refs
2051
+ ref="graph"
2052
+ box={box}
2053
+ labels={this.props.labels}
2054
+ range={this.props.range}
2055
+ step={
2056
+ isMobile
2057
+ ? Util.constrainedTickStepsFromTickSteps(
2058
+ this.props.step,
2059
+ this.props.range,
2060
+ )
2061
+ : this.props.step
2062
+ }
2063
+ gridStep={gridStep}
2064
+ snapStep={snapStep}
2065
+ markings={this.props.markings}
2066
+ backgroundImage={this.props.backgroundImage}
2067
+ showProtractor={this.props.showProtractor}
2068
+ showRuler={this.props.showRuler}
2069
+ rulerLabel={this.props.rulerLabel}
2070
+ rulerTicks={this.props.rulerTicks}
2071
+ onMouseDown={onMouseDown}
2072
+ onGraphieUpdated={this.setGraphie}
2073
+ setDrawingAreaAvailable={
2074
+ this.props.apiOptions.setDrawingAreaAvailable
2075
+ }
2076
+ isMobile={isMobile}
2077
+ />
2078
+ </div>
2079
+ );
2080
+ }
2081
+
2082
+ static getQuadraticCoefficients(
2083
+ coords: ReadonlyArray<Coord>,
2084
+ ): QuadraticCoefficient {
2085
+ const p1 = coords[0];
2086
+ const p2 = coords[1];
2087
+ const p3 = coords[2];
2088
+
2089
+ const denom = (p1[0] - p2[0]) * (p1[0] - p3[0]) * (p2[0] - p3[0]);
2090
+ if (denom === 0) {
2091
+ // Many of the callers assume that the return value is always defined.
2092
+ // @ts-expect-error [FEI-5003] - TS2322 - Type 'undefined' is not assignable to type 'QuadraticCoefficient'.
2093
+ return;
2094
+ }
2095
+ const a =
2096
+ (p3[0] * (p2[1] - p1[1]) +
2097
+ p2[0] * (p1[1] - p3[1]) +
2098
+ p1[0] * (p3[1] - p2[1])) /
2099
+ denom;
2100
+ const b =
2101
+ (p3[0] * p3[0] * (p1[1] - p2[1]) +
2102
+ p2[0] * p2[0] * (p3[1] - p1[1]) +
2103
+ p1[0] * p1[0] * (p2[1] - p3[1])) /
2104
+ denom;
2105
+ const c =
2106
+ (p2[0] * p3[0] * (p2[0] - p3[0]) * p1[1] +
2107
+ p3[0] * p1[0] * (p3[0] - p1[0]) * p2[1] +
2108
+ p1[0] * p2[0] * (p1[0] - p2[0]) * p3[1]) /
2109
+ denom;
2110
+ return [a, b, c];
2111
+ }
2112
+
2113
+ static getSinusoidCoefficients(
2114
+ coords: ReadonlyArray<Coord>,
2115
+ ): SineCoefficient {
2116
+ // It's assumed that p1 is the root and p2 is the first peak
2117
+ const p1 = coords[0];
2118
+ const p2 = coords[1];
2119
+
2120
+ // Resulting coefficients are canonical for this sine curve
2121
+ const amplitude = p2[1] - p1[1];
2122
+ const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
2123
+ const phase = p1[0] * angularFrequency;
2124
+ const verticalOffset = p1[1];
2125
+
2126
+ return [amplitude, angularFrequency, phase, verticalOffset];
2127
+ }
2128
+
2129
+ /**
2130
+ * @param {object} graph Like props.graph or props.correct
2131
+ * @param {object} props of an InteractiveGraph instance
2132
+ */
2133
+ static getLineCoords(
2134
+ graph: PerseusGraphType,
2135
+ props: Props,
2136
+ ): ReadonlyArray<Coord> {
2137
+ return (
2138
+ // $FlowFixMe[incompatible-return]
2139
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
2140
+ graph.coords ||
2141
+ InteractiveGraph.pointsFromNormalized(props, [
2142
+ [0.25, 0.75],
2143
+ [0.75, 0.75],
2144
+ ])
2145
+ );
2146
+ }
2147
+
2148
+ /**
2149
+ * @param {object} graph Like props.graph or props.correct
2150
+ * @param {object} props of an InteractiveGraph instance
2151
+ */
2152
+ static getPointCoords(
2153
+ graph: PerseusGraphTypePoint,
2154
+ props: Props,
2155
+ ): ReadonlyArray<Coord> {
2156
+ const numPoints = graph.numPoints || 1;
2157
+ let coords = graph.coords;
2158
+
2159
+ if (coords) {
2160
+ return coords;
2161
+ }
2162
+ switch (numPoints) {
2163
+ case 1:
2164
+ // Back in the day, one point's coords were in graph.coord
2165
+ coords = [graph.coord || [0, 0]];
2166
+ break;
2167
+ case 2:
2168
+ coords = [
2169
+ [-5, 0],
2170
+ [5, 0],
2171
+ ];
2172
+ break;
2173
+ case 3:
2174
+ coords = [
2175
+ [-5, 0],
2176
+ [0, 0],
2177
+ [5, 0],
2178
+ ];
2179
+ break;
2180
+ case 4:
2181
+ coords = [
2182
+ [-6, 0],
2183
+ [-2, 0],
2184
+ [2, 0],
2185
+ [6, 0],
2186
+ ];
2187
+ break;
2188
+ case 5:
2189
+ coords = [
2190
+ [-6, 0],
2191
+ [-3, 0],
2192
+ [0, 0],
2193
+ [3, 0],
2194
+ [6, 0],
2195
+ ];
2196
+ break;
2197
+ case 6:
2198
+ coords = [
2199
+ [-5, 0],
2200
+ [-3, 0],
2201
+ [-1, 0],
2202
+ [1, 0],
2203
+ [3, 0],
2204
+ [5, 0],
2205
+ ];
2206
+ break;
2207
+ case UNLIMITED:
2208
+ coords = [];
2209
+ break;
2210
+ }
2211
+ // Transform coords from their -10 to 10 space to 0 to 1
2212
+ // because of the old graph.coord, and also it's easier.
2213
+ const range = [
2214
+ [-10, 10],
2215
+ [-10, 10],
2216
+ ];
2217
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
2218
+ const newCoords = InteractiveGraph.normalizeCoords(coords, range);
2219
+
2220
+ return InteractiveGraph.pointsFromNormalized(props, newCoords);
2221
+ }
2222
+
2223
+ /**
2224
+ * @param {object} graph Like props.graph or props.correct
2225
+ * @param {object} props of an InteractiveGraph instance
2226
+ */
2227
+ static getLinearSystemCoords(
2228
+ graph: PerseusGraphType,
2229
+ props: Props,
2230
+ ): ReadonlyArray<ReadonlyArray<Coord>> {
2231
+ return (
2232
+ // The callers assume that we're return an array of points
2233
+ // $FlowFixMe[incompatible-return]
2234
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
2235
+ graph.coords ||
2236
+ _.map(
2237
+ [
2238
+ [
2239
+ [0.25, 0.75],
2240
+ [0.75, 0.75],
2241
+ ],
2242
+ [
2243
+ [0.25, 0.25],
2244
+ [0.75, 0.25],
2245
+ ],
2246
+ ],
2247
+ (coords) => {
2248
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
2249
+ return InteractiveGraph.pointsFromNormalized(props, coords);
2250
+ },
2251
+ )
2252
+ );
2253
+ }
2254
+
2255
+ /**
2256
+ * @param {object} graph Like props.graph or props.correct
2257
+ * @param {object} props of an InteractiveGraph instance
2258
+ */
2259
+ static getPolygonCoords(
2260
+ graph: PerseusGraphType,
2261
+ props: Props,
2262
+ ): ReadonlyArray<Coord> {
2263
+ if (graph.type !== "polygon") {
2264
+ throw makeInvalidTypeError("toggleShowSides", "polygon");
2265
+ }
2266
+
2267
+ let coords = graph.coords;
2268
+ if (coords) {
2269
+ return coords;
2270
+ }
2271
+
2272
+ const n = graph.numSides || 3;
2273
+
2274
+ if (n === UNLIMITED) {
2275
+ coords = [];
2276
+ } else {
2277
+ const angle = (2 * Math.PI) / n;
2278
+ const offset = (1 / n - 1 / 2) * Math.PI;
2279
+
2280
+ // TODO(alex): Generalize this to more than just triangles so that
2281
+ // all polygons have whole number side lengths if snapping to sides
2282
+ const radius =
2283
+ graph.snapTo === "sides" ? (Math.sqrt(3) / 3) * 7 : 4;
2284
+
2285
+ // Generate coords of a regular polygon with n sides
2286
+ coords = _.times(n, function (i) {
2287
+ return [
2288
+ radius * Math.cos(i * angle + offset),
2289
+ radius * Math.sin(i * angle + offset),
2290
+ ];
2291
+ });
2292
+ }
2293
+
2294
+ const ranges: [Range, Range] = [
2295
+ [-10, 10],
2296
+ [-10, 10],
2297
+ ];
2298
+ coords = InteractiveGraph.normalizeCoords(coords, ranges);
2299
+
2300
+ const snapToGrid = !_.contains(["angles", "sides"], graph.snapTo);
2301
+ coords = InteractiveGraph.pointsFromNormalized(
2302
+ props,
2303
+ coords,
2304
+ /* noSnap */ !snapToGrid,
2305
+ );
2306
+
2307
+ return coords;
2308
+ }
2309
+
2310
+ /**
2311
+ * @param {object} graph Like props.graph or props.correct
2312
+ * @param {object} props of an InteractiveGraph instance
2313
+ */
2314
+ static getSegmentCoords(
2315
+ graph: PerseusGraphTypeSegment,
2316
+ props: Props,
2317
+ ): ReadonlyArray<ReadonlyArray<Coord>> {
2318
+ const coords = graph.coords;
2319
+ if (coords) {
2320
+ return coords;
2321
+ }
2322
+
2323
+ const n = graph.numSegments || 1;
2324
+ // @ts-expect-error [FEI-5003] - TS2322 - Type 'number[] | undefined' is not assignable to type 'readonly number[]'.
2325
+ const ys: ReadonlyArray<number> = {
2326
+ // $FlowFixMe[unsupported-syntax]
2327
+ 1: [5],
2328
+ // $FlowFixMe[unsupported-syntax]
2329
+ 2: [5, -5],
2330
+ // $FlowFixMe[unsupported-syntax]
2331
+ 3: [5, 0, -5],
2332
+ // $FlowFixMe[unsupported-syntax]
2333
+ 4: [6, 2, -2, -6],
2334
+ // $FlowFixMe[unsupported-syntax]
2335
+ 5: [6, 3, 0, -3, -6],
2336
+ // $FlowFixMe[unsupported-syntax]
2337
+ 6: [5, 3, 1, -1, -3, -5],
2338
+ }[n];
2339
+ const range = [
2340
+ [-10, 10],
2341
+ [-10, 10],
2342
+ ];
2343
+
2344
+ // @ts-expect-error [FEI-5003] - TS2322 - Type 'number[][][]' is not assignable to type 'readonly (readonly Coord[])[]'.
2345
+ return ys.map(function (y) {
2346
+ let segment = [
2347
+ [-5, y],
2348
+ [5, y],
2349
+ ];
2350
+ // @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly Coord[]' is 'readonly' and cannot be assigned to the mutable type 'number[][]'. | TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
2351
+ segment = InteractiveGraph.normalizeCoords(segment, range);
2352
+ // @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly Coord[]' is 'readonly' and cannot be assigned to the mutable type 'number[][]'. | TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
2353
+ segment = InteractiveGraph.pointsFromNormalized(props, segment);
2354
+ return segment;
2355
+ });
2356
+ }
2357
+
2358
+ /**
2359
+ * @param {object} graph Like props.graph or props.correct
2360
+ * @param {object} props of an InteractiveGraph instance
2361
+ */
2362
+ static getAngleCoords(
2363
+ graph: PerseusGraphTypeAngle,
2364
+ props: Props,
2365
+ ): ReadonlyArray<Coord> {
2366
+ let coords = graph.coords;
2367
+ if (coords) {
2368
+ return coords;
2369
+ }
2370
+
2371
+ const snap = graph.snapDegrees || 1;
2372
+ let angle = snap;
2373
+ while (angle < 20) {
2374
+ angle += snap;
2375
+ }
2376
+ angle = (angle * Math.PI) / 180;
2377
+ const offset = ((graph.angleOffsetDeg || 0) * Math.PI) / 180;
2378
+
2379
+ coords = InteractiveGraph.pointsFromNormalized(props, [
2380
+ [0.85, 0.5],
2381
+ [0.5, 0.5],
2382
+ ]);
2383
+
2384
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
2385
+ const radius = magnitude(vector(...coords));
2386
+
2387
+ // Adjust the lower point by angleOffsetDeg degrees
2388
+ // @ts-expect-error [FEI-5003] - TS2542 - Index signature in type 'readonly Coord[]' only permits reading.
2389
+ coords[0] = [
2390
+ coords[1][0] + radius * Math.cos(offset),
2391
+ coords[1][1] + radius * Math.sin(offset),
2392
+ ];
2393
+ // Position the upper point angle radians from the
2394
+ // lower point
2395
+ // @ts-expect-error [FEI-5003] - TS2542 - Index signature in type 'readonly Coord[]' only permits reading.
2396
+ coords[2] = [
2397
+ coords[1][0] + radius * Math.cos(angle + offset),
2398
+ coords[1][1] + radius * Math.sin(angle + offset),
2399
+ ];
2400
+
2401
+ return coords;
2402
+ }
2403
+
2404
+ static normalizeCoords(
2405
+ coordsList: ReadonlyArray<Coord>,
2406
+ ranges: [Range, Range],
2407
+ ): ReadonlyArray<Coord> {
2408
+ // @ts-expect-error [FEI-5003] - TS2322 - Type 'number[][]' is not assignable to type 'readonly Coord[]'.
2409
+ return _.map(coordsList, function (coords) {
2410
+ return _.map(coords, function (coord, i) {
2411
+ const extent = ranges[i][1] - ranges[i][0];
2412
+ return (coord + ranges[i][1]) / extent;
2413
+ });
2414
+ });
2415
+ }
2416
+
2417
+ static getEquationString(props: Props): string {
2418
+ const type = props.graph.type;
2419
+ const funcName = "get" + capitalize(type) + "EquationString";
2420
+ return InteractiveGraph[funcName](props);
2421
+ }
2422
+
2423
+ static pointsFromNormalized(
2424
+ props: Props,
2425
+ coordsList: ReadonlyArray<Coord>,
2426
+ noSnap?: boolean,
2427
+ ): ReadonlyArray<Coord> {
2428
+ // @ts-expect-error [FEI-5003] - TS2322 - Type 'number[][]' is not assignable to type 'readonly Coord[]'.
2429
+ return _.map(coordsList, function (coords) {
2430
+ return _.map(coords, function (coord, i) {
2431
+ const range: Range = props.range[i];
2432
+ if (noSnap) {
2433
+ return range[0] + (range[1] - range[0]) * coord;
2434
+ }
2435
+ const step = props.step[i];
2436
+ const nSteps = numSteps(range, step);
2437
+ const tick = Math.round(coord * nSteps);
2438
+ return range[0] + step * tick;
2439
+ });
2440
+ });
2441
+ }
2442
+
2443
+ static getLinearEquationString(props: Props): string {
2444
+ const coords = InteractiveGraph.getLineCoords(props.graph, props);
2445
+ if (eq(coords[0][0], coords[1][0])) {
2446
+ return "x = " + coords[0][0].toFixed(3);
2447
+ }
2448
+ const m = (coords[1][1] - coords[0][1]) / (coords[1][0] - coords[0][0]);
2449
+ const b = coords[0][1] - m * coords[0][0];
2450
+ if (eq(m, 0)) {
2451
+ return "y = " + b.toFixed(3);
2452
+ }
2453
+ return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
2454
+ }
2455
+
2456
+ static getCurrentQuadraticCoefficients(props: Props): QuadraticCoefficient {
2457
+ // TODO(alpert): Don't duplicate
2458
+ const coords =
2459
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
2460
+ props.graph.coords ||
2461
+ InteractiveGraph.defaultQuadraticCoords(props);
2462
+ return InteractiveGraph.getQuadraticCoefficients(coords);
2463
+ }
2464
+
2465
+ static defaultQuadraticCoords(props: Props): ReadonlyArray<Coord> {
2466
+ const coords = [
2467
+ [0.25, 0.75],
2468
+ [0.5, 0.25],
2469
+ [0.75, 0.75],
2470
+ ];
2471
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
2472
+ return InteractiveGraph.pointsFromNormalized(props, coords);
2473
+ }
2474
+
2475
+ static getQuadraticEquationString(props: Props): string {
2476
+ const coeffs = InteractiveGraph.getCurrentQuadraticCoefficients(props);
2477
+ return (
2478
+ "y = " +
2479
+ coeffs[0].toFixed(3) +
2480
+ "x^2 + " +
2481
+ coeffs[1].toFixed(3) +
2482
+ "x + " +
2483
+ coeffs[2].toFixed(3)
2484
+ );
2485
+ }
2486
+
2487
+ static getCurrentSinusoidCoefficients(props: Props): SineCoefficient {
2488
+ const coords =
2489
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
2490
+ props.graph.coords || InteractiveGraph.defaultSinusoidCoords(props);
2491
+ return InteractiveGraph.getSinusoidCoefficients(coords);
2492
+ }
2493
+
2494
+ static defaultSinusoidCoords(props: Props): ReadonlyArray<Coord> {
2495
+ const coords = [
2496
+ [0.5, 0.5],
2497
+ [0.65, 0.6],
2498
+ ];
2499
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
2500
+ return InteractiveGraph.pointsFromNormalized(props, coords);
2501
+ }
2502
+
2503
+ static getSinusoidEquationString(props: Props): string {
2504
+ const coeffs = InteractiveGraph.getCurrentSinusoidCoefficients(props);
2505
+ return (
2506
+ "y = " +
2507
+ coeffs[0].toFixed(3) +
2508
+ "sin(" +
2509
+ coeffs[1].toFixed(3) +
2510
+ "x - " +
2511
+ coeffs[2].toFixed(3) +
2512
+ ") + " +
2513
+ coeffs[3].toFixed(3)
2514
+ );
2515
+ }
2516
+
2517
+ static getCircleEquationString(props: Props): string {
2518
+ const graph = props.graph;
2519
+ // TODO(alpert): Don't duplicate
2520
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'.
2521
+ const center = graph.center || [0, 0];
2522
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
2523
+ const radius = graph.radius || 2;
2524
+ return (
2525
+ "center (" + center[0] + ", " + center[1] + "), radius " + radius
2526
+ );
2527
+ }
2528
+
2529
+ static getLinearSystemEquationString(props: Props): string {
2530
+ const coords = InteractiveGraph.getLinearSystemCoords(
2531
+ props.graph,
2532
+ props,
2533
+ );
2534
+
2535
+ return (
2536
+ "\n" +
2537
+ getLineEquation(coords[0][0], coords[0][1]) +
2538
+ "\n" +
2539
+ getLineEquation(coords[1][0], coords[1][1]) +
2540
+ "\n" +
2541
+ getLineIntersection(coords[0], coords[1])
2542
+ );
2543
+ }
2544
+
2545
+ static getPointEquationString(props: Props): string {
2546
+ if (props.graph.type !== "point") {
2547
+ throw makeInvalidTypeError("getPointEquationString", "point");
2548
+ }
2549
+
2550
+ const coords = InteractiveGraph.getPointCoords(props.graph, props);
2551
+ return coords
2552
+ .map(function (coord) {
2553
+ return "(" + coord[0] + ", " + coord[1] + ")";
2554
+ })
2555
+ .join(", ");
2556
+ }
2557
+
2558
+ static getSegmentEquationString(props: Props): string {
2559
+ if (props.graph.type !== "segment") {
2560
+ throw makeInvalidTypeError("getSegmentEquationString", "segment");
2561
+ }
2562
+
2563
+ const segments = InteractiveGraph.getSegmentCoords(props.graph, props);
2564
+ return _.map(segments, function (segment) {
2565
+ return (
2566
+ "[" +
2567
+ _.map(segment, function (coord) {
2568
+ return "(" + coord.join(", ") + ")";
2569
+ }).join(" ") +
2570
+ "]"
2571
+ );
2572
+ }).join(" ");
2573
+ }
2574
+
2575
+ static getRayEquationString(props: Props): string {
2576
+ if (props.graph.type !== "ray") {
2577
+ throw makeInvalidTypeError("createPointForPolygonType", "ray");
2578
+ }
2579
+
2580
+ const coords = InteractiveGraph.getLineCoords(props.graph, props);
2581
+ const a = coords[0];
2582
+ const b = coords[1];
2583
+ let eq = InteractiveGraph.getLinearEquationString(props);
2584
+
2585
+ if (a[0] > b[0]) {
2586
+ eq += " (for x <= " + a[0].toFixed(3) + ")";
2587
+ } else if (a[0] < b[0]) {
2588
+ eq += " (for x >= " + a[0].toFixed(3) + ")";
2589
+ } else if (a[1] > b[1]) {
2590
+ eq += " (for y <= " + a[1].toFixed(3) + ")";
2591
+ } else {
2592
+ eq += " (for y >= " + a[1].toFixed(3) + ")";
2593
+ }
2594
+
2595
+ return eq;
2596
+ }
2597
+
2598
+ static getPolygonEquationString(props: Props): string {
2599
+ if (props.graph.type !== "polygon") {
2600
+ throw makeInvalidTypeError("getPolygonEquationString", "polygon");
2601
+ }
2602
+ const coords = InteractiveGraph.getPolygonCoords(props.graph, props);
2603
+ return _.map(coords, function (coord) {
2604
+ return "(" + coord.join(", ") + ")";
2605
+ }).join(" ");
2606
+ }
2607
+
2608
+ static getAngleEquationString(props: Props): string {
2609
+ if (props.graph.type !== "angle") {
2610
+ throw makeInvalidTypeError("getAngleEquationString", "angle");
2611
+ }
2612
+ const coords = InteractiveGraph.getAngleCoords(props.graph, props);
2613
+ const angle = GraphUtils.findAngle(coords[2], coords[0], coords[1]);
2614
+ return (
2615
+ angle.toFixed(0) +
2616
+ "\u00B0 angle" +
2617
+ " at (" +
2618
+ coords[1].join(", ") +
2619
+ ")"
2620
+ );
2621
+ }
2622
+
2623
+ static validate(
2624
+ // TODO(kevinb): rename state to userInput
2625
+ // state === userInput
2626
+ state: PerseusGraphType,
2627
+ rubric: Rubric,
2628
+ component: any,
2629
+ ): PerseusScore {
2630
+ // When nothing has moved, there will neither be coords nor the
2631
+ // circle's center/radius fields. When those fields are absent, skip
2632
+ // all these checks; just go mark the answer as empty.
2633
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'center' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
2634
+ const hasValue = !!(state.coords || (state.center && state.radius));
2635
+
2636
+ if (state.type === rubric.correct.type && hasValue) {
2637
+ if (
2638
+ state.type === "linear" &&
2639
+ rubric.correct.type === "linear" &&
2640
+ state.coords != null
2641
+ ) {
2642
+ const guess = state.coords;
2643
+ const correct = rubric.correct.coords;
2644
+
2645
+ // If both of the guess points are on the correct line, it's
2646
+ // correct.
2647
+ if (
2648
+ // $FlowFixMe[incompatible-use] - Fix type so coords can't be omitted
2649
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
2650
+ collinear(correct[0], correct[1], guess[0]) &&
2651
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
2652
+ collinear(correct[0], correct[1], guess[1])
2653
+ ) {
2654
+ return {
2655
+ type: "points",
2656
+ earned: 1,
2657
+ total: 1,
2658
+ message: null,
2659
+ };
2660
+ }
2661
+ } else if (
2662
+ state.type === "linear-system" &&
2663
+ rubric.correct.type === "linear-system" &&
2664
+ state.coords != null
2665
+ ) {
2666
+ const guess = state.coords;
2667
+ const correct = rubric.correct.coords as ReadonlyArray<
2668
+ ReadonlyArray<Coord>
2669
+ >;
2670
+
2671
+ if (
2672
+ (collinear(correct[0][0], correct[0][1], guess[0][0]) &&
2673
+ collinear(correct[0][0], correct[0][1], guess[0][1]) &&
2674
+ collinear(correct[1][0], correct[1][1], guess[1][0]) &&
2675
+ collinear(correct[1][0], correct[1][1], guess[1][1])) ||
2676
+ (collinear(correct[0][0], correct[0][1], guess[1][0]) &&
2677
+ collinear(correct[0][0], correct[0][1], guess[1][1]) &&
2678
+ collinear(correct[1][0], correct[1][1], guess[0][0]) &&
2679
+ collinear(correct[1][0], correct[1][1], guess[0][1]))
2680
+ ) {
2681
+ return {
2682
+ type: "points",
2683
+ earned: 1,
2684
+ total: 1,
2685
+ message: null,
2686
+ };
2687
+ }
2688
+ } else if (
2689
+ state.type === "quadratic" &&
2690
+ rubric.correct.type === "quadratic" &&
2691
+ state.coords != null
2692
+ ) {
2693
+ // If the parabola coefficients match, it's correct.
2694
+ const guessCoeffs = this.getQuadraticCoefficients(state.coords);
2695
+ const correctCoeffs = this.getQuadraticCoefficients(
2696
+ // $FlowFixMe[incompatible-call]
2697
+ // $FlowFixMe[prop-missing]
2698
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
2699
+ rubric.correct.coords,
2700
+ );
2701
+ if (deepEq(guessCoeffs, correctCoeffs)) {
2702
+ return {
2703
+ type: "points",
2704
+ earned: 1,
2705
+ total: 1,
2706
+ message: null,
2707
+ };
2708
+ }
2709
+ } else if (
2710
+ state.type === "sinusoid" &&
2711
+ rubric.correct.type === "sinusoid" &&
2712
+ state.coords != null
2713
+ ) {
2714
+ const guessCoeffs = this.getSinusoidCoefficients(state.coords);
2715
+ const correctCoeffs = this.getSinusoidCoefficients(
2716
+ // $FlowFixMe[incompatible-call]
2717
+ // $FlowFixMe[prop-missing]
2718
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
2719
+ rubric.correct.coords,
2720
+ );
2721
+
2722
+ const canonicalGuessCoeffs =
2723
+ canonicalSineCoefficients(guessCoeffs);
2724
+ const canonicalCorrectCoeffs =
2725
+ canonicalSineCoefficients(correctCoeffs);
2726
+ // If the canonical coefficients match, it's correct.
2727
+ if (deepEq(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
2728
+ return {
2729
+ type: "points",
2730
+ earned: 1,
2731
+ total: 1,
2732
+ message: null,
2733
+ };
2734
+ }
2735
+ } else if (
2736
+ state.type === "circle" &&
2737
+ rubric.correct.type === "circle"
2738
+ ) {
2739
+ if (
2740
+ deepEq(state.center, rubric.correct.center) &&
2741
+ // $FlowFixMe[prop-missing]
2742
+ eq(state.radius, rubric.correct.radius)
2743
+ ) {
2744
+ return {
2745
+ type: "points",
2746
+ earned: 1,
2747
+ total: 1,
2748
+ message: null,
2749
+ };
2750
+ }
2751
+ } else if (
2752
+ state.type === "point" &&
2753
+ rubric.correct.type === "point" &&
2754
+ state.coords != null
2755
+ ) {
2756
+ let correct = InteractiveGraph.getPointCoords(
2757
+ rubric.correct,
2758
+ component,
2759
+ );
2760
+ const guess = state.coords?.slice();
2761
+ correct = correct.slice();
2762
+ // Everything's already rounded so we shouldn't need to do an
2763
+ // eq() comparison but _.isEqual(0, -0) is false, so we'll use
2764
+ // eq() anyway. The sort should be fine because it'll stringify
2765
+ // it and -0 converted to a string is "0"
2766
+ guess?.sort();
2767
+ // @ts-expect-error [FEI-5003] - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
2768
+ correct.sort();
2769
+ if (deepEq(guess, correct)) {
2770
+ return {
2771
+ type: "points",
2772
+ earned: 1,
2773
+ total: 1,
2774
+ message: null,
2775
+ };
2776
+ }
2777
+ } else if (
2778
+ state.type === "polygon" &&
2779
+ rubric.correct.type === "polygon" &&
2780
+ state.coords != null
2781
+ ) {
2782
+ const guess: Array<Coord> = state.coords?.slice();
2783
+ // @ts-expect-error [FEI-5003] - TS2322 - Type 'Coord[] | undefined' is not assignable to type 'Coord[]'.
2784
+ const correct: Array<Coord> = rubric.correct.coords?.slice();
2785
+
2786
+ let match;
2787
+ if (rubric.correct.match === "similar") {
2788
+ match = similar(guess, correct, Number.POSITIVE_INFINITY);
2789
+ } else if (rubric.correct.match === "congruent") {
2790
+ match = similar(guess, correct, knumber.DEFAULT_TOLERANCE);
2791
+ } else if (rubric.correct.match === "approx") {
2792
+ match = similar(guess, correct, 0.1);
2793
+ } else {
2794
+ /* exact */
2795
+ guess.sort();
2796
+ correct.sort();
2797
+ match = deepEq(guess, correct);
2798
+ }
2799
+
2800
+ if (match) {
2801
+ return {
2802
+ type: "points",
2803
+ earned: 1,
2804
+ total: 1,
2805
+ message: null,
2806
+ };
2807
+ }
2808
+ } else if (
2809
+ state.type === "segment" &&
2810
+ rubric.correct.type === "segment" &&
2811
+ state.coords != null
2812
+ ) {
2813
+ let guess = state.coords.slice();
2814
+ let correct = rubric.correct.coords?.slice();
2815
+ guess = _.invoke(guess, "sort").sort();
2816
+ // @ts-expect-error [FEI-5003] - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
2817
+ correct = _.invoke(correct, "sort").sort();
2818
+ if (deepEq(guess, correct)) {
2819
+ return {
2820
+ type: "points",
2821
+ earned: 1,
2822
+ total: 1,
2823
+ message: null,
2824
+ };
2825
+ }
2826
+ } else if (
2827
+ state.type === "ray" &&
2828
+ rubric.correct.type === "ray" &&
2829
+ state.coords != null
2830
+ ) {
2831
+ const guess = state.coords;
2832
+ const correct = rubric.correct.coords;
2833
+ if (
2834
+ // $FlowFixMe[incompatible-use]
2835
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
2836
+ deepEq(guess[0], correct[0]) &&
2837
+ // $FlowFixMe[incompatible-use]
2838
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
2839
+ collinear(correct[0], correct[1], guess[1])
2840
+ ) {
2841
+ return {
2842
+ type: "points",
2843
+ earned: 1,
2844
+ total: 1,
2845
+ message: null,
2846
+ };
2847
+ }
2848
+ } else if (
2849
+ state.type === "angle" &&
2850
+ rubric.correct.type === "angle"
2851
+ ) {
2852
+ const guess = state.coords;
2853
+ const correct = rubric.correct.coords;
2854
+
2855
+ let match;
2856
+ if (rubric.correct.match === "congruent") {
2857
+ const angles = _.map([guess, correct], function (coords) {
2858
+ const angle = GraphUtils.findAngle(
2859
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
2860
+ coords[2],
2861
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
2862
+ coords[0],
2863
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
2864
+ coords[1],
2865
+ );
2866
+ return (angle + 360) % 360;
2867
+ });
2868
+ // @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
2869
+ match = eq(...angles);
2870
+ } else {
2871
+ /* exact */
2872
+ match =
2873
+ // $FlowFixMe[incompatible-use]
2874
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
2875
+ deepEq(guess[1], correct[1]) &&
2876
+ // $FlowFixMe[incompatible-use]
2877
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
2878
+ collinear(correct[1], correct[0], guess[0]) &&
2879
+ // $FlowFixMe[incompatible-use]
2880
+ // @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
2881
+ collinear(correct[1], correct[2], guess[2]);
2882
+ }
2883
+
2884
+ if (match) {
2885
+ return {
2886
+ type: "points",
2887
+ earned: 1,
2888
+ total: 1,
2889
+ message: null,
2890
+ };
2891
+ }
2892
+ }
2893
+ }
2894
+
2895
+ // The input wasn't correct, so check if it's a blank input or if it's
2896
+ // actually just wrong
2897
+ if (!hasValue || _.isEqual(state, rubric.graph)) {
2898
+ // We're where we started.
2899
+ return {
2900
+ type: "invalid",
2901
+ message: null,
2902
+ };
2903
+ }
2904
+ return {
2905
+ type: "points",
2906
+ earned: 0,
2907
+ total: 1,
2908
+ message: null,
2909
+ };
2910
+ }
2911
+
2912
+ static getUserInputFromProps(props: Props): PerseusGraphType {
2913
+ return props.graph;
2914
+ }
2915
+ }
2916
+
2917
+ export default {
2918
+ name: "interactive-graph",
2919
+ displayName: "Interactive graph",
2920
+ widget: InteractiveGraph,
2921
+ } as WidgetExports<typeof InteractiveGraph>;