@khanacademy/perseus 2.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1212) hide show
  1. package/.babelrc.js +1 -1
  2. package/.eslintrc.js +1 -0
  3. package/CHANGELOG.md +22 -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 +23239 -26937
  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 +13063 -16571
  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 +8 -8
  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__/__snapshots__/input-number.test.ts.snap +497 -0
  691. package/src/widgets/__tests__/__snapshots__/unit.test.tsx.snap +20 -0
  692. package/src/widgets/__tests__/categorizer.test.ts +203 -0
  693. package/src/widgets/__tests__/cs-program.test.ts +43 -0
  694. package/src/widgets/__tests__/definition.test.ts +136 -0
  695. package/src/widgets/__tests__/dropdown.test.ts +105 -0
  696. package/src/widgets/__tests__/explanation.test.ts +198 -0
  697. package/src/widgets/__tests__/expression.test.ts +431 -0
  698. package/src/widgets/__tests__/graded-group-set-jipt.test.ts +30 -0
  699. package/src/widgets/__tests__/graded-group-set.test.ts +288 -0
  700. package/src/widgets/__tests__/graded-group.test.ts +232 -0
  701. package/src/widgets/__tests__/grapher.cypress.ts +576 -0
  702. package/src/widgets/__tests__/grapher.test.ts +33 -0
  703. package/src/widgets/__tests__/group.test.tsx +512 -0
  704. package/src/widgets/__tests__/iframe.test.ts +45 -0
  705. package/src/widgets/__tests__/image.test.ts +62 -0
  706. package/src/widgets/__tests__/input-number.test.ts +354 -0
  707. package/src/widgets/__tests__/interaction.test.ts +31 -0
  708. package/src/widgets/__tests__/interactive-graph.cypress.ts +815 -0
  709. package/src/widgets/__tests__/interactive-graph.test.ts +106 -0
  710. package/src/widgets/__tests__/matcher.test.tsx +152 -0
  711. package/src/widgets/__tests__/matrix.test.ts +82 -0
  712. package/src/widgets/__tests__/number-line.test.ts +79 -0
  713. package/src/widgets/__tests__/numeric-input.test.ts +502 -0
  714. package/src/widgets/__tests__/orderer.test.ts +73 -0
  715. package/src/widgets/__tests__/passage-ref.test.ts +168 -0
  716. package/src/widgets/__tests__/passage.test.tsx +224 -0
  717. package/src/widgets/__tests__/radio/base-radio.test.tsx +470 -0
  718. package/src/widgets/__tests__/radio/choice-icon.test.tsx +90 -0
  719. package/src/widgets/__tests__/radio/choice.test.tsx +310 -0
  720. package/src/widgets/__tests__/radio/focus-ring.test.tsx +23 -0
  721. package/src/widgets/__tests__/radio/option-status.test.tsx +77 -0
  722. package/src/widgets/__tests__/radio.test.ts +807 -0
  723. package/src/widgets/__tests__/renderQuestion.tsx +77 -0
  724. package/src/widgets/__tests__/unit.test.tsx +168 -0
  725. package/src/widgets/__tests__/video.test.ts +53 -0
  726. package/src/widgets/categorizer.tsx +343 -0
  727. package/src/widgets/cs-program.tsx +227 -0
  728. package/src/widgets/definition.tsx +121 -0
  729. package/src/widgets/dropdown.tsx +142 -0
  730. package/src/widgets/explanation.tsx +317 -0
  731. package/src/widgets/expression.tsx +647 -0
  732. package/src/widgets/graded-group-answer-bar.tsx +142 -0
  733. package/src/widgets/graded-group-set.tsx +296 -0
  734. package/src/widgets/graded-group.tsx +466 -0
  735. package/src/widgets/grapher/util.tsx +803 -0
  736. package/src/widgets/grapher.tsx +674 -0
  737. package/src/widgets/group.tsx +196 -0
  738. package/src/widgets/iframe.tsx +195 -0
  739. package/src/widgets/image.tsx +309 -0
  740. package/src/widgets/input-number.tsx +413 -0
  741. package/src/widgets/interaction/arrow-picker.tsx +38 -0
  742. package/src/widgets/interaction/color-picker.tsx +69 -0
  743. package/src/widgets/interaction/dash-picker.tsx +45 -0
  744. package/src/widgets/interaction/element-container.tsx +101 -0
  745. package/src/widgets/interaction.tsx +826 -0
  746. package/src/widgets/interactive-graph.tsx +2921 -0
  747. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +98 -0
  748. package/src/widgets/label-image/__stories__/marker.stories.tsx +181 -0
  749. package/src/widgets/label-image/answer-choices.tsx +300 -0
  750. package/src/widgets/label-image/marker.tsx +364 -0
  751. package/src/widgets/label-image/types.ts +26 -0
  752. package/src/widgets/label-image.test.ts +840 -0
  753. package/src/widgets/label-image.tsx +849 -0
  754. package/src/widgets/lights-puzzle.tsx +359 -0
  755. package/src/widgets/matcher.tsx +278 -0
  756. package/src/widgets/matrix.tsx +637 -0
  757. package/src/widgets/measurer.tsx +217 -0
  758. package/src/widgets/molecule/__tests__/layout.test.ts +162 -0
  759. package/src/widgets/molecule/__tests__/parser.test.ts +155 -0
  760. package/src/widgets/molecule/molecule-drawing.ts +173 -0
  761. package/src/widgets/molecule/molecule-layout.ts +362 -0
  762. package/src/widgets/molecule/smiles-parser.ts +296 -0
  763. package/src/widgets/molecule.tsx +172 -0
  764. package/src/widgets/number-line.tsx +864 -0
  765. package/src/widgets/numeric-input.tsx +563 -0
  766. package/src/widgets/orderer.tsx +788 -0
  767. package/src/widgets/passage/__tests__/passage-markdown.test.ts +376 -0
  768. package/src/widgets/passage/passage-markdown.tsx +475 -0
  769. package/src/widgets/passage-ref-target.tsx +73 -0
  770. package/src/widgets/passage-ref.tsx +215 -0
  771. package/src/widgets/passage.tsx +584 -0
  772. package/src/widgets/plotter.tsx +1204 -0
  773. package/src/widgets/radio/__stories__/base-radio.stories.tsx +204 -0
  774. package/src/widgets/radio/__stories__/choice-icon.stories.tsx +168 -0
  775. package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +40 -0
  776. package/src/widgets/radio/__stories__/choice.stories.tsx +137 -0
  777. package/src/widgets/radio/__stories__/focus-ring.stories.tsx +43 -0
  778. package/src/widgets/radio/__stories__/option-status.stories.tsx +105 -0
  779. package/src/widgets/radio/base-radio.tsx +509 -0
  780. package/src/widgets/radio/choice-a11y-text.ts +61 -0
  781. package/src/widgets/radio/choice-icon/choice-icon.tsx +217 -0
  782. package/src/widgets/radio/choice-icon/cross-out-line.tsx +53 -0
  783. package/src/widgets/radio/choice-icon/shared-styles.ts +15 -0
  784. package/src/widgets/radio/choice-none-above.tsx +56 -0
  785. package/src/widgets/radio/choice.jsx.fixture.ts +45 -0
  786. package/src/widgets/radio/choice.tsx +358 -0
  787. package/src/widgets/radio/focus-ring.tsx +57 -0
  788. package/src/widgets/radio/option-status.tsx +100 -0
  789. package/src/widgets/radio/util.test.ts +18 -0
  790. package/src/widgets/radio/util.ts +31 -0
  791. package/src/widgets/radio/widget.tsx +521 -0
  792. package/src/widgets/radio.ts +157 -0
  793. package/src/widgets/reaction-diagram.tsx +144 -0
  794. package/src/widgets/sequence.tsx +137 -0
  795. package/src/widgets/simulator.tsx +831 -0
  796. package/src/widgets/sorter.tsx +154 -0
  797. package/src/widgets/table.tsx +393 -0
  798. package/src/widgets/transformer.tsx +2951 -0
  799. package/src/widgets/unit.tsx +374 -0
  800. package/src/widgets/video-transcript-link.test.tsx +213 -0
  801. package/src/widgets/video-transcript-link.tsx +84 -0
  802. package/src/widgets/video.tsx +124 -0
  803. package/src/widgets.ts +444 -0
  804. package/src/zoom.ts +558 -0
  805. package/tsconfig.json +25 -0
  806. package/tsconfig.tsbuildinfo +1 -0
  807. package/src/__stories__/hints-renderer.stories.jsx +0 -60
  808. package/src/__stories__/item-renderer.stories.jsx +0 -27
  809. package/src/__stories__/server-item-renderer.stories.jsx +0 -27
  810. package/src/__testdata__/graphie_testdata.js +0 -53
  811. package/src/__testdata__/item-renderer_testdata.js +0 -161
  812. package/src/__testdata__/renderer_testdata.js +0 -123
  813. package/src/__tests__/a11y_test.jsx +0 -304
  814. package/src/__tests__/item-renderer_test.jsx +0 -892
  815. package/src/__tests__/jipt-paragraphs_test.js +0 -34
  816. package/src/__tests__/mock-asset-loading-widget.jsx +0 -57
  817. package/src/__tests__/mock-widget.jsx +0 -35
  818. package/src/__tests__/perseus-markdown_test.jsx +0 -747
  819. package/src/__tests__/renderability_test.jsx +0 -481
  820. package/src/__tests__/renderer-api_test.jsx +0 -309
  821. package/src/__tests__/renderer_test.jsx +0 -1792
  822. package/src/__tests__/server-item-renderer_test.jsx +0 -516
  823. package/src/__tests__/test-items/image-item.js +0 -25
  824. package/src/__tests__/test-items/input-number-1-item.js +0 -27
  825. package/src/__tests__/test-items/input-number-2-item.js +0 -39
  826. package/src/__tests__/test-items/table-item.js +0 -30
  827. package/src/__tests__/tex-wrangler_test.js +0 -83
  828. package/src/__tests__/util_test.js +0 -41
  829. package/src/a11y.js +0 -46
  830. package/src/all-widgets.js +0 -9
  831. package/src/article-renderer.jsx +0 -214
  832. package/src/asset-context.js +0 -30
  833. package/src/basic-widgets.js +0 -13
  834. package/src/components/__stories__/button-group.stories.jsx +0 -61
  835. package/src/components/__stories__/fixed-to-responsive.stories.jsx +0 -132
  836. package/src/components/__stories__/graph.stories.jsx +0 -27
  837. package/src/components/__stories__/graphie.stories.jsx +0 -32
  838. package/src/components/__stories__/hud.stories.jsx +0 -36
  839. package/src/components/__stories__/icon.stories.jsx +0 -56
  840. package/src/components/__stories__/image-loader.stories.jsx +0 -60
  841. package/src/components/__stories__/info-tip.stories.jsx +0 -33
  842. package/src/components/__stories__/inline-icon.stories.jsx +0 -39
  843. package/src/components/__stories__/input-with-examples.stories.jsx +0 -52
  844. package/src/components/__stories__/lint.stories.jsx +0 -40
  845. package/src/components/__stories__/math-input.stories.jsx +0 -29
  846. package/src/components/__stories__/math-output.stories.jsx +0 -24
  847. package/src/components/__stories__/multi-button-group.stories.jsx +0 -69
  848. package/src/components/__stories__/number-input.stories.jsx +0 -42
  849. package/src/components/__stories__/prop-check-box.stories.jsx +0 -62
  850. package/src/components/__stories__/range-input.stories.jsx +0 -28
  851. package/src/components/__stories__/simple-keypad-input.stories.jsx +0 -28
  852. package/src/components/__stories__/sortable.stories.jsx +0 -73
  853. package/src/components/__stories__/stub-tag-editor.stories.jsx +0 -44
  854. package/src/components/__stories__/svg-image.stories.jsx +0 -71
  855. package/src/components/__stories__/tex.stories.jsx +0 -24
  856. package/src/components/__stories__/text-buttons.stories.jsx +0 -51
  857. package/src/components/__stories__/text-input.stories.jsx +0 -34
  858. package/src/components/__stories__/text-list-editor.stories.jsx +0 -33
  859. package/src/components/__stories__/zoomable-tex.stories.jsx +0 -39
  860. package/src/components/__stories__/zoomable.stories.jsx +0 -40
  861. package/src/components/__testdata__/sorter_testdata.js +0 -20
  862. package/src/components/__tests__/number-input_test.jsx +0 -112
  863. package/src/components/__tests__/sortable_test.jsx +0 -234
  864. package/src/components/__tests__/sorter_test.jsx +0 -111
  865. package/src/components/__tests__/zoomable_test.jsx +0 -388
  866. package/src/components/button-group.jsx +0 -131
  867. package/src/components/constants.js +0 -15
  868. package/src/components/fixed-to-responsive.jsx +0 -165
  869. package/src/components/graph.jsx +0 -413
  870. package/src/components/graphie-classes.jsx +0 -148
  871. package/src/components/graphie-movables.jsx +0 -342
  872. package/src/components/graphie.jsx +0 -353
  873. package/src/components/highlighting/highlightable-content.jsx +0 -245
  874. package/src/components/highlighting/highlights.js +0 -208
  875. package/src/components/highlighting/ranges.js +0 -299
  876. package/src/components/highlighting/types.js +0 -73
  877. package/src/components/highlighting/ui/highlight-renderer.jsx +0 -179
  878. package/src/components/highlighting/ui/highlight-set-renderer.jsx +0 -190
  879. package/src/components/highlighting/ui/highlight-tooltip.jsx +0 -143
  880. package/src/components/highlighting/ui/highlighting-ui.jsx +0 -163
  881. package/src/components/highlighting/ui/selection-tracker.jsx +0 -184
  882. package/src/components/highlighting/ui/types.js +0 -21
  883. package/src/components/highlighting/ui/util.js +0 -166
  884. package/src/components/highlighting/word-indexer.jsx +0 -155
  885. package/src/components/hud.jsx +0 -179
  886. package/src/components/icon.jsx +0 -149
  887. package/src/components/image-loader.jsx +0 -175
  888. package/src/components/info-tip/info-tip.jsx +0 -146
  889. package/src/components/info-tip.jsx +0 -38
  890. package/src/components/inline-icon.jsx +0 -81
  891. package/src/components/input-with-examples.jsx +0 -248
  892. package/src/components/lint.jsx +0 -455
  893. package/src/components/math-input.jsx +0 -293
  894. package/src/components/math-output.jsx +0 -128
  895. package/src/components/multi-button-group.jsx +0 -137
  896. package/src/components/number-input.jsx +0 -253
  897. package/src/components/prop-check-box.jsx +0 -80
  898. package/src/components/range-input.jsx +0 -61
  899. package/src/components/simple-keypad-input.jsx +0 -88
  900. package/src/components/sortable.jsx +0 -872
  901. package/src/components/stub-tag-editor.jsx +0 -54
  902. package/src/components/svg-image.jsx +0 -765
  903. package/src/components/tex-buttons.jsx +0 -320
  904. package/src/components/tex.jsx +0 -70
  905. package/src/components/text-input.jsx +0 -133
  906. package/src/components/text-list-editor.jsx +0 -157
  907. package/src/components/tooltip.jsx +0 -392
  908. package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +0 -140
  909. package/src/components/visibility-observer/get-scroll-parent.js +0 -45
  910. package/src/components/visibility-observer/visibility-observer.js +0 -109
  911. package/src/components/visibility-observer/visibility-observer.jsx +0 -123
  912. package/src/components/zoomable-tex.jsx +0 -70
  913. package/src/components/zoomable.jsx +0 -294
  914. package/src/components.js +0 -17
  915. package/src/definition-context.js +0 -59
  916. package/src/dependencies.js +0 -22
  917. package/src/error-boundary.jsx +0 -48
  918. package/src/extra-widgets.js +0 -75
  919. package/src/hint-renderer.jsx +0 -155
  920. package/src/hints-renderer.jsx +0 -367
  921. package/src/icon-paths.js +0 -120
  922. package/src/index.js +0 -159
  923. package/src/init.js +0 -49
  924. package/src/interaction-tracker.js +0 -60
  925. package/src/interactive2/__tests__/movable-mock.js +0 -48
  926. package/src/interactive2/__tests__/movable-point_test.js +0 -146
  927. package/src/interactive2/interactive-util.js +0 -152
  928. package/src/interactive2/movable-helper-methods.js +0 -160
  929. package/src/interactive2/movable-line-options.js +0 -392
  930. package/src/interactive2/movable-line.js +0 -322
  931. package/src/interactive2/movable-point-options.js +0 -190
  932. package/src/interactive2/movable-point.jsx +0 -597
  933. package/src/interactive2/movable-polygon-options.js +0 -351
  934. package/src/interactive2/movable-polygon.js +0 -368
  935. package/src/interactive2/movable.js +0 -273
  936. package/src/interactive2/objective_.js +0 -79
  937. package/src/interactive2/types.js +0 -3
  938. package/src/interactive2/wrapped-defaults.js +0 -86
  939. package/src/interactive2/wrapped-ellipse.js +0 -86
  940. package/src/interactive2/wrapped-line.js +0 -109
  941. package/src/interactive2/wrapped-path.js +0 -47
  942. package/src/interactive2.js +0 -91
  943. package/src/item-renderer.jsx +0 -514
  944. package/src/item-version.js +0 -12
  945. package/src/jipt-paragraphs.js +0 -52
  946. package/src/keypad-context.js +0 -33
  947. package/src/loading-context.js +0 -20
  948. package/src/logging/log.js +0 -107
  949. package/src/logging/log_test.js +0 -41
  950. package/src/mixins/__tests__/widget-prop-denylist_test.js +0 -23
  951. package/src/mixins/changeable.jsx +0 -115
  952. package/src/mixins/editor-jsonify.jsx +0 -13
  953. package/src/mixins/provide-keypad.jsx +0 -131
  954. package/src/mixins/widget-jsonify-deprecated.jsx +0 -23
  955. package/src/mixins/widget-prop-denylist.js +0 -49
  956. package/src/multi-items/__stories__/multi-renderer.stories.js +0 -88
  957. package/src/multi-items/__testdata__/multi-renderer_testdata.js +0 -174
  958. package/src/multi-items/__tests__/items_test.js +0 -245
  959. package/src/multi-items/__tests__/multi-renderer_test.js +0 -903
  960. package/src/multi-items/__tests__/prop-type-builders_test.js +0 -134
  961. package/src/multi-items/__tests__/shapes_test.js +0 -44
  962. package/src/multi-items/__tests__/trees_test.js +0 -316
  963. package/src/multi-items/items.js +0 -176
  964. package/src/multi-items/multi-renderer.jsx +0 -543
  965. package/src/multi-items/prop-type-builders.js +0 -105
  966. package/src/multi-items/shape-types.js +0 -47
  967. package/src/multi-items/shapes.js +0 -53
  968. package/src/multi-items/tree-types.js +0 -42
  969. package/src/multi-items/trees.js +0 -345
  970. package/src/multi-items.js +0 -76
  971. package/src/perseus-api.jsx +0 -221
  972. package/src/perseus-env.js +0 -26
  973. package/src/perseus-error.js +0 -19
  974. package/src/perseus-markdown.jsx +0 -408
  975. package/src/perseus-types.js +0 -1471
  976. package/src/question-paragraph.jsx +0 -30
  977. package/src/renderability.jsx +0 -108
  978. package/src/renderer.jsx +0 -1937
  979. package/src/server-item-renderer.jsx +0 -467
  980. package/src/sigfigs.jsx +0 -492
  981. package/src/styles/constants.js +0 -96
  982. package/src/styles/global-constants.js +0 -344
  983. package/src/styles/global-styles.js +0 -307
  984. package/src/styles/media-queries.js +0 -48
  985. package/src/styles/shared.js +0 -132
  986. package/src/tex-wrangler.js +0 -176
  987. package/src/translation-linter.js +0 -125
  988. package/src/traversal.jsx +0 -141
  989. package/src/types.js +0 -488
  990. package/src/util/a11y.js +0 -24
  991. package/src/util/answer-types.js +0 -861
  992. package/src/util/answer-types_test.js +0 -115
  993. package/src/util/colors.js +0 -94
  994. package/src/util/fix-passage-refs.jsx +0 -122
  995. package/src/util/graph-utils.js +0 -16
  996. package/src/util/graphie.js +0 -1586
  997. package/src/util/interactive.js +0 -4078
  998. package/src/util/katex-preprocess.js +0 -16
  999. package/src/util/math.js +0 -289
  1000. package/src/util/mobile-native-utils.js +0 -14
  1001. package/src/util/mobile-native-utils_test.js +0 -24
  1002. package/src/util/react-render.js +0 -27
  1003. package/src/util/register-all-widgets-for-testing.js +0 -12
  1004. package/src/util/scroll-utils.js +0 -162
  1005. package/src/util/sizing-utils.js +0 -46
  1006. package/src/util/tex.js +0 -203
  1007. package/src/util/url-utils.js +0 -36
  1008. package/src/util.js +0 -984
  1009. package/src/version.js +0 -11
  1010. package/src/widget-container.jsx +0 -178
  1011. package/src/widgets/__stories__/categorizer.stories.jsx +0 -15
  1012. package/src/widgets/__stories__/cs-program.stories.jsx +0 -15
  1013. package/src/widgets/__stories__/definition.stories.jsx +0 -108
  1014. package/src/widgets/__stories__/dropdown.stories.jsx +0 -15
  1015. package/src/widgets/__stories__/explanation.stories.jsx +0 -19
  1016. package/src/widgets/__stories__/expression.stories.jsx +0 -26
  1017. package/src/widgets/__stories__/graded-group-set.stories.jsx +0 -32
  1018. package/src/widgets/__stories__/graded-group.stories.jsx +0 -32
  1019. package/src/widgets/__stories__/grapher.stories.jsx +0 -47
  1020. package/src/widgets/__stories__/group.stories.jsx +0 -15
  1021. package/src/widgets/__stories__/iframe.stories.jsx +0 -15
  1022. package/src/widgets/__stories__/image.stories.jsx +0 -62
  1023. package/src/widgets/__stories__/input-number.stories.jsx +0 -102
  1024. package/src/widgets/__stories__/interaction.stories.jsx +0 -15
  1025. package/src/widgets/__stories__/interactive-graph.stories.jsx +0 -61
  1026. package/src/widgets/__stories__/matcher.stories.jsx +0 -15
  1027. package/src/widgets/__stories__/matrix.stories.jsx +0 -15
  1028. package/src/widgets/__stories__/number-line.stories.jsx +0 -15
  1029. package/src/widgets/__stories__/numeric-input.stories.jsx +0 -106
  1030. package/src/widgets/__stories__/on-screen-keypad.stories.jsx +0 -118
  1031. package/src/widgets/__stories__/orderer.stories.jsx +0 -15
  1032. package/src/widgets/__stories__/passage-ref.stories.jsx +0 -23
  1033. package/src/widgets/__stories__/passage.stories.jsx +0 -27
  1034. package/src/widgets/__stories__/radio.stories.jsx +0 -90
  1035. package/src/widgets/__stories__/sorter.stories.jsx +0 -15
  1036. package/src/widgets/__stories__/transformer.stories.jsx +0 -15
  1037. package/src/widgets/__stories__/unit.stories.jsx +0 -45
  1038. package/src/widgets/__stories__/video-transcript-link.stories.jsx +0 -24
  1039. package/src/widgets/__stories__/video.stories.jsx +0 -15
  1040. package/src/widgets/__testdata__/base-radio_testdata.js +0 -22
  1041. package/src/widgets/__testdata__/categorizer_testdata.js +0 -50
  1042. package/src/widgets/__testdata__/cs-program_testdata.js +0 -28
  1043. package/src/widgets/__testdata__/dropdown_testdata.js +0 -34
  1044. package/src/widgets/__testdata__/explanation_testdata.js +0 -56
  1045. package/src/widgets/__testdata__/expression_testdata.js +0 -95
  1046. package/src/widgets/__testdata__/graded-group-set_testdata.js +0 -358
  1047. package/src/widgets/__testdata__/graded-group_testdata.js +0 -79
  1048. package/src/widgets/__testdata__/grapher_testdata.js +0 -376
  1049. package/src/widgets/__testdata__/group_testdata.js +0 -156
  1050. package/src/widgets/__testdata__/iframe_testdata.js +0 -36
  1051. package/src/widgets/__testdata__/image_testdata.js +0 -38
  1052. package/src/widgets/__testdata__/input-number_testdata.js +0 -73
  1053. package/src/widgets/__testdata__/interaction_testdata.js +0 -264
  1054. package/src/widgets/__testdata__/interactive-graph_testdata.js +0 -646
  1055. package/src/widgets/__testdata__/matcher_testdata.js +0 -34
  1056. package/src/widgets/__testdata__/matrix_testdata.js +0 -29
  1057. package/src/widgets/__testdata__/number-line_testdata.js +0 -31
  1058. package/src/widgets/__testdata__/numeric-input_testdata.js +0 -266
  1059. package/src/widgets/__testdata__/orderer_testdata.js +0 -58
  1060. package/src/widgets/__testdata__/passage-ref_testdata.js +0 -103
  1061. package/src/widgets/__testdata__/passage_testdata.js +0 -96
  1062. package/src/widgets/__testdata__/radio_testdata.js +0 -262
  1063. package/src/widgets/__testdata__/sorter_testdata.js +0 -20
  1064. package/src/widgets/__testdata__/transformer_testdata.js +0 -109
  1065. package/src/widgets/__testdata__/video_testdata.js +0 -22
  1066. package/src/widgets/__tests__/__snapshots__/expression_test.jsx.snap +0 -38
  1067. package/src/widgets/__tests__/__snapshots__/input-number_test.jsx.snap +0 -978
  1068. package/src/widgets/__tests__/categorizer_test.jsx +0 -205
  1069. package/src/widgets/__tests__/cs-program_test.jsx +0 -45
  1070. package/src/widgets/__tests__/definition_test.js +0 -137
  1071. package/src/widgets/__tests__/dropdown_test.jsx +0 -106
  1072. package/src/widgets/__tests__/explanation_test.js +0 -199
  1073. package/src/widgets/__tests__/expression_test.jsx +0 -439
  1074. package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +0 -32
  1075. package/src/widgets/__tests__/graded-group-set_test.jsx +0 -290
  1076. package/src/widgets/__tests__/graded-group_test.jsx +0 -233
  1077. package/src/widgets/__tests__/grapher.cypress.jsx +0 -546
  1078. package/src/widgets/__tests__/grapher_test.jsx +0 -35
  1079. package/src/widgets/__tests__/group_test.jsx +0 -512
  1080. package/src/widgets/__tests__/iframe_test.jsx +0 -47
  1081. package/src/widgets/__tests__/image_test.jsx +0 -65
  1082. package/src/widgets/__tests__/input-number_test.jsx +0 -360
  1083. package/src/widgets/__tests__/interaction_test.jsx +0 -33
  1084. package/src/widgets/__tests__/interactive-graph.cypress.jsx +0 -777
  1085. package/src/widgets/__tests__/interactive-graph_test.jsx +0 -105
  1086. package/src/widgets/__tests__/matcher_test.jsx +0 -157
  1087. package/src/widgets/__tests__/matrix_test.jsx +0 -84
  1088. package/src/widgets/__tests__/number-line_test.jsx +0 -81
  1089. package/src/widgets/__tests__/numeric-input_test.jsx +0 -514
  1090. package/src/widgets/__tests__/orderer_test.jsx +0 -75
  1091. package/src/widgets/__tests__/passage-ref_test.jsx +0 -166
  1092. package/src/widgets/__tests__/passage_test.jsx +0 -212
  1093. package/src/widgets/__tests__/radio/base-radio_test.jsx +0 -443
  1094. package/src/widgets/__tests__/radio/choice-icon_test.jsx +0 -91
  1095. package/src/widgets/__tests__/radio/choice_test.jsx +0 -311
  1096. package/src/widgets/__tests__/radio/focus-ring_test.jsx +0 -23
  1097. package/src/widgets/__tests__/radio/option-status_test.jsx +0 -65
  1098. package/src/widgets/__tests__/radio_test.jsx +0 -810
  1099. package/src/widgets/__tests__/renderQuestion.jsx +0 -66
  1100. package/src/widgets/__tests__/unit_test.jsx +0 -144
  1101. package/src/widgets/__tests__/video_test.jsx +0 -55
  1102. package/src/widgets/categorizer.jsx +0 -343
  1103. package/src/widgets/cs-program.jsx +0 -227
  1104. package/src/widgets/definition.jsx +0 -121
  1105. package/src/widgets/dropdown.jsx +0 -141
  1106. package/src/widgets/explanation.jsx +0 -315
  1107. package/src/widgets/expression.jsx +0 -652
  1108. package/src/widgets/graded-group-answer-bar.jsx +0 -152
  1109. package/src/widgets/graded-group-set.jsx +0 -296
  1110. package/src/widgets/graded-group.jsx +0 -453
  1111. package/src/widgets/grapher/util.jsx +0 -806
  1112. package/src/widgets/grapher.jsx +0 -663
  1113. package/src/widgets/group.jsx +0 -195
  1114. package/src/widgets/iframe.jsx +0 -198
  1115. package/src/widgets/image.jsx +0 -310
  1116. package/src/widgets/input-number.jsx +0 -412
  1117. package/src/widgets/interaction/arrow-picker.jsx +0 -39
  1118. package/src/widgets/interaction/color-picker.jsx +0 -69
  1119. package/src/widgets/interaction/dash-picker.jsx +0 -46
  1120. package/src/widgets/interaction/element-container.jsx +0 -97
  1121. package/src/widgets/interaction.jsx +0 -742
  1122. package/src/widgets/interactive-graph.jsx +0 -2820
  1123. package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +0 -93
  1124. package/src/widgets/label-image/__stories__/marker.stories.jsx +0 -172
  1125. package/src/widgets/label-image/answer-choices.jsx +0 -311
  1126. package/src/widgets/label-image/marker.jsx +0 -365
  1127. package/src/widgets/label-image/types.js +0 -33
  1128. package/src/widgets/label-image.jsx +0 -841
  1129. package/src/widgets/label-image_test.js +0 -840
  1130. package/src/widgets/lights-puzzle.jsx +0 -356
  1131. package/src/widgets/matcher.jsx +0 -274
  1132. package/src/widgets/matrix.jsx +0 -632
  1133. package/src/widgets/measurer.jsx +0 -215
  1134. package/src/widgets/molecule/__tests__/layout_test.jsx +0 -159
  1135. package/src/widgets/molecule/__tests__/parser_test.jsx +0 -142
  1136. package/src/widgets/molecule/molecule-drawing.jsx +0 -175
  1137. package/src/widgets/molecule/molecule-layout.jsx +0 -367
  1138. package/src/widgets/molecule/smiles-parser.jsx +0 -300
  1139. package/src/widgets/molecule.jsx +0 -172
  1140. package/src/widgets/number-line.jsx +0 -858
  1141. package/src/widgets/numeric-input.jsx +0 -553
  1142. package/src/widgets/orderer.jsx +0 -756
  1143. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -373
  1144. package/src/widgets/passage/passage-markdown.jsx +0 -472
  1145. package/src/widgets/passage-ref-target.jsx +0 -73
  1146. package/src/widgets/passage-ref.jsx +0 -214
  1147. package/src/widgets/passage.jsx +0 -576
  1148. package/src/widgets/plotter.jsx +0 -1198
  1149. package/src/widgets/radio/__stories__/base-radio.stories.jsx +0 -189
  1150. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +0 -166
  1151. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +0 -41
  1152. package/src/widgets/radio/__stories__/choice.stories.jsx +0 -138
  1153. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +0 -44
  1154. package/src/widgets/radio/__stories__/option-status.stories.jsx +0 -104
  1155. package/src/widgets/radio/base-radio.jsx +0 -494
  1156. package/src/widgets/radio/choice-a11y-text.js +0 -62
  1157. package/src/widgets/radio/choice-icon/choice-icon.jsx +0 -214
  1158. package/src/widgets/radio/choice-icon/cross-out-line.jsx +0 -52
  1159. package/src/widgets/radio/choice-icon/shared-styles.js +0 -16
  1160. package/src/widgets/radio/choice-none-above.jsx +0 -63
  1161. package/src/widgets/radio/choice.jsx +0 -382
  1162. package/src/widgets/radio/choice.jsx.fixture.js +0 -46
  1163. package/src/widgets/radio/focus-ring.jsx +0 -58
  1164. package/src/widgets/radio/option-status.jsx +0 -98
  1165. package/src/widgets/radio/util.js +0 -32
  1166. package/src/widgets/radio/util_test.js +0 -19
  1167. package/src/widgets/radio/widget.jsx +0 -515
  1168. package/src/widgets/radio.jsx +0 -155
  1169. package/src/widgets/reaction-diagram.jsx +0 -137
  1170. package/src/widgets/sequence.jsx +0 -143
  1171. package/src/widgets/simulator.jsx +0 -806
  1172. package/src/widgets/sorter.jsx +0 -149
  1173. package/src/widgets/table.jsx +0 -397
  1174. package/src/widgets/transformer.jsx +0 -2876
  1175. package/src/widgets/unit.jsx +0 -372
  1176. package/src/widgets/video-transcript-link.jsx +0 -84
  1177. package/src/widgets/video-transcript-link_test.js +0 -211
  1178. package/src/widgets/video.jsx +0 -126
  1179. package/src/widgets.js +0 -431
  1180. package/src/zoom.js +0 -539
  1181. /package/{src/multi-items/item-types.js → dist/multi-items/item-types.js.flow} +0 -0
  1182. /package/dist/{strings.js → strings} +0 -0
  1183. /package/src/__tests__/__snapshots__/{item-renderer_test.jsx.snap → item-renderer.test.tsx.snap} +0 -0
  1184. /package/src/__tests__/__snapshots__/{perseus-markdown_test.jsx.snap → perseus-markdown.test.ts.snap} +0 -0
  1185. /package/src/__tests__/__snapshots__/{renderer_test.jsx.snap → renderer.test.tsx.snap} +0 -0
  1186. /package/src/__tests__/__snapshots__/{server-item-renderer_test.jsx.snap → server-item-renderer.test.tsx.snap} +0 -0
  1187. /package/src/components/__tests__/__snapshots__/{sortable_test.jsx.snap → sortable.test.tsx.snap} +0 -0
  1188. /package/src/components/__tests__/__snapshots__/{sorter_test.jsx.snap → sorter.test.tsx.snap} +0 -0
  1189. /package/src/multi-items/__tests__/__snapshots__/{multi-renderer_test.js.snap → multi-renderer.test.tsx.snap} +0 -0
  1190. /package/src/widgets/__tests__/__snapshots__/{categorizer_test.jsx.snap → categorizer.test.ts.snap} +0 -0
  1191. /package/src/widgets/__tests__/__snapshots__/{cs-program_test.jsx.snap → cs-program.test.ts.snap} +0 -0
  1192. /package/src/widgets/__tests__/__snapshots__/{definition_test.js.snap → definition.test.ts.snap} +0 -0
  1193. /package/src/widgets/__tests__/__snapshots__/{dropdown_test.jsx.snap → dropdown.test.ts.snap} +0 -0
  1194. /package/src/widgets/__tests__/__snapshots__/{explanation_test.js.snap → explanation.test.ts.snap} +0 -0
  1195. /package/src/widgets/__tests__/__snapshots__/{graded-group-set-jipt_test.jsx.snap → graded-group-set-jipt.test.ts.snap} +0 -0
  1196. /package/src/widgets/__tests__/__snapshots__/{graded-group-set_test.jsx.snap → graded-group-set.test.ts.snap} +0 -0
  1197. /package/src/widgets/__tests__/__snapshots__/{graded-group_test.jsx.snap → graded-group.test.ts.snap} +0 -0
  1198. /package/src/widgets/__tests__/__snapshots__/{grapher_test.jsx.snap → grapher.test.ts.snap} +0 -0
  1199. /package/src/widgets/__tests__/__snapshots__/{group_test.jsx.snap → group.test.tsx.snap} +0 -0
  1200. /package/src/widgets/__tests__/__snapshots__/{iframe_test.jsx.snap → iframe.test.ts.snap} +0 -0
  1201. /package/src/widgets/__tests__/__snapshots__/{image_test.jsx.snap → image.test.ts.snap} +0 -0
  1202. /package/src/widgets/__tests__/__snapshots__/{interaction_test.jsx.snap → interaction.test.ts.snap} +0 -0
  1203. /package/src/widgets/__tests__/__snapshots__/{interactive-graph_test.jsx.snap → interactive-graph.test.ts.snap} +0 -0
  1204. /package/src/widgets/__tests__/__snapshots__/{matcher_test.jsx.snap → matcher.test.tsx.snap} +0 -0
  1205. /package/src/widgets/__tests__/__snapshots__/{matrix_test.jsx.snap → matrix.test.ts.snap} +0 -0
  1206. /package/src/widgets/__tests__/__snapshots__/{number-line_test.jsx.snap → number-line.test.ts.snap} +0 -0
  1207. /package/src/widgets/__tests__/__snapshots__/{numeric-input_test.jsx.snap → numeric-input.test.ts.snap} +0 -0
  1208. /package/src/widgets/__tests__/__snapshots__/{orderer_test.jsx.snap → orderer.test.ts.snap} +0 -0
  1209. /package/src/widgets/__tests__/__snapshots__/{passage-ref_test.jsx.snap → passage-ref.test.ts.snap} +0 -0
  1210. /package/src/widgets/__tests__/__snapshots__/{passage_test.jsx.snap → passage.test.tsx.snap} +0 -0
  1211. /package/src/widgets/__tests__/__snapshots__/{radio_test.jsx.snap → radio.test.ts.snap} +0 -0
  1212. /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>;