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