@khanacademy/perseus 19.1.5 → 19.1.6

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 (478) hide show
  1. package/dist/es/index.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/package.json +11 -8
  4. package/.babelrc.js +0 -8
  5. package/.eslintrc.js +0 -12
  6. package/CHANGELOG.md +0 -1432
  7. package/src/__stories__/article-renderer.stories.tsx +0 -100
  8. package/src/__stories__/hints-renderer.stories.tsx +0 -55
  9. package/src/__stories__/server-item-renderer.stories.tsx +0 -44
  10. package/src/__testdata__/article-renderer.testdata.ts +0 -212
  11. package/src/__testdata__/graphie.testdata.ts +0 -51
  12. package/src/__testdata__/renderer.testdata.ts +0 -274
  13. package/src/__testdata__/server-item-renderer.testdata.ts +0 -171
  14. package/src/__tests__/__snapshots__/perseus-markdown.test.ts.snap +0 -507
  15. package/src/__tests__/__snapshots__/renderer.test.tsx.snap +0 -525
  16. package/src/__tests__/__snapshots__/server-item-renderer.test.tsx.snap +0 -285
  17. package/src/__tests__/a11y.test.ts +0 -303
  18. package/src/__tests__/article-renderer.test.tsx +0 -130
  19. package/src/__tests__/error-boundary.test.tsx +0 -76
  20. package/src/__tests__/extract-perseus-data.test.ts +0 -1027
  21. package/src/__tests__/init.test.ts +0 -10
  22. package/src/__tests__/jipt-paragraphs.test.ts +0 -32
  23. package/src/__tests__/mock-asset-loading-widget.tsx +0 -53
  24. package/src/__tests__/mock-widget.tsx +0 -33
  25. package/src/__tests__/perseus-markdown.test.ts +0 -764
  26. package/src/__tests__/renderability.test.ts +0 -480
  27. package/src/__tests__/renderer-api.test.tsx +0 -302
  28. package/src/__tests__/renderer.test.tsx +0 -1816
  29. package/src/__tests__/server-item-renderer.test.tsx +0 -582
  30. package/src/__tests__/test-items/image-item.ts +0 -24
  31. package/src/__tests__/test-items/input-number-1-item.ts +0 -26
  32. package/src/__tests__/test-items/input-number-2-item.ts +0 -38
  33. package/src/__tests__/test-items/table-item.ts +0 -29
  34. package/src/__tests__/tex-wrangler.test.ts +0 -82
  35. package/src/__tests__/util.test.ts +0 -40
  36. package/src/__tests__/widget-container.test.tsx +0 -126
  37. package/src/__tests__/widgets.test.ts +0 -91
  38. package/src/a11y.ts +0 -45
  39. package/src/all-widgets.ts +0 -9
  40. package/src/article-renderer.tsx +0 -237
  41. package/src/asset-context.ts +0 -30
  42. package/src/basic-widgets.ts +0 -16
  43. package/src/components/__stories__/button-group.stories.tsx +0 -55
  44. package/src/components/__stories__/fixed-to-responsive.stories.tsx +0 -141
  45. package/src/components/__stories__/graph.stories.tsx +0 -28
  46. package/src/components/__stories__/graphie.stories.tsx +0 -33
  47. package/src/components/__stories__/hud.stories.tsx +0 -35
  48. package/src/components/__stories__/icon.stories.tsx +0 -63
  49. package/src/components/__stories__/image-loader.stories.tsx +0 -63
  50. package/src/components/__stories__/info-tip.stories.tsx +0 -31
  51. package/src/components/__stories__/inline-icon.stories.tsx +0 -40
  52. package/src/components/__stories__/input-with-examples.stories.tsx +0 -53
  53. package/src/components/__stories__/lint.stories.tsx +0 -86
  54. package/src/components/__stories__/math-input.stories.tsx +0 -43
  55. package/src/components/__stories__/math-output.stories.tsx +0 -23
  56. package/src/components/__stories__/multi-button-group.stories.tsx +0 -66
  57. package/src/components/__stories__/number-input.stories.tsx +0 -41
  58. package/src/components/__stories__/prop-check-box.stories.tsx +0 -65
  59. package/src/components/__stories__/range-input.stories.tsx +0 -29
  60. package/src/components/__stories__/simple-keypad-input.stories.tsx +0 -27
  61. package/src/components/__stories__/sortable.stories.tsx +0 -77
  62. package/src/components/__stories__/stub-tag-editor.stories.tsx +0 -45
  63. package/src/components/__stories__/svg-image.stories.tsx +0 -74
  64. package/src/components/__stories__/tex.stories.tsx +0 -23
  65. package/src/components/__stories__/text-input.stories.tsx +0 -33
  66. package/src/components/__stories__/text-list-editor.stories.tsx +0 -32
  67. package/src/components/__stories__/tooltip.stories.tsx +0 -52
  68. package/src/components/__stories__/zoomable-tex.stories.tsx +0 -41
  69. package/src/components/__stories__/zoomable.stories.tsx +0 -49
  70. package/src/components/__testdata__/sorter.testdata.ts +0 -19
  71. package/src/components/__tests__/__snapshots__/sortable.test.tsx.snap +0 -163
  72. package/src/components/__tests__/__snapshots__/sorter.test.tsx.snap +0 -265
  73. package/src/components/__tests__/math-input.test.tsx +0 -147
  74. package/src/components/__tests__/number-input.test.tsx +0 -128
  75. package/src/components/__tests__/sortable.test.tsx +0 -269
  76. package/src/components/__tests__/sorter.test.tsx +0 -114
  77. package/src/components/__tests__/zoomable.test.tsx +0 -399
  78. package/src/components/button-group.tsx +0 -130
  79. package/src/components/constants.ts +0 -14
  80. package/src/components/fixed-to-responsive.tsx +0 -169
  81. package/src/components/graph.tsx +0 -444
  82. package/src/components/graphie-classes.ts +0 -153
  83. package/src/components/graphie-movables.ts +0 -338
  84. package/src/components/graphie.tsx +0 -400
  85. package/src/components/highlighting/highlightable-content.tsx +0 -240
  86. package/src/components/highlighting/highlights.ts +0 -207
  87. package/src/components/highlighting/ranges.ts +0 -304
  88. package/src/components/highlighting/types.ts +0 -73
  89. package/src/components/highlighting/ui/highlight-renderer.tsx +0 -172
  90. package/src/components/highlighting/ui/highlight-set-renderer.tsx +0 -185
  91. package/src/components/highlighting/ui/highlight-tooltip.tsx +0 -141
  92. package/src/components/highlighting/ui/highlighting-ui.tsx +0 -143
  93. package/src/components/highlighting/ui/selection-tracker.tsx +0 -186
  94. package/src/components/highlighting/ui/types.ts +0 -21
  95. package/src/components/highlighting/ui/util.ts +0 -165
  96. package/src/components/highlighting/word-indexer.tsx +0 -152
  97. package/src/components/hud.tsx +0 -167
  98. package/src/components/icon.tsx +0 -141
  99. package/src/components/image-loader.tsx +0 -177
  100. package/src/components/info-tip/info-tip.tsx +0 -144
  101. package/src/components/info-tip.tsx +0 -39
  102. package/src/components/inline-icon.tsx +0 -60
  103. package/src/components/input-with-examples.tsx +0 -249
  104. package/src/components/lint.tsx +0 -459
  105. package/src/components/math-input.tsx +0 -474
  106. package/src/components/math-output.tsx +0 -134
  107. package/src/components/multi-button-group.tsx +0 -135
  108. package/src/components/number-input.tsx +0 -249
  109. package/src/components/prop-check-box.tsx +0 -79
  110. package/src/components/range-input.tsx +0 -60
  111. package/src/components/simple-keypad-input.tsx +0 -88
  112. package/src/components/sortable.tsx +0 -958
  113. package/src/components/stub-tag-editor.tsx +0 -53
  114. package/src/components/svg-image.tsx +0 -779
  115. package/src/components/tex.tsx +0 -67
  116. package/src/components/text-input.tsx +0 -129
  117. package/src/components/text-list-editor.tsx +0 -166
  118. package/src/components/tooltip.tsx +0 -410
  119. package/src/components/visibility-observer/__stories__/visibility-observer.stories.tsx +0 -141
  120. package/src/components/visibility-observer/create-visibility-observer.ts +0 -108
  121. package/src/components/visibility-observer/get-scroll-parent.ts +0 -46
  122. package/src/components/visibility-observer/visibility-observer.tsx +0 -124
  123. package/src/components/zoomable-tex.tsx +0 -78
  124. package/src/components/zoomable.tsx +0 -304
  125. package/src/components.ts +0 -15
  126. package/src/definition-context.tsx +0 -59
  127. package/src/dependencies.ts +0 -40
  128. package/src/error-boundary.tsx +0 -70
  129. package/src/extra-widgets.ts +0 -75
  130. package/src/hint-renderer.tsx +0 -151
  131. package/src/hints-renderer.tsx +0 -372
  132. package/src/icon-paths.ts +0 -119
  133. package/src/index.ts +0 -171
  134. package/src/init.ts +0 -56
  135. package/src/interaction-tracker.ts +0 -62
  136. package/src/interactive2/__tests__/movable-mock.ts +0 -55
  137. package/src/interactive2/__tests__/movable-point.test.ts +0 -144
  138. package/src/interactive2/interactive-util.test.ts +0 -93
  139. package/src/interactive2/interactive-util.ts +0 -159
  140. package/src/interactive2/movable-helper-methods.ts +0 -166
  141. package/src/interactive2/movable-line-options.ts +0 -419
  142. package/src/interactive2/movable-line.ts +0 -307
  143. package/src/interactive2/movable-point-options.ts +0 -224
  144. package/src/interactive2/movable-point.tsx +0 -585
  145. package/src/interactive2/movable-polygon-options.ts +0 -389
  146. package/src/interactive2/movable-polygon.ts +0 -360
  147. package/src/interactive2/movable.test.ts +0 -249
  148. package/src/interactive2/movable.ts +0 -437
  149. package/src/interactive2/objective_.ts +0 -79
  150. package/src/interactive2/types.ts +0 -17
  151. package/src/interactive2/wrapped-defaults.ts +0 -87
  152. package/src/interactive2/wrapped-ellipse.ts +0 -94
  153. package/src/interactive2/wrapped-line.ts +0 -116
  154. package/src/interactive2/wrapped-path.ts +0 -51
  155. package/src/interactive2.ts +0 -70
  156. package/src/item-version.ts +0 -10
  157. package/src/jipt-paragraphs.ts +0 -49
  158. package/src/jquery.mobile.vmouse.js +0 -545
  159. package/src/loading-context.ts +0 -21
  160. package/src/logging/log.test.ts +0 -39
  161. package/src/logging/log.ts +0 -97
  162. package/src/mixins/__tests__/widget-prop-denylist.test.ts +0 -21
  163. package/src/mixins/changeable.ts +0 -133
  164. package/src/mixins/editor-jsonify.ts +0 -13
  165. package/src/mixins/widget-jsonify-deprecated.ts +0 -23
  166. package/src/mixins/widget-prop-denylist.ts +0 -48
  167. package/src/multi-items/__stories__/multi-renderer.stories.tsx +0 -83
  168. package/src/multi-items/__testdata__/multi-renderer.testdata.ts +0 -172
  169. package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +0 -1164
  170. package/src/multi-items/__tests__/items.test.ts +0 -240
  171. package/src/multi-items/__tests__/multi-renderer.test.tsx +0 -914
  172. package/src/multi-items/__tests__/prop-type-builders.test.ts +0 -133
  173. package/src/multi-items/__tests__/shapes.test.ts +0 -43
  174. package/src/multi-items/__tests__/trees.test.ts +0 -317
  175. package/src/multi-items/item-types.ts +0 -56
  176. package/src/multi-items/items.ts +0 -176
  177. package/src/multi-items/multi-renderer.tsx +0 -569
  178. package/src/multi-items/prop-type-builders.ts +0 -106
  179. package/src/multi-items/shape-types.ts +0 -52
  180. package/src/multi-items/shapes.ts +0 -52
  181. package/src/multi-items/tree-types.ts +0 -47
  182. package/src/multi-items/trees.ts +0 -360
  183. package/src/multi-items.ts +0 -75
  184. package/src/perseus-api.tsx +0 -188
  185. package/src/perseus-env.ts +0 -27
  186. package/src/perseus-error.ts +0 -17
  187. package/src/perseus-markdown.tsx +0 -405
  188. package/src/perseus-types.ts +0 -1366
  189. package/src/perseus-version.ts +0 -9
  190. package/src/question-paragraph.tsx +0 -34
  191. package/src/renderability.ts +0 -109
  192. package/src/renderer.tsx +0 -2000
  193. package/src/server-item-renderer.tsx +0 -495
  194. package/src/sigfigs.ts +0 -490
  195. package/src/styles/articles.less +0 -749
  196. package/src/styles/constants.ts +0 -94
  197. package/src/styles/global-constants.ts +0 -343
  198. package/src/styles/global-styles.ts +0 -306
  199. package/src/styles/khan-exercise.css +0 -1468
  200. package/src/styles/media-queries.ts +0 -47
  201. package/src/styles/perseus-renderer.less +0 -732
  202. package/src/styles/shared.ts +0 -130
  203. package/src/styles/util.less +0 -47
  204. package/src/styles/variables.boostrap.less +0 -277
  205. package/src/styles/variables.less +0 -56
  206. package/src/styles/variables.shared.less +0 -340
  207. package/src/styles/widgets/categorizer.less +0 -38
  208. package/src/styles/widgets/dropdown.less +0 -3
  209. package/src/styles/widgets/expression.less +0 -65
  210. package/src/styles/widgets/graded-group.less +0 -36
  211. package/src/styles/widgets/grapher.less +0 -12
  212. package/src/styles/widgets/group.less +0 -19
  213. package/src/styles/widgets/image.less +0 -54
  214. package/src/styles/widgets/interactive-graph.less +0 -64
  215. package/src/styles/widgets/label-image.less +0 -57
  216. package/src/styles/widgets/matcher.less +0 -8
  217. package/src/styles/widgets/matrix.less +0 -126
  218. package/src/styles/widgets/measurer.less +0 -20
  219. package/src/styles/widgets/orderer.less +0 -98
  220. package/src/styles/widgets/passage.less +0 -154
  221. package/src/styles/widgets/plotter.less +0 -31
  222. package/src/styles/widgets/radio.less +0 -58
  223. package/src/styles/widgets/reaction-diagram.less +0 -35
  224. package/src/styles/widgets/sequence.less +0 -5
  225. package/src/styles/widgets/sortable.less +0 -113
  226. package/src/styles/widgets/table.less +0 -57
  227. package/src/styles/widgets/unit.less +0 -15
  228. package/src/tex-wrangler.ts +0 -182
  229. package/src/translation-linter.ts +0 -126
  230. package/src/traversal.ts +0 -138
  231. package/src/types.ts +0 -513
  232. package/src/util/a11y.ts +0 -23
  233. package/src/util/answer-types.test.ts +0 -114
  234. package/src/util/answer-types.ts +0 -876
  235. package/src/util/colors.ts +0 -93
  236. package/src/util/drawing-transform.test.ts +0 -67
  237. package/src/util/drawing-transform.ts +0 -102
  238. package/src/util/extract-perseus-data.ts +0 -539
  239. package/src/util/fix-passage-refs.ts +0 -121
  240. package/src/util/geometry.test.ts +0 -75
  241. package/src/util/geometry.ts +0 -312
  242. package/src/util/graph-bounds.ts +0 -23
  243. package/src/util/graph-utils.ts +0 -15
  244. package/src/util/graphie.test.ts +0 -1277
  245. package/src/util/graphie.ts +0 -1576
  246. package/src/util/interactive.ts +0 -4174
  247. package/src/util/interval.ts +0 -4
  248. package/src/util/katex-preprocess.ts +0 -14
  249. package/src/util/math.test.ts +0 -11
  250. package/src/util/math.ts +0 -294
  251. package/src/util/mobile-native-utils.test.ts +0 -22
  252. package/src/util/mobile-native-utils.ts +0 -12
  253. package/src/util/parse-perseus-json.test.ts +0 -10
  254. package/src/util/parse-perseus-json.ts +0 -13
  255. package/src/util/react-render.tsx +0 -27
  256. package/src/util/register-all-widgets-for-testing.ts +0 -13
  257. package/src/util/scroll-utils.ts +0 -155
  258. package/src/util/sizing-utils.ts +0 -46
  259. package/src/util/tex.ts +0 -73
  260. package/src/util/url-utils.ts +0 -34
  261. package/src/util.graphie.test.ts +0 -45
  262. package/src/util.graphie.ts +0 -59
  263. package/src/util.ts +0 -966
  264. package/src/version.ts +0 -10
  265. package/src/widget-container.tsx +0 -199
  266. package/src/widgets/__stories__/categorizer.stories.tsx +0 -14
  267. package/src/widgets/__stories__/cs-program.stories.tsx +0 -14
  268. package/src/widgets/__stories__/definition.stories.tsx +0 -114
  269. package/src/widgets/__stories__/dropdown.stories.tsx +0 -14
  270. package/src/widgets/__stories__/explanation.stories.tsx +0 -18
  271. package/src/widgets/__stories__/expression.stories.tsx +0 -145
  272. package/src/widgets/__stories__/graded-group-set.stories.tsx +0 -31
  273. package/src/widgets/__stories__/graded-group.stories.tsx +0 -31
  274. package/src/widgets/__stories__/grapher.stories.tsx +0 -46
  275. package/src/widgets/__stories__/group.stories.tsx +0 -14
  276. package/src/widgets/__stories__/iframe.stories.tsx +0 -14
  277. package/src/widgets/__stories__/image.stories.tsx +0 -85
  278. package/src/widgets/__stories__/input-number.stories.tsx +0 -101
  279. package/src/widgets/__stories__/interaction.stories.tsx +0 -14
  280. package/src/widgets/__stories__/interactive-graph.stories.tsx +0 -60
  281. package/src/widgets/__stories__/label-image.stories.tsx +0 -130
  282. package/src/widgets/__stories__/matcher.stories.tsx +0 -14
  283. package/src/widgets/__stories__/matrix.stories.tsx +0 -14
  284. package/src/widgets/__stories__/number-line.stories.tsx +0 -14
  285. package/src/widgets/__stories__/numeric-input.stories.tsx +0 -105
  286. package/src/widgets/__stories__/orderer.stories.tsx +0 -18
  287. package/src/widgets/__stories__/passage-ref.stories.tsx +0 -33
  288. package/src/widgets/__stories__/passage.stories.tsx +0 -39
  289. package/src/widgets/__stories__/plotter.stories.tsx +0 -27
  290. package/src/widgets/__stories__/radio.stories.tsx +0 -86
  291. package/src/widgets/__stories__/random-widgets.stories.tsx +0 -59
  292. package/src/widgets/__stories__/sorter.stories.tsx +0 -14
  293. package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +0 -66
  294. package/src/widgets/__stories__/unit.stories.tsx +0 -28
  295. package/src/widgets/__stories__/video-transcript-link.stories.tsx +0 -23
  296. package/src/widgets/__stories__/video.stories.tsx +0 -22
  297. package/src/widgets/__testdata__/base-radio.testdata.ts +0 -21
  298. package/src/widgets/__testdata__/categorizer.testdata.ts +0 -89
  299. package/src/widgets/__testdata__/cs-program.testdata.ts +0 -27
  300. package/src/widgets/__testdata__/dropdown.testdata.ts +0 -71
  301. package/src/widgets/__testdata__/explanation.testdata.ts +0 -85
  302. package/src/widgets/__testdata__/expression.testdata.ts +0 -198
  303. package/src/widgets/__testdata__/graded-group-set.testdata.ts +0 -356
  304. package/src/widgets/__testdata__/graded-group.testdata.ts +0 -77
  305. package/src/widgets/__testdata__/grapher.testdata.ts +0 -374
  306. package/src/widgets/__testdata__/group.testdata.ts +0 -153
  307. package/src/widgets/__testdata__/iframe.testdata.ts +0 -35
  308. package/src/widgets/__testdata__/image.testdata.ts +0 -36
  309. package/src/widgets/__testdata__/input-number.testdata.ts +0 -72
  310. package/src/widgets/__testdata__/interaction.testdata.ts +0 -262
  311. package/src/widgets/__testdata__/interactive-graph-random.testdata.ts +0 -269
  312. package/src/widgets/__testdata__/interactive-graph.testdata.ts +0 -644
  313. package/src/widgets/__testdata__/label-image.testdata.ts +0 -308
  314. package/src/widgets/__testdata__/matcher.testdata.ts +0 -33
  315. package/src/widgets/__testdata__/matrix.testdata.ts +0 -28
  316. package/src/widgets/__testdata__/number-line.testdata.ts +0 -30
  317. package/src/widgets/__testdata__/numeric-input.testdata.ts +0 -265
  318. package/src/widgets/__testdata__/orderer.testdata.ts +0 -123
  319. package/src/widgets/__testdata__/passage-ref.testdata.ts +0 -101
  320. package/src/widgets/__testdata__/passage.testdata.ts +0 -95
  321. package/src/widgets/__testdata__/plotter.testdata.ts +0 -36
  322. package/src/widgets/__testdata__/radio.testdata.ts +0 -324
  323. package/src/widgets/__testdata__/randomizers.ts +0 -51
  324. package/src/widgets/__testdata__/sorter.testdata.ts +0 -19
  325. package/src/widgets/__testdata__/video.testdata.ts +0 -61
  326. package/src/widgets/__tests__/__snapshots__/categorizer.test.ts.snap +0 -940
  327. package/src/widgets/__tests__/__snapshots__/cs-program.test.ts.snap +0 -67
  328. package/src/widgets/__tests__/__snapshots__/definition.test.ts.snap +0 -97
  329. package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +0 -133
  330. package/src/widgets/__tests__/__snapshots__/explanation.test.ts.snap +0 -425
  331. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt.test.ts.snap +0 -729
  332. package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +0 -339
  333. package/src/widgets/__tests__/__snapshots__/graded-group.test.ts.snap +0 -801
  334. package/src/widgets/__tests__/__snapshots__/grapher.test.ts.snap +0 -2673
  335. package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +0 -1211
  336. package/src/widgets/__tests__/__snapshots__/iframe.test.ts.snap +0 -61
  337. package/src/widgets/__tests__/__snapshots__/image.test.ts.snap +0 -378
  338. package/src/widgets/__tests__/__snapshots__/input-number.test.ts.snap +0 -495
  339. package/src/widgets/__tests__/__snapshots__/interaction.test.ts.snap +0 -292
  340. package/src/widgets/__tests__/__snapshots__/interactive-graph.test.ts.snap +0 -2889
  341. package/src/widgets/__tests__/__snapshots__/matcher.test.tsx.snap +0 -883
  342. package/src/widgets/__tests__/__snapshots__/matrix.test.ts.snap +0 -514
  343. package/src/widgets/__tests__/__snapshots__/number-line.test.ts.snap +0 -814
  344. package/src/widgets/__tests__/__snapshots__/numeric-input.test.ts.snap +0 -470
  345. package/src/widgets/__tests__/__snapshots__/orderer.test.ts.snap +0 -243
  346. package/src/widgets/__tests__/__snapshots__/passage-ref.test.ts.snap +0 -434
  347. package/src/widgets/__tests__/__snapshots__/passage.test.tsx.snap +0 -1956
  348. package/src/widgets/__tests__/__snapshots__/plotter.test.tsx.snap +0 -827
  349. package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +0 -4237
  350. package/src/widgets/__tests__/__snapshots__/unit.test.tsx.snap +0 -20
  351. package/src/widgets/__tests__/__snapshots__/video.test.ts.snap +0 -153
  352. package/src/widgets/__tests__/categorizer.test.ts +0 -203
  353. package/src/widgets/__tests__/cs-program.test.ts +0 -43
  354. package/src/widgets/__tests__/definition.test.ts +0 -136
  355. package/src/widgets/__tests__/dropdown.test.ts +0 -105
  356. package/src/widgets/__tests__/explanation.test.ts +0 -198
  357. package/src/widgets/__tests__/expression-mobile.test.tsx +0 -363
  358. package/src/widgets/__tests__/expression.test.tsx +0 -484
  359. package/src/widgets/__tests__/graded-group-set-jipt.test.ts +0 -30
  360. package/src/widgets/__tests__/graded-group-set.test.ts +0 -288
  361. package/src/widgets/__tests__/graded-group.test.ts +0 -232
  362. package/src/widgets/__tests__/grapher.cypress.ts +0 -576
  363. package/src/widgets/__tests__/grapher.test.ts +0 -33
  364. package/src/widgets/__tests__/group.test.tsx +0 -510
  365. package/src/widgets/__tests__/iframe.test.ts +0 -45
  366. package/src/widgets/__tests__/image.test.ts +0 -62
  367. package/src/widgets/__tests__/input-number.test.ts +0 -354
  368. package/src/widgets/__tests__/interaction.test.ts +0 -31
  369. package/src/widgets/__tests__/interactive-graph.cypress.ts +0 -815
  370. package/src/widgets/__tests__/interactive-graph.test.ts +0 -106
  371. package/src/widgets/__tests__/matcher.test.tsx +0 -176
  372. package/src/widgets/__tests__/matrix.test.ts +0 -82
  373. package/src/widgets/__tests__/number-line.test.ts +0 -79
  374. package/src/widgets/__tests__/numeric-input.test.ts +0 -575
  375. package/src/widgets/__tests__/orderer.test.ts +0 -73
  376. package/src/widgets/__tests__/passage-ref.test.ts +0 -168
  377. package/src/widgets/__tests__/passage.test.tsx +0 -224
  378. package/src/widgets/__tests__/plotter.test.tsx +0 -69
  379. package/src/widgets/__tests__/radio/base-radio.test.tsx +0 -470
  380. package/src/widgets/__tests__/radio/choice-icon.test.tsx +0 -90
  381. package/src/widgets/__tests__/radio/choice.test.tsx +0 -310
  382. package/src/widgets/__tests__/radio/focus-ring.test.tsx +0 -22
  383. package/src/widgets/__tests__/radio/option-status.test.tsx +0 -77
  384. package/src/widgets/__tests__/radio.test.ts +0 -807
  385. package/src/widgets/__tests__/randomizers.test.ts +0 -53
  386. package/src/widgets/__tests__/renderQuestion.tsx +0 -101
  387. package/src/widgets/__tests__/unit.test.tsx +0 -168
  388. package/src/widgets/__tests__/video.test.ts +0 -69
  389. package/src/widgets/categorizer.tsx +0 -344
  390. package/src/widgets/cs-program.tsx +0 -227
  391. package/src/widgets/definition.tsx +0 -121
  392. package/src/widgets/deprecated-standin/__stories__/deprecated-standin.stories.tsx +0 -30
  393. package/src/widgets/deprecated-standin/__tests__/__snapshots__/deprecated-standin.test.ts.snap +0 -70
  394. package/src/widgets/deprecated-standin/__tests__/deprecated-standin.test.ts +0 -93
  395. package/src/widgets/deprecated-standin/index.tsx +0 -58
  396. package/src/widgets/dropdown.tsx +0 -142
  397. package/src/widgets/explanation.tsx +0 -317
  398. package/src/widgets/expression.tsx +0 -707
  399. package/src/widgets/graded-group-answer-bar.tsx +0 -142
  400. package/src/widgets/graded-group-set.tsx +0 -338
  401. package/src/widgets/graded-group.tsx +0 -471
  402. package/src/widgets/grapher/util.tsx +0 -799
  403. package/src/widgets/grapher.tsx +0 -660
  404. package/src/widgets/group.tsx +0 -195
  405. package/src/widgets/iframe.tsx +0 -195
  406. package/src/widgets/image.tsx +0 -312
  407. package/src/widgets/input-number.tsx +0 -412
  408. package/src/widgets/interaction/arrow-picker.tsx +0 -38
  409. package/src/widgets/interaction/color-picker.tsx +0 -69
  410. package/src/widgets/interaction/dash-picker.tsx +0 -45
  411. package/src/widgets/interaction/element-container.tsx +0 -100
  412. package/src/widgets/interaction.tsx +0 -826
  413. package/src/widgets/interactive-graph.tsx +0 -2597
  414. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -91
  415. package/src/widgets/label-image/__stories__/answer-pill.stories.tsx +0 -62
  416. package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -171
  417. package/src/widgets/label-image/__tests__/answer-pill.test.tsx +0 -75
  418. package/src/widgets/label-image/__tests__/hide-answers-toggle.test.tsx +0 -39
  419. package/src/widgets/label-image/answer-choices.tsx +0 -88
  420. package/src/widgets/label-image/answer-pill.tsx +0 -103
  421. package/src/widgets/label-image/hide-answers-toggle.tsx +0 -40
  422. package/src/widgets/label-image/marker.tsx +0 -268
  423. package/src/widgets/label-image/strings.ts +0 -5
  424. package/src/widgets/label-image/types.ts +0 -27
  425. package/src/widgets/label-image.test.ts +0 -928
  426. package/src/widgets/label-image.tsx +0 -819
  427. package/src/widgets/lights-puzzle.tsx +0 -359
  428. package/src/widgets/matcher.tsx +0 -298
  429. package/src/widgets/matrix.tsx +0 -634
  430. package/src/widgets/measurer.tsx +0 -222
  431. package/src/widgets/molecule/__tests__/layout.test.ts +0 -162
  432. package/src/widgets/molecule/__tests__/parser.test.ts +0 -155
  433. package/src/widgets/molecule/molecule-drawing.ts +0 -173
  434. package/src/widgets/molecule/molecule-layout.ts +0 -362
  435. package/src/widgets/molecule/smiles-parser.ts +0 -296
  436. package/src/widgets/molecule.tsx +0 -172
  437. package/src/widgets/number-line.tsx +0 -862
  438. package/src/widgets/numeric-input.tsx +0 -576
  439. package/src/widgets/orderer.tsx +0 -786
  440. package/src/widgets/passage/__tests__/passage-markdown.test.ts +0 -376
  441. package/src/widgets/passage/passage-markdown.tsx +0 -475
  442. package/src/widgets/passage-ref-target.tsx +0 -76
  443. package/src/widgets/passage-ref.tsx +0 -213
  444. package/src/widgets/passage.tsx +0 -582
  445. package/src/widgets/plotter.tsx +0 -1194
  446. package/src/widgets/radio/__stories__/base-radio.stories.tsx +0 -196
  447. package/src/widgets/radio/__stories__/choice-icon.stories.tsx +0 -166
  448. package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +0 -40
  449. package/src/widgets/radio/__stories__/choice.stories.tsx +0 -137
  450. package/src/widgets/radio/__stories__/focus-ring.stories.tsx +0 -43
  451. package/src/widgets/radio/__stories__/option-status.stories.tsx +0 -103
  452. package/src/widgets/radio/base-radio.tsx +0 -511
  453. package/src/widgets/radio/choice-a11y-text.ts +0 -61
  454. package/src/widgets/radio/choice-icon/choice-icon.tsx +0 -218
  455. package/src/widgets/radio/choice-icon/cross-out-line.tsx +0 -51
  456. package/src/widgets/radio/choice-icon/shared-styles.ts +0 -15
  457. package/src/widgets/radio/choice-none-above.tsx +0 -56
  458. package/src/widgets/radio/choice.jsx.fixture.ts +0 -45
  459. package/src/widgets/radio/choice.tsx +0 -359
  460. package/src/widgets/radio/focus-ring.tsx +0 -57
  461. package/src/widgets/radio/option-status.tsx +0 -98
  462. package/src/widgets/radio/util.test.ts +0 -18
  463. package/src/widgets/radio/util.ts +0 -31
  464. package/src/widgets/radio/widget.tsx +0 -520
  465. package/src/widgets/radio.ts +0 -157
  466. package/src/widgets/reaction-diagram.tsx +0 -144
  467. package/src/widgets/sequence.tsx +0 -142
  468. package/src/widgets/simulator.tsx +0 -829
  469. package/src/widgets/sorter.tsx +0 -155
  470. package/src/widgets/table.tsx +0 -393
  471. package/src/widgets/unit.tsx +0 -374
  472. package/src/widgets/video-transcript-link.test.tsx +0 -213
  473. package/src/widgets/video-transcript-link.tsx +0 -84
  474. package/src/widgets/video.tsx +0 -135
  475. package/src/widgets.ts +0 -489
  476. package/src/zoom.ts +0 -555
  477. package/tsconfig-build.json +0 -26
  478. package/tsconfig-build.tsbuildinfo +0 -1
@@ -1,1576 +0,0 @@
1
- /* eslint-disable @babel/no-invalid-this */
2
- import {
3
- point as kpoint,
4
- vector as kvector,
5
- number as knumber,
6
- } from "@khanacademy/kmath";
7
- import {entries} from "@khanacademy/wonder-stuff-core";
8
- import $ from "jquery";
9
- // eslint-disable-next-line import/no-extraneous-dependencies
10
- import Raphael from "raphael";
11
-
12
- // Minify Raphael ourselves because IE8 has a problem with the 1.5.2 minified
13
- // release
14
- // http://groups.google.com/group/raphaeljs/browse_thread/thread/c34c75ad8d431544
15
-
16
- import {Errors, Log} from "../logging/log";
17
- import {PerseusError} from "../perseus-error";
18
-
19
- import KhanColors from "./colors";
20
- import {DrawingTransform} from "./drawing-transform";
21
- import {GraphBounds} from "./graph-bounds";
22
- import KhanMath from "./math";
23
- import Tex from "./tex";
24
-
25
- import type {Interval} from "./interval";
26
- import type {Coord} from "../interactive2/types";
27
-
28
- const {processMath} = Tex;
29
-
30
- export function polar(r: number | Coord, th: number): Coord {
31
- if (typeof r === "number") {
32
- r = [r, r];
33
- }
34
- th = (th * Math.PI) / 180;
35
- return [r[0] * Math.cos(th), r[1] * Math.sin(th)];
36
- }
37
-
38
- interface RaphaelElement {
39
- type: string;
40
- attrs: Record<string, any>;
41
- node: {
42
- style: {
43
- shapeRendering: "crispEdges" | "geometricPrecision";
44
- };
45
- };
46
- }
47
-
48
- type PositionedShape = {wrapper: HTMLDivElement; visibleShape: RaphaelElement};
49
-
50
- export type StyleParams = {
51
- fill?: string;
52
- labelDistance?: number;
53
- opacity?: number;
54
- step?: Coord;
55
- stroke?: string;
56
- strokeWidth?: number;
57
- };
58
-
59
- type LabelPosition =
60
- | "center"
61
- | "above"
62
- | "below"
63
- | "right"
64
- | "left"
65
- | "above right"
66
- | "above left"
67
- | "below right"
68
- | "below left";
69
-
70
- interface LabelMethod {
71
- (point: Coord, text: string, position: LabelPosition): any;
72
- (
73
- point: Coord,
74
- text: string,
75
- position: LabelPosition,
76
- renderTex: boolean,
77
- ): any;
78
- (
79
- point: Coord,
80
- text: string,
81
- position: LabelPosition,
82
- style: StyleParams,
83
- ): any;
84
- (
85
- point: Coord,
86
- text: string,
87
- position: LabelPosition,
88
- renderTex: boolean,
89
- style: StyleParams,
90
- ): any;
91
- }
92
-
93
- export class Graphie {
94
- el: Element;
95
- #bounds?: GraphBounds;
96
- #drawingTransform?: DrawingTransform;
97
- raphael?: any;
98
- isMobile = false;
99
- // Set up some reasonable defaults
100
- currentStyle: any = {
101
- "stroke-width": 2,
102
- fill: "none",
103
- };
104
-
105
- range?: [Interval, Interval];
106
- scale?: Coord;
107
- dimensions?: Coord;
108
- // TODO(benchristel): xpixels and ypixels are never used by this class, but
109
- // other code reaches in to access them :(
110
- // Refactor to accessor methods that look at this.dimensions.
111
- xpixels?: number;
112
- ypixels?: number;
113
-
114
- // set by Movables
115
- // TODO(benchristel): consider putting this state on the Movable instead of
116
- // on the Graphie
117
- isDragging?: boolean;
118
-
119
- constructor(el: Element) {
120
- this.el = el;
121
- $(el).css("position", "relative");
122
- this.raphael = Raphael(el);
123
-
124
- // For a sometimes-reproducible IE8 bug; doesn't affect SVG browsers at all
125
- $(el).children("div").css("position", "absolute");
126
- }
127
-
128
- init(options: {
129
- range: [Interval, Interval];
130
- scale?: number | Coord;
131
- isMobile?: boolean;
132
- }) {
133
- let scale = options.scale || [40, 40];
134
- scale = typeof scale === "number" ? [scale, scale] : scale;
135
-
136
- // TODO(jeremy): This can be removed once we're confident that callers
137
- // obey the Typescript types.
138
- if (options.range == null) {
139
- throw new PerseusError(
140
- "range should be specified in graph init",
141
- Errors.Internal,
142
- );
143
- }
144
-
145
- this.#bounds = new GraphBounds(...options.range);
146
-
147
- this.#drawingTransform = new DrawingTransform(
148
- this.raphael,
149
- scale,
150
- this.bounds(),
151
- );
152
-
153
- const [w, h] = this.drawingTransform().canvasDimensions();
154
-
155
- $(this.el).css({
156
- width: w,
157
- height: h,
158
- });
159
-
160
- this.range = options.range;
161
- this.scale = scale;
162
- // TODO(benchristel): I don't think dimensions is used. Can we
163
- // remove it?
164
- this.dimensions = [w, h];
165
- this.xpixels = w;
166
- this.ypixels = h;
167
-
168
- this.isMobile = options.isMobile ?? false;
169
-
170
- return this;
171
- }
172
-
173
- // Initializes graphie settings for a graph and draws the basic graph
174
- // features (axes, grid, tick marks, and axis labels)
175
- // Options expected are:
176
- // - range: [[a, b], [c, d]] or [a, b]
177
- // - scale: [a, b] or number
178
- // - gridOpacity: number (0 - 1)
179
- // - gridStep: [a, b] or number (relative to units)
180
- // - tickStep: [a, b] or number (relative to grid steps)
181
- // - tickLen: [a, b] or number (in pixels)
182
- // - labelStep: [a, b] or number (relative to tick steps)
183
- // - yLabelFormat: fn to format label string for y-axis
184
- // - xLabelFormat: fn to format label string for x-axis
185
- graphInit(options: {
186
- range?: [Interval, Interval] | Coord;
187
- grid?: boolean;
188
- gridRange?: [Interval, Interval] | Coord;
189
- scale: Coord | number;
190
- axes?: boolean;
191
- axisArrows?: "<->" | "->" | true | "";
192
- axisOpacity?: number;
193
- axisCenter?: Coord;
194
- axisLabels?: [string, string] | false;
195
- gridOpacity?: number;
196
- gridStep?: Coord | number;
197
- ticks?: boolean;
198
- tickStep?: Coord | number;
199
- tickLen?: Coord | number;
200
- tickOpacity?: number;
201
- labels?: boolean;
202
- labelStep?: number;
203
- labelOpacity?: number;
204
- labelFormat?: (a: number) => string;
205
- yLabelFormat?: (y: number) => string;
206
- xLabelFormat?: (x: number) => string;
207
- unityLabels?: boolean | [boolean, boolean];
208
- isMobile?: boolean;
209
- }) {
210
- options = options || {};
211
-
212
- for (const [prop, val] of entries(options)) {
213
- // allow options to be specified by a single number for shorthand if
214
- // the horizontal and vertical components are the same
215
- if (
216
- !prop.match(/.*Opacity$/) &&
217
- prop !== "range" &&
218
- typeof val === "number"
219
- ) {
220
- options[prop] = [val, val];
221
- }
222
-
223
- // allow symmetric ranges to be specified by the absolute values
224
- if (prop === "range" || prop === "gridRange") {
225
- options[prop] = normalizeRange(options[prop]);
226
- }
227
- }
228
-
229
- const range = normalizeRange(
230
- options.range || [
231
- [-10, 10],
232
- [-10, 10],
233
- ],
234
- );
235
- const gridRange = normalizeRange(options.gridRange || range);
236
- const scale = options.scale || [20, 20];
237
- const grid = options.grid != null ? options.grid : true;
238
- const gridOpacity = options.gridOpacity || 0.1;
239
- const gridStep = toPair(options.gridStep || [1, 1]);
240
- const axes = options.axes != null ? options.axes : true;
241
- const axisArrows = options.axisArrows || "";
242
- const axisOpacity = options.axisOpacity || 1.0;
243
- const axisCenter = options.axisCenter || [
244
- Math.min(Math.max(range[0][0], 0), range[0][1]),
245
- Math.min(Math.max(range[1][0], 0), range[1][1]),
246
- ];
247
- const axisLabels =
248
- options.axisLabels != null ? options.axisLabels : false;
249
- const ticks = options.ticks != null ? options.ticks : true;
250
- const tickStep = options.tickStep || [2, 2];
251
- const tickLen = options.tickLen || [5, 5];
252
- const tickOpacity = options.tickOpacity || 1.0;
253
- const labels = options.labels || options.labelStep || false;
254
- const labelStep = options.labelStep || [1, 1];
255
- const labelOpacity = options.labelOpacity || 1.0;
256
- let unityLabels = options.unityLabels || false;
257
- const labelFormat =
258
- options.labelFormat ||
259
- function (a: any) {
260
- return a;
261
- };
262
- let xLabelFormat = options.xLabelFormat || labelFormat;
263
- let yLabelFormat = options.yLabelFormat || labelFormat;
264
- const realRange = [
265
- [
266
- range[0][0] - (range[0][0] > 0 ? 1 : 0),
267
- range[0][1] + (range[0][1] < 0 ? 1 : 0),
268
- ],
269
- [
270
- range[1][0] - (range[1][0] > 0 ? 1 : 0),
271
- range[1][1] + (range[1][1] < 0 ? 1 : 0),
272
- ],
273
- ] as [Interval, Interval];
274
-
275
- if (!Array.isArray(unityLabels)) {
276
- unityLabels = [unityLabels, unityLabels];
277
- }
278
-
279
- const minusIgnorer = function (lf: (a: number) => string) {
280
- return function (a: number) {
281
- return (lf(a) + "").replace(/-(\d)/g, "\\llap{-}$1");
282
- };
283
- };
284
-
285
- xLabelFormat = minusIgnorer(xLabelFormat);
286
- yLabelFormat = minusIgnorer(yLabelFormat);
287
-
288
- this.init({
289
- range: realRange,
290
- scale: scale,
291
- isMobile: options.isMobile,
292
- });
293
-
294
- // draw grid
295
- if (grid) {
296
- this.grid(gridRange[0], gridRange[1], {
297
- stroke: options.isMobile ? KhanColors.GRAY_C : "#000000",
298
- opacity: options.isMobile ? 1 : gridOpacity,
299
- step: gridStep,
300
- strokeWidth: options.isMobile ? 1 : 2,
301
- });
302
- }
303
-
304
- // draw axes
305
- if (axes) {
306
- // this is a slight hack until <-> arrowheads work
307
- if (axisArrows === "<->" || axisArrows === true) {
308
- const thisGraphie = this;
309
- this.style(
310
- {
311
- stroke: options.isMobile
312
- ? KhanColors.GRAY_G
313
- : "#000000",
314
- opacity: options.isMobile ? 1 : axisOpacity,
315
- strokeWidth: options.isMobile ? 1 : 2,
316
- arrows: "->",
317
- },
318
- function () {
319
- if (range[1][0] < 0 && range[1][1] > 0) {
320
- thisGraphie.path([
321
- axisCenter,
322
- [gridRange[0][0], axisCenter[1]],
323
- ]);
324
- thisGraphie.path([
325
- axisCenter,
326
- [gridRange[0][1], axisCenter[1]],
327
- ]);
328
- }
329
- if (range[0][0] < 0 && range[0][1] > 0) {
330
- thisGraphie.path([
331
- axisCenter,
332
- [axisCenter[0], gridRange[1][0]],
333
- ]);
334
- thisGraphie.path([
335
- axisCenter,
336
- [axisCenter[0], gridRange[1][1]],
337
- ]);
338
- }
339
- },
340
- );
341
-
342
- // also, we don't support "<-" arrows yet, but why you
343
- // would want that on your graph is beyond me.
344
- } else if (axisArrows === "->" || axisArrows === "") {
345
- const thisGraphie = this;
346
- this.style(
347
- {
348
- stroke: "#000000",
349
- opacity: axisOpacity,
350
- strokeWidth: 2,
351
- arrows: axisArrows,
352
- },
353
- function () {
354
- thisGraphie.path([
355
- [gridRange[0][0], axisCenter[1]],
356
- [gridRange[0][1], axisCenter[1]],
357
- ]);
358
- thisGraphie.path([
359
- [axisCenter[0], gridRange[1][0]],
360
- [axisCenter[0], gridRange[1][1]],
361
- ]);
362
- },
363
- );
364
- }
365
-
366
- if (axisLabels && axisLabels.length === 2) {
367
- this.label(
368
- [gridRange[0][1], axisCenter[1]],
369
- axisLabels[0],
370
- "right",
371
- );
372
- this.label(
373
- [axisCenter[0], gridRange[1][1]],
374
- axisLabels[1],
375
- "above",
376
- );
377
- }
378
- }
379
-
380
- // draw tick marks
381
- if (ticks) {
382
- const halfWidthTicks = options.isMobile;
383
- const thisGraphie = this;
384
- this.style(
385
- {
386
- stroke: options.isMobile ? KhanColors.GRAY_G : "#000000",
387
- opacity: options.isMobile ? 1 : tickOpacity,
388
- strokeWidth: 1,
389
- },
390
- () => {
391
- // horizontal axis
392
- let step = gridStep[0] * tickStep[0];
393
- let len = tickLen[0] / scale[1];
394
- let start = gridRange[0][0];
395
- let stop = gridRange[0][1];
396
-
397
- if (range[1][0] < 0 && range[1][1] > 0) {
398
- for (
399
- let x = step + axisCenter[0];
400
- x <= stop;
401
- x += step
402
- ) {
403
- if (x < stop || !axisArrows) {
404
- thisGraphie.line(
405
- [x, -len + axisCenter[1]],
406
- [
407
- x,
408
- halfWidthTicks
409
- ? 0
410
- : len + axisCenter[1],
411
- ],
412
- );
413
- }
414
- }
415
-
416
- for (
417
- let x = -step + axisCenter[0];
418
- x >= start;
419
- x -= step
420
- ) {
421
- if (x > start || !axisArrows) {
422
- thisGraphie.line(
423
- [x, -len + axisCenter[1]],
424
- [
425
- x,
426
- halfWidthTicks
427
- ? 0
428
- : len + axisCenter[1],
429
- ],
430
- );
431
- }
432
- }
433
- }
434
-
435
- // vertical axis
436
- step = gridStep[1] * tickStep[1];
437
- len = tickLen[1] / scale[0];
438
- start = gridRange[1][0];
439
- stop = gridRange[1][1];
440
-
441
- if (range[0][0] < 0 && range[0][1] > 0) {
442
- for (
443
- let y = step + axisCenter[1];
444
- y <= stop;
445
- y += step
446
- ) {
447
- if (y < stop || !axisArrows) {
448
- this.line(
449
- [-len + axisCenter[0], y],
450
- [
451
- halfWidthTicks
452
- ? 0
453
- : len + axisCenter[0],
454
- y,
455
- ],
456
- );
457
- }
458
- }
459
-
460
- for (
461
- let y = -step + axisCenter[1];
462
- y >= start;
463
- y -= step
464
- ) {
465
- if (y > start || !axisArrows) {
466
- this.line(
467
- [-len + axisCenter[0], y],
468
- [
469
- halfWidthTicks
470
- ? 0
471
- : len + axisCenter[0],
472
- y,
473
- ],
474
- );
475
- }
476
- }
477
- }
478
- },
479
- );
480
- }
481
-
482
- // draw axis labels
483
- if (labels) {
484
- const thisGraphie = this;
485
- this.style(
486
- {
487
- stroke: options.isMobile ? KhanColors.GRAY_G : "#000000",
488
- opacity: options.isMobile ? 1 : labelOpacity,
489
- },
490
- function () {
491
- // horizontal axis
492
- let step = gridStep[0] * tickStep[0] * labelStep[0];
493
- let start = gridRange[0][0];
494
- let stop = gridRange[0][1];
495
- const xAxisPosition = axisCenter[0] < 0 ? "above" : "below";
496
- const yAxisPosition = axisCenter[0] < 0 ? "right" : "left";
497
- const xShowZero =
498
- axisCenter[0] === 0 && axisCenter[1] !== 0;
499
- const yShowZero =
500
- axisCenter[0] !== 0 && axisCenter[1] === 0;
501
- const axisOffCenter =
502
- axisCenter[0] !== 0 || axisCenter[1] !== 0;
503
- const showUnityX = unityLabels[0] || axisOffCenter;
504
- const showUnityY = unityLabels[1] || axisOffCenter;
505
-
506
- // positive x-axis
507
- for (
508
- let x = (xShowZero ? 0 : step) + axisCenter[0];
509
- x <= stop;
510
- x += step
511
- ) {
512
- if (x < stop || !axisArrows) {
513
- thisGraphie.label(
514
- [x, axisCenter[1]],
515
- xLabelFormat(x),
516
- xAxisPosition,
517
- );
518
- }
519
- }
520
-
521
- // negative x-axis
522
- for (
523
- let x = -step * (showUnityX ? 1 : 2) + axisCenter[0];
524
- x >= start;
525
- x -= step
526
- ) {
527
- if (x > start || !axisArrows) {
528
- thisGraphie.label(
529
- [x, axisCenter[1]],
530
- xLabelFormat(x),
531
- xAxisPosition,
532
- );
533
- }
534
- }
535
-
536
- step = gridStep[1] * tickStep[1] * labelStep[1];
537
- start = gridRange[1][0];
538
- stop = gridRange[1][1];
539
-
540
- // positive y-axis
541
- for (
542
- let y = (yShowZero ? 0 : step) + axisCenter[1];
543
- y <= stop;
544
- y += step
545
- ) {
546
- if (y < stop || !axisArrows) {
547
- thisGraphie.label(
548
- [axisCenter[0], y],
549
- yLabelFormat(y),
550
- yAxisPosition,
551
- );
552
- }
553
- }
554
-
555
- // negative y-axis
556
- for (
557
- let y = -step * (showUnityY ? 1 : 2) + axisCenter[1];
558
- y >= start;
559
- y -= step
560
- ) {
561
- if (y > start || !axisArrows) {
562
- thisGraphie.label(
563
- [axisCenter[0], y],
564
- yLabelFormat(y),
565
- yAxisPosition,
566
- );
567
- }
568
- }
569
- },
570
- );
571
- }
572
- }
573
-
574
- private drawingTransform(): DrawingTransform {
575
- if (this.#drawingTransform == null) {
576
- throw new Error(
577
- "Can't get drawingTransform of an uninitialized Graphie",
578
- );
579
- }
580
- return this.#drawingTransform;
581
- }
582
-
583
- private bounds(): GraphBounds {
584
- if (this.#bounds == null) {
585
- throw new Error("Can't get bounds of an uninitialized Graphie");
586
- }
587
- return this.#bounds;
588
- }
589
-
590
- style<T>(attrs: any, fn?: (this: Graphie) => T): T | undefined {
591
- const processed = this.processAttributes(attrs);
592
-
593
- if (typeof fn === "function") {
594
- const oldStyle = this.currentStyle;
595
- this.currentStyle = {...this.currentStyle, ...processed};
596
- const result = fn.call(this);
597
- this.currentStyle = oldStyle;
598
- return result;
599
- }
600
- Object.assign(this.currentStyle, processed);
601
- }
602
-
603
- grid(xr: Interval, yr: Interval, style?: StyleParams): unknown {
604
- return this.withStyle(style, () => {
605
- const step: any = this.currentStyle.step || [1, 1];
606
- const set = this.raphael.set();
607
-
608
- let x = step[0] * Math.ceil(xr[0] / step[0]);
609
- for (; x <= xr[1]; x += step[0]) {
610
- set.push(this.line([x, yr[0]], [x, yr[1]]));
611
- }
612
-
613
- let y = step[1] * Math.ceil(yr[0] / step[1]);
614
- for (; y <= yr[1]; y += step[1]) {
615
- set.push(this.line([xr[0], y], [xr[1], y]));
616
- }
617
-
618
- return set;
619
- });
620
- }
621
-
622
- arc(
623
- center: Coord,
624
- radius: Coord,
625
- startAngle: number,
626
- endAngle: number,
627
- sector: boolean,
628
- style?: StyleParams,
629
- ): RaphaelElement {
630
- return this.withStyle(style, () => {
631
- startAngle = ((startAngle % 360) + 360) % 360;
632
- endAngle = ((endAngle % 360) + 360) % 360;
633
-
634
- const cent = this.scalePoint(center);
635
- const radii = this.scaleVector(radius);
636
- const startVector = polar(radius, startAngle);
637
- const endVector = polar(radius, endAngle);
638
-
639
- // We round the coordinates to make testing easier, because trig
640
- // operations return unround numbers.
641
- // TODO(benchristel): move rounding to scalePoint?
642
- const round = (x) => knumber.round(x, 6);
643
- const startPoint = this.scalePoint([
644
- round(center[0] + startVector[0]),
645
- round(center[1] + startVector[1]),
646
- ]);
647
- const endPoint = this.scalePoint([
648
- round(center[0] + endVector[0]),
649
- round(center[1] + endVector[1]),
650
- ]);
651
-
652
- const largeAngle =
653
- (((endAngle - startAngle) % 360) + 360) % 360 > 180;
654
-
655
- return this.raphael.path(
656
- "M" +
657
- startPoint.join(" ") +
658
- "A" +
659
- radii.join(" ") +
660
- " 0 " + // ellipse rotation
661
- (largeAngle ? 1 : 0) +
662
- " 0 " + // sweep flag
663
- endPoint.join(" ") +
664
- (sector ? "L" + cent.join(" ") + "z" : ""),
665
- );
666
- });
667
- }
668
-
669
- circle(center: Coord, radius: number, style?: StyleParams) {
670
- return this.withStyle(style, () =>
671
- this.raphael.ellipse(
672
- ...this.scalePoint(center),
673
- ...this.scaleVector([radius, radius]),
674
- ),
675
- );
676
- }
677
-
678
- // (x, y) is coordinate of bottom left corner
679
- rect(x, y, width, height, style?: StyleParams) {
680
- return this.withStyle(style, () => {
681
- // Raphael needs (x, y) to be coordinate of upper left corner
682
- const corner = this.scalePoint([x, y + height]);
683
- const dims = this.scaleVector([width, height]);
684
- const elem = this.raphael.rect(...corner.concat(dims));
685
-
686
- if (this.isMobile) {
687
- elem.node.style.shapeRendering = "crispEdges";
688
- }
689
-
690
- return elem;
691
- });
692
- }
693
-
694
- ellipse(center, radii, style?: StyleParams) {
695
- return this.withStyle(style, () =>
696
- this.raphael.ellipse(
697
- ...this.scalePoint(center).concat(this.scaleVector(radii)),
698
- ),
699
- );
700
- }
701
-
702
- fixedEllipse(
703
- center: number | Coord,
704
- // Different type than Coord, this is radiusX, radiusY
705
- radii: number | [number, number],
706
- maxScale: number,
707
- padding: number,
708
- style?: StyleParams,
709
- ): PositionedShape {
710
- return this.withStyle(style, () => {
711
- // Scale point and radius
712
- const scaledPoint = this.scalePoint(center);
713
- const scaledRadii = this.scaleVector(radii);
714
-
715
- const width = 2 * scaledRadii[0] * maxScale + padding;
716
- const height = 2 * scaledRadii[1] * maxScale + padding;
717
-
718
- // Calculate absolute left, top
719
- const left = scaledPoint[0] - width / 2;
720
- const top = scaledPoint[1] - height / 2;
721
-
722
- // Wrap in <div>
723
- const wrapper = document.createElement("div");
724
- $(wrapper).css({
725
- position: "absolute",
726
- width: width + "px",
727
- height: height + "px",
728
- left: left + "px",
729
- top: top + "px",
730
- });
731
- // wrapper.setAttribute("data-graphie-type", "ellipse");
732
-
733
- // Create Raphael canvas
734
- const localRaphael = Raphael(wrapper, width, height);
735
- const visibleShape = localRaphael.ellipse(
736
- width / 2,
737
- height / 2,
738
- scaledRadii[0],
739
- scaledRadii[1],
740
- );
741
-
742
- return {
743
- wrapper: wrapper,
744
- visibleShape: visibleShape,
745
- };
746
- });
747
- }
748
-
749
- private unstyledPath(points: Coord[]): RaphaelElement {
750
- const p = this.raphael.path(this.svgPath(points));
751
- p.graphiePath = points;
752
- return p;
753
- }
754
-
755
- path(points: Coord[], style?: StyleParams): RaphaelElement {
756
- return this.withStyle(style, () => {
757
- return this.unstyledPath(points);
758
- });
759
- }
760
-
761
- // fixedPath is a stub that gets overwritten with a function from
762
- // drawingTools in createGraphie
763
- fixedPath(
764
- points: Coord[],
765
- center: Coord | null,
766
- createPath: (scaledPoints: Coord[]) => string,
767
- ): PositionedShape {
768
- points = points.map(this.scalePoint);
769
- center = center ? this.scalePoint(center) : null;
770
- createPath = createPath || this.svgPath;
771
-
772
- // Compute bounding box
773
- const xs = points.map((p) => p[0]);
774
- const ys = points.map((p) => p[1]);
775
- const pathLeft = Math.min(...xs);
776
- const pathRight = Math.max(...xs);
777
- const pathTop = Math.min(...ys);
778
- const pathBottom = Math.max(...ys);
779
-
780
- // Apply padding to line
781
- const padding: Coord = [4, 4];
782
-
783
- // Calculate and apply additional offset
784
- const topLeftOfBoundingBox: Coord = [pathLeft, pathTop];
785
-
786
- // Apply padding and offset to points to convert from
787
- // canvas coordinates to pixel coordinates relative to bounding box
788
- points = points.map(function (point) {
789
- return kvector.add(
790
- kvector.subtract(point, topLeftOfBoundingBox),
791
- kvector.scale(padding, 0.5),
792
- );
793
- });
794
-
795
- // Calculate <div> dimensions
796
- const width = pathRight - pathLeft + padding[0];
797
- const height = pathBottom - pathTop + padding[1];
798
- const left = topLeftOfBoundingBox[0] - padding[0] / 2;
799
- const top = topLeftOfBoundingBox[1] - padding[1] / 2;
800
-
801
- // Create <div>
802
- const wrapper = document.createElement("div");
803
- $(wrapper).css({
804
- position: "absolute",
805
- width: width + "px",
806
- height: height + "px",
807
- left: left + "px",
808
- top: top + "px",
809
- // If user specified a center, set it
810
- // NOTE(kevinb): jQuery doesn't like that `transformOrigin `could be `null`
811
- // so we cast to `any` here.
812
- transformOrigin: center
813
- ? width / 2 +
814
- center[0] +
815
- "px " +
816
- (height / 2 + center[1]) +
817
- "px"
818
- : null,
819
- } as any);
820
-
821
- // Create Raphael canvas
822
- const localRaphael = Raphael(wrapper, width, height);
823
-
824
- // Calculate path
825
- const visibleShape = localRaphael.path(createPath(points));
826
-
827
- return {
828
- wrapper: wrapper,
829
- visibleShape: visibleShape,
830
- };
831
- }
832
-
833
- scaledPath(points: Coord[], style?: StyleParams): RaphaelElement {
834
- return this.withStyle(style, () => {
835
- const p = this.raphael.path(
836
- this.svgPath(points, /* alreadyScaled */ true),
837
- );
838
- p.graphiePath = points;
839
- return p;
840
- });
841
- }
842
-
843
- line(start: Coord, end: Coord, style?: StyleParams): RaphaelElement {
844
- return this.withStyle(style, () => {
845
- const l = this.unstyledPath([start, end]);
846
-
847
- if (this.isMobile) {
848
- l.node.style.shapeRendering = "crispEdges";
849
- }
850
-
851
- return l;
852
- });
853
- }
854
-
855
- parabola(
856
- a: number,
857
- b: number,
858
- c: number,
859
- style?: StyleParams,
860
- ): RaphaelElement {
861
- return this.withStyle(style, () =>
862
- this.raphael.path(this.svgParabolaPath(a, b, c)),
863
- );
864
- }
865
-
866
- fixedLine(start: Coord, end: Coord, thickness: number): PositionedShape {
867
- // Apply padding to line
868
- const padding: Coord = [thickness, thickness];
869
-
870
- // Scale points to get values in pixels
871
- start = this.scalePoint(start);
872
- end = this.scalePoint(end);
873
-
874
- // Calculate and apply additional offset
875
- const extraOffset: Coord = [
876
- Math.min(start[0], end[0]),
877
- Math.min(start[1], end[1]),
878
- ];
879
-
880
- // Apply padding and offset to start, end points
881
- start = kvector.add(
882
- kvector.subtract(start, extraOffset),
883
- kvector.scale(padding, 0.5),
884
- );
885
- end = kvector.add(
886
- kvector.subtract(end, extraOffset),
887
- kvector.scale(padding, 0.5),
888
- );
889
-
890
- // Calculate <div> dimensions
891
- const left = extraOffset[0] - padding[0] / 2;
892
- const top = extraOffset[1] - padding[1] / 2;
893
- const width = Math.abs(start[0] - end[0]) + padding[0];
894
- const height = Math.abs(start[1] - end[1]) + padding[1];
895
-
896
- // Create <div>
897
- const wrapper = document.createElement("div");
898
- $(wrapper).css({
899
- position: "absolute",
900
- width: width + "px",
901
- height: height + "px",
902
- left: left + "px",
903
- top: top + "px",
904
- // Outsiders should feel like the line's 'origin' (i.e., for
905
- // rotation) is the starting point
906
- transformOrigin: start[0] + "px " + start[1] + "px",
907
- });
908
-
909
- // Create Raphael canvas
910
- const localRaphael = Raphael(wrapper, width, height);
911
-
912
- // Calculate path
913
- const path =
914
- "M" + start[0] + " " + start[1] + " " + "L" + end[0] + " " + end[1];
915
- const visibleShape = localRaphael.path(path);
916
- visibleShape.graphiePath = [start, end];
917
-
918
- return {
919
- wrapper: wrapper,
920
- visibleShape: visibleShape,
921
- };
922
- }
923
-
924
- sinusoid(
925
- a: number,
926
- b: number,
927
- c: number,
928
- d: number,
929
- style?: StyleParams,
930
- ): RaphaelElement {
931
- return this.withStyle(style, () =>
932
- // Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
933
- this.raphael.path(this.svgSinusoidPath(a, b, c, d)),
934
- );
935
- }
936
-
937
- label: LabelMethod = (
938
- point: Coord,
939
- text: string,
940
- direction:
941
- | "center"
942
- | "above"
943
- | "below"
944
- | "right"
945
- | "left"
946
- | "above right"
947
- | "above left"
948
- | "below right"
949
- | "below left",
950
- arg4?: boolean | StyleParams,
951
- arg5?: StyleParams,
952
- ): any => {
953
- const style = typeof arg4 === "object" ? arg4 : arg5;
954
- const latex = typeof arg4 === "boolean" ? arg4 : true;
955
- return this.withStyle(style, () => {
956
- const $span = $("<span>").addClass("graphie-label");
957
-
958
- const pad = this.currentStyle["label-distance"];
959
-
960
- $span
961
- .css({
962
- position: "absolute",
963
- padding: (pad != null ? pad : 7) + "px",
964
- color: "black",
965
- })
966
- .data("labelDirection", direction)
967
- .appendTo(this.el);
968
-
969
- // @ts-expect-error - TS2339 - Property 'setPosition' does not exist on type 'JQuery<HTMLElement>'.
970
- $span.setPosition = (point) => {
971
- const scaledPoint = this.scalePoint(point);
972
- $span.css({
973
- left: scaledPoint[0],
974
- top: scaledPoint[1],
975
- });
976
- };
977
-
978
- // @ts-expect-error - TS2339 - Property 'setPosition' does not exist on type 'JQuery<HTMLElement>'.
979
- $span.setPosition(point);
980
-
981
- const span = $span[0];
982
-
983
- // @ts-expect-error - TS2339 - Property 'processMath' does not exist on type 'JQuery<HTMLElement>'.
984
- $span.processMath = function (math, force) {
985
- processMath(span, math, force, function () {
986
- const width = span.scrollWidth;
987
- const height = span.scrollHeight;
988
- setLabelMargins(span, [width, height]);
989
- });
990
- };
991
-
992
- // @ts-expect-error - TS2339 - Property 'processText' does not exist on type 'JQuery<HTMLElement>'.
993
- $span.processText = function (text: string) {
994
- $span.html(text);
995
- const width = span.scrollWidth;
996
- const height = span.scrollHeight;
997
- setLabelMargins(span, [width, height]);
998
- };
999
-
1000
- if (latex) {
1001
- // @ts-expect-error - TS2339 - Property 'processMath' does not exist on type 'JQuery<HTMLElement>'.
1002
- $span.processMath(text, /* force */ false);
1003
- } else {
1004
- // @ts-expect-error - TS2339 - Property 'processText' does not exist on type 'JQuery<HTMLElement>'.
1005
- $span.processText(text);
1006
- }
1007
-
1008
- return $span;
1009
- });
1010
- };
1011
-
1012
- plotParametric(
1013
- fn: (t: number) => Coord,
1014
- range: Interval,
1015
- style?: StyleParams,
1016
- ): RaphaelElement {
1017
- return this.withStyle(style, () => {
1018
- // We truncate to 500,000, since anything bigger causes
1019
- // overflow in the firefox svg renderer. This is safe
1020
- // since 500,000 is outside the viewport anyway. We
1021
- // write these functions the way we do to handle undefined.
1022
- const clip = (xy) => {
1023
- if (Math.abs(xy[1]) > 500000) {
1024
- return [xy[0], Math.min(Math.max(xy[1], -500000), 500000)];
1025
- }
1026
- return xy;
1027
- };
1028
- const clippedFn = (x) => clip(fn(x));
1029
-
1030
- const min = range[0];
1031
- const max = range[1];
1032
- let step = (max - min) / (this.currentStyle["plot-points"] || 800);
1033
- if (step === 0) {
1034
- step = 1;
1035
- }
1036
-
1037
- const paths = this.raphael.set();
1038
- let points: Coord[] = [];
1039
- let lastY = clippedFn(min)[1];
1040
-
1041
- for (let t = min; t <= max; t += step) {
1042
- const point = clippedFn(t);
1043
- const y = point[1];
1044
-
1045
- // Find points where it flips
1046
- if (
1047
- // if there is an asymptote here, meaning that the graph
1048
- // switches signs and has a large difference
1049
- (y > 0 !== lastY > 0 &&
1050
- Math.abs(y - lastY) >
1051
- 2 * this.drawingTransform().pixelsPerUnitY()) ||
1052
- // or the function is undefined
1053
- isNaN(y)
1054
- ) {
1055
- // split the path at this point, and draw it
1056
- paths.push(this.unstyledPath(points));
1057
- // restart the path, excluding this point
1058
- points = [];
1059
- } else {
1060
- // otherwise, just add the point to the path
1061
- points.push(point);
1062
- }
1063
-
1064
- lastY = y;
1065
- }
1066
-
1067
- paths.push(this.unstyledPath(points));
1068
-
1069
- return paths;
1070
- });
1071
- }
1072
-
1073
- plot(
1074
- fn: (x: number) => number,
1075
- range: Interval,
1076
- style?: StyleParams,
1077
- ): RaphaelElement {
1078
- return this.withStyle(style, () => {
1079
- const min = range[0];
1080
- const max = range[1];
1081
- if (!this.currentStyle["plot-points"]) {
1082
- this.currentStyle["plot-points"] =
1083
- 2 * (max - min) * this.drawingTransform().pixelsPerUnitX();
1084
- }
1085
-
1086
- const parametricFn = (x): Coord => [x, fn(x)];
1087
- return this.plotParametric(parametricFn, range);
1088
- });
1089
- }
1090
-
1091
- svgPath = (points: (Coord | true)[], alreadyScaled?: boolean) => {
1092
- return points
1093
- .map((point, i) => {
1094
- if (point === true) {
1095
- return "z";
1096
- }
1097
- const scaled = alreadyScaled ? point : this.scalePoint(point);
1098
- return (
1099
- (i === 0 ? "M" : "L") +
1100
- KhanMath.bound(scaled[0]) +
1101
- " " +
1102
- KhanMath.bound(scaled[1])
1103
- );
1104
- })
1105
- .join("");
1106
- };
1107
-
1108
- svgParabolaPath = (a: number, b: number, c: number) => {
1109
- const computeParabola = function (x) {
1110
- return (a * x + b) * x + c;
1111
- };
1112
-
1113
- // If points are collinear, plot a line instead
1114
- if (a === 0) {
1115
- const points: Coord[] = [
1116
- [this.bounds().xMin, computeParabola(this.bounds().xMin)],
1117
- [this.bounds().xMax, computeParabola(this.bounds().xMax)],
1118
- ];
1119
- return this.svgPath(points);
1120
- }
1121
-
1122
- // Calculate x coordinates of points on parabola
1123
- const xVertex = -b / (2 * a);
1124
- const distToEdge = Math.max(
1125
- Math.abs(xVertex - this.bounds().xMin),
1126
- Math.abs(xVertex - this.bounds().xMax),
1127
- );
1128
-
1129
- // To guarantee that drawn parabola to spans the viewport, use a point
1130
- // on the edge of the graph furtherest from the vertex
1131
- const xPoint = xVertex + distToEdge;
1132
-
1133
- // Compute parabola and other point on the curve
1134
- const vertex = [xVertex, computeParabola(xVertex)];
1135
- const point = [xPoint, computeParabola(xPoint)];
1136
-
1137
- // Calculate SVG 'control' point, defined by spec
1138
- const control: Coord = [vertex[0], vertex[1] - (point[1] - vertex[1])];
1139
-
1140
- // Calculate mirror points across parabola's axis of symmetry
1141
- const dx = Math.abs(vertex[0] - point[0]);
1142
- const left: Coord = [vertex[0] - dx, point[1]];
1143
- const right: Coord = [vertex[0] + dx, point[1]];
1144
-
1145
- // Scale and bound
1146
- const points = [left, control, right].map(this.scalePoint);
1147
- const values = points.flat().map(KhanMath.bound);
1148
- return (
1149
- "M" +
1150
- values[0] +
1151
- "," +
1152
- values[1] +
1153
- " Q" +
1154
- values[2] +
1155
- "," +
1156
- values[3] +
1157
- " " +
1158
- values[4] +
1159
- "," +
1160
- values[5]
1161
- );
1162
- };
1163
-
1164
- svgSinusoidPath = (a: number, b: number, c: number, d: number) => {
1165
- // Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
1166
- const quarterPeriod = Math.abs(Math.PI / (2 * b));
1167
-
1168
- const computeSine = function (x: number) {
1169
- return a * Math.sin(b * x - c) + d;
1170
- };
1171
-
1172
- const computeDerivative = function (x) {
1173
- return a * b * Math.cos(c - b * x);
1174
- };
1175
-
1176
- const coordsForOffset = (initial: number, i: number) => {
1177
- // Return the cubic coordinates (including the two anchor and two
1178
- // control points) for the ith portion of the sinusoid.
1179
- const x0 = initial + quarterPeriod * i;
1180
- const x1 = x0 + quarterPeriod;
1181
-
1182
- // Interpolate using derivative technique
1183
- // See: http://stackoverflow.com/questions/13932704/how-to-draw-sine-waves-with-svg-js
1184
- const xCoords = [
1185
- x0,
1186
- (x0 * 2) / 3 + (x1 * 1) / 3,
1187
- (x0 * 1) / 3 + (x1 * 2) / 3,
1188
- x1,
1189
- ];
1190
- const yCoords = [
1191
- computeSine(x0),
1192
- computeSine(x0) + (computeDerivative(x0) * (x1 - x0)) / 3,
1193
- computeSine(x1) - (computeDerivative(x1) * (x1 - x0)) / 3,
1194
- computeSine(x1),
1195
- ];
1196
-
1197
- const points = kvector.zip(xCoords, yCoords);
1198
- return points.map(this.scalePoint);
1199
- };
1200
-
1201
- // How many quarter-periods do we need to span the graph?
1202
- const extent = this.bounds().width();
1203
- const numQuarterPeriods = Math.ceil(extent / quarterPeriod) + 1;
1204
-
1205
- // Find starting coordinate: first anchor point curve left of bounds.xMin
1206
- let initial = c / b;
1207
- const distToEdge = initial - this.bounds().xMin;
1208
- initial -= quarterPeriod * Math.ceil(distToEdge / quarterPeriod);
1209
-
1210
- // First portion of path is special-case, requiring move-to ('M')
1211
- let coords = coordsForOffset(initial, 0);
1212
- let path =
1213
- "M" +
1214
- coords[0][0] +
1215
- "," +
1216
- coords[0][1] +
1217
- " C" +
1218
- coords[1][0] +
1219
- "," +
1220
- coords[1][1] +
1221
- " " +
1222
- coords[2][0] +
1223
- "," +
1224
- coords[2][1] +
1225
- " " +
1226
- coords[3][0] +
1227
- "," +
1228
- coords[3][1];
1229
- for (let i = 1; i < numQuarterPeriods; i++) {
1230
- coords = coordsForOffset(initial, i);
1231
- path +=
1232
- " C" +
1233
- coords[1][0] +
1234
- "," +
1235
- coords[1][1] +
1236
- " " +
1237
- coords[2][0] +
1238
- "," +
1239
- coords[2][1] +
1240
- " " +
1241
- coords[3][0] +
1242
- "," +
1243
- coords[3][1];
1244
- }
1245
-
1246
- return path;
1247
- };
1248
-
1249
- private withStyle<T>(style: StyleParams | undefined, fn: () => T): T {
1250
- const oldStyle = this.currentStyle;
1251
- this.currentStyle = {
1252
- ...this.currentStyle,
1253
- ...this.processAttributes(style),
1254
- };
1255
- const result = this.postprocessDrawingResult(fn());
1256
- this.currentStyle = oldStyle;
1257
- return result;
1258
- }
1259
-
1260
- private postprocessDrawingResult(result: any): any {
1261
- // Bad heuristic for recognizing Raphael elements and sets
1262
- const type = result.constructor.prototype;
1263
- if (type === Raphael.el || type === Raphael.st) {
1264
- result.attr(this.currentStyle);
1265
-
1266
- if (this.currentStyle.arrows) {
1267
- result = this.addArrowheads(result);
1268
- }
1269
- } else if (result instanceof $) {
1270
- // We assume that if it's not a Raphael element/set, it
1271
- // does not contain SVG.
1272
- // @ts-expect-error - TS2339 - Property 'css' does not exist on type '{}'.
1273
- result.css({
1274
- ...this.currentStyle,
1275
- ...SVG_SPECIFIC_STYLE_MASK,
1276
- });
1277
- }
1278
-
1279
- return result;
1280
- }
1281
-
1282
- private addArrowheads(path: any) {
1283
- const type = path.constructor.prototype;
1284
-
1285
- if (type === Raphael.el) {
1286
- if (
1287
- path.type === "path" &&
1288
- typeof path.arrowheadsDrawn === "undefined"
1289
- ) {
1290
- const w = path.attr("stroke-width");
1291
- const s = 0.6 + 0.4 * w;
1292
- const l = path.getTotalLength();
1293
- const set = this.raphael.set();
1294
- const head = this.raphael.path(
1295
- this.isMobile
1296
- ? "M-4,4 C-4,4 -0.25,0 -0.25,0 C-0.25,0 -4,-4 -4,-4"
1297
- : "M-3 4 C-2.75 2.5 0 0.25 0.75 0C0 -0.25 -2.75 -2.5 -3 -4",
1298
- );
1299
- const end = path.getPointAtLength(l - 0.4);
1300
- const almostTheEnd = path.getPointAtLength(l - 0.75 * s);
1301
- const angle =
1302
- (Math.atan2(
1303
- end.y - almostTheEnd.y,
1304
- end.x - almostTheEnd.x,
1305
- ) *
1306
- 180) /
1307
- Math.PI;
1308
- const attrs = path.attr();
1309
- delete attrs.path;
1310
-
1311
- let subpath = path.getSubpath(0, l - 0.75 * s);
1312
- subpath = this.raphael.path(subpath).attr(attrs);
1313
- subpath.arrowheadsDrawn = true;
1314
- path.remove();
1315
-
1316
- // For some unknown reason 0 doesn't work for the rotation
1317
- // origin so we use a tiny number.
1318
- head.rotate(angle, this.isMobile ? 1e-5 : 0.75, 0)
1319
- .scale(s, s, 0.75, 0)
1320
- .translate(almostTheEnd.x, almostTheEnd.y)
1321
- .attr(attrs)
1322
- .attr({
1323
- "stroke-linejoin": "round",
1324
- "stroke-linecap": "round",
1325
- });
1326
-
1327
- head.arrowheadsDrawn = true;
1328
- set.push(subpath);
1329
- set.push(head);
1330
- return set;
1331
- }
1332
- } else if (type === Raphael.st) {
1333
- for (let i = 0, l = path.items.length; i < l; i++) {
1334
- this.addArrowheads(path.items[i]);
1335
- }
1336
- }
1337
- return path;
1338
- }
1339
-
1340
- scalePoint = (point: number | Coord): Coord => {
1341
- return this.drawingTransform().scalePoint(point);
1342
- };
1343
-
1344
- scaleVector = (point: number | Coord) => {
1345
- return this.drawingTransform().scaleVector(point);
1346
- };
1347
-
1348
- unscalePoint = (point: Coord) => {
1349
- return this.drawingTransform().unscalePoint(point);
1350
- };
1351
-
1352
- unscaleVector = (point: Coord) => {
1353
- return this.drawingTransform().unscaleVector(point);
1354
- };
1355
-
1356
- private processAttributes(attrs: Record<string, any> | undefined) {
1357
- const transformers = {
1358
- scale: (scale) => {
1359
- this.drawingTransform().setScale(scale);
1360
- },
1361
-
1362
- clipRect: (pair) => {
1363
- const point = pair[0];
1364
- const size = pair[1];
1365
- point[1] += size[1]; // because our coordinates are flipped
1366
-
1367
- return {
1368
- "clip-rect": this.scalePoint(point)
1369
- .concat(this.scaleVector(size))
1370
- .join(" "),
1371
- };
1372
- },
1373
-
1374
- strokeWidth: function (val) {
1375
- return {"stroke-width": parseFloat(val)};
1376
- },
1377
-
1378
- rx: (val) => {
1379
- return {rx: this.scaleVector([val, 0])[0]};
1380
- },
1381
-
1382
- ry: (val) => {
1383
- return {ry: this.scaleVector([0, val])[1]};
1384
- },
1385
-
1386
- r: (val) => {
1387
- const scaled = this.scaleVector([val, val]);
1388
- return {rx: scaled[0], ry: scaled[1]};
1389
- },
1390
- } as const;
1391
-
1392
- const processed: Record<string, any> = {};
1393
- Object.entries(attrs || {}).forEach(function ([key, value]) {
1394
- const transformer = transformers[key];
1395
-
1396
- if (typeof transformer === "function") {
1397
- Object.assign(processed, transformer(value));
1398
- } else {
1399
- const dasherized = String(key)
1400
- .replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
1401
- .replace(/([a-z\d])([A-Z])/g, "$1-$2")
1402
- .toLowerCase();
1403
- processed[dasherized] = value;
1404
- }
1405
- });
1406
-
1407
- return processed;
1408
- }
1409
-
1410
- // This is a stub that's overridden in interactive.ts
1411
- getMouseCoord(event: Readonly<{pageX?: number; pageY?: number}>): Coord {
1412
- throw new Error("getMouseCoord is a stub, and is not implemented");
1413
- }
1414
- }
1415
-
1416
- const labelDirections = {
1417
- center: [-0.5, -0.5],
1418
- above: [-0.5, -1.0],
1419
- "above right": [0.0, -1.0],
1420
- right: [0.0, -0.5],
1421
- "below right": [0.0, 0.0],
1422
- below: [-0.5, 0.0],
1423
- "below left": [-1.0, 0.0],
1424
- left: [-1.0, -0.5],
1425
- "above left": [-1.0, -1.0],
1426
- } as const;
1427
-
1428
- // A range for a Graphie can be specified in one of three forms:
1429
- // - A pair of intervals, [[xMin, xMax], [yMin, yMax]]. This is the
1430
- // "normalized" form.
1431
- // - A pair of magnitudes for the x and y ranges, respectively. E.g. [5, 10]
1432
- // is equivalent to [[-5, 5], [-10, 10]].
1433
- // - A single magnitude to be used for both x and y. E.g. 10 is equivalent to
1434
- // [[-10, 10], [-10, 10]]
1435
- type RangeSpecifier = [Interval, Interval] | Coord | number;
1436
-
1437
- // Overload normalizeRange to specify that it only returns undefined if its
1438
- // argument is undefined.
1439
- export function normalizeRange(range: RangeSpecifier): [Interval, Interval];
1440
- export function normalizeRange(
1441
- range: RangeSpecifier | undefined,
1442
- ): [Interval, Interval] | undefined;
1443
-
1444
- // normalizeRange converts a RangeSpecifier into a pair of Intervals that give
1445
- // the x and y ranges, respectively
1446
- export function normalizeRange(
1447
- range: RangeSpecifier | undefined,
1448
- ): [Interval, Interval] | undefined {
1449
- function normalizeInterval(magnitude: number | Interval): Interval {
1450
- if (typeof magnitude === "number") {
1451
- return [-magnitude, magnitude];
1452
- } else {
1453
- return magnitude;
1454
- }
1455
- }
1456
-
1457
- function getXAndYRanges(
1458
- range: RangeSpecifier,
1459
- ): Coord | [Interval, Interval] {
1460
- if (Array.isArray(range)) {
1461
- return range;
1462
- } else {
1463
- return [range, range];
1464
- }
1465
- }
1466
-
1467
- if (range == null) {
1468
- return range;
1469
- }
1470
-
1471
- const [xRange, yRange] = getXAndYRanges(range);
1472
- return [normalizeInterval(xRange), normalizeInterval(yRange)];
1473
- }
1474
-
1475
- function toPair(x: number | [number, number]): [number, number] {
1476
- if (Array.isArray(x)) {
1477
- return x;
1478
- }
1479
- return [x, x];
1480
- }
1481
-
1482
- /**
1483
- * Safari applies some SVG-specific styles to things that are not SVGs, so we
1484
- * need to exclude those styles from things that are not SVGs.
1485
- *
1486
- * To see this behavior in action, open https://codepen.io/anon/pen/zENEoa in
1487
- * Safari.
1488
- *
1489
- * Usage `{...someStyles, ...SVG_SPECIFIC_STYLE_MASK}`
1490
- */
1491
- const SVG_SPECIFIC_STYLE_MASK = {
1492
- "stroke-width": null,
1493
- } as const;
1494
-
1495
- const setLabelMargins = function (span: HTMLElement, size: Coord): void {
1496
- const $span = $(span);
1497
- const direction = $span.data("labelDirection");
1498
- let [width, height] = size;
1499
- // This can happen when a span
1500
- // is invisible but we still want to update the CSS. At worst, we will
1501
- // be off by a few pixels instead of in a different position entirely.
1502
- if (width === 0 && height === 0) {
1503
- [width, height] = [1, 1];
1504
- Log.log("Label size was 0x0 in graphie.js; using 1x1 instead");
1505
- }
1506
- $span.css("visibility", "");
1507
-
1508
- if (typeof direction === "number") {
1509
- const x = Math.cos(direction);
1510
- const y = Math.sin(direction);
1511
-
1512
- const scale = Math.min(
1513
- width / 2 / Math.abs(x),
1514
- height / 2 / Math.abs(y),
1515
- );
1516
-
1517
- $span.css({
1518
- marginLeft: -width / 2 + x * scale,
1519
- marginTop: -height / 2 - y * scale,
1520
- });
1521
- } else {
1522
- const multipliers = labelDirections[direction || "center"];
1523
- $span.css({
1524
- marginLeft: Math.round(width * multipliers[0]),
1525
- marginTop: Math.round(height * multipliers[1]),
1526
- });
1527
- }
1528
- };
1529
-
1530
- const GraphUtils = {
1531
- Graphie,
1532
-
1533
- createGraphie: function (el: Element): Graphie {
1534
- return new Graphie(el);
1535
- },
1536
-
1537
- unscaledSvgPath: function (points: (Coord | true)[]): string {
1538
- // If this is an empty closed path, return "" instead of "z", which
1539
- // would give an error
1540
- if (points[0] === true) {
1541
- return "";
1542
- }
1543
- return points
1544
- .map(function (point, i) {
1545
- if (point === true) {
1546
- return "z";
1547
- }
1548
- return (i === 0 ? "M" : "L") + point[0] + " " + point[1];
1549
- })
1550
- .join("");
1551
- },
1552
-
1553
- getDistance: function (point1: Coord, point2: Coord): number {
1554
- return kpoint.distanceToPoint(point1, point2);
1555
- },
1556
-
1557
- // Find the angle in degrees between two or three points
1558
- findAngle: function (point1: Coord, point2: Coord, vertex?: Coord) {
1559
- if (vertex === undefined) {
1560
- const x = point1[0] - point2[0];
1561
- const y = point1[1] - point2[1];
1562
- if (!x && !y) {
1563
- return 0;
1564
- }
1565
- return (180 + (Math.atan2(-y, -x) * 180) / Math.PI + 360) % 360;
1566
- }
1567
- return (
1568
- GraphUtils.findAngle(point1, vertex) -
1569
- GraphUtils.findAngle(point2, vertex)
1570
- );
1571
- },
1572
-
1573
- graphs: {},
1574
- };
1575
-
1576
- export default GraphUtils;