@trebco/treb 37.0.0 → 37.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. package/build/package.json +119 -0
  2. package/build/treb-base-types/src/api_types.d.ts +11 -0
  3. package/build/treb-base-types/src/api_types.js +22 -0
  4. package/build/treb-base-types/src/api_types.js.map +1 -0
  5. package/build/treb-base-types/src/area-utils.d.ts +9 -0
  6. package/build/treb-base-types/src/area-utils.js +50 -0
  7. package/build/treb-base-types/src/area-utils.js.map +1 -0
  8. package/build/treb-base-types/src/area.d.ts +182 -0
  9. package/build/treb-base-types/src/area.js +715 -0
  10. package/build/treb-base-types/src/area.js.map +1 -0
  11. package/build/treb-base-types/src/basic_types.d.ts +20 -0
  12. package/build/treb-base-types/src/basic_types.js +22 -0
  13. package/build/treb-base-types/src/basic_types.js.map +1 -0
  14. package/build/treb-base-types/src/cell.d.ts +167 -0
  15. package/build/treb-base-types/src/cell.js +432 -0
  16. package/build/treb-base-types/src/cell.js.map +1 -0
  17. package/build/treb-base-types/src/cells.d.ts +251 -0
  18. package/build/treb-base-types/src/cells.js +1136 -0
  19. package/build/treb-base-types/src/cells.js.map +1 -0
  20. package/build/treb-base-types/src/color.d.ts +35 -0
  21. package/build/treb-base-types/src/color.js +162 -0
  22. package/build/treb-base-types/src/color.js.map +1 -0
  23. package/build/treb-base-types/src/dom-utilities.d.ts +70 -0
  24. package/build/treb-base-types/src/dom-utilities.js +144 -0
  25. package/build/treb-base-types/src/dom-utilities.js.map +1 -0
  26. package/build/treb-base-types/src/evaluate-options.d.ts +35 -0
  27. package/build/treb-base-types/src/evaluate-options.js +22 -0
  28. package/build/treb-base-types/src/evaluate-options.js.map +1 -0
  29. package/build/treb-base-types/src/font-stack.d.ts +37 -0
  30. package/build/treb-base-types/src/font-stack.js +93 -0
  31. package/build/treb-base-types/src/font-stack.js.map +1 -0
  32. package/build/treb-base-types/src/gradient.d.ts +18 -0
  33. package/build/treb-base-types/src/gradient.js +86 -0
  34. package/build/treb-base-types/src/gradient.js.map +1 -0
  35. package/build/treb-base-types/src/import.d.ts +48 -0
  36. package/build/treb-base-types/src/import.js +22 -0
  37. package/build/treb-base-types/src/import.js.map +1 -0
  38. package/build/treb-base-types/src/index-standalone.d.ts +6 -0
  39. package/build/treb-base-types/src/index-standalone.js +27 -0
  40. package/build/treb-base-types/src/index-standalone.js.map +1 -0
  41. package/build/treb-base-types/src/index.d.ts +22 -0
  42. package/build/treb-base-types/src/index.js +45 -0
  43. package/build/treb-base-types/src/index.js.map +1 -0
  44. package/build/treb-base-types/src/layout.d.ts +22 -0
  45. package/build/treb-base-types/src/layout.js +22 -0
  46. package/build/treb-base-types/src/layout.js.map +1 -0
  47. package/build/treb-base-types/src/localization.d.ts +37 -0
  48. package/build/treb-base-types/src/localization.js +157 -0
  49. package/build/treb-base-types/src/localization.js.map +1 -0
  50. package/build/treb-base-types/src/rectangle.d.ts +51 -0
  51. package/build/treb-base-types/src/rectangle.js +123 -0
  52. package/build/treb-base-types/src/rectangle.js.map +1 -0
  53. package/build/treb-base-types/src/render_text.d.ts +34 -0
  54. package/build/treb-base-types/src/render_text.js +22 -0
  55. package/build/treb-base-types/src/render_text.js.map +1 -0
  56. package/build/treb-base-types/src/style.d.ts +214 -0
  57. package/build/treb-base-types/src/style.js +373 -0
  58. package/build/treb-base-types/src/style.js.map +1 -0
  59. package/build/treb-base-types/src/table.d.ts +58 -0
  60. package/build/treb-base-types/src/table.js +27 -0
  61. package/build/treb-base-types/src/table.js.map +1 -0
  62. package/build/treb-base-types/src/text_part.d.ts +26 -0
  63. package/build/treb-base-types/src/text_part.js +47 -0
  64. package/build/treb-base-types/src/text_part.js.map +1 -0
  65. package/build/treb-base-types/src/theme.d.ts +120 -0
  66. package/build/treb-base-types/src/theme.js +460 -0
  67. package/build/treb-base-types/src/theme.js.map +1 -0
  68. package/build/treb-base-types/src/union.d.ts +73 -0
  69. package/build/treb-base-types/src/union.js +61 -0
  70. package/build/treb-base-types/src/union.js.map +1 -0
  71. package/build/treb-base-types/src/value-type.d.ts +86 -0
  72. package/build/treb-base-types/src/value-type.js +168 -0
  73. package/build/treb-base-types/src/value-type.js.map +1 -0
  74. package/build/treb-base-types/src/worker-proxy.d.ts +95 -0
  75. package/build/treb-base-types/src/worker-proxy.js +221 -0
  76. package/build/treb-base-types/src/worker-proxy.js.map +1 -0
  77. package/build/treb-calculator/src/calculator.d.ts +249 -0
  78. package/build/treb-calculator/src/calculator.js +2755 -0
  79. package/build/treb-calculator/src/calculator.js.map +1 -0
  80. package/build/treb-calculator/src/complex-math.d.ts +75 -0
  81. package/build/treb-calculator/src/complex-math.js +559 -0
  82. package/build/treb-calculator/src/complex-math.js.map +1 -0
  83. package/build/treb-calculator/src/dag/array-vertex.d.ts +71 -0
  84. package/build/treb-calculator/src/dag/array-vertex.js +156 -0
  85. package/build/treb-calculator/src/dag/array-vertex.js.map +1 -0
  86. package/build/treb-calculator/src/dag/calculation_leaf_vertex.d.ts +48 -0
  87. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js +84 -0
  88. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js.map +1 -0
  89. package/build/treb-calculator/src/dag/graph.d.ts +134 -0
  90. package/build/treb-calculator/src/dag/graph.js +842 -0
  91. package/build/treb-calculator/src/dag/graph.js.map +1 -0
  92. package/build/treb-calculator/src/dag/spreadsheet_vertex.d.ts +58 -0
  93. package/build/treb-calculator/src/dag/spreadsheet_vertex.js +232 -0
  94. package/build/treb-calculator/src/dag/spreadsheet_vertex.js.map +1 -0
  95. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.d.ts +20 -0
  96. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js +25 -0
  97. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js.map +1 -0
  98. package/build/treb-calculator/src/dag/state_leaf_vertex.d.ts +43 -0
  99. package/build/treb-calculator/src/dag/state_leaf_vertex.js +81 -0
  100. package/build/treb-calculator/src/dag/state_leaf_vertex.js.map +1 -0
  101. package/build/treb-calculator/src/dag/vertex.d.ts +71 -0
  102. package/build/treb-calculator/src/dag/vertex.js +274 -0
  103. package/build/treb-calculator/src/dag/vertex.js.map +1 -0
  104. package/build/treb-calculator/src/descriptors.d.ts +189 -0
  105. package/build/treb-calculator/src/descriptors.js +22 -0
  106. package/build/treb-calculator/src/descriptors.js.map +1 -0
  107. package/build/treb-calculator/src/expression-calculator.d.ts +127 -0
  108. package/build/treb-calculator/src/expression-calculator.js +1033 -0
  109. package/build/treb-calculator/src/expression-calculator.js.map +1 -0
  110. package/build/treb-calculator/src/function-error.d.ts +35 -0
  111. package/build/treb-calculator/src/function-error.js +85 -0
  112. package/build/treb-calculator/src/function-error.js.map +1 -0
  113. package/build/treb-calculator/src/function-library.d.ts +22 -0
  114. package/build/treb-calculator/src/function-library.js +96 -0
  115. package/build/treb-calculator/src/function-library.js.map +1 -0
  116. package/build/treb-calculator/src/functions/base-functions.d.ts +7 -0
  117. package/build/treb-calculator/src/functions/base-functions.js +2611 -0
  118. package/build/treb-calculator/src/functions/base-functions.js.map +1 -0
  119. package/build/treb-calculator/src/functions/beta.d.ts +17 -0
  120. package/build/treb-calculator/src/functions/beta.js +201 -0
  121. package/build/treb-calculator/src/functions/beta.js.map +1 -0
  122. package/build/treb-calculator/src/functions/checkbox.d.ts +3 -0
  123. package/build/treb-calculator/src/functions/checkbox.js +128 -0
  124. package/build/treb-calculator/src/functions/checkbox.js.map +1 -0
  125. package/build/treb-calculator/src/functions/complex-functions.d.ts +2 -0
  126. package/build/treb-calculator/src/functions/complex-functions.js +217 -0
  127. package/build/treb-calculator/src/functions/complex-functions.js.map +1 -0
  128. package/build/treb-calculator/src/functions/date-utils.d.ts +3 -0
  129. package/build/treb-calculator/src/functions/date-utils.js +59 -0
  130. package/build/treb-calculator/src/functions/date-utils.js.map +1 -0
  131. package/build/treb-calculator/src/functions/finance-functions.d.ts +2 -0
  132. package/build/treb-calculator/src/functions/finance-functions.js +547 -0
  133. package/build/treb-calculator/src/functions/finance-functions.js.map +1 -0
  134. package/build/treb-calculator/src/functions/fp.d.ts +2 -0
  135. package/build/treb-calculator/src/functions/fp.js +463 -0
  136. package/build/treb-calculator/src/functions/fp.js.map +1 -0
  137. package/build/treb-calculator/src/functions/function-utilities.d.ts +2 -0
  138. package/build/treb-calculator/src/functions/function-utilities.js +36 -0
  139. package/build/treb-calculator/src/functions/function-utilities.js.map +1 -0
  140. package/build/treb-calculator/src/functions/gamma.d.ts +20 -0
  141. package/build/treb-calculator/src/functions/gamma.js +142 -0
  142. package/build/treb-calculator/src/functions/gamma.js.map +1 -0
  143. package/build/treb-calculator/src/functions/information-functions.d.ts +2 -0
  144. package/build/treb-calculator/src/functions/information-functions.js +71 -0
  145. package/build/treb-calculator/src/functions/information-functions.js.map +1 -0
  146. package/build/treb-calculator/src/functions/lambda-functions.d.ts +2 -0
  147. package/build/treb-calculator/src/functions/lambda-functions.js +85 -0
  148. package/build/treb-calculator/src/functions/lambda-functions.js.map +1 -0
  149. package/build/treb-calculator/src/functions/matrix-functions.d.ts +2 -0
  150. package/build/treb-calculator/src/functions/matrix-functions.js +144 -0
  151. package/build/treb-calculator/src/functions/matrix-functions.js.map +1 -0
  152. package/build/treb-calculator/src/functions/normal.d.ts +2 -0
  153. package/build/treb-calculator/src/functions/normal.js +32 -0
  154. package/build/treb-calculator/src/functions/normal.js.map +1 -0
  155. package/build/treb-calculator/src/functions/regex-functions.d.ts +2 -0
  156. package/build/treb-calculator/src/functions/regex-functions.js +188 -0
  157. package/build/treb-calculator/src/functions/regex-functions.js.map +1 -0
  158. package/build/treb-calculator/src/functions/sparkline.d.ts +37 -0
  159. package/build/treb-calculator/src/functions/sparkline.js +264 -0
  160. package/build/treb-calculator/src/functions/sparkline.js.map +1 -0
  161. package/build/treb-calculator/src/functions/statistics-functions.d.ts +6 -0
  162. package/build/treb-calculator/src/functions/statistics-functions.js +989 -0
  163. package/build/treb-calculator/src/functions/statistics-functions.js.map +1 -0
  164. package/build/treb-calculator/src/functions/students-t.d.ts +3 -0
  165. package/build/treb-calculator/src/functions/students-t.js +64 -0
  166. package/build/treb-calculator/src/functions/students-t.js.map +1 -0
  167. package/build/treb-calculator/src/functions/text-functions.d.ts +3 -0
  168. package/build/treb-calculator/src/functions/text-functions.js +320 -0
  169. package/build/treb-calculator/src/functions/text-functions.js.map +1 -0
  170. package/build/treb-calculator/src/index.d.ts +2 -0
  171. package/build/treb-calculator/src/index.js +22 -0
  172. package/build/treb-calculator/src/index.js.map +1 -0
  173. package/build/treb-calculator/src/notifier-types.d.ts +26 -0
  174. package/build/treb-calculator/src/notifier-types.js +22 -0
  175. package/build/treb-calculator/src/notifier-types.js.map +1 -0
  176. package/build/treb-calculator/src/primitives.d.ts +15 -0
  177. package/build/treb-calculator/src/primitives.js +398 -0
  178. package/build/treb-calculator/src/primitives.js.map +1 -0
  179. package/build/treb-calculator/src/utilities.d.ts +68 -0
  180. package/build/treb-calculator/src/utilities.js +324 -0
  181. package/build/treb-calculator/src/utilities.js.map +1 -0
  182. package/build/treb-charts/src/chart-functions.d.ts +8 -0
  183. package/build/treb-charts/src/chart-functions.js +209 -0
  184. package/build/treb-charts/src/chart-functions.js.map +1 -0
  185. package/build/treb-charts/src/chart-types.d.ts +233 -0
  186. package/build/treb-charts/src/chart-types.js +57 -0
  187. package/build/treb-charts/src/chart-types.js.map +1 -0
  188. package/build/treb-charts/src/chart-utils.d.ts +106 -0
  189. package/build/treb-charts/src/chart-utils.js +1060 -0
  190. package/build/treb-charts/src/chart-utils.js.map +1 -0
  191. package/build/treb-charts/src/chart.d.ts +23 -0
  192. package/build/treb-charts/src/chart.js +94 -0
  193. package/build/treb-charts/src/chart.js.map +1 -0
  194. package/build/treb-charts/src/default-chart-renderer.d.ts +16 -0
  195. package/build/treb-charts/src/default-chart-renderer.js +533 -0
  196. package/build/treb-charts/src/default-chart-renderer.js.map +1 -0
  197. package/build/treb-charts/src/index.d.ts +5 -0
  198. package/build/treb-charts/src/index.js +24 -0
  199. package/build/treb-charts/src/index.js.map +1 -0
  200. package/build/treb-charts/src/main.d.ts +1 -0
  201. package/build/treb-charts/src/main.js +34 -0
  202. package/build/treb-charts/src/main.js.map +1 -0
  203. package/build/treb-charts/src/quicksort.d.ts +1 -0
  204. package/build/treb-charts/src/quicksort.js +49 -0
  205. package/build/treb-charts/src/quicksort.js.map +1 -0
  206. package/build/treb-charts/src/rectangle.d.ts +18 -0
  207. package/build/treb-charts/src/rectangle.js +41 -0
  208. package/build/treb-charts/src/rectangle.js.map +1 -0
  209. package/build/treb-charts/src/renderer-type.d.ts +24 -0
  210. package/build/treb-charts/src/renderer-type.js +22 -0
  211. package/build/treb-charts/src/renderer-type.js.map +1 -0
  212. package/build/treb-charts/src/renderer.d.ts +127 -0
  213. package/build/treb-charts/src/renderer.js +1518 -0
  214. package/build/treb-charts/src/renderer.js.map +1 -0
  215. package/build/treb-charts/src/util.d.ts +18 -0
  216. package/build/treb-charts/src/util.js +71 -0
  217. package/build/treb-charts/src/util.js.map +1 -0
  218. package/build/treb-data-model/src/annotation.d.ts +167 -0
  219. package/build/treb-data-model/src/annotation.js +120 -0
  220. package/build/treb-data-model/src/annotation.js.map +1 -0
  221. package/build/treb-data-model/src/conditional_format.d.ts +155 -0
  222. package/build/treb-data-model/src/conditional_format.js +62 -0
  223. package/build/treb-data-model/src/conditional_format.js.map +1 -0
  224. package/build/treb-data-model/src/data-validation.d.ts +28 -0
  225. package/build/treb-data-model/src/data-validation.js +22 -0
  226. package/build/treb-data-model/src/data-validation.js.map +1 -0
  227. package/build/treb-data-model/src/data_model.d.ts +173 -0
  228. package/build/treb-data-model/src/data_model.js +637 -0
  229. package/build/treb-data-model/src/data_model.js.map +1 -0
  230. package/build/treb-data-model/src/index.d.ts +13 -0
  231. package/build/treb-data-model/src/index.js +28 -0
  232. package/build/treb-data-model/src/index.js.map +1 -0
  233. package/build/treb-data-model/src/language-model.d.ts +22 -0
  234. package/build/treb-data-model/src/language-model.js +22 -0
  235. package/build/treb-data-model/src/language-model.js.map +1 -0
  236. package/build/treb-data-model/src/named.d.ts +124 -0
  237. package/build/treb-data-model/src/named.js +372 -0
  238. package/build/treb-data-model/src/named.js.map +1 -0
  239. package/build/treb-data-model/src/serialize_options.d.ts +49 -0
  240. package/build/treb-data-model/src/serialize_options.js +22 -0
  241. package/build/treb-data-model/src/serialize_options.js.map +1 -0
  242. package/build/treb-data-model/src/sheet.d.ts +499 -0
  243. package/build/treb-data-model/src/sheet.js +2904 -0
  244. package/build/treb-data-model/src/sheet.js.map +1 -0
  245. package/build/treb-data-model/src/sheet_collection.d.ts +58 -0
  246. package/build/treb-data-model/src/sheet_collection.js +112 -0
  247. package/build/treb-data-model/src/sheet_collection.js.map +1 -0
  248. package/build/treb-data-model/src/sheet_selection.d.ts +42 -0
  249. package/build/treb-data-model/src/sheet_selection.js +39 -0
  250. package/build/treb-data-model/src/sheet_selection.js.map +1 -0
  251. package/build/treb-data-model/src/sheet_types.d.ts +104 -0
  252. package/build/treb-data-model/src/sheet_types.js +22 -0
  253. package/build/treb-data-model/src/sheet_types.js.map +1 -0
  254. package/build/treb-data-model/src/types.d.ts +59 -0
  255. package/build/treb-data-model/src/types.js +22 -0
  256. package/build/treb-data-model/src/types.js.map +1 -0
  257. package/build/treb-embed/src/custom-element/spreadsheet-constructor.d.ts +75 -0
  258. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js +1144 -0
  259. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js.map +1 -0
  260. package/build/treb-embed/src/custom-element/treb-global.d.ts +36 -0
  261. package/build/treb-embed/src/custom-element/treb-global.js +64 -0
  262. package/build/treb-embed/src/custom-element/treb-global.js.map +1 -0
  263. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.d.ts +1 -0
  264. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js +61 -0
  265. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js.map +1 -0
  266. package/build/treb-embed/src/embedded-spreadsheet.d.ts +1358 -0
  267. package/build/treb-embed/src/embedded-spreadsheet.js +5205 -0
  268. package/build/treb-embed/src/embedded-spreadsheet.js.map +1 -0
  269. package/build/treb-embed/src/index.d.ts +12 -0
  270. package/build/treb-embed/src/index.js +34 -0
  271. package/build/treb-embed/src/index.js.map +1 -0
  272. package/build/treb-embed/src/options.d.ts +266 -0
  273. package/build/treb-embed/src/options.js +56 -0
  274. package/build/treb-embed/src/options.js.map +1 -0
  275. package/build/treb-embed/src/plugin.d.ts +9 -0
  276. package/build/treb-embed/src/plugin.js +22 -0
  277. package/build/treb-embed/src/plugin.js.map +1 -0
  278. package/build/treb-embed/src/progress-dialog.d.ts +49 -0
  279. package/build/treb-embed/src/progress-dialog.js +178 -0
  280. package/build/treb-embed/src/progress-dialog.js.map +1 -0
  281. package/build/treb-embed/src/selection-state.d.ts +15 -0
  282. package/build/treb-embed/src/selection-state.js +22 -0
  283. package/build/treb-embed/src/selection-state.js.map +1 -0
  284. package/build/treb-embed/src/spinner.d.ts +8 -0
  285. package/build/treb-embed/src/spinner.js +40 -0
  286. package/build/treb-embed/src/spinner.js.map +1 -0
  287. package/build/treb-embed/src/toolbar-message.d.ts +72 -0
  288. package/build/treb-embed/src/toolbar-message.js +22 -0
  289. package/build/treb-embed/src/toolbar-message.js.map +1 -0
  290. package/build/treb-embed/src/types.d.ts +185 -0
  291. package/build/treb-embed/src/types.js +45 -0
  292. package/build/treb-embed/src/types.js.map +1 -0
  293. package/build/treb-embed/tsconfig.tsbuildinfo +1 -0
  294. package/build/treb-export/src/address-type.d.ts +34 -0
  295. package/build/treb-export/src/address-type.js +53 -0
  296. package/build/treb-export/src/address-type.js.map +1 -0
  297. package/build/treb-export/src/base-template.d.ts +1 -0
  298. package/build/treb-export/src/base-template.js +22 -0
  299. package/build/treb-export/src/base-template.js.map +1 -0
  300. package/build/treb-export/src/column-width.d.ts +2 -0
  301. package/build/treb-export/src/column-width.js +80 -0
  302. package/build/treb-export/src/column-width.js.map +1 -0
  303. package/build/treb-export/src/drawing/bubble-chart-template.d.ts +514 -0
  304. package/build/treb-export/src/drawing/bubble-chart-template.js +544 -0
  305. package/build/treb-export/src/drawing/bubble-chart-template.js.map +1 -0
  306. package/build/treb-export/src/drawing/chart-template-components2.d.ts +365 -0
  307. package/build/treb-export/src/drawing/chart-template-components2.js +386 -0
  308. package/build/treb-export/src/drawing/chart-template-components2.js.map +1 -0
  309. package/build/treb-export/src/drawing/chart.d.ts +26 -0
  310. package/build/treb-export/src/drawing/chart.js +247 -0
  311. package/build/treb-export/src/drawing/chart.js.map +1 -0
  312. package/build/treb-export/src/drawing/column-chart-template2.d.ts +490 -0
  313. package/build/treb-export/src/drawing/column-chart-template2.js +518 -0
  314. package/build/treb-export/src/drawing/column-chart-template2.js.map +1 -0
  315. package/build/treb-export/src/drawing/donut-chart-template2.d.ts +272 -0
  316. package/build/treb-export/src/drawing/donut-chart-template2.js +293 -0
  317. package/build/treb-export/src/drawing/donut-chart-template2.js.map +1 -0
  318. package/build/treb-export/src/drawing/drawing.d.ts +49 -0
  319. package/build/treb-export/src/drawing/drawing.js +193 -0
  320. package/build/treb-export/src/drawing/drawing.js.map +1 -0
  321. package/build/treb-export/src/drawing/embedded-image.d.ts +12 -0
  322. package/build/treb-export/src/drawing/embedded-image.js +54 -0
  323. package/build/treb-export/src/drawing/embedded-image.js.map +1 -0
  324. package/build/treb-export/src/drawing/scatter-chart-template2.d.ts +520 -0
  325. package/build/treb-export/src/drawing/scatter-chart-template2.js +551 -0
  326. package/build/treb-export/src/drawing/scatter-chart-template2.js.map +1 -0
  327. package/build/treb-export/src/export.d.ts +72 -0
  328. package/build/treb-export/src/export.js +2039 -0
  329. package/build/treb-export/src/export.js.map +1 -0
  330. package/build/treb-export/src/import-export-messages.d.ts +31 -0
  331. package/build/treb-export/src/import-export-messages.js +22 -0
  332. package/build/treb-export/src/import-export-messages.js.map +1 -0
  333. package/build/treb-export/src/import.d.ts +33 -0
  334. package/build/treb-export/src/import.js +1258 -0
  335. package/build/treb-export/src/import.js.map +1 -0
  336. package/build/treb-export/src/index.worker.d.ts +1 -0
  337. package/build/treb-export/src/index.worker.js +93 -0
  338. package/build/treb-export/src/index.worker.js.map +1 -0
  339. package/build/treb-export/src/metadata.d.ts +51 -0
  340. package/build/treb-export/src/metadata.js +153 -0
  341. package/build/treb-export/src/metadata.js.map +1 -0
  342. package/build/treb-export/src/ooxml.d.ts +7 -0
  343. package/build/treb-export/src/ooxml.js +41 -0
  344. package/build/treb-export/src/ooxml.js.map +1 -0
  345. package/build/treb-export/src/relationship.d.ts +8 -0
  346. package/build/treb-export/src/relationship.js +27 -0
  347. package/build/treb-export/src/relationship.js.map +1 -0
  348. package/build/treb-export/src/shared-strings.d.ts +11 -0
  349. package/build/treb-export/src/shared-strings.js +105 -0
  350. package/build/treb-export/src/shared-strings.js.map +1 -0
  351. package/build/treb-export/src/template-2.d.ts +1 -0
  352. package/build/treb-export/src/template-2.js +22 -0
  353. package/build/treb-export/src/template-2.js.map +1 -0
  354. package/build/treb-export/src/unescape_xml.d.ts +1 -0
  355. package/build/treb-export/src/unescape_xml.js +61 -0
  356. package/build/treb-export/src/unescape_xml.js.map +1 -0
  357. package/build/treb-export/src/workbook-sheet.d.ts +75 -0
  358. package/build/treb-export/src/workbook-sheet.js +128 -0
  359. package/build/treb-export/src/workbook-sheet.js.map +1 -0
  360. package/build/treb-export/src/workbook-style.d.ts +110 -0
  361. package/build/treb-export/src/workbook-style.js +1134 -0
  362. package/build/treb-export/src/workbook-style.js.map +1 -0
  363. package/build/treb-export/src/workbook-theme.d.ts +13 -0
  364. package/build/treb-export/src/workbook-theme.js +85 -0
  365. package/build/treb-export/src/workbook-theme.js.map +1 -0
  366. package/build/treb-export/src/workbook.d.ts +123 -0
  367. package/build/treb-export/src/workbook.js +644 -0
  368. package/build/treb-export/src/workbook.js.map +1 -0
  369. package/build/treb-export/src/xml-test.d.ts +9 -0
  370. package/build/treb-export/src/xml-test.js +52 -0
  371. package/build/treb-export/src/xml-test.js.map +1 -0
  372. package/build/treb-export/src/xml-utils.d.ts +76 -0
  373. package/build/treb-export/src/xml-utils.js +223 -0
  374. package/build/treb-export/src/xml-utils.js.map +1 -0
  375. package/build/treb-export/src/zip-wrapper.d.ts +22 -0
  376. package/build/treb-export/src/zip-wrapper.js +93 -0
  377. package/build/treb-export/src/zip-wrapper.js.map +1 -0
  378. package/build/treb-format/src/format.d.ts +130 -0
  379. package/build/treb-format/src/format.js +805 -0
  380. package/build/treb-format/src/format.js.map +1 -0
  381. package/build/treb-format/src/format_cache.d.ts +55 -0
  382. package/build/treb-format/src/format_cache.js +166 -0
  383. package/build/treb-format/src/format_cache.js.map +1 -0
  384. package/build/treb-format/src/format_parser.d.ts +70 -0
  385. package/build/treb-format/src/format_parser.js +618 -0
  386. package/build/treb-format/src/format_parser.js.map +1 -0
  387. package/build/treb-format/src/index.d.ts +4 -0
  388. package/build/treb-format/src/index.js +25 -0
  389. package/build/treb-format/src/index.js.map +1 -0
  390. package/build/treb-format/src/number_format_section.d.ts +58 -0
  391. package/build/treb-format/src/number_format_section.js +78 -0
  392. package/build/treb-format/src/number_format_section.js.map +1 -0
  393. package/build/treb-format/src/value_parser.d.ts +48 -0
  394. package/build/treb-format/src/value_parser.js +244 -0
  395. package/build/treb-format/src/value_parser.js.map +1 -0
  396. package/build/treb-grid/src/editors/autocomplete.d.ts +39 -0
  397. package/build/treb-grid/src/editors/autocomplete.js +316 -0
  398. package/build/treb-grid/src/editors/autocomplete.js.map +1 -0
  399. package/build/treb-grid/src/editors/autocomplete_matcher.d.ts +74 -0
  400. package/build/treb-grid/src/editors/autocomplete_matcher.js +212 -0
  401. package/build/treb-grid/src/editors/autocomplete_matcher.js.map +1 -0
  402. package/build/treb-grid/src/editors/editor.d.ts +214 -0
  403. package/build/treb-grid/src/editors/editor.js +879 -0
  404. package/build/treb-grid/src/editors/editor.js.map +1 -0
  405. package/build/treb-grid/src/editors/external_editor.d.ts +11 -0
  406. package/build/treb-grid/src/editors/external_editor.js +118 -0
  407. package/build/treb-grid/src/editors/external_editor.js.map +1 -0
  408. package/build/treb-grid/src/editors/formula_bar.d.ts +85 -0
  409. package/build/treb-grid/src/editors/formula_bar.js +444 -0
  410. package/build/treb-grid/src/editors/formula_bar.js.map +1 -0
  411. package/build/treb-grid/src/editors/overlay_editor.d.ts +85 -0
  412. package/build/treb-grid/src/editors/overlay_editor.js +353 -0
  413. package/build/treb-grid/src/editors/overlay_editor.js.map +1 -0
  414. package/build/treb-grid/src/index.d.ts +12 -0
  415. package/build/treb-grid/src/index.js +28 -0
  416. package/build/treb-grid/src/index.js.map +1 -0
  417. package/build/treb-grid/src/layout/base_layout.d.ts +346 -0
  418. package/build/treb-grid/src/layout/base_layout.js +2050 -0
  419. package/build/treb-grid/src/layout/base_layout.js.map +1 -0
  420. package/build/treb-grid/src/layout/grid_layout.d.ts +19 -0
  421. package/build/treb-grid/src/layout/grid_layout.js +235 -0
  422. package/build/treb-grid/src/layout/grid_layout.js.map +1 -0
  423. package/build/treb-grid/src/layout/mock-layout.d.ts +10 -0
  424. package/build/treb-grid/src/layout/mock-layout.js +37 -0
  425. package/build/treb-grid/src/layout/mock-layout.js.map +1 -0
  426. package/build/treb-grid/src/render/selection-renderer.d.ts +97 -0
  427. package/build/treb-grid/src/render/selection-renderer.js +315 -0
  428. package/build/treb-grid/src/render/selection-renderer.js.map +1 -0
  429. package/build/treb-grid/src/render/svg_header_overlay.d.ts +20 -0
  430. package/build/treb-grid/src/render/svg_header_overlay.js +76 -0
  431. package/build/treb-grid/src/render/svg_header_overlay.js.map +1 -0
  432. package/build/treb-grid/src/render/svg_selection_block.d.ts +27 -0
  433. package/build/treb-grid/src/render/svg_selection_block.js +106 -0
  434. package/build/treb-grid/src/render/svg_selection_block.js.map +1 -0
  435. package/build/treb-grid/src/render/tile_renderer.d.ts +121 -0
  436. package/build/treb-grid/src/render/tile_renderer.js +1609 -0
  437. package/build/treb-grid/src/render/tile_renderer.js.map +1 -0
  438. package/build/treb-grid/src/types/border_constants.d.ts +9 -0
  439. package/build/treb-grid/src/types/border_constants.js +34 -0
  440. package/build/treb-grid/src/types/border_constants.js.map +1 -0
  441. package/build/treb-grid/src/types/clipboard_data.d.ts +11 -0
  442. package/build/treb-grid/src/types/clipboard_data.js +22 -0
  443. package/build/treb-grid/src/types/clipboard_data.js.map +1 -0
  444. package/build/treb-grid/src/types/clipboard_data2.d.ts +46 -0
  445. package/build/treb-grid/src/types/clipboard_data2.js +22 -0
  446. package/build/treb-grid/src/types/clipboard_data2.js.map +1 -0
  447. package/build/treb-grid/src/types/drag_mask.d.ts +10 -0
  448. package/build/treb-grid/src/types/drag_mask.js +78 -0
  449. package/build/treb-grid/src/types/drag_mask.js.map +1 -0
  450. package/build/treb-grid/src/types/external_editor_config.d.ts +33 -0
  451. package/build/treb-grid/src/types/external_editor_config.js +22 -0
  452. package/build/treb-grid/src/types/external_editor_config.js.map +1 -0
  453. package/build/treb-grid/src/types/grid.d.ts +806 -0
  454. package/build/treb-grid/src/types/grid.js +6410 -0
  455. package/build/treb-grid/src/types/grid.js.map +1 -0
  456. package/build/treb-grid/src/types/grid_base.d.ts +442 -0
  457. package/build/treb-grid/src/types/grid_base.js +3523 -0
  458. package/build/treb-grid/src/types/grid_base.js.map +1 -0
  459. package/build/treb-grid/src/types/grid_command.d.ts +408 -0
  460. package/build/treb-grid/src/types/grid_command.js +75 -0
  461. package/build/treb-grid/src/types/grid_command.js.map +1 -0
  462. package/build/treb-grid/src/types/grid_events.d.ts +93 -0
  463. package/build/treb-grid/src/types/grid_events.js +36 -0
  464. package/build/treb-grid/src/types/grid_events.js.map +1 -0
  465. package/build/treb-grid/src/types/grid_options.d.ts +50 -0
  466. package/build/treb-grid/src/types/grid_options.js +34 -0
  467. package/build/treb-grid/src/types/grid_options.js.map +1 -0
  468. package/build/treb-grid/src/types/scale-control.d.ts +21 -0
  469. package/build/treb-grid/src/types/scale-control.js +148 -0
  470. package/build/treb-grid/src/types/scale-control.js.map +1 -0
  471. package/build/treb-grid/src/types/set_range_options.d.ts +24 -0
  472. package/build/treb-grid/src/types/set_range_options.js +22 -0
  473. package/build/treb-grid/src/types/set_range_options.js.map +1 -0
  474. package/build/treb-grid/src/types/tab_bar.d.ts +84 -0
  475. package/build/treb-grid/src/types/tab_bar.js +426 -0
  476. package/build/treb-grid/src/types/tab_bar.js.map +1 -0
  477. package/build/treb-grid/src/types/tile.d.ts +29 -0
  478. package/build/treb-grid/src/types/tile.js +22 -0
  479. package/build/treb-grid/src/types/tile.js.map +1 -0
  480. package/build/treb-grid/src/types/update_flags.d.ts +48 -0
  481. package/build/treb-grid/src/types/update_flags.js +22 -0
  482. package/build/treb-grid/src/types/update_flags.js.map +1 -0
  483. package/build/treb-grid/src/util/fontmetrics.d.ts +21 -0
  484. package/build/treb-grid/src/util/fontmetrics.js +82 -0
  485. package/build/treb-grid/src/util/fontmetrics.js.map +1 -0
  486. package/build/treb-grid/src/util/ua.d.ts +33 -0
  487. package/build/treb-grid/src/util/ua.js +86 -0
  488. package/build/treb-grid/src/util/ua.js.map +1 -0
  489. package/build/treb-parser/src/csv-parser.d.ts +13 -0
  490. package/build/treb-parser/src/csv-parser.js +107 -0
  491. package/build/treb-parser/src/csv-parser.js.map +1 -0
  492. package/build/treb-parser/src/index.d.ts +4 -0
  493. package/build/treb-parser/src/index.js +25 -0
  494. package/build/treb-parser/src/index.js.map +1 -0
  495. package/build/treb-parser/src/md-parser.d.ts +97 -0
  496. package/build/treb-parser/src/md-parser.js +403 -0
  497. package/build/treb-parser/src/md-parser.js.map +1 -0
  498. package/build/treb-parser/src/parser-types.d.ts +345 -0
  499. package/build/treb-parser/src/parser-types.js +53 -0
  500. package/build/treb-parser/src/parser-types.js.map +1 -0
  501. package/build/treb-parser/src/parser.d.ts +422 -0
  502. package/build/treb-parser/src/parser.js +2418 -0
  503. package/build/treb-parser/src/parser.js.map +1 -0
  504. package/build/treb-utils/src/event_source.d.ts +34 -0
  505. package/build/treb-utils/src/event_source.js +110 -0
  506. package/build/treb-utils/src/event_source.js.map +1 -0
  507. package/build/treb-utils/src/ievent_source.d.ts +9 -0
  508. package/build/treb-utils/src/ievent_source.js +22 -0
  509. package/build/treb-utils/src/ievent_source.js.map +1 -0
  510. package/build/treb-utils/src/index.d.ts +6 -0
  511. package/build/treb-utils/src/index.js +30 -0
  512. package/build/treb-utils/src/index.js.map +1 -0
  513. package/build/treb-utils/src/measurement.d.ts +42 -0
  514. package/build/treb-utils/src/measurement.js +145 -0
  515. package/build/treb-utils/src/measurement.js.map +1 -0
  516. package/build/treb-utils/src/scale.d.ts +16 -0
  517. package/build/treb-utils/src/scale.js +106 -0
  518. package/build/treb-utils/src/scale.js.map +1 -0
  519. package/build/treb-utils/src/serialize_html.d.ts +5 -0
  520. package/build/treb-utils/src/serialize_html.js +128 -0
  521. package/build/treb-utils/src/serialize_html.js.map +1 -0
  522. package/build/treb-utils/src/validate_uri.d.ts +20 -0
  523. package/build/treb-utils/src/validate_uri.js +55 -0
  524. package/build/treb-utils/src/validate_uri.js.map +1 -0
  525. package/dist/{chunk-XD5PEZBZ.mjs → chunk-A2NJA5VB.mjs} +1 -1
  526. package/dist/treb-export-worker.mjs +2 -2
  527. package/dist/treb-spreadsheet.mjs +4 -4
  528. package/dist/treb.d.ts +1 -1
  529. package/package.json +1 -1
@@ -0,0 +1,1518 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2026 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+ import { Area } from './rectangle';
22
+ import { LegendLayout, LegendPosition, LegendStyle } from './chart-types';
23
+ const SVGNS = 'http://www.w3.org/2000/svg';
24
+ const SVGNode = (tag, attribute_map = {}, text) => {
25
+ const node = document.createElementNS(SVGNS, tag);
26
+ for (const key of Object.keys(attribute_map)) {
27
+ if (attribute_map[key] !== undefined) {
28
+ const value = attribute_map[key] ?? '';
29
+ node.setAttribute(key, Array.isArray(value) ? value.join(' ') : value.toString());
30
+ }
31
+ }
32
+ if (text) {
33
+ node.textContent = text;
34
+ }
35
+ return node;
36
+ };
37
+ /**
38
+ * FIXME: normalize API, make canvas version
39
+ */
40
+ export class ChartRenderer {
41
+ parent;
42
+ svg_node;
43
+ text_measurement_node;
44
+ container_group;
45
+ group;
46
+ axis_group;
47
+ label_group;
48
+ size = { width: 0, height: 0 };
49
+ bounds = new Area();
50
+ // public smoothing_factor = 0.2;
51
+ constructor() {
52
+ this.container_group = SVGNode('g');
53
+ this.group = SVGNode('g');
54
+ this.axis_group = SVGNode('g', { class: 'axis-group' });
55
+ this.label_group = SVGNode('g', { class: 'label-group' });
56
+ this.container_group.appendChild(this.axis_group);
57
+ this.container_group.appendChild(this.group);
58
+ this.container_group.appendChild(this.label_group);
59
+ }
60
+ Initialize(node) {
61
+ this.parent = node;
62
+ this.svg_node = SVGNode('svg', {
63
+ class: 'treb-chart',
64
+ // style: 'overflow: hidden; position: relative; width: 100%; height: 100%;'
65
+ });
66
+ this.svg_node.style.overflow = 'hidden';
67
+ this.svg_node.style.position = 'relative';
68
+ this.svg_node.style.width = '100%';
69
+ this.svg_node.style.height = '100%';
70
+ this.svg_node.appendChild(this.container_group);
71
+ // FIXME: validate parent is relative/absolute
72
+ this.parent.appendChild(this.svg_node);
73
+ this.Resize();
74
+ }
75
+ Legend(options) {
76
+ const group = SVGNode('g');
77
+ this.group.appendChild(group);
78
+ const measure = SVGNode('text');
79
+ group.appendChild(measure);
80
+ // IE says no
81
+ // group.classList.add('legend');
82
+ group.setAttribute('class', 'legend');
83
+ const rows = [[]];
84
+ const padding = 10;
85
+ let space = options.area.width;
86
+ let row = 0;
87
+ let max_height = 0;
88
+ const width = options.area.width;
89
+ const marker_width = (options.style === LegendStyle.marker) ? 14 : 26;
90
+ const metrics = options.labels.map((label, index) => {
91
+ measure.textContent = label.label;
92
+ const text_rect = measure.getBoundingClientRect();
93
+ const text_metrics = { width: text_rect.width, height: text_rect.height };
94
+ const composite = text_metrics.width + marker_width + padding;
95
+ max_height = Math.max(max_height, text_metrics.height);
96
+ if (options.layout === LegendLayout.vertical) {
97
+ rows[index] = [index];
98
+ }
99
+ else {
100
+ if (composite > space) {
101
+ if (rows[row].length === 0) {
102
+ // there's nothing in this row, so moving to the next
103
+ // row will not help; stick it in here regardless
104
+ rows[row].push(index);
105
+ row++;
106
+ rows[row] = [];
107
+ space = width;
108
+ }
109
+ else {
110
+ row++;
111
+ rows[row] = [index];
112
+ space = width - composite;
113
+ }
114
+ }
115
+ else {
116
+ rows[row].push(index);
117
+ space -= composite;
118
+ }
119
+ }
120
+ return text_metrics;
121
+ });
122
+ // IE11: SVG element doesn't have parent element? (...)
123
+ // measure.parentElement?.removeChild(measure);
124
+ group.removeChild(measure);
125
+ let y = max_height;
126
+ let layout = options.layout || LegendLayout.horizontal;
127
+ if (layout === LegendLayout.horizontal && rows.every(row => row.length <= 1)) {
128
+ layout = LegendLayout.horizontal;
129
+ }
130
+ for (let row = 0; row < rows.length; row++) {
131
+ const row_width = rows[row].reduce((a, x) => a + metrics[x].width + marker_width, (rows[row].length - 1) * padding);
132
+ let h = 0;
133
+ let x = layout === LegendLayout.horizontal ?
134
+ Math.round((width - row_width) / 2) :
135
+ Math.round(padding / 2);
136
+ for (let col = 0; col < rows[row].length; col++) {
137
+ const index = rows[row][col];
138
+ const text_metrrics = metrics[index];
139
+ const label = options.labels[index];
140
+ const marker_y = y - 1; // Math.round(y + text_metrrics.height / 2);
141
+ // NOTE: trident offset is inlined here
142
+ let trident = false;
143
+ if (typeof navigator !== 'undefined') {
144
+ trident = /trident/i.test(navigator?.userAgent || '');
145
+ }
146
+ const color = typeof label.index === 'number' ? label.index : index + 1;
147
+ group.appendChild(SVGNode('text', {
148
+ 'dominant-baseline': 'middle', x: x + marker_width, y, dy: (trident ? '.3em' : undefined)
149
+ }, label.label));
150
+ switch (options.style) {
151
+ case LegendStyle.marker:
152
+ group.appendChild(SVGNode('rect', {
153
+ class: `series-${color}`, x, y: marker_y - 4, width: 8, height: 8
154
+ }));
155
+ break;
156
+ case LegendStyle.bubble:
157
+ group.appendChild(SVGNode('circle', {
158
+ class: `series-${color}`, cx: x + marker_width - 11, cy: marker_y - 4 + 3, /* r: '0.25em' */
159
+ }));
160
+ break;
161
+ default:
162
+ group.appendChild(SVGNode('rect', {
163
+ class: `series-${color}`, x, y: marker_y - 1, width: marker_width - 3, height: 2
164
+ }));
165
+ break;
166
+ }
167
+ h = Math.max(h, text_metrrics.height);
168
+ x += text_metrrics.width + marker_width + padding;
169
+ }
170
+ y = Math.round(y + h * 1.1);
171
+ }
172
+ const rect = group.getBoundingClientRect();
173
+ const legend_size = { width: rect.width, height: rect.height + max_height };
174
+ switch (options.position) {
175
+ case LegendPosition.bottom:
176
+ group.setAttribute('transform', `translate(${options.area.left}, ${options.area.bottom - legend_size.height})`);
177
+ break;
178
+ case LegendPosition.left:
179
+ group.setAttribute('transform', `translate(${options.area.left}, ${options.area.top})`);
180
+ break;
181
+ case LegendPosition.right:
182
+ group.setAttribute('transform', `translate(${options.area.right - legend_size.width}, ${options.area.top})`);
183
+ break;
184
+ case LegendPosition.top:
185
+ default:
186
+ group.setAttribute('transform', `translate(${options.area.left}, ${options.area.top})`);
187
+ }
188
+ if (options.position === LegendPosition.top) {
189
+ options.area.top += legend_size.height || 0;
190
+ }
191
+ else if (options.position === LegendPosition.right) {
192
+ options.area.right -= ((legend_size.width || 0) + 8); // 8?
193
+ }
194
+ else if (options.position === LegendPosition.left) {
195
+ options.area.left += ((legend_size.width || 0) + 8);
196
+ }
197
+ else {
198
+ options.area.bottom -= legend_size.height || 0;
199
+ }
200
+ // return legend_size;
201
+ }
202
+ Clear(class_name) {
203
+ this.group.textContent = '';
204
+ this.axis_group.textContent = '';
205
+ this.label_group.textContent = '';
206
+ class_name = 'treb-chart' + (class_name ? ' ' + class_name : '');
207
+ this.svg_node.setAttribute('class', class_name);
208
+ }
209
+ Resize() {
210
+ const bounds = this.svg_node.getBoundingClientRect();
211
+ this.svg_node.setAttribute('width', bounds.width.toString());
212
+ this.svg_node.setAttribute('height', bounds.height.toString());
213
+ this.size = {
214
+ width: bounds.width,
215
+ height: bounds.height,
216
+ };
217
+ }
218
+ /**
219
+ * initialize before render. this assumes that document layout/scroll
220
+ * won't change during the render pass, so we can cache some values.
221
+ */
222
+ Prerender() {
223
+ const bounds = this.svg_node.getBoundingClientRect();
224
+ this.bounds.top = bounds.top;
225
+ this.bounds.left = bounds.left;
226
+ this.bounds.right = bounds.right;
227
+ this.bounds.bottom = bounds.bottom;
228
+ }
229
+ /**
230
+ * render title. this method modifies "area" in place -- that's
231
+ * the style we want to use going forward.
232
+ *
233
+ * @param title
234
+ * @param area
235
+ * @param margin
236
+ * @param layout
237
+ */
238
+ RenderTitle(title, area, margin, layout) {
239
+ const text = SVGNode('text', {
240
+ class: 'chart-title',
241
+ x: Math.round(area.width / 2),
242
+ // style: 'text-anchor: middle',
243
+ }, title);
244
+ text.style.textAnchor = 'middle';
245
+ this.group.appendChild(text);
246
+ const bounds = text.getBoundingClientRect();
247
+ switch (layout) {
248
+ case 'bottom':
249
+ text.setAttribute('y', Math.round(area.bottom - bounds.height).toString());
250
+ area.bottom -= (bounds.height + margin);
251
+ break;
252
+ default:
253
+ text.setAttribute('y', Math.round(area.top + margin + bounds.height).toString());
254
+ area.top += (bounds.height + margin);
255
+ break;
256
+ }
257
+ }
258
+ /**
259
+ * measure a label, optionally with class name(s)
260
+ *
261
+ * this is silly. you are doing the measurement on a random node and
262
+ * trying to match classes, while you could just do the measurement on
263
+ * the actual node, get actual classes right, and not bother with junk
264
+ * nodes.
265
+ *
266
+ * FIXME: decprecate
267
+ *
268
+ */
269
+ MeasureText(label, classes, ceil = false) {
270
+ if (!this.text_measurement_node) {
271
+ this.text_measurement_node = SVGNode('text', { x: '-100px', y: '-100px' });
272
+ this.svg_node.appendChild(this.text_measurement_node);
273
+ }
274
+ if (typeof classes !== 'undefined') {
275
+ if (typeof classes === 'string') {
276
+ classes = [classes];
277
+ }
278
+ this.text_measurement_node.setAttribute('class', classes.join(' '));
279
+ }
280
+ else {
281
+ this.text_measurement_node.setAttribute('class', '');
282
+ }
283
+ this.text_measurement_node.textContent = label;
284
+ const bounds = this.text_measurement_node.getBoundingClientRect();
285
+ const metrics = {
286
+ width: bounds.width,
287
+ height: bounds.height,
288
+ // wtf is this?
289
+ y_offset: bounds.height - ((this.bounds.top - bounds.top) - 100),
290
+ };
291
+ if (ceil) {
292
+ metrics.width = Math.ceil(metrics.width);
293
+ metrics.height = Math.ceil(metrics.height);
294
+ metrics.y_offset = Math.ceil(metrics.y_offset);
295
+ }
296
+ return metrics;
297
+ }
298
+ RenderTicks(area, top, bottom, count, classes) {
299
+ const d = [];
300
+ const step = area.width / (count);
301
+ for (let i = 0; i < count; i++) {
302
+ const center = Math.round(area.left + step / 2 + step * i) - 0.5;
303
+ d.push(`M${center} ${top} L${center} ${bottom}`);
304
+ }
305
+ this.group.appendChild(SVGNode('path', { d, class: classes }));
306
+ }
307
+ /*
308
+ public GetAxisNode(): SVGElement {
309
+ if (!this.axis_group) {
310
+ this.axis_group = SVGNode('g', {class: 'axis-group'});
311
+ this.group.appendChild(this.axis_group);
312
+ }
313
+ return this.axis_group;
314
+ }
315
+ */
316
+ /** specialization for bar; it's different enough that we want special treatment */
317
+ RenderXAxisBar(area, offset, labels, metrics, classes) {
318
+ const count = labels.length;
319
+ if (!count)
320
+ return;
321
+ // FIXME: base on font, ' ' character
322
+ const label_buffer = 4;
323
+ const step = offset ? area.width / count : area.width / (count - 1);
324
+ const initial_offset = offset ? (step / 2) : 0;
325
+ // calculate increment (skip_count)
326
+ let increment = 1;
327
+ let repeat = true;
328
+ while (repeat) {
329
+ repeat = false;
330
+ let extent = 0;
331
+ for (let i = 0; i < count; i += increment) {
332
+ const center = Math.round(area.left + initial_offset + step * i);
333
+ const left = center - metrics[i].width / 2;
334
+ if (extent && (left <= extent)) {
335
+ increment++;
336
+ repeat = true;
337
+ break;
338
+ }
339
+ // FIXME: buffer? they get pretty tight sometimes
340
+ extent = center + (metrics[i].width / 2) + label_buffer;
341
+ }
342
+ }
343
+ // const axis = this.GetAxisNode();
344
+ for (let i = 0; i < count; i += increment) {
345
+ const x = Math.round(area.left + initial_offset + step * i);
346
+ // if (x + metrics[i].width / 2 >= area.right) { break; }
347
+ this.RenderText(this.axis_group, labels[i], 'center', { x, y: area.bottom }, classes);
348
+ }
349
+ }
350
+ RenderXAxisTicks(area, offset, count) {
351
+ const step = offset ? area.width / count : area.width / (count - 1);
352
+ const initial_offset = offset ? (step / 2) : 0;
353
+ const d = [];
354
+ for (let i = 0; i < count; i++) {
355
+ const center = Math.round(area.left + initial_offset + step * i) + .5;
356
+ d.push(`M${center},${area.bottom + .5} v${6}`);
357
+ }
358
+ this.axis_group.appendChild(SVGNode('path', { d: d.join(' '), class: 'x-axis-tick axis-tick' }));
359
+ }
360
+ /**
361
+ * render x axis labels; skips over labels to prevent overlap
362
+ *
363
+ * @param offset - move label by 1/2 step width, to center it under columns.
364
+ */
365
+ RenderXAxis(area, offset, labels, metrics, classes) {
366
+ const count = labels.length;
367
+ if (!count)
368
+ return;
369
+ // FIXME: base on font, ' ' character
370
+ const label_buffer = 4;
371
+ const step = offset ? area.width / count : area.width / (count - 1);
372
+ // const initial_offset = shift ? (step / 2) : 0;
373
+ const initial_offset = offset ? (step / 2) : 0;
374
+ // calculate increment (skip_count)
375
+ let increment = 1;
376
+ let repeat = true;
377
+ const f2 = (labels.length - 1) % 2 === 0;
378
+ const f3 = (labels.length - 1) % 3 === 0;
379
+ // const f5 = (labels.length - 1) % 5 === 0;
380
+ while (repeat) {
381
+ repeat = false;
382
+ let extent = 0;
383
+ for (let i = 0; i < count; i += increment) {
384
+ const center = Math.round(area.left + initial_offset + step * i);
385
+ const left = center - metrics[i].width / 2;
386
+ if (extent && (left <= extent)) {
387
+ increment++;
388
+ repeat = true;
389
+ break;
390
+ }
391
+ // FIXME: buffer? they get pretty tight sometimes
392
+ extent = center + (metrics[i].width / 2) + label_buffer;
393
+ }
394
+ }
395
+ // special patch for 0% - 100% range...
396
+ if (increment === 3 && !f3 && f2) {
397
+ increment++;
398
+ }
399
+ // const axis = this.GetAxisNode();
400
+ for (let i = 0; i < count; i += increment) {
401
+ const x = Math.round(area.left + initial_offset + step * i);
402
+ // if (x + metrics[i].width / 2 >= area.right) { break; }
403
+ this.RenderText(this.axis_group, labels[i], 'center', { x, y: area.bottom }, classes);
404
+ }
405
+ }
406
+ /** specialization for bar; it's different enough that we want special treatment */
407
+ RenderYAxisBar(area, left, labels, classes) {
408
+ labels = labels.slice(0);
409
+ labels.reverse();
410
+ const count = labels.length;
411
+ if (!count)
412
+ return;
413
+ const step = area.height / count;
414
+ // calculate increment (skip count)
415
+ let increment = 1;
416
+ let repeat = true;
417
+ while (repeat) {
418
+ repeat = false;
419
+ let extent = 0;
420
+ for (let i = 0; i < count; i += increment) {
421
+ const label = labels[i];
422
+ const y = Math.round(area.bottom - step * (i + .5) + label.metrics.height / 4);
423
+ if (extent && y >= extent) {
424
+ increment++;
425
+ repeat = true;
426
+ break;
427
+ }
428
+ extent = y - label.metrics.height;
429
+ }
430
+ }
431
+ // const axis = this.GetAxisNode();
432
+ for (let i = 0; i < count; i += increment) {
433
+ const label = labels[i];
434
+ const y = Math.round(area.bottom - step * (i + .5) + label.metrics.height / 4);
435
+ this.RenderText(this.axis_group, label.label, 'right', { x: left, y }, classes);
436
+ }
437
+ }
438
+ /**
439
+ * render y axis labels; skips over labels to prevent overlap
440
+ */
441
+ RenderYAxis(area, x, labels, classes, align = 'right') {
442
+ const count = labels.length;
443
+ if (!count)
444
+ return;
445
+ const step = area.height / (count - 1);
446
+ // calculate increment (skip count)
447
+ let increment = 1;
448
+ let repeat = true;
449
+ while (repeat) {
450
+ repeat = false;
451
+ let extent = 0;
452
+ for (let i = 0; i < count; i += increment) {
453
+ const label = labels[i];
454
+ const y = Math.round(area.bottom - step * i + label.metrics.height / 4);
455
+ if (extent && y >= extent) {
456
+ increment++;
457
+ repeat = true;
458
+ break;
459
+ }
460
+ extent = y - label.metrics.height;
461
+ }
462
+ }
463
+ // const axis = this.GetAxisNode();
464
+ for (let i = 0; i < count; i += increment) {
465
+ const label = labels[i];
466
+ const y = Math.round(area.bottom - step * i + label.metrics.height / 4);
467
+ this.RenderText(this.axis_group, label.label, align, { x, y }, classes);
468
+ }
469
+ }
470
+ /*
471
+ public ControlPoint(current: Point, previous?: Point, next?: Point, reverse = false): Point {
472
+
473
+ previous = previous || current;
474
+ next = next || current;
475
+
476
+ const o = this.LineProperties(previous, next);
477
+ const factor = Math.pow(1 - Math.abs(o.angle) / Math.PI, 2) * this.smoothing_factor;
478
+
479
+ const angle = o.angle + (reverse ? Math.PI : 0);
480
+ const length = o.length * factor;
481
+
482
+ const x = current.x + Math.cos(angle) * length;
483
+ const y = current.y + Math.sin(angle) * length;
484
+
485
+ return { x, y };
486
+
487
+ }
488
+ */
489
+ LineProperties(a, b) {
490
+ const x = b.x - a.x;
491
+ const y = b.y - a.y;
492
+ return {
493
+ length: Math.sqrt((x * x) + (y * y)),
494
+ angle: Math.atan2(y, x),
495
+ };
496
+ }
497
+ RenderSmoothLine(area, data, fill = false, titles, classes) {
498
+ const group = SVGNode('g');
499
+ const d1 = [];
500
+ const d2 = [];
501
+ const count = data.length;
502
+ const steps = count - 1;
503
+ const step = (area.width / count) / 2;
504
+ const circles = [];
505
+ const points = data.map((value, i) => {
506
+ if (typeof value === 'undefined') {
507
+ return undefined;
508
+ }
509
+ return {
510
+ x: Math.round(area.left + area.width / steps * i),
511
+ y: area.bottom - value,
512
+ };
513
+ });
514
+ ///
515
+ // we need to split into segments in the event of missing data
516
+ let segment = [];
517
+ const render_segment = () => {
518
+ if (segment.length < 2) {
519
+ return;
520
+ }
521
+ let line = '';
522
+ const first = segment[0];
523
+ const last = segment[segment.length - 1];
524
+ // note here we're not adding the leading M because for area,
525
+ // we want to use an L instead (or it won't be contiguous)
526
+ if (segment.length === 2) {
527
+ line = `${segment[0].x},${segment[0].y} L${segment[1].x},${segment[1].y}`;
528
+ }
529
+ else if (segment.length > 2) {
530
+ const curve = this.CatmullRomChain(segment);
531
+ line = '' + curve.map(point => `${point.x},${point.y}`).join(' L');
532
+ }
533
+ if (line) {
534
+ d1.push('M' + line);
535
+ if (fill) {
536
+ d2.push(`M ${first.x},${area.bottom} L ${first.x},${first.y}`);
537
+ d2.push('L' + line);
538
+ d2.push(`L ${last.x},${area.bottom}`);
539
+ }
540
+ }
541
+ };
542
+ for (const point of points) {
543
+ if (!point) {
544
+ render_segment();
545
+ segment = [];
546
+ }
547
+ else {
548
+ segment.push(point);
549
+ }
550
+ }
551
+ // render?
552
+ if (segment.length) {
553
+ render_segment();
554
+ }
555
+ ///
556
+ /*
557
+
558
+ for (let i = 0; i < points.length; i++) {
559
+
560
+ const point = points[i];
561
+
562
+ if (point) {
563
+ if (move) {
564
+ d1.push(`M ${[point.x]},${point.y}`);
565
+ if (fill) {
566
+ d2.push(`M ${point.x} ${area.bottom} L ${[point.x]},${point.y}`);
567
+ }
568
+ }
569
+ else {
570
+ const cp_start = this.ControlPoint(points[i - 1] as Point, points[i - 2], point);
571
+ const cp_end = this.ControlPoint(point, points[i - 1], points[i + 1], true);
572
+ d1.push(`C ${cp_start.x},${cp_start.y} ${cp_end.x},${cp_end.y} ${point.x},${point.y}`);
573
+ d2.push(`C ${cp_start.x},${cp_start.y} ${cp_end.x},${cp_end.y} ${point.x},${point.y}`);
574
+ }
575
+ move = false;
576
+ last_point = point;
577
+
578
+ }
579
+ else {
580
+ move = true;
581
+ if (fill && last_point) {
582
+ d2.push(`L ${last_point.x},${area.bottom} Z`);
583
+ }
584
+ last_point = undefined;
585
+ }
586
+
587
+ }
588
+
589
+ if (fill && last_point) {
590
+ d2.push(`L ${last_point.x},${area.bottom} Z`);
591
+ }
592
+
593
+ */
594
+ /*
595
+
596
+ for (; i < count; i++ ){
597
+ const point = data[i];
598
+ if (typeof point === 'undefined') {
599
+ move = true;
600
+ if (fill && (typeof last_x !== 'undefined')) {
601
+ d2.push(`L${last_x} ${area.bottom}Z`);
602
+ }
603
+ last_x = undefined;
604
+ continue;
605
+ }
606
+ const x = Math.round(area.left + area.width / steps * i);
607
+ if (move) {
608
+ if (fill) {
609
+ d2.push(`M${x} ${area.bottom} L${x} ${area.bottom - point}`);
610
+ }
611
+ d1.push(`M${x} ${area.bottom - point}`);
612
+ }
613
+ else {
614
+ d1.push(`L${x} ${area.bottom - point}`);
615
+ d2.push(`L${x} ${area.bottom - point}`);
616
+ }
617
+
618
+ circles.push({x, y: area.bottom - point, i});
619
+
620
+ last_x = x;
621
+ move = false;
622
+ }
623
+
624
+ */
625
+ /*
626
+ if (fill && (typeof last_x !== 'undefined')) {
627
+ d2.push(`L${last_x} ${area.bottom}Z`);
628
+ }
629
+ */
630
+ // fill first, underneath
631
+ if (fill) {
632
+ group.appendChild(SVGNode('path', { class: 'fill', d: d2 }));
633
+ }
634
+ // then line
635
+ group.appendChild(SVGNode('path', { class: 'line', d: d1 }));
636
+ if (typeof classes !== 'undefined') {
637
+ if (typeof classes === 'string') {
638
+ classes = [classes];
639
+ }
640
+ group.setAttribute('class', classes.join(' '));
641
+ }
642
+ this.group.appendChild(group);
643
+ // circles...
644
+ if (titles && circles.length) {
645
+ const circle_group = SVGNode('g');
646
+ for (const circle of circles) {
647
+ const shape = SVGNode('circle', { cx: circle.x, cy: circle.y, r: step });
648
+ shape.addEventListener('mouseenter', () => {
649
+ this.parent.setAttribute('title', titles[circle.i] || '');
650
+ });
651
+ shape.addEventListener('mouseleave', () => {
652
+ this.parent.setAttribute('title', '');
653
+ });
654
+ circle_group.appendChild(shape);
655
+ }
656
+ circle_group.setAttribute('class', 'mouse-layer');
657
+ this.group.appendChild(circle_group);
658
+ }
659
+ }
660
+ RenderLine(area, data, fill = false, titles, classes) {
661
+ const group = SVGNode('g');
662
+ const d1 = [];
663
+ const d2 = [];
664
+ const count = data.length;
665
+ const steps = count - 1;
666
+ const step = (area.width / count) / 2;
667
+ const circles = [];
668
+ let i = 0;
669
+ let move = true;
670
+ let last_x;
671
+ for (; i < count; i++) {
672
+ const point = data[i];
673
+ if (typeof point === 'undefined') {
674
+ move = true;
675
+ if (fill && (typeof last_x !== 'undefined')) {
676
+ d2.push(`L${last_x} ${area.bottom}Z`);
677
+ }
678
+ last_x = undefined;
679
+ continue;
680
+ }
681
+ const x = Math.round(/*step*/ +area.left + area.width / steps * i);
682
+ if (move) {
683
+ if (fill) {
684
+ d2.push(`M${x} ${area.bottom} L${x} ${area.bottom - point}`);
685
+ }
686
+ d1.push(`M${x} ${area.bottom - point}`);
687
+ }
688
+ else {
689
+ d1.push(`L${x} ${area.bottom - point}`);
690
+ d2.push(`L${x} ${area.bottom - point}`);
691
+ }
692
+ circles.push({ x, y: area.bottom - point, i });
693
+ last_x = x;
694
+ move = false;
695
+ }
696
+ if (fill && (typeof last_x !== 'undefined')) {
697
+ d2.push(`L${last_x} ${area.bottom}Z`);
698
+ }
699
+ // fill first, underneath
700
+ if (fill) {
701
+ group.appendChild(SVGNode('path', { class: 'fill', d: d2 }));
702
+ }
703
+ // then line
704
+ group.appendChild(SVGNode('path', { class: 'line', d: d1 }));
705
+ if (typeof classes !== 'undefined') {
706
+ if (typeof classes === 'string') {
707
+ classes = [classes];
708
+ }
709
+ group.setAttribute('class', classes.join(' '));
710
+ }
711
+ this.group.appendChild(group);
712
+ // circles...
713
+ if (titles && circles.length) {
714
+ const circle_group = SVGNode('g');
715
+ for (const circle of circles) {
716
+ const shape = SVGNode('circle', { cx: circle.x, cy: circle.y, r: step });
717
+ shape.addEventListener('mouseenter', () => {
718
+ this.parent.setAttribute('title', titles[circle.i] || '');
719
+ });
720
+ shape.addEventListener('mouseleave', () => {
721
+ this.parent.setAttribute('title', '');
722
+ });
723
+ circle_group.appendChild(shape);
724
+ }
725
+ circle_group.setAttribute('class', 'mouse-layer');
726
+ this.group.appendChild(circle_group);
727
+ }
728
+ }
729
+ /**
730
+ * the other RenderGrid function has semantics specifically for area/line.
731
+ * rather than try to shoehorn this in we'll use a different method.
732
+ */
733
+ RenderBarGrid(area, x_count, classes) {
734
+ const d = [];
735
+ const step = area.width / (x_count);
736
+ for (let i = 0; i <= x_count; i++) {
737
+ const x = Math.round(area.left + step * i) - 0.5;
738
+ d.push(`M${x} ${area.top} L${x} ${area.bottom}`);
739
+ }
740
+ this.group.appendChild(SVGNode('path', { d, class: classes }));
741
+ }
742
+ RenderGrid(area, y_count, x_count = 0, classes, zeros) {
743
+ const d = [];
744
+ const d2 = [];
745
+ let step = area.height / y_count;
746
+ for (let i = 0; i <= y_count; i++) {
747
+ const y = Math.round(area.top + step * i) - 0.5;
748
+ if (zeros && zeros[1] === i) {
749
+ d2.push(`M${area.left} ${y} L${area.right} ${y}`);
750
+ }
751
+ else {
752
+ d.push(`M${area.left} ${y} L${area.right} ${y}`);
753
+ }
754
+ }
755
+ step = area.width / (x_count - 1);
756
+ for (let i = 0; i < x_count; i++) {
757
+ const x = Math.round(area.left + step * i) - 0.5;
758
+ if (zeros && zeros[0] === i) {
759
+ d2.push(`M${x} ${area.top} L${x} ${area.bottom}`);
760
+ }
761
+ else {
762
+ d.push(`M${x} ${area.top} L${x} ${area.bottom}`);
763
+ }
764
+ }
765
+ this.group.appendChild(SVGNode('path', { d, class: classes }));
766
+ if (d2.length) {
767
+ if (classes) {
768
+ if (!Array.isArray(classes)) {
769
+ classes = classes + ' zero';
770
+ }
771
+ else {
772
+ classes.push('zero');
773
+ }
774
+ }
775
+ else {
776
+ classes = 'zero';
777
+ }
778
+ this.group.appendChild(SVGNode('path', { d: d2, class: classes }));
779
+ }
780
+ }
781
+ /* *
782
+ * return the intersection point of two lines (assuming
783
+ * infinite projection) or undefined if they are parallel
784
+ * /
785
+ public LineIntersection(a1: Point, a2: Point, b1: Point, b2: Point): Point|undefined {
786
+
787
+ const det = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x));
788
+
789
+ if (!det) {
790
+ return undefined; // parallel
791
+ }
792
+
793
+ const t = ((a1.x - b1.x) * (b1.y - b2.y) - (a1.y - b1.y) * (b1.x - b2.x)) / det;
794
+
795
+ return { x: a1.x + t * (a2.x - a1.x), y: a1.y + t * (a2.y - a1.y) };
796
+
797
+ }
798
+ */
799
+ MultiplyPoint(point, scalar) {
800
+ return {
801
+ x: point.x * scalar,
802
+ y: point.y * scalar,
803
+ };
804
+ }
805
+ AddPoints(a, b) {
806
+ return {
807
+ x: a.x + b.x,
808
+ y: a.y + b.y,
809
+ };
810
+ }
811
+ /**
812
+ * algo from
813
+ * https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline
814
+ */
815
+ CatmullRomSpline(P, n) {
816
+ // Parametric constant: 0.5 for the centripetal spline,
817
+ // 0.0 for the uniform spline, 1.0 for the chordal spline.
818
+ let alpha = .5;
819
+ // Premultiplied power constant for the following tj() function.
820
+ alpha = alpha / 2;
821
+ const tj = (ti, Pi, Pj) => {
822
+ const { x: xi, y: yi } = Pi;
823
+ const { x: xj, y: yj } = Pj;
824
+ return ((xj - xi) ** 2 + (yj - yi) ** 2) ** alpha + ti;
825
+ };
826
+ const t0 = 0;
827
+ const t1 = tj(t0, P[0], P[1]);
828
+ const t2 = tj(t1, P[1], P[2]);
829
+ const t3 = tj(t2, P[2], P[3]);
830
+ const step = (t2 - t1) / n;
831
+ const points = [];
832
+ for (let i = 0; i < n; i++) {
833
+ const t = t1 + step * i;
834
+ const A1 = this.AddPoints(this.MultiplyPoint(P[0], (t1 - t) / (t1 - t0)), this.MultiplyPoint(P[1], (t - t0) / (t1 - t0)));
835
+ const A2 = this.AddPoints(this.MultiplyPoint(P[1], (t2 - t) / (t2 - t1)), this.MultiplyPoint(P[2], (t - t1) / (t2 - t1)));
836
+ const A3 = this.AddPoints(this.MultiplyPoint(P[2], (t3 - t) / (t3 - t2)), this.MultiplyPoint(P[3], (t - t2) / (t3 - t2)));
837
+ const B1 = this.AddPoints(this.MultiplyPoint(A1, (t2 - t) / (t2 - t0)), this.MultiplyPoint(A2, (t - t0) / (t2 - t0)));
838
+ const B2 = this.AddPoints(this.MultiplyPoint(A2, (t3 - t) / (t3 - t1)), this.MultiplyPoint(A3, (t - t1) / (t3 - t1)));
839
+ const C = this.AddPoints(this.MultiplyPoint(B1, (t2 - t) / (t2 - t1)), this.MultiplyPoint(B2, (t - t1) / (t2 - t1)));
840
+ points.push(C);
841
+ }
842
+ return points;
843
+ }
844
+ /**
845
+ * NOTE: we are munging the point list here, so don't use it after
846
+ * calling this function or pass in a temp copy
847
+ *
848
+ * OK so that was rude, we will not munge the list
849
+ */
850
+ CatmullRomChain(original, n = 30) {
851
+ const points = original.slice(0);
852
+ const result = [];
853
+ const len = points.length;
854
+ if (len) {
855
+ // add two trailing points, extended linearly from existing segmnet
856
+ let dx = points[len - 1].x - points[len - 2].x;
857
+ let dy = points[len - 1].y - points[len - 2].y;
858
+ points.push({
859
+ x: points[len - 1].x + dx,
860
+ y: points[len - 1].y + dy,
861
+ });
862
+ points.push({
863
+ x: points[len - 1].x + dx,
864
+ y: points[len - 1].y + dy,
865
+ });
866
+ // some for the first point, in the other direction
867
+ dx = points[1].x - points[0].x;
868
+ dy = points[1].y - points[0].y;
869
+ points.unshift({
870
+ x: points[0].x - dx,
871
+ y: points[0].y - dy,
872
+ });
873
+ for (let i = 0; i < points.length - 4; i++) {
874
+ const subset = points.slice(i, i + 4);
875
+ const step = this.CatmullRomSpline(subset, n);
876
+ result.push(...step);
877
+ }
878
+ }
879
+ return result;
880
+ }
881
+ RenderDataLabels(area, x, y, x_scale, y_scale, data_labels, series_index) {
882
+ // const label_group = SVGNode('g');
883
+ // this.group.appendChild(label_group);
884
+ const count = Math.max(x.length, y.length);
885
+ const xrange = (x_scale.max - x_scale.min) || 1;
886
+ const yrange = (y_scale.max - y_scale.min) || 1;
887
+ for (let i = 0; i < count; i++) {
888
+ const a = x[i];
889
+ const b = y[i];
890
+ if (a !== undefined && b !== undefined) {
891
+ const point = {
892
+ x: area.left + ((a - x_scale.min) / xrange) * area.width,
893
+ y: area.bottom - ((b - y_scale.min) / yrange) * area.height,
894
+ };
895
+ const label = data_labels[i];
896
+ if (label) {
897
+ this.label_group.appendChild(SVGNode('circle', { class: 'label-target', cx: point.x, cy: point.y, r: 10 }));
898
+ const g = SVGNode('g', { class: 'data-label', transform: `translate(${point.x + 10},${point.y})` });
899
+ this.label_group.appendChild(g);
900
+ const circle = SVGNode('circle', {
901
+ cx: -10, y: 0, r: 5, class: `marker-highlight series-${series_index}`
902
+ });
903
+ g.appendChild(circle);
904
+ const text = SVGNode('text', { x: 4, y: 0 }, label);
905
+ g.appendChild(text);
906
+ const bounds = text.getBoundingClientRect();
907
+ const h = bounds.height;
908
+ const w = bounds.width + 8;
909
+ if (w + 15 + point.x >= area.right) {
910
+ g.setAttribute('transform', `translate(${point.x - w - 15},${point.y})`);
911
+ circle.setAttribute('cx', (w + 15).toString());
912
+ }
913
+ const rect = SVGNode('path', { d: `M0,5 h${w} v-${h} h-${w} Z` });
914
+ g.insertBefore(rect, text);
915
+ }
916
+ }
917
+ }
918
+ }
919
+ RenderBoxAndWhisker(area, data, index, max_n, scale, n, classes) {
920
+ const group = SVGNode('g', { class: classes });
921
+ this.group.appendChild(group);
922
+ // space for each box is 1/n of the chart area. we'll allocate margin on a box basis.
923
+ const width = area.width / n;
924
+ const margin = width * .20; // ??
925
+ const box_default_width = Math.min(width - 2 * margin, 90);
926
+ let box_width = box_default_width;
927
+ if (max_n > 0) {
928
+ box_width = box_width * Math.sqrt(data.n) / Math.sqrt(max_n);
929
+ }
930
+ const Y = (value) => area.top + area.height - (value - scale.min) / (scale.max - scale.min) * area.height; // wtf?
931
+ const center = area.left + index * width + width / 2;
932
+ const q1 = Y(data.quartiles[0]);
933
+ const q3 = Y(data.quartiles[2]);
934
+ group.appendChild(SVGNode('rect', {
935
+ class: `iqr`,
936
+ x: area.left + index * width + (width - box_width) / 2,
937
+ y: q3,
938
+ width: box_width,
939
+ height: q1 - q3,
940
+ }));
941
+ group.appendChild(SVGNode('line', {
942
+ class: `median`,
943
+ x1: center - box_width * .5,
944
+ x2: center + box_width * .5,
945
+ y1: Y(data.quartiles[1]),
946
+ y2: Y(data.quartiles[1]),
947
+ }));
948
+ group.appendChild(SVGNode('line', {
949
+ class: `whisker`,
950
+ x1: center - box_width * .25,
951
+ x2: center + box_width * .25,
952
+ y1: Y(data.whiskers[0]),
953
+ y2: Y(data.whiskers[0]),
954
+ }));
955
+ group.appendChild(SVGNode('line', {
956
+ class: `whisker-extent`,
957
+ x1: center,
958
+ x2: center,
959
+ y1: Y(data.whiskers[0]) - 2,
960
+ y2: Y(data.quartiles[0]) + 2,
961
+ }));
962
+ group.appendChild(SVGNode('line', {
963
+ class: `whisker`,
964
+ x1: center - box_width * .25,
965
+ x2: center + box_width * .25,
966
+ y1: Y(data.whiskers[1]),
967
+ y2: Y(data.whiskers[1]),
968
+ }));
969
+ group.appendChild(SVGNode('line', {
970
+ class: `whisker-extent`,
971
+ x1: center,
972
+ x2: center,
973
+ y1: Y(data.whiskers[1]) + 2,
974
+ y2: Y(data.quartiles[2]) - 2,
975
+ }));
976
+ for (const point of data.data) {
977
+ if (point < data.whiskers[0] || point > data.whiskers[1]) {
978
+ group.appendChild(SVGNode('circle', {
979
+ class: `outlier`,
980
+ cx: center,
981
+ cy: Y(point),
982
+ r: 3, // default; we can override in css
983
+ }));
984
+ }
985
+ }
986
+ }
987
+ RenderBubbleSeries(area, series, x_scale, y_scale, classes) {
988
+ const xrange = (x_scale.max - x_scale.min) || 1;
989
+ const yrange = (y_scale.max - y_scale.min) || 1;
990
+ // const marker_elements: string[] = [];
991
+ const points = [];
992
+ const labels = [];
993
+ // const d: string[] = [];
994
+ // const areas: string[] = [];
995
+ const group = SVGNode('g', { class: classes });
996
+ // if (title) node.setAttribute('title', title);
997
+ this.group.appendChild(group);
998
+ if (series.z) {
999
+ for (const [index, z] of series.z.data.entries()) {
1000
+ const x = series.x.data[index];
1001
+ const y = series.y.data[index];
1002
+ if (typeof x !== 'undefined' && typeof y !== 'undefined' && typeof z !== 'undefined' && z > 0) {
1003
+ const size_x = z / xrange * area.width;
1004
+ const size_y = z / yrange * area.height;
1005
+ const size = Math.max(size_x, size_y);
1006
+ const point = {
1007
+ x: area.left + ((x - x_scale.min) / xrange) * area.width,
1008
+ y: area.bottom - ((y - y_scale.min) / yrange) * area.height,
1009
+ z: size,
1010
+ };
1011
+ points.push(point);
1012
+ if (series.labels?.[index]) {
1013
+ const r = point.z / 2;
1014
+ labels.push({
1015
+ x: point.x, // + Math.cos(Math.PI/4) * r,
1016
+ y: point.y, // + Math.sin(Math.PI/4) * r,
1017
+ text: series.labels?.[index] || '',
1018
+ offset: Math.cos(Math.PI / 4) * r,
1019
+ });
1020
+ }
1021
+ }
1022
+ }
1023
+ }
1024
+ /*
1025
+
1026
+ let z_min = z[0] || 0;
1027
+ let z_max = z[0] || 0;
1028
+
1029
+ const map: Map<string, number> = new Map();
1030
+
1031
+ for (let i = 0; i < count; i++) {
1032
+
1033
+ const a = x[i];
1034
+ const b = y[i];
1035
+
1036
+ if (typeof a === 'undefined' || typeof b === 'undefined') {
1037
+ points.push(undefined);
1038
+ }
1039
+ else {
1040
+
1041
+ const series_key = c[i] || '';
1042
+ let series = map.get(series_key);
1043
+
1044
+ if (typeof series === 'undefined') {
1045
+
1046
+ series = map.size + 1;
1047
+
1048
+ map.set(series_key, series);
1049
+ }
1050
+
1051
+ let size = z[i] || 0;
1052
+ if (size) {
1053
+ const size_x = size / xrange * area.width;
1054
+ const size_y = size / yrange * area.height;
1055
+ size = Math.min(size_x, size_y);
1056
+ }
1057
+
1058
+ points.push({
1059
+ x: area.left + ((a - x_scale.min) / xrange) * area.width,
1060
+ y: area.bottom - ((b - y_scale.min) / yrange) * area.height,
1061
+ z: size,
1062
+ series,
1063
+ });
1064
+
1065
+ }
1066
+
1067
+ }
1068
+
1069
+ */
1070
+ for (const point of points) {
1071
+ if (point) {
1072
+ group.appendChild(SVGNode('circle', {
1073
+ cx: point.x,
1074
+ cy: point.y,
1075
+ r: point.z / 2,
1076
+ class: `point`,
1077
+ }));
1078
+ }
1079
+ }
1080
+ if (labels.length) {
1081
+ const container = this.label_group.getBoundingClientRect();
1082
+ for (const entry of labels) {
1083
+ if (entry.text) {
1084
+ const group = this.label_group.appendChild(SVGNode('g', {
1085
+ class: 'bubble-label',
1086
+ }));
1087
+ const rect = group.appendChild(SVGNode('rect', {
1088
+ x: entry.x, // + entry.offset,
1089
+ y: entry.y, // + entry.offset,
1090
+ // rx: `3px`,
1091
+ // fill: 'Canvas',
1092
+ // 'fill-opacity': '60%',
1093
+ // stroke: `none`,
1094
+ // 'style': `--translate-offset: ${Math.round(entry.offset)}px`,
1095
+ class: 'label-background'
1096
+ }));
1097
+ const label = group.appendChild(SVGNode('text', {
1098
+ x: entry.x, // + entry.offset,
1099
+ y: entry.y, // + entry.offset,
1100
+ offset: entry.offset,
1101
+ class: 'label-text',
1102
+ 'text-anchor': 'middle',
1103
+ 'alignment-baseline': 'middle',
1104
+ 'style': `--translate-offset: ${Math.round(entry.offset)}px`,
1105
+ }, entry.text));
1106
+ const bounds = label.getBoundingClientRect();
1107
+ rect.setAttribute('x', (bounds.left - container.left - 2).toString());
1108
+ rect.setAttribute('y', (bounds.top - container.top - 1).toString());
1109
+ rect.style.height = (bounds.height + 2) + `px`;
1110
+ rect.style.width = (bounds.width + 4) + `px`;
1111
+ }
1112
+ }
1113
+ }
1114
+ }
1115
+ RenderScatterSeries(area, x, y, x_scale, y_scale, lines = true, plot_points = false, filled = false, markers = false, smooth = false, classes) {
1116
+ // ...
1117
+ const count = Math.max(x.length, y.length);
1118
+ const xrange = (x_scale.max - x_scale.min) || 1;
1119
+ const yrange = (y_scale.max - y_scale.min) || 1;
1120
+ // const marker_elements: string[] = [];
1121
+ const points = [];
1122
+ const d = [];
1123
+ const areas = [];
1124
+ const group = SVGNode('g', { class: classes });
1125
+ // if (title) node.setAttribute('title', title);
1126
+ this.group.appendChild(group);
1127
+ for (let i = 0; i < count; i++) {
1128
+ const a = x[i];
1129
+ const b = y[i];
1130
+ if (typeof a === 'undefined' || typeof b === 'undefined') {
1131
+ points.push(undefined);
1132
+ }
1133
+ else {
1134
+ points.push({
1135
+ x: area.left + ((a - x_scale.min) / xrange) * area.width,
1136
+ y: area.bottom - ((b - y_scale.min) / yrange) * area.height,
1137
+ });
1138
+ }
1139
+ }
1140
+ // FIXME: merge loops, if possible
1141
+ /*
1142
+ if (markers) {
1143
+ for (const point of points) {
1144
+ if (point) {
1145
+
1146
+ // if we can't use CSS to update the path (except in chrome)
1147
+ // then it's probably not worth it... leave it for now
1148
+
1149
+ // marker_elements.push(`<path d='M0,-1.5 a1.5,1.5,0,1,1,0,3 a1.5,1.5,0,1,1,0,-3' transform='translate(${point.x},${point.y})' class='marker'/>`);
1150
+
1151
+ }
1152
+ }
1153
+ }
1154
+ */
1155
+ if (lines) {
1156
+ // we need to split into segments in the event of missing data
1157
+ let segment = [];
1158
+ const render_segment = smooth ? () => {
1159
+ // segments < 3 should be straight lines (or points)
1160
+ if (segment.length === 2) {
1161
+ return `${segment[0].x},${segment[0].y} L${segment[1].x},${segment[1].y}`;
1162
+ }
1163
+ else if (segment.length > 2) {
1164
+ const curve = this.CatmullRomChain(segment);
1165
+ return curve.map(point => `${point.x},${point.y}`).join(' L');
1166
+ }
1167
+ return '';
1168
+ } : () => {
1169
+ return segment.map(point => `${point.x},${point.y}`).join(' L');
1170
+ };
1171
+ for (const point of points) {
1172
+ if (!point) {
1173
+ if (segment.length >= 2) {
1174
+ const line = render_segment();
1175
+ d.push('M' + line);
1176
+ areas.push(`M${segment[0].x},${area.bottom}L` + line + `L${segment[segment.length - 1].x},${area.bottom}Z`);
1177
+ }
1178
+ segment = [];
1179
+ }
1180
+ else {
1181
+ segment.push(point);
1182
+ }
1183
+ }
1184
+ if (segment.length >= 2) {
1185
+ const line = render_segment();
1186
+ d.push('M' + line);
1187
+ areas.push(`M${segment[0].x},${area.bottom}L` + line + `L${segment[segment.length - 1].x},${area.bottom}Z`);
1188
+ }
1189
+ }
1190
+ if (filled) {
1191
+ group.appendChild(SVGNode('path', { d: areas, class: 'fill' }));
1192
+ }
1193
+ if (lines) {
1194
+ group.appendChild(SVGNode('path', { d, class: 'line' }));
1195
+ }
1196
+ if (plot_points) {
1197
+ for (const point of points) {
1198
+ if (point) {
1199
+ group.appendChild(SVGNode('circle', { cx: point.x, cy: point.y, r: 1, class: 'point' }));
1200
+ // if we can't use CSS to update the path (except in chrome)
1201
+ // then it's probably not worth it... leave it for now
1202
+ // marker_elements.push(`<path d='M0,-1.5 a1.5,1.5,0,1,1,0,3 a1.5,1.5,0,1,1,0,-3' transform='translate(${point.x},${point.y})' class='marker'/>`);
1203
+ }
1204
+ }
1205
+ }
1206
+ if (markers) {
1207
+ for (const point of points) {
1208
+ if (point) {
1209
+ group.appendChild(SVGNode('circle', { cx: point.x, cy: point.y, r: 3, class: 'marker' }));
1210
+ // if we can't use CSS to update the path (except in chrome)
1211
+ // then it's probably not worth it... leave it for now
1212
+ // marker_elements.push(`<path d='M0,-1.5 a1.5,1.5,0,1,1,0,3 a1.5,1.5,0,1,1,0,-3' transform='translate(${point.x},${point.y})' class='marker'/>`);
1213
+ }
1214
+ }
1215
+ }
1216
+ // SetSVG(group, `<path d='${d.join(' ')}' class='line' />${marker_elements.join('')}`);
1217
+ }
1218
+ RenderPoints(area, x, y, classes) {
1219
+ const d = [];
1220
+ for (let i = 0; i < x.length; i++) {
1221
+ const px = x[i] * area.width + area.left;
1222
+ const py = area.bottom - y[i] * area.height;
1223
+ d.push(`M${px - 1},${py - 1} L${px + 1},${py + 1}`);
1224
+ d.push(`M${px - 1},${py + 1} L${px + 1},${py - 1}`);
1225
+ }
1226
+ this.group.appendChild(SVGNode('path', { d, class: classes }));
1227
+ }
1228
+ RenderPoint(cx, cy, classes) {
1229
+ this.group.appendChild(SVGNode('circle', { cx, cy, r: 1, class: classes }));
1230
+ }
1231
+ RenderCalloutLines(lines) {
1232
+ const g = SVGNode('g', { class: 'callouts' });
1233
+ this.label_group.appendChild(g);
1234
+ for (const line of lines) {
1235
+ g.appendChild(SVGNode('path', {
1236
+ d: `M${line.x1},${line.y1} L${line.x2},${line.y2}`,
1237
+ class: 'callout ' + (line.classes || '').trim(),
1238
+ }));
1239
+ }
1240
+ }
1241
+ RenderRectangle(area, corner_radius, classes, title, label, label_point) {
1242
+ let d = '';
1243
+ if (corner_radius) {
1244
+ // two cases we have to worry about: top L/R corner radius > height,
1245
+ // and top/bottom L radius > width
1246
+ if (corner_radius[0] &&
1247
+ corner_radius[0] === corner_radius[1] &&
1248
+ corner_radius[0] >= area.height) {
1249
+ const c = corner_radius[0];
1250
+ const b = corner_radius[0] - area.height;
1251
+ const a = Math.sqrt(c * c - b * b);
1252
+ d = `M${area.left + area.width / 2 - a},${area.bottom} a${c},${c} 0 0 1 ${a * 2},0 z`;
1253
+ }
1254
+ else if (corner_radius[1] &&
1255
+ corner_radius[1] === corner_radius[2] &&
1256
+ corner_radius[1] >= area.width) {
1257
+ const c = corner_radius[1];
1258
+ const b = corner_radius[1] - area.width;
1259
+ const a = Math.sqrt(c * c - b * b);
1260
+ d = `M${area.left},${area.top + area.height / 2 - a} a${c},${c} 0 0 1 0,${a * 2} z`;
1261
+ }
1262
+ else {
1263
+ d = `M${area.left},${area.top + corner_radius[0]} `
1264
+ + `a${corner_radius[0]},${corner_radius[0]} 0 0 1 ${corner_radius[0]},${-corner_radius[0]} `
1265
+ + `h${area.width - corner_radius[0] - corner_radius[1]} `
1266
+ + `a${corner_radius[1]},${corner_radius[1]} 0 0 1 ${corner_radius[1]},${corner_radius[1]} `
1267
+ + `v${area.height - corner_radius[1] - corner_radius[2]} `
1268
+ + `a${corner_radius[2]},${corner_radius[2]} 0 0 1 ${-corner_radius[2]},${corner_radius[2]} `
1269
+ + `h${-area.width + corner_radius[2] + corner_radius[3]} `
1270
+ + `a${corner_radius[3]},${corner_radius[3]} 0 0 1 ${-corner_radius[3]},${-corner_radius[3]} `
1271
+ + `v${-area.height + corner_radius[3] + corner_radius[0]} `;
1272
+ }
1273
+ }
1274
+ else {
1275
+ /*
1276
+ node = SVGNode('rect', {
1277
+ x: area.left,
1278
+ y: area.top,
1279
+ width: area.width,
1280
+ height: area.height,
1281
+ class: classes });
1282
+ */
1283
+ d = `M${area.left},${area.top} `
1284
+ + `h${area.width} `
1285
+ + `v${area.height} `
1286
+ + `h${-area.width} `
1287
+ + `v${-area.height} `;
1288
+ }
1289
+ const node = SVGNode('path', {
1290
+ d, class: classes,
1291
+ });
1292
+ if (title) {
1293
+ node.addEventListener('mouseenter', () => {
1294
+ this.parent.setAttribute('title', title);
1295
+ });
1296
+ node.addEventListener('mouseleave', () => {
1297
+ this.parent.setAttribute('title', '');
1298
+ });
1299
+ }
1300
+ this.group.appendChild(node);
1301
+ if (label) {
1302
+ this.label_group.appendChild(SVGNode('path', { class: 'label-target', d }));
1303
+ const point = label_point || {
1304
+ x: Math.round(area.left + area.width / 2),
1305
+ y: Math.round(area.top - 10),
1306
+ };
1307
+ const g = SVGNode('g', { class: 'data-label', transform: `translate(${point.x},${point.y})` });
1308
+ this.label_group.appendChild(g);
1309
+ const text = SVGNode('text', { x: 0, y: 0 }, label);
1310
+ g.appendChild(text);
1311
+ const bounds = text.getBoundingClientRect();
1312
+ const h = bounds.height;
1313
+ const w = bounds.width + 8;
1314
+ if (point.y - bounds.height < 4) {
1315
+ point.y -= (point.y - bounds.height - 4);
1316
+ g.setAttribute('transform', `translate(${point.x},${point.y})`);
1317
+ }
1318
+ text.setAttribute('x', Math.floor(-bounds.width / 2).toString());
1319
+ /*
1320
+ if (w + 15 + point.x >= area.right) {
1321
+ g.setAttribute('transform', `translate(${point.x - w - 15},${point.y})`)
1322
+ // circle.setAttribute('cx', (w + 15).toString());
1323
+ }
1324
+ */
1325
+ const vertical_padding = Math.ceil(h * .125);
1326
+ // const rect = SVGNode('path', {d:`M${-w/2},${vertical_padding} h${w} v-${h + vertical_padding / 2} h-${w} Z`});
1327
+ const rect = SVGNode('rect', { rx: 3, x: -w / 2, y: Math.round(-h + vertical_padding * 2 / 3), width: w, height: h + vertical_padding });
1328
+ g.insertBefore(rect, text);
1329
+ }
1330
+ }
1331
+ /**
1332
+ * render text at point
1333
+ */
1334
+ RenderText(target, text, align, point, classes) {
1335
+ const node = SVGNode('text', { x: point.x, y: point.y, class: classes }, text);
1336
+ switch (align) {
1337
+ case 'right':
1338
+ node.style.textAnchor = 'end';
1339
+ break;
1340
+ case 'center':
1341
+ node.style.textAnchor = 'middle';
1342
+ break;
1343
+ default:
1344
+ node.style.textAnchor = 'start';
1345
+ break;
1346
+ }
1347
+ (target || this.group).appendChild(node);
1348
+ }
1349
+ /**
1350
+ * render a donut, given a list of slices (as %)
1351
+ * @param values
1352
+ */
1353
+ RenderDonut(slices, center, outer_radius, inner_radius, bounds_area, callouts, classes) {
1354
+ let start_angle = -Math.PI / 2; // start at 12:00
1355
+ let end_angle = 0;
1356
+ if (callouts) {
1357
+ outer_radius *= .8;
1358
+ inner_radius *= .7;
1359
+ }
1360
+ const PointOnCircle = (center, radius, angle) => {
1361
+ return [
1362
+ Math.cos(angle) * radius + center.x,
1363
+ Math.sin(angle) * radius + center.y,
1364
+ ];
1365
+ };
1366
+ for (const slice of slices) {
1367
+ const title = slice.title || '';
1368
+ const value = slice.percent;
1369
+ const index = slice.index;
1370
+ let d = [];
1371
+ let half_angle = 0;
1372
+ const outer = PointOnCircle.bind(0, center, outer_radius);
1373
+ const inner = PointOnCircle.bind(0, center, inner_radius);
1374
+ if (value > 0.5) {
1375
+ // split into two segments
1376
+ half_angle = start_angle + (value / 2) * Math.PI * 2;
1377
+ end_angle = start_angle + value * Math.PI * 2;
1378
+ const delta1 = half_angle - start_angle;
1379
+ const delta2 = end_angle - half_angle;
1380
+ d.push(`M${outer(start_angle)}`, `A${outer_radius},${outer_radius},${delta1},0,1,${outer(half_angle)}`, `A${outer_radius},${outer_radius},${delta2},0,1,${outer(end_angle)}`, `L${inner(end_angle)}`, `A${inner_radius},${inner_radius},${delta2},0,0,${inner(half_angle)}`, `A${inner_radius},${inner_radius},${delta1},0,0,${inner(start_angle)}`, 'Z');
1381
+ }
1382
+ else {
1383
+ end_angle = start_angle + value * Math.PI * 2;
1384
+ half_angle = (end_angle - start_angle) / 2 + start_angle;
1385
+ const delta = end_angle - start_angle;
1386
+ d.push(`M${outer(start_angle)}`, `A${outer_radius},${outer_radius},${delta},0,1,${outer(end_angle)}`, `L${inner(end_angle)}`, `A${inner_radius},${inner_radius},${delta},0,0,${inner(start_angle)}`, 'Z');
1387
+ }
1388
+ const node = SVGNode('path', {
1389
+ d, class: (typeof index === 'undefined' ? undefined : `series-${index}`)
1390
+ });
1391
+ if (typeof index !== 'undefined') {
1392
+ node.setAttribute('data-index', index.toString());
1393
+ }
1394
+ /*
1395
+ if (title) {
1396
+ node.addEventListener('mouseenter', (event) => {
1397
+ this.parent.setAttribute('title', title);
1398
+ });
1399
+ node.addEventListener('mouseleave', (event) => {
1400
+ this.parent.setAttribute('title', '');
1401
+ });
1402
+ }
1403
+ */
1404
+ // we're creating a containing group so that we can nth-child the slices,
1405
+ // otherwise they'll be in the same group as the title
1406
+ const donut = SVGNode('g', { class: classes });
1407
+ donut.appendChild(node);
1408
+ this.group.appendChild(donut);
1409
+ if ( /*callouts &&*/value >= .05 && title) {
1410
+ const length = outer_radius - inner_radius;
1411
+ d = [];
1412
+ const anchor = PointOnCircle(center, inner_radius + (outer_radius - inner_radius) / 2 + length, half_angle);
1413
+ d.push(`M${PointOnCircle(center, inner_radius + (outer_radius - inner_radius) / 2, half_angle)}`);
1414
+ d.push(`L${anchor}`);
1415
+ donut.appendChild(SVGNode('path', { d, class: 'callout' }));
1416
+ const text_parts = [];
1417
+ const callout_label = SVGNode('text', { class: 'callout-label' });
1418
+ donut.appendChild(callout_label);
1419
+ const corrected = half_angle + Math.PI / 2;
1420
+ const text = title;
1421
+ callout_label.textContent = text;
1422
+ let bounds = callout_label.getBoundingClientRect();
1423
+ const metrics = {
1424
+ width: bounds.width,
1425
+ height: bounds.height,
1426
+ };
1427
+ // const metrics = this.MeasureText(text, ['donut', 'callout-label']);
1428
+ let [x, y] = anchor;
1429
+ x += metrics.height / 2 * Math.cos(half_angle);
1430
+ y += metrics.height / 4 + metrics.height / 2 * Math.sin(half_angle);
1431
+ let try_break = false;
1432
+ if (corrected > Math.PI) {
1433
+ if (x - metrics.width <= bounds_area.left) {
1434
+ try_break = true;
1435
+ }
1436
+ }
1437
+ else {
1438
+ if (x + metrics.width > bounds_area.right) {
1439
+ try_break = true;
1440
+ }
1441
+ }
1442
+ // this breaks numbers, bad!
1443
+ // const break_regex = /[\s-\W]/;
1444
+ const break_regex = /[\s-]/;
1445
+ if (try_break && break_regex.test(text)) {
1446
+ let break_index = -1;
1447
+ let break_value = 1;
1448
+ // const indices: number[] = [];
1449
+ for (let i = 0; i < text.length; i++) {
1450
+ if (break_regex.test(text[i])) {
1451
+ const index_value = Math.abs(0.5 - (i / text.length));
1452
+ if (index_value < break_value) {
1453
+ break_value = index_value;
1454
+ break_index = i;
1455
+ }
1456
+ }
1457
+ }
1458
+ if (break_index > 0) {
1459
+ text_parts.push(text.substr(0, break_index + 1).trim());
1460
+ text_parts.push(text.substr(break_index + 1).trim());
1461
+ }
1462
+ }
1463
+ else {
1464
+ // ... ellipsis?
1465
+ }
1466
+ /*
1467
+ if (y <= bounds_area.top) {
1468
+ console.info("break top", title, y);
1469
+ }
1470
+ if (y >= bounds_area.bottom) {
1471
+ console.info("break bottom", title, y);
1472
+ }
1473
+ */
1474
+ if (text_parts.length) {
1475
+ let dy = 0;
1476
+ let widest = 0;
1477
+ const parts = text_parts.map((part) => {
1478
+ callout_label.textContent = part;
1479
+ bounds = callout_label.getBoundingClientRect();
1480
+ const m = {
1481
+ width: bounds.width,
1482
+ height: bounds.height,
1483
+ };
1484
+ //const m = this.MeasureText(part, ['donut', 'callout-label']);
1485
+ widest = Math.max(widest, m.width);
1486
+ return { text: part, metrics: m };
1487
+ });
1488
+ // console.info('p', parts);
1489
+ callout_label.textContent = '';
1490
+ for (const part of parts) {
1491
+ const tspan = SVGNode('tspan');
1492
+ tspan.textContent = part.text;
1493
+ const part_x = (corrected > Math.PI) ?
1494
+ (x - (widest - part.metrics.width) / 2) :
1495
+ (x + (widest - part.metrics.width) / 2);
1496
+ tspan.setAttribute('x', part_x.toString());
1497
+ tspan.setAttribute('dy', dy.toString());
1498
+ callout_label.appendChild(tspan);
1499
+ dy = part.metrics.height;
1500
+ }
1501
+ }
1502
+ else {
1503
+ // already in from measurement
1504
+ // callout_label.textContent = title;
1505
+ }
1506
+ const text_anchor = corrected > Math.PI ? 'end' : 'start';
1507
+ callout_label.setAttribute('text-anchor', text_anchor);
1508
+ callout_label.setAttribute('x', x.toString());
1509
+ callout_label.setAttribute('y', y.toString());
1510
+ if (typeof index !== 'undefined') {
1511
+ callout_label.setAttribute('data-index', index.toString());
1512
+ }
1513
+ }
1514
+ start_angle = end_angle;
1515
+ }
1516
+ }
1517
+ }
1518
+ //# sourceMappingURL=renderer.js.map