@graphty/graph-io 0.3.43 → 0.3.44

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 (674) hide show
  1. package/dist/chunks/approximations-3Q4TsdTh.js +40 -0
  2. package/dist/chunks/approximations-3Q4TsdTh.js.map +1 -0
  3. package/dist/chunks/block-JQiwViEt.js +2777 -0
  4. package/dist/chunks/block-JQiwViEt.js.map +1 -0
  5. package/dist/chunks/{choose-DvpbdRuz.js → choose-BemygfH8.js} +3 -2
  6. package/dist/chunks/{choose-DvpbdRuz.js.map → choose-BemygfH8.js.map} +1 -1
  7. package/dist/chunks/{constants-fT3E8JYY.js → constants-BFUR_CtU.js} +6 -2
  8. package/dist/chunks/constants-BFUR_CtU.js.map +1 -0
  9. package/dist/chunks/convert-BcQ8_jMo.js +59 -0
  10. package/dist/chunks/convert-BcQ8_jMo.js.map +1 -0
  11. package/dist/chunks/emit-DCwFLts5.js +3456 -0
  12. package/dist/chunks/emit-DCwFLts5.js.map +1 -0
  13. package/dist/chunks/{escape-B62ZGM_a.js → escape-B1rMftd9.js} +43 -20
  14. package/dist/chunks/escape-B1rMftd9.js.map +1 -0
  15. package/dist/chunks/{export-rKLeGQMo.js → export-D7IGzTG4.js} +196 -792
  16. package/dist/chunks/export-D7IGzTG4.js.map +1 -0
  17. package/dist/chunks/{exporter-BWwEuOPT.js → exporter-BtnqYuuj.js} +22 -12
  18. package/dist/chunks/exporter-BtnqYuuj.js.map +1 -0
  19. package/dist/chunks/{exporter-yLxKBvLE.js → exporter-ChPyhBOv.js} +4 -3
  20. package/dist/chunks/{exporter-yLxKBvLE.js.map → exporter-ChPyhBOv.js.map} +1 -1
  21. package/dist/chunks/{exporter-8hLNemFk.js → exporter-KEPxYUXD.js} +133 -74
  22. package/dist/chunks/exporter-KEPxYUXD.js.map +1 -0
  23. package/dist/chunks/{exporter-RVtXVetw.js → exporter-quPUCBiC.js} +1805 -287
  24. package/dist/chunks/exporter-quPUCBiC.js.map +1 -0
  25. package/dist/chunks/format-GDmO6m6Q.js +676 -0
  26. package/dist/chunks/format-GDmO6m6Q.js.map +1 -0
  27. package/dist/chunks/generic-BL06jrJf.js +1347 -0
  28. package/dist/chunks/generic-BL06jrJf.js.map +1 -0
  29. package/dist/chunks/{importer-WrGRZb0N.js → importer-B7r2owVf.js} +449 -102
  30. package/dist/chunks/importer-B7r2owVf.js.map +1 -0
  31. package/dist/chunks/{importer-Ht7LNg8L.js → importer-BFmOP96W.js} +116 -296
  32. package/dist/chunks/importer-BFmOP96W.js.map +1 -0
  33. package/dist/chunks/{importer-BbNb7eNg.js → importer-C2nzGhdV.js} +1390 -129
  34. package/dist/chunks/importer-C2nzGhdV.js.map +1 -0
  35. package/dist/chunks/{importer-Dz7tI8Sa.js → importer-CbX2_rNF.js} +617 -115
  36. package/dist/chunks/importer-CbX2_rNF.js.map +1 -0
  37. package/dist/chunks/{importer-D794XUTe.js → importer-CdzeN6X4.js} +31 -8
  38. package/dist/chunks/importer-CdzeN6X4.js.map +1 -0
  39. package/dist/chunks/{importer-xZ-3suY1.js → importer-ChKNC_GN.js} +791 -135
  40. package/dist/chunks/importer-ChKNC_GN.js.map +1 -0
  41. package/dist/chunks/{importer-C98dpSH4.js → importer-dwoYpSqb.js} +141 -82
  42. package/dist/chunks/importer-dwoYpSqb.js.map +1 -0
  43. package/dist/chunks/index-B9fdNpTA.js +4310 -0
  44. package/dist/chunks/index-B9fdNpTA.js.map +1 -0
  45. package/dist/chunks/{json-elements-DKSqpNW1.js → json-elements-C4FwgnH5.js} +123 -32
  46. package/dist/chunks/json-elements-C4FwgnH5.js.map +1 -0
  47. package/dist/chunks/lexicon-B-knUi-l.js +508 -0
  48. package/dist/chunks/lexicon-B-knUi-l.js.map +1 -0
  49. package/dist/chunks/{ontology-export-CtRXjg_i.js → ontology-export-BSpbY5FL.js} +3 -2
  50. package/dist/chunks/{ontology-export-CtRXjg_i.js.map → ontology-export-BSpbY5FL.js.map} +1 -1
  51. package/dist/chunks/{records-Cu4bxYOm.js → records-Nwz_rbNt.js} +2 -2
  52. package/dist/chunks/{records-Cu4bxYOm.js.map → records-Nwz_rbNt.js.map} +1 -1
  53. package/dist/chunks/styles-Brt9JMfs.js +690 -0
  54. package/dist/chunks/styles-Brt9JMfs.js.map +1 -0
  55. package/dist/chunks/styles-_N-C8S-U.js +2102 -0
  56. package/dist/chunks/styles-_N-C8S-U.js.map +1 -0
  57. package/dist/chunks/styles-yed-DAfcIkf7.js +842 -0
  58. package/dist/chunks/styles-yed-DAfcIkf7.js.map +1 -0
  59. package/dist/chunks/{text-DHOJXnED.js → text-CgzMqHQb.js} +2 -2
  60. package/dist/chunks/{text-DHOJXnED.js.map → text-CgzMqHQb.js.map} +1 -1
  61. package/dist/chunks/units-XMZFIFe1.js +19 -0
  62. package/dist/chunks/units-XMZFIFe1.js.map +1 -0
  63. package/dist/csv.js +5 -4
  64. package/dist/csv.js.map +1 -1
  65. package/dist/cx.js +1 -1
  66. package/dist/cx2.js +2 -2
  67. package/dist/cys.js +1 -1
  68. package/dist/dot.js +5 -2
  69. package/dist/dot.js.map +1 -1
  70. package/dist/gexf.js +292 -412
  71. package/dist/gexf.js.map +1 -1
  72. package/dist/gml.js +8 -3031
  73. package/dist/gml.js.map +1 -1
  74. package/dist/graph-io.js +354 -240
  75. package/dist/graph-io.js.map +1 -1
  76. package/dist/graphml.js +4 -2
  77. package/dist/graphml.js.map +1 -1
  78. package/dist/json.js +1 -1
  79. package/dist/neo4j.js +4 -3
  80. package/dist/neo4j.js.map +1 -1
  81. package/dist/obo.js +1 -1
  82. package/dist/pajek.js +4 -2
  83. package/dist/positions.d.ts +1 -0
  84. package/dist/positions.js +427 -0
  85. package/dist/positions.js.map +1 -0
  86. package/dist/src/common/codes.d.ts +6 -0
  87. package/dist/src/common/codes.d.ts.map +1 -1
  88. package/dist/src/common/codes.js +6 -0
  89. package/dist/src/common/codes.js.map +1 -1
  90. package/dist/src/common/escape.d.ts.map +1 -1
  91. package/dist/src/common/escape.js +4 -2
  92. package/dist/src/common/escape.js.map +1 -1
  93. package/dist/src/common/export.d.ts +10 -0
  94. package/dist/src/common/export.d.ts.map +1 -1
  95. package/dist/src/common/export.js +123 -100
  96. package/dist/src/common/export.js.map +1 -1
  97. package/dist/src/common/input.d.ts.map +1 -1
  98. package/dist/src/common/input.js +8 -1
  99. package/dist/src/common/input.js.map +1 -1
  100. package/dist/src/common/json-elements.d.ts +12 -9
  101. package/dist/src/common/json-elements.d.ts.map +1 -1
  102. package/dist/src/common/json-elements.js +160 -37
  103. package/dist/src/common/json-elements.js.map +1 -1
  104. package/dist/src/common/notes.d.ts +42 -0
  105. package/dist/src/common/notes.d.ts.map +1 -0
  106. package/dist/src/common/notes.js +72 -0
  107. package/dist/src/common/notes.js.map +1 -0
  108. package/dist/src/common/report.d.ts +18 -0
  109. package/dist/src/common/report.d.ts.map +1 -1
  110. package/dist/src/common/report.js +36 -1
  111. package/dist/src/common/report.js.map +1 -1
  112. package/dist/src/common/units.d.ts +24 -0
  113. package/dist/src/common/units.d.ts.map +1 -0
  114. package/dist/src/common/units.js +26 -0
  115. package/dist/src/common/units.js.map +1 -0
  116. package/dist/src/common/xml.d.ts +18 -0
  117. package/dist/src/common/xml.d.ts.map +1 -1
  118. package/dist/src/common/xml.js +42 -5
  119. package/dist/src/common/xml.js.map +1 -1
  120. package/dist/src/formats/cx/exporter.d.ts +6 -0
  121. package/dist/src/formats/cx/exporter.d.ts.map +1 -1
  122. package/dist/src/formats/cx/exporter.js +71 -8
  123. package/dist/src/formats/cx/exporter.js.map +1 -1
  124. package/dist/src/formats/cx/importer.d.ts +5 -2
  125. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  126. package/dist/src/formats/cx/importer.js +102 -72
  127. package/dist/src/formats/cx/importer.js.map +1 -1
  128. package/dist/src/formats/cx/styles.d.ts +102 -0
  129. package/dist/src/formats/cx/styles.d.ts.map +1 -0
  130. package/dist/src/formats/cx/styles.js +225 -0
  131. package/dist/src/formats/cx/styles.js.map +1 -0
  132. package/dist/src/formats/cx2/exporter.d.ts +5 -0
  133. package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
  134. package/dist/src/formats/cx2/exporter.js +181 -71
  135. package/dist/src/formats/cx2/exporter.js.map +1 -1
  136. package/dist/src/formats/cx2/importer.d.ts +4 -1
  137. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  138. package/dist/src/formats/cx2/importer.js +19 -1
  139. package/dist/src/formats/cx2/importer.js.map +1 -1
  140. package/dist/src/formats/cx2/styles.d.ts +18 -0
  141. package/dist/src/formats/cx2/styles.d.ts.map +1 -0
  142. package/dist/src/formats/cx2/styles.js +24 -0
  143. package/dist/src/formats/cx2/styles.js.map +1 -0
  144. package/dist/src/formats/cys/constants.d.ts +2 -0
  145. package/dist/src/formats/cys/constants.d.ts.map +1 -1
  146. package/dist/src/formats/cys/constants.js +2 -0
  147. package/dist/src/formats/cys/constants.js.map +1 -1
  148. package/dist/src/formats/cys/exporter.d.ts +6 -2
  149. package/dist/src/formats/cys/exporter.d.ts.map +1 -1
  150. package/dist/src/formats/cys/exporter.js +215 -53
  151. package/dist/src/formats/cys/exporter.js.map +1 -1
  152. package/dist/src/formats/cys/importer.d.ts +2 -1
  153. package/dist/src/formats/cys/importer.d.ts.map +1 -1
  154. package/dist/src/formats/cys/importer.js +119 -6
  155. package/dist/src/formats/cys/importer.js.map +1 -1
  156. package/dist/src/formats/cys/session.d.ts +2 -0
  157. package/dist/src/formats/cys/session.d.ts.map +1 -1
  158. package/dist/src/formats/cys/session.js +5 -0
  159. package/dist/src/formats/cys/session.js.map +1 -1
  160. package/dist/src/formats/cys/styles.d.ts +38 -0
  161. package/dist/src/formats/cys/styles.d.ts.map +1 -0
  162. package/dist/src/formats/cys/styles.js +213 -0
  163. package/dist/src/formats/cys/styles.js.map +1 -0
  164. package/dist/src/formats/cys/tables.d.ts +8 -3
  165. package/dist/src/formats/cys/tables.d.ts.map +1 -1
  166. package/dist/src/formats/cys/tables.js +88 -34
  167. package/dist/src/formats/cys/tables.js.map +1 -1
  168. package/dist/src/formats/dot/exporter.d.ts +2 -0
  169. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  170. package/dist/src/formats/dot/exporter.js +195 -81
  171. package/dist/src/formats/dot/exporter.js.map +1 -1
  172. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  173. package/dist/src/formats/dot/importer.js +100 -20
  174. package/dist/src/formats/dot/importer.js.map +1 -1
  175. package/dist/src/formats/dot/index.d.ts +1 -0
  176. package/dist/src/formats/dot/index.d.ts.map +1 -1
  177. package/dist/src/formats/dot/index.js +1 -0
  178. package/dist/src/formats/dot/index.js.map +1 -1
  179. package/dist/src/formats/dot/positions.d.ts +14 -0
  180. package/dist/src/formats/dot/positions.d.ts.map +1 -0
  181. package/dist/src/formats/dot/positions.js +70 -0
  182. package/dist/src/formats/dot/positions.js.map +1 -0
  183. package/dist/src/formats/dot/styles.d.ts +82 -0
  184. package/dist/src/formats/dot/styles.d.ts.map +1 -0
  185. package/dist/src/formats/dot/styles.js +1484 -0
  186. package/dist/src/formats/dot/styles.js.map +1 -0
  187. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  188. package/dist/src/formats/gexf/exporter.js +20 -5
  189. package/dist/src/formats/gexf/exporter.js.map +1 -1
  190. package/dist/src/formats/gexf/importer.d.ts +8 -1
  191. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  192. package/dist/src/formats/gexf/importer.js +251 -131
  193. package/dist/src/formats/gexf/importer.js.map +1 -1
  194. package/dist/src/formats/gexf/index.d.ts +8 -0
  195. package/dist/src/formats/gexf/index.d.ts.map +1 -1
  196. package/dist/src/formats/gexf/index.js +10 -2
  197. package/dist/src/formats/gexf/index.js.map +1 -1
  198. package/dist/src/formats/gexf/schema.d.ts +5 -0
  199. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  200. package/dist/src/formats/gexf/schema.js +5 -0
  201. package/dist/src/formats/gexf/schema.js.map +1 -1
  202. package/dist/src/formats/gexf/styles.d.ts +74 -0
  203. package/dist/src/formats/gexf/styles.d.ts.map +1 -0
  204. package/dist/src/formats/gexf/styles.js +604 -0
  205. package/dist/src/formats/gexf/styles.js.map +1 -0
  206. package/dist/src/formats/gml/exporter.d.ts +1 -5
  207. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  208. package/dist/src/formats/gml/exporter.js +19 -8
  209. package/dist/src/formats/gml/exporter.js.map +1 -1
  210. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  211. package/dist/src/formats/gml/importer.js +4 -0
  212. package/dist/src/formats/gml/importer.js.map +1 -1
  213. package/dist/src/formats/gml/index.d.ts +2 -1
  214. package/dist/src/formats/gml/index.d.ts.map +1 -1
  215. package/dist/src/formats/gml/index.js +2 -1
  216. package/dist/src/formats/gml/index.js.map +1 -1
  217. package/dist/src/formats/gml/positions.d.ts +14 -0
  218. package/dist/src/formats/gml/positions.d.ts.map +1 -0
  219. package/dist/src/formats/gml/positions.js +46 -0
  220. package/dist/src/formats/gml/positions.js.map +1 -0
  221. package/dist/src/formats/gml/styles.d.ts +59 -0
  222. package/dist/src/formats/gml/styles.d.ts.map +1 -0
  223. package/dist/src/formats/gml/styles.js +1056 -0
  224. package/dist/src/formats/gml/styles.js.map +1 -0
  225. package/dist/src/formats/gml/syntax.d.ts +11 -0
  226. package/dist/src/formats/gml/syntax.d.ts.map +1 -1
  227. package/dist/src/formats/gml/syntax.js +151 -62
  228. package/dist/src/formats/gml/syntax.js.map +1 -1
  229. package/dist/src/formats/graphml/constants.d.ts +4 -0
  230. package/dist/src/formats/graphml/constants.d.ts.map +1 -1
  231. package/dist/src/formats/graphml/constants.js +5 -1
  232. package/dist/src/formats/graphml/constants.js.map +1 -1
  233. package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
  234. package/dist/src/formats/graphml/exporter.js +133 -65
  235. package/dist/src/formats/graphml/exporter.js.map +1 -1
  236. package/dist/src/formats/graphml/importer.d.ts +4 -1
  237. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  238. package/dist/src/formats/graphml/importer.js +100 -44
  239. package/dist/src/formats/graphml/importer.js.map +1 -1
  240. package/dist/src/formats/graphml/index.d.ts +1 -0
  241. package/dist/src/formats/graphml/index.d.ts.map +1 -1
  242. package/dist/src/formats/graphml/index.js +1 -0
  243. package/dist/src/formats/graphml/index.js.map +1 -1
  244. package/dist/src/formats/graphml/styles-plain.d.ts +19 -0
  245. package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -0
  246. package/dist/src/formats/graphml/styles-plain.js +187 -0
  247. package/dist/src/formats/graphml/styles-plain.js.map +1 -0
  248. package/dist/src/formats/graphml/styles-yed.d.ts +179 -0
  249. package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -0
  250. package/dist/src/formats/graphml/styles-yed.js +1086 -0
  251. package/dist/src/formats/graphml/styles-yed.js.map +1 -0
  252. package/dist/src/formats/graphml/styles-yfiles3.d.ts +19 -0
  253. package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -0
  254. package/dist/src/formats/graphml/styles-yfiles3.js +508 -0
  255. package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -0
  256. package/dist/src/formats/graphml/tree.d.ts.map +1 -1
  257. package/dist/src/formats/graphml/tree.js +34 -19
  258. package/dist/src/formats/graphml/tree.js.map +1 -1
  259. package/dist/src/formats/graphml/yfiles.d.ts +16 -2
  260. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  261. package/dist/src/formats/graphml/yfiles.js +83 -12
  262. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  263. package/dist/src/formats/json/exporter.d.ts +5 -0
  264. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  265. package/dist/src/formats/json/exporter.js +68 -25
  266. package/dist/src/formats/json/exporter.js.map +1 -1
  267. package/dist/src/formats/json/importer.d.ts +22 -3
  268. package/dist/src/formats/json/importer.d.ts.map +1 -1
  269. package/dist/src/formats/json/importer.js +439 -264
  270. package/dist/src/formats/json/importer.js.map +1 -1
  271. package/dist/src/formats/json/positions.d.ts +16 -0
  272. package/dist/src/formats/json/positions.d.ts.map +1 -0
  273. package/dist/src/formats/json/positions.js +44 -0
  274. package/dist/src/formats/json/positions.js.map +1 -0
  275. package/dist/src/formats/json/styles.d.ts +102 -0
  276. package/dist/src/formats/json/styles.d.ts.map +1 -0
  277. package/dist/src/formats/json/styles.js +1753 -0
  278. package/dist/src/formats/json/styles.js.map +1 -0
  279. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  280. package/dist/src/formats/neo4j/exporter.js +2 -1
  281. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  282. package/dist/src/formats/pajek/exporter.d.ts +2 -0
  283. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  284. package/dist/src/formats/pajek/exporter.js +142 -76
  285. package/dist/src/formats/pajek/exporter.js.map +1 -1
  286. package/dist/src/formats/pajek/importer.d.ts +4 -0
  287. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  288. package/dist/src/formats/pajek/importer.js +60 -6
  289. package/dist/src/formats/pajek/importer.js.map +1 -1
  290. package/dist/src/formats/pajek/index.d.ts +2 -0
  291. package/dist/src/formats/pajek/index.d.ts.map +1 -1
  292. package/dist/src/formats/pajek/index.js +2 -0
  293. package/dist/src/formats/pajek/index.js.map +1 -1
  294. package/dist/src/formats/pajek/styles.d.ts +50 -0
  295. package/dist/src/formats/pajek/styles.d.ts.map +1 -0
  296. package/dist/src/formats/pajek/styles.js +569 -0
  297. package/dist/src/formats/pajek/styles.js.map +1 -0
  298. package/dist/src/formats/pajek/syntax.d.ts +6 -0
  299. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  300. package/dist/src/formats/pajek/syntax.js +6 -0
  301. package/dist/src/formats/pajek/syntax.js.map +1 -1
  302. package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
  303. package/dist/src/formats/xgmml/columns.js +34 -9
  304. package/dist/src/formats/xgmml/columns.js.map +1 -1
  305. package/dist/src/formats/xgmml/constants.d.ts +2 -0
  306. package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
  307. package/dist/src/formats/xgmml/constants.js +3 -1
  308. package/dist/src/formats/xgmml/constants.js.map +1 -1
  309. package/dist/src/formats/xgmml/document.d.ts +12 -8
  310. package/dist/src/formats/xgmml/document.d.ts.map +1 -1
  311. package/dist/src/formats/xgmml/document.js +56 -13
  312. package/dist/src/formats/xgmml/document.js.map +1 -1
  313. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  314. package/dist/src/formats/xgmml/emit.js +12 -6
  315. package/dist/src/formats/xgmml/emit.js.map +1 -1
  316. package/dist/src/formats/xgmml/exporter.d.ts +3 -1
  317. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  318. package/dist/src/formats/xgmml/exporter.js +22 -9
  319. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  320. package/dist/src/formats/xgmml/importer.d.ts +2 -1
  321. package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
  322. package/dist/src/formats/xgmml/importer.js +31 -24
  323. package/dist/src/formats/xgmml/importer.js.map +1 -1
  324. package/dist/src/formats/xgmml/styles.d.ts +104 -0
  325. package/dist/src/formats/xgmml/styles.d.ts.map +1 -0
  326. package/dist/src/formats/xgmml/styles.js +310 -0
  327. package/dist/src/formats/xgmml/styles.js.map +1 -0
  328. package/dist/src/index.d.ts +9 -7
  329. package/dist/src/index.d.ts.map +1 -1
  330. package/dist/src/index.js +7 -7
  331. package/dist/src/index.js.map +1 -1
  332. package/dist/src/positions/codes.d.ts +31 -0
  333. package/dist/src/positions/codes.d.ts.map +1 -0
  334. package/dist/src/positions/codes.js +26 -0
  335. package/dist/src/positions/codes.js.map +1 -0
  336. package/dist/src/positions/convert.d.ts +58 -0
  337. package/dist/src/positions/convert.d.ts.map +1 -0
  338. package/dist/src/positions/convert.js +107 -0
  339. package/dist/src/positions/convert.js.map +1 -0
  340. package/dist/src/positions/extract.d.ts +27 -0
  341. package/dist/src/positions/extract.d.ts.map +1 -0
  342. package/dist/src/positions/extract.js +526 -0
  343. package/dist/src/positions/extract.js.map +1 -0
  344. package/dist/src/positions/index.d.ts +10 -0
  345. package/dist/src/positions/index.d.ts.map +1 -0
  346. package/dist/src/positions/index.js +9 -0
  347. package/dist/src/positions/index.js.map +1 -0
  348. package/dist/src/positions/types.d.ts +208 -0
  349. package/dist/src/positions/types.d.ts.map +1 -0
  350. package/dist/src/positions/types.js +10 -0
  351. package/dist/src/positions/types.js.map +1 -0
  352. package/dist/src/registry.d.ts +30 -0
  353. package/dist/src/registry.d.ts.map +1 -1
  354. package/dist/src/registry.js +130 -9
  355. package/dist/src/registry.js.map +1 -1
  356. package/dist/src/styles/annotations.d.ts +132 -0
  357. package/dist/src/styles/annotations.d.ts.map +1 -0
  358. package/dist/src/styles/annotations.js +320 -0
  359. package/dist/src/styles/annotations.js.map +1 -0
  360. package/dist/src/styles/budget.d.ts +45 -0
  361. package/dist/src/styles/budget.d.ts.map +1 -0
  362. package/dist/src/styles/budget.js +58 -0
  363. package/dist/src/styles/budget.js.map +1 -0
  364. package/dist/src/styles/canon/bends.d.ts +32 -0
  365. package/dist/src/styles/canon/bends.d.ts.map +1 -0
  366. package/dist/src/styles/canon/bends.js +74 -0
  367. package/dist/src/styles/canon/bends.js.map +1 -0
  368. package/dist/src/styles/canon/color-tables.d.ts +22 -0
  369. package/dist/src/styles/canon/color-tables.d.ts.map +1 -0
  370. package/dist/src/styles/canon/color-tables.js +369 -0
  371. package/dist/src/styles/canon/color-tables.js.map +1 -0
  372. package/dist/src/styles/canon/color.d.ts +66 -0
  373. package/dist/src/styles/canon/color.d.ts.map +1 -0
  374. package/dist/src/styles/canon/color.js +548 -0
  375. package/dist/src/styles/canon/color.js.map +1 -0
  376. package/dist/src/styles/canon/enums.d.ts +25 -0
  377. package/dist/src/styles/canon/enums.d.ts.map +1 -0
  378. package/dist/src/styles/canon/enums.js +187 -0
  379. package/dist/src/styles/canon/enums.js.map +1 -0
  380. package/dist/src/styles/canon/graphviz.d.ts +48 -0
  381. package/dist/src/styles/canon/graphviz.d.ts.map +1 -0
  382. package/dist/src/styles/canon/graphviz.js +160 -0
  383. package/dist/src/styles/canon/graphviz.js.map +1 -0
  384. package/dist/src/styles/canon/index.d.ts +13 -0
  385. package/dist/src/styles/canon/index.d.ts.map +1 -0
  386. package/dist/src/styles/canon/index.js +13 -0
  387. package/dist/src/styles/canon/index.js.map +1 -0
  388. package/dist/src/styles/canon/number.d.ts +59 -0
  389. package/dist/src/styles/canon/number.d.ts.map +1 -0
  390. package/dist/src/styles/canon/number.js +142 -0
  391. package/dist/src/styles/canon/number.js.map +1 -0
  392. package/dist/src/styles/canon/site.d.ts +51 -0
  393. package/dist/src/styles/canon/site.d.ts.map +1 -0
  394. package/dist/src/styles/canon/site.js +63 -0
  395. package/dist/src/styles/canon/site.js.map +1 -0
  396. package/dist/src/styles/canon/text.d.ts +61 -0
  397. package/dist/src/styles/canon/text.d.ts.map +1 -0
  398. package/dist/src/styles/canon/text.js +199 -0
  399. package/dist/src/styles/canon/text.js.map +1 -0
  400. package/dist/src/styles/codes.d.ts +95 -0
  401. package/dist/src/styles/codes.d.ts.map +1 -0
  402. package/dist/src/styles/codes.js +97 -0
  403. package/dist/src/styles/codes.js.map +1 -0
  404. package/dist/src/styles/cyjs/cascade.d.ts +134 -0
  405. package/dist/src/styles/cyjs/cascade.d.ts.map +1 -0
  406. package/dist/src/styles/cyjs/cascade.js +2252 -0
  407. package/dist/src/styles/cyjs/cascade.js.map +1 -0
  408. package/dist/src/styles/cyjs/defaults-table.d.ts +25 -0
  409. package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -0
  410. package/dist/src/styles/cyjs/defaults-table.js +76 -0
  411. package/dist/src/styles/cyjs/defaults-table.js.map +1 -0
  412. package/dist/src/styles/cyjs/emit.d.ts +82 -0
  413. package/dist/src/styles/cyjs/emit.d.ts.map +1 -0
  414. package/dist/src/styles/cyjs/emit.js +790 -0
  415. package/dist/src/styles/cyjs/emit.js.map +1 -0
  416. package/dist/src/styles/cyjs/index.d.ts +13 -0
  417. package/dist/src/styles/cyjs/index.d.ts.map +1 -0
  418. package/dist/src/styles/cyjs/index.js +13 -0
  419. package/dist/src/styles/cyjs/index.js.map +1 -0
  420. package/dist/src/styles/cyjs/live.d.ts +46 -0
  421. package/dist/src/styles/cyjs/live.d.ts.map +1 -0
  422. package/dist/src/styles/cyjs/live.js +108 -0
  423. package/dist/src/styles/cyjs/live.js.map +1 -0
  424. package/dist/src/styles/cyjs/mappers.d.ts +147 -0
  425. package/dist/src/styles/cyjs/mappers.d.ts.map +1 -0
  426. package/dist/src/styles/cyjs/mappers.js +744 -0
  427. package/dist/src/styles/cyjs/mappers.js.map +1 -0
  428. package/dist/src/styles/cyjs/selector.d.ts +113 -0
  429. package/dist/src/styles/cyjs/selector.d.ts.map +1 -0
  430. package/dist/src/styles/cyjs/selector.js +611 -0
  431. package/dist/src/styles/cyjs/selector.js.map +1 -0
  432. package/dist/src/styles/cyjs/stylesheet-text.d.ts +37 -0
  433. package/dist/src/styles/cyjs/stylesheet-text.d.ts.map +1 -0
  434. package/dist/src/styles/cyjs/stylesheet-text.js +181 -0
  435. package/dist/src/styles/cyjs/stylesheet-text.js.map +1 -0
  436. package/dist/src/styles/cytoscape/emit.d.ts +125 -0
  437. package/dist/src/styles/cytoscape/emit.d.ts.map +1 -0
  438. package/dist/src/styles/cytoscape/emit.js +1025 -0
  439. package/dist/src/styles/cytoscape/emit.js.map +1 -0
  440. package/dist/src/styles/cytoscape/index.d.ts +10 -0
  441. package/dist/src/styles/cytoscape/index.d.ts.map +1 -0
  442. package/dist/src/styles/cytoscape/index.js +10 -0
  443. package/dist/src/styles/cytoscape/index.js.map +1 -0
  444. package/dist/src/styles/cytoscape/lexicon.d.ts +49 -0
  445. package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -0
  446. package/dist/src/styles/cytoscape/lexicon.js +603 -0
  447. package/dist/src/styles/cytoscape/lexicon.js.map +1 -0
  448. package/dist/src/styles/cytoscape/mappings.d.ts +171 -0
  449. package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -0
  450. package/dist/src/styles/cytoscape/mappings.js +460 -0
  451. package/dist/src/styles/cytoscape/mappings.js.map +1 -0
  452. package/dist/src/styles/cytoscape/resolve.d.ts +137 -0
  453. package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -0
  454. package/dist/src/styles/cytoscape/resolve.js +381 -0
  455. package/dist/src/styles/cytoscape/resolve.js.map +1 -0
  456. package/dist/src/styles/cytoscape/translate.d.ts +50 -0
  457. package/dist/src/styles/cytoscape/translate.d.ts.map +1 -0
  458. package/dist/src/styles/cytoscape/translate.js +156 -0
  459. package/dist/src/styles/cytoscape/translate.js.map +1 -0
  460. package/dist/src/styles/defaults.d.ts +90 -0
  461. package/dist/src/styles/defaults.d.ts.map +1 -0
  462. package/dist/src/styles/defaults.js +414 -0
  463. package/dist/src/styles/defaults.js.map +1 -0
  464. package/dist/src/styles/index.d.ts +15 -0
  465. package/dist/src/styles/index.d.ts.map +1 -0
  466. package/dist/src/styles/index.js +14 -0
  467. package/dist/src/styles/index.js.map +1 -0
  468. package/dist/src/styles/internal.d.ts +17 -0
  469. package/dist/src/styles/internal.d.ts.map +1 -0
  470. package/dist/src/styles/internal.js +16 -0
  471. package/dist/src/styles/internal.js.map +1 -0
  472. package/dist/src/styles/io/approximations.d.ts +24 -0
  473. package/dist/src/styles/io/approximations.d.ts.map +1 -0
  474. package/dist/src/styles/io/approximations.js +51 -0
  475. package/dist/src/styles/io/approximations.js.map +1 -0
  476. package/dist/src/styles/io/block.d.ts +166 -0
  477. package/dist/src/styles/io/block.d.ts.map +1 -0
  478. package/dist/src/styles/io/block.js +930 -0
  479. package/dist/src/styles/io/block.js.map +1 -0
  480. package/dist/src/styles/io/generic.d.ts +88 -0
  481. package/dist/src/styles/io/generic.d.ts.map +1 -0
  482. package/dist/src/styles/io/generic.js +516 -0
  483. package/dist/src/styles/io/generic.js.map +1 -0
  484. package/dist/src/styles/io/index.d.ts +8 -0
  485. package/dist/src/styles/io/index.d.ts.map +1 -0
  486. package/dist/src/styles/io/index.js +7 -0
  487. package/dist/src/styles/io/index.js.map +1 -0
  488. package/dist/src/styles/io/writer.d.ts +80 -0
  489. package/dist/src/styles/io/writer.d.ts.map +1 -0
  490. package/dist/src/styles/io/writer.js +596 -0
  491. package/dist/src/styles/io/writer.js.map +1 -0
  492. package/dist/src/styles/notes.d.ts +47 -0
  493. package/dist/src/styles/notes.d.ts.map +1 -0
  494. package/dist/src/styles/notes.js +140 -0
  495. package/dist/src/styles/notes.js.map +1 -0
  496. package/dist/src/styles/plan/classes.d.ts +31 -0
  497. package/dist/src/styles/plan/classes.d.ts.map +1 -0
  498. package/dist/src/styles/plan/classes.js +139 -0
  499. package/dist/src/styles/plan/classes.js.map +1 -0
  500. package/dist/src/styles/plan/continuous.d.ts +32 -0
  501. package/dist/src/styles/plan/continuous.d.ts.map +1 -0
  502. package/dist/src/styles/plan/continuous.js +165 -0
  503. package/dist/src/styles/plan/continuous.js.map +1 -0
  504. package/dist/src/styles/plan/discrete.d.ts +39 -0
  505. package/dist/src/styles/plan/discrete.d.ts.map +1 -0
  506. package/dist/src/styles/plan/discrete.js +127 -0
  507. package/dist/src/styles/plan/discrete.js.map +1 -0
  508. package/dist/src/styles/plan/families.d.ts +31 -0
  509. package/dist/src/styles/plan/families.d.ts.map +1 -0
  510. package/dist/src/styles/plan/families.js +95 -0
  511. package/dist/src/styles/plan/families.js.map +1 -0
  512. package/dist/src/styles/plan/features.d.ts +58 -0
  513. package/dist/src/styles/plan/features.d.ts.map +1 -0
  514. package/dist/src/styles/plan/features.js +185 -0
  515. package/dist/src/styles/plan/features.js.map +1 -0
  516. package/dist/src/styles/plan/index.d.ts +7 -0
  517. package/dist/src/styles/plan/index.d.ts.map +1 -0
  518. package/dist/src/styles/plan/index.js +7 -0
  519. package/dist/src/styles/plan/index.js.map +1 -0
  520. package/dist/src/styles/plan/plan.d.ts +71 -0
  521. package/dist/src/styles/plan/plan.d.ts.map +1 -0
  522. package/dist/src/styles/plan/plan.js +893 -0
  523. package/dist/src/styles/plan/plan.js.map +1 -0
  524. package/dist/src/styles/plan/resolve.d.ts +137 -0
  525. package/dist/src/styles/plan/resolve.d.ts.map +1 -0
  526. package/dist/src/styles/plan/resolve.js +587 -0
  527. package/dist/src/styles/plan/resolve.js.map +1 -0
  528. package/dist/src/styles/types.d.ts +702 -0
  529. package/dist/src/styles/types.d.ts.map +1 -0
  530. package/dist/src/styles/types.js +10 -0
  531. package/dist/src/styles/types.js.map +1 -0
  532. package/dist/src/styles/vocabulary.d.ts +41 -0
  533. package/dist/src/styles/vocabulary.d.ts.map +1 -0
  534. package/dist/src/styles/vocabulary.js +140 -0
  535. package/dist/src/styles/vocabulary.js.map +1 -0
  536. package/dist/src/types.d.ts +64 -1
  537. package/dist/src/types.d.ts.map +1 -1
  538. package/dist/src/types.js.map +1 -1
  539. package/dist/styles.d.ts +1 -0
  540. package/dist/styles.js +322 -0
  541. package/dist/styles.js.map +1 -0
  542. package/dist/xgmml.js +5 -4
  543. package/dist/xgmml.js.map +1 -1
  544. package/package.json +13 -2
  545. package/src/common/codes.ts +7 -0
  546. package/src/common/escape.ts +5 -2
  547. package/src/common/export.ts +179 -140
  548. package/src/common/input.ts +8 -4
  549. package/src/common/json-elements.ts +169 -38
  550. package/src/common/notes.ts +94 -0
  551. package/src/common/report.ts +41 -1
  552. package/src/common/units.ts +42 -0
  553. package/src/common/xml.ts +45 -5
  554. package/src/formats/cx/exporter.ts +95 -11
  555. package/src/formats/cx/importer.ts +126 -90
  556. package/src/formats/cx/styles.ts +304 -0
  557. package/src/formats/cx2/exporter.ts +234 -77
  558. package/src/formats/cx2/importer.ts +19 -1
  559. package/src/formats/cx2/styles.ts +29 -0
  560. package/src/formats/cys/constants.ts +3 -0
  561. package/src/formats/cys/exporter.ts +246 -60
  562. package/src/formats/cys/importer.ts +125 -9
  563. package/src/formats/cys/session.ts +6 -0
  564. package/src/formats/cys/styles.ts +254 -0
  565. package/src/formats/cys/tables.ts +117 -48
  566. package/src/formats/dot/exporter.ts +262 -126
  567. package/src/formats/dot/importer.ts +111 -20
  568. package/src/formats/dot/index.ts +1 -0
  569. package/src/formats/dot/positions.ts +77 -0
  570. package/src/formats/dot/styles.ts +1741 -0
  571. package/src/formats/gexf/exporter.ts +27 -11
  572. package/src/formats/gexf/importer.ts +308 -158
  573. package/src/formats/gexf/index.ts +10 -0
  574. package/src/formats/gexf/schema.ts +5 -0
  575. package/src/formats/gexf/styles.ts +719 -0
  576. package/src/formats/gml/exporter.ts +33 -14
  577. package/src/formats/gml/importer.ts +4 -0
  578. package/src/formats/gml/index.ts +2 -1
  579. package/src/formats/gml/positions.ts +52 -0
  580. package/src/formats/gml/styles.ts +1248 -0
  581. package/src/formats/gml/syntax.ts +173 -62
  582. package/src/formats/graphml/constants.ts +5 -0
  583. package/src/formats/graphml/exporter.ts +163 -71
  584. package/src/formats/graphml/importer.ts +119 -45
  585. package/src/formats/graphml/index.ts +1 -0
  586. package/src/formats/graphml/styles-plain.ts +236 -0
  587. package/src/formats/graphml/styles-yed.ts +1221 -0
  588. package/src/formats/graphml/styles-yfiles3.ts +573 -0
  589. package/src/formats/graphml/tree.ts +36 -20
  590. package/src/formats/graphml/yfiles.ts +89 -13
  591. package/src/formats/json/exporter.ts +76 -26
  592. package/src/formats/json/importer.ts +541 -322
  593. package/src/formats/json/positions.ts +50 -0
  594. package/src/formats/json/styles.ts +2082 -0
  595. package/src/formats/neo4j/exporter.ts +2 -1
  596. package/src/formats/pajek/exporter.ts +195 -106
  597. package/src/formats/pajek/importer.ts +78 -9
  598. package/src/formats/pajek/index.ts +2 -0
  599. package/src/formats/pajek/styles.ts +685 -0
  600. package/src/formats/pajek/syntax.ts +7 -0
  601. package/src/formats/xgmml/columns.ts +39 -9
  602. package/src/formats/xgmml/constants.ts +3 -0
  603. package/src/formats/xgmml/document.ts +54 -12
  604. package/src/formats/xgmml/emit.ts +11 -6
  605. package/src/formats/xgmml/exporter.ts +32 -13
  606. package/src/formats/xgmml/importer.ts +43 -29
  607. package/src/formats/xgmml/styles.ts +400 -0
  608. package/src/index.ts +16 -0
  609. package/src/positions/codes.ts +32 -0
  610. package/src/positions/convert.ts +123 -0
  611. package/src/positions/extract.ts +619 -0
  612. package/src/positions/index.ts +23 -0
  613. package/src/positions/types.ts +221 -0
  614. package/src/registry.ts +179 -8
  615. package/src/styles/annotations.ts +478 -0
  616. package/src/styles/budget.ts +60 -0
  617. package/src/styles/canon/bends.ts +84 -0
  618. package/src/styles/canon/color-tables.ts +386 -0
  619. package/src/styles/canon/color.ts +624 -0
  620. package/src/styles/canon/enums.ts +211 -0
  621. package/src/styles/canon/graphviz.ts +189 -0
  622. package/src/styles/canon/index.ts +13 -0
  623. package/src/styles/canon/number.ts +152 -0
  624. package/src/styles/canon/site.ts +87 -0
  625. package/src/styles/canon/text.ts +221 -0
  626. package/src/styles/codes.ts +104 -0
  627. package/src/styles/cyjs/cascade.ts +2670 -0
  628. package/src/styles/cyjs/defaults-table.ts +82 -0
  629. package/src/styles/cyjs/emit.ts +951 -0
  630. package/src/styles/cyjs/index.ts +37 -0
  631. package/src/styles/cyjs/live.ts +147 -0
  632. package/src/styles/cyjs/mappers.ts +879 -0
  633. package/src/styles/cyjs/selector.ts +778 -0
  634. package/src/styles/cyjs/stylesheet-text.ts +214 -0
  635. package/src/styles/cytoscape/emit.ts +1264 -0
  636. package/src/styles/cytoscape/index.ts +40 -0
  637. package/src/styles/cytoscape/lexicon.ts +656 -0
  638. package/src/styles/cytoscape/mappings.ts +603 -0
  639. package/src/styles/cytoscape/resolve.ts +521 -0
  640. package/src/styles/cytoscape/translate.ts +237 -0
  641. package/src/styles/defaults.ts +504 -0
  642. package/src/styles/index.ts +57 -0
  643. package/src/styles/internal.ts +17 -0
  644. package/src/styles/io/approximations.ts +47 -0
  645. package/src/styles/io/block.ts +1138 -0
  646. package/src/styles/io/generic.ts +687 -0
  647. package/src/styles/io/index.ts +18 -0
  648. package/src/styles/io/writer.ts +745 -0
  649. package/src/styles/notes.ts +165 -0
  650. package/src/styles/plan/classes.ts +166 -0
  651. package/src/styles/plan/continuous.ts +218 -0
  652. package/src/styles/plan/discrete.ts +165 -0
  653. package/src/styles/plan/families.ts +115 -0
  654. package/src/styles/plan/features.ts +234 -0
  655. package/src/styles/plan/index.ts +7 -0
  656. package/src/styles/plan/plan.ts +1129 -0
  657. package/src/styles/plan/resolve.ts +685 -0
  658. package/src/styles/types.ts +814 -0
  659. package/src/styles/vocabulary.ts +172 -0
  660. package/src/types.ts +76 -1
  661. package/dist/chunks/constants-fT3E8JYY.js.map +0 -1
  662. package/dist/chunks/escape-B62ZGM_a.js.map +0 -1
  663. package/dist/chunks/export-rKLeGQMo.js.map +0 -1
  664. package/dist/chunks/exporter-8hLNemFk.js.map +0 -1
  665. package/dist/chunks/exporter-BWwEuOPT.js.map +0 -1
  666. package/dist/chunks/exporter-RVtXVetw.js.map +0 -1
  667. package/dist/chunks/importer-BbNb7eNg.js.map +0 -1
  668. package/dist/chunks/importer-C98dpSH4.js.map +0 -1
  669. package/dist/chunks/importer-D794XUTe.js.map +0 -1
  670. package/dist/chunks/importer-Dz7tI8Sa.js.map +0 -1
  671. package/dist/chunks/importer-Ht7LNg8L.js.map +0 -1
  672. package/dist/chunks/importer-WrGRZb0N.js.map +0 -1
  673. package/dist/chunks/importer-xZ-3suY1.js.map +0 -1
  674. package/dist/chunks/json-elements-DKSqpNW1.js.map +0 -1
@@ -0,0 +1,1264 @@
1
+ /**
2
+ * Neutral style rules back to a Cytoscape visual style, for the CX, CX2 and session writers: rules over every element
3
+ * become defaults and mappings, everything Cytoscape cannot say as a rule (a rule over a group of elements, a second
4
+ * mapping of one property, a value written per element) becomes per-element bypasses, so what Cytoscape draws is
5
+ * what the rules give. Every value the style cannot hold is a coded note.
6
+ */
7
+
8
+ import type { GraphSnapshot } from "@graphty/graph-format";
9
+
10
+ import { escapeXmlAttribute } from "../../common/escape.js";
11
+ import { num6 } from "../canon/number.js";
12
+ import { STYLE_ISSUE } from "../codes.js";
13
+ import { StyleNotes } from "../notes.js";
14
+ import { canonicalFor, degreeValues, refSource, resolveRules } from "../plan/resolve.js";
15
+ import type {
16
+ ColumnRef,
17
+ ContinuousPoint,
18
+ ExtractedStyles,
19
+ Mapping,
20
+ Scalar,
21
+ StyleRule,
22
+ StyleTarget,
23
+ StyleValue,
24
+ } from "../types.js";
25
+ import { STYLE_PROPERTIES } from "../vocabulary.js";
26
+ import { JAVA_FONTS } from "./lexicon.js";
27
+ import {
28
+ type CytoscapeContinuousPoint,
29
+ type CytoscapeMapping,
30
+ type CytoscapeStyle,
31
+ emptyCytoscapeStyle,
32
+ } from "./mappings.js";
33
+
34
+ /**
35
+ * The spelling a Cytoscape style is written in: `"desktop"` for CX version 1 and session files (text values,
36
+ * `NODE_FILL_COLOR`, 0..255 transparency), `"cx2"` for CX2 (typed JSON values, `NODE_BACKGROUND_COLOR`, 0..1
37
+ * opacity).
38
+ * @category Styles
39
+ */
40
+ export type CytoscapeDialect = "desktop" | "cx2";
41
+
42
+ /**
43
+ * Options of `emitCytoscapeStyle()`.
44
+ * @category Styles
45
+ */
46
+ export interface CytoscapeEmitOptions {
47
+ /** The spelling to write. */
48
+ readonly dialect: CytoscapeDialect;
49
+ /** Where every loss is noted (`W_STYLE_NOT_WRITTEN`, `W_STYLE_APPROXIMATED`, `W_STYLE_LAYER_FLATTENED`). */
50
+ readonly notes?: StyleNotes;
51
+ /**
52
+ * The name of the attribute column a mapping over a style column or a computed degree reads, which the writer
53
+ * adds to the file; defaults to `graphty.style.<target>.<key>` (`<key>` is the style column's key or the degree's
54
+ * name).
55
+ */
56
+ readonly columnName?: (target: StyleTarget, ref: ColumnRef) => string;
57
+ }
58
+
59
+ /**
60
+ * A Cytoscape style made from neutral rules.
61
+ * @category Styles
62
+ */
63
+ export interface CytoscapeEmitted {
64
+ /** The style: defaults, mappings and dependencies, values in the dialect's spelling. */
65
+ readonly style: CytoscapeStyle;
66
+ /** Each element's bypasses (visual property to value in the dialect's spelling), or null where it has none. */
67
+ readonly bypasses: {
68
+ /** Per node, by node index. */
69
+ readonly node: (Map<string, unknown> | null)[];
70
+ /** Per edge, by edge index. */
71
+ readonly edge: (Map<string, unknown> | null)[];
72
+ };
73
+ /** Attribute columns the mappings read that the graph does not hold, by name, with each element's value. */
74
+ readonly columns: {
75
+ /** Node columns. */
76
+ readonly node: Map<string, readonly (StyleValue | null)[]>;
77
+ /** Edge columns. */
78
+ readonly edge: Map<string, readonly (StyleValue | null)[]>;
79
+ };
80
+ }
81
+
82
+ /** Writes one canonical value in a dialect's spelling; calls `approx` when the value is changed to fit. */
83
+ type Write = (v: StyleValue, approx: () => void) => unknown;
84
+
85
+ /** A visual property and how a value is written into it. */
86
+ interface Writer {
87
+ readonly vp: string;
88
+ readonly write: Write;
89
+ }
90
+
91
+ /**
92
+ * `#RRGGBB`.
93
+ * @param v - a canonical color
94
+ * @returns the color as Cytoscape writes it
95
+ */
96
+ const colorOut: Write = (v) => String(v).toUpperCase();
97
+
98
+ /**
99
+ * A writer of an enumeration.
100
+ * @param table - canonical value to the dialect's spelling
101
+ * @param near - canonical value to the nearest spelling, for values the dialect does not have
102
+ * @param upper - whether an `other:` value is written uppercase with underscores
103
+ * @param cytoscape - the `other:` values (lowercase, hyphenated) that are Cytoscape's own names, written as they
104
+ * are; any other `other:` value (another program's, such as Graphviz's `invtrapezium`) is no Cytoscape value and
105
+ * is written as its nearest
106
+ * @returns the writer
107
+ */
108
+ function enumOut(
109
+ table: Readonly<Record<string, string>>,
110
+ near: Readonly<Record<string, string>>,
111
+ upper: boolean,
112
+ cytoscape: ReadonlySet<string> = new Set(),
113
+ ): Write {
114
+ return (v, approx) => {
115
+ const s = String(v);
116
+ if (Object.hasOwn(table, s)) {
117
+ return table[s];
118
+ }
119
+ if (s.startsWith("other:") && cytoscape.has(s.slice("other:".length).toLowerCase().replaceAll("_", "-"))) {
120
+ const raw = s.slice("other:".length);
121
+ const out = upper ? raw.toUpperCase().replaceAll("-", "_") : raw;
122
+ if (out !== raw) {
123
+ // read back in Cytoscape's spelling, which is another value
124
+ approx();
125
+ }
126
+ return out;
127
+ }
128
+ approx();
129
+ return Object.hasOwn(near, s) ? near[s] : Object.values(table)[0];
130
+ };
131
+ }
132
+
133
+ const DESKTOP_SHAPES = {
134
+ ellipse: "ELLIPSE",
135
+ rectangle: "RECTANGLE",
136
+ "round-rectangle": "ROUND_RECTANGLE",
137
+ triangle: "TRIANGLE",
138
+ diamond: "DIAMOND",
139
+ hexagon: "HEXAGON",
140
+ octagon: "OCTAGON",
141
+ parallelogram: "PARALLELOGRAM",
142
+ vee: "V",
143
+ };
144
+ const NEAR_SHAPES = {
145
+ pentagon: "hexagon",
146
+ star: "octagon",
147
+ trapezoid: "parallelogram",
148
+ none: "ellipse",
149
+ // Graphviz shapes no canonical shape names
150
+ "other:invtrapezium": "parallelogram",
151
+ "other:house": "hexagon",
152
+ "other:invhouse": "hexagon",
153
+ };
154
+ const lower = (t: Readonly<Record<string, string>>): Record<string, string> =>
155
+ Object.fromEntries(Object.entries(t).map(([k, v]) => [k, v === "V" ? "vee" : v.toLowerCase()]));
156
+ const nearIn = (t: Readonly<Record<string, string>>, near: Readonly<Record<string, string>>): Record<string, string> =>
157
+ Object.fromEntries(Object.entries(near).map(([k, v]) => [k, t[v]]));
158
+
159
+ const DESKTOP_BORDERS = {
160
+ solid: "SOLID",
161
+ dashed: "EQUAL_DASH",
162
+ dotted: "DOT",
163
+ "dash-dot": "DASH_DOT",
164
+ double: "PARALLEL_LINES",
165
+ };
166
+ // LONG_DASH is read as dashed (CX2 has no long dash), so long-dash is written as its nearest, EQUAL_DASH
167
+ const DESKTOP_LINES = { ...DESKTOP_BORDERS, zigzag: "ZIGZAG", sinewave: "SINEWAVE" };
168
+ const CX2_LINES = { solid: "solid", dashed: "dashed", dotted: "dotted", double: "double" };
169
+ const NEAR_LINES = { "dash-dot": "dashed", "long-dash": "dashed", zigzag: "solid", sinewave: "solid" };
170
+
171
+ const DESKTOP_ARROWS = {
172
+ none: "NONE",
173
+ triangle: "DELTA",
174
+ vee: "ARROW",
175
+ tee: "T",
176
+ diamond: "DIAMOND",
177
+ circle: "CIRCLE",
178
+ square: "SQUARE",
179
+ "triangle-open": "OPEN_DELTA",
180
+ "diamond-open": "OPEN_DIAMOND",
181
+ "circle-open": "OPEN_CIRCLE",
182
+ half: "HALF_TOP",
183
+ };
184
+ const CX2_ARROWS = {
185
+ none: "none",
186
+ triangle: "triangle",
187
+ vee: "vee",
188
+ tee: "tee",
189
+ diamond: "diamond",
190
+ circle: "circle",
191
+ square: "square",
192
+ "triangle-open": "open-triangle",
193
+ "diamond-open": "open-diamond",
194
+ "circle-open": "open-circle",
195
+ half: "half-top",
196
+ };
197
+ const NEAR_ARROWS = { crow: "vee", inverted: "triangle" };
198
+
199
+ /** The Cytoscape arrow names no canonical arrow names, which Cytoscape's readers keep as `other:` values. */
200
+ const CY_OTHER_ARROWS: ReadonlySet<string> = new Set(["open-square", "open-half-circle", "half-circle"]);
201
+ /** The Cytoscape line types no canonical line style names. */
202
+ const CY_OTHER_LINES: ReadonlySet<string> = new Set([
203
+ "backward-slash",
204
+ "forward-slash",
205
+ "vertical-slash",
206
+ "separate-arrow",
207
+ "contiguous-arrow",
208
+ ]);
209
+ /** The Cytoscape line types a border can take that no canonical border style names. */
210
+ const CY_OTHER_BORDERS: ReadonlySet<string> = new Set([...CY_OTHER_LINES, "zigzag", "sinewave"]);
211
+
212
+ /** A label position: the node's anchor and the label's opposite anchor. */
213
+ const DESKTOP_POSITIONS = {
214
+ center: "C,C,c,0.00,0.00",
215
+ top: "N,S,c,0.00,0.00",
216
+ bottom: "S,N,c,0.00,0.00",
217
+ left: "W,E,c,0.00,0.00",
218
+ right: "E,W,c,0.00,0.00",
219
+ "top-left": "NW,SE,c,0.00,0.00",
220
+ "top-right": "NE,SW,c,0.00,0.00",
221
+ "bottom-left": "SW,NE,c,0.00,0.00",
222
+ "bottom-right": "SE,NW,c,0.00,0.00",
223
+ };
224
+ const CX2_ANCHORS: Readonly<Record<string, readonly [string, string]>> = {
225
+ center: ["center", "center"],
226
+ top: ["center", "top"],
227
+ bottom: ["center", "bottom"],
228
+ left: ["left", "center"],
229
+ right: ["right", "center"],
230
+ "top-left": ["left", "top"],
231
+ "top-right": ["right", "top"],
232
+ "bottom-left": ["left", "bottom"],
233
+ "bottom-right": ["right", "bottom"],
234
+ };
235
+
236
+ const desktopNumber: Write = String;
237
+ const cx2Number: Write = (v) => v;
238
+ const textOut: Write = String;
239
+ const desktopBool: Write = (v) => String(v === true);
240
+ const opacity255: Write = (v, approx) => {
241
+ const b = Math.round(Number(v) * 255);
242
+ if (num6(b / 255) !== v) {
243
+ approx();
244
+ }
245
+ return String(b);
246
+ };
247
+
248
+ /** The writers of each dialect, per target and property. */
249
+ const WRITERS: Readonly<Record<CytoscapeDialect, Readonly<Record<StyleTarget, ReadonlyMap<string, Writer>>>>> = {
250
+ desktop: {
251
+ node: new Map<string, Writer>([
252
+ ["fill", { vp: "NODE_FILL_COLOR", write: colorOut }],
253
+ ["fillOpacity", { vp: "NODE_TRANSPARENCY", write: opacity255 }],
254
+ ["borderColor", { vp: "NODE_BORDER_PAINT", write: colorOut }],
255
+ ["borderWidth", { vp: "NODE_BORDER_WIDTH", write: desktopNumber }],
256
+ [
257
+ "borderStyle",
258
+ {
259
+ vp: "NODE_BORDER_LINE_TYPE",
260
+ write: enumOut(DESKTOP_BORDERS, nearIn(DESKTOP_BORDERS, NEAR_LINES), true, CY_OTHER_BORDERS),
261
+ },
262
+ ],
263
+ ["shape", { vp: "NODE_SHAPE", write: enumOut(DESKTOP_SHAPES, nearIn(DESKTOP_SHAPES, NEAR_SHAPES), true) }],
264
+ ["width", { vp: "NODE_WIDTH", write: desktopNumber }],
265
+ ["height", { vp: "NODE_HEIGHT", write: desktopNumber }],
266
+ ["label", { vp: "NODE_LABEL", write: textOut }],
267
+ ["labelColor", { vp: "NODE_LABEL_COLOR", write: colorOut }],
268
+ ["labelSize", { vp: "NODE_LABEL_FONT_SIZE", write: desktopNumber }],
269
+ ["labelPosition", { vp: "NODE_LABEL_POSITION", write: enumOut(DESKTOP_POSITIONS, {}, false) }],
270
+ ["tooltip", { vp: "NODE_TOOLTIP", write: textOut }],
271
+ ["visible", { vp: "NODE_VISIBLE", write: desktopBool }],
272
+ ]),
273
+ edge: new Map<string, Writer>([
274
+ ["color", { vp: "EDGE_STROKE_UNSELECTED_PAINT", write: colorOut }],
275
+ ["opacity", { vp: "EDGE_TRANSPARENCY", write: opacity255 }],
276
+ ["width", { vp: "EDGE_WIDTH", write: desktopNumber }],
277
+ [
278
+ "lineStyle",
279
+ {
280
+ vp: "EDGE_LINE_TYPE",
281
+ write: enumOut(DESKTOP_LINES, nearIn(DESKTOP_LINES, NEAR_LINES), true, CY_OTHER_LINES),
282
+ },
283
+ ],
284
+ [
285
+ "sourceArrow",
286
+ {
287
+ vp: "EDGE_SOURCE_ARROW_SHAPE",
288
+ write: enumOut(DESKTOP_ARROWS, nearIn(DESKTOP_ARROWS, NEAR_ARROWS), true, CY_OTHER_ARROWS),
289
+ },
290
+ ],
291
+ [
292
+ "targetArrow",
293
+ {
294
+ vp: "EDGE_TARGET_ARROW_SHAPE",
295
+ write: enumOut(DESKTOP_ARROWS, nearIn(DESKTOP_ARROWS, NEAR_ARROWS), true, CY_OTHER_ARROWS),
296
+ },
297
+ ],
298
+ ["sourceArrowColor", { vp: "EDGE_SOURCE_ARROW_UNSELECTED_PAINT", write: colorOut }],
299
+ ["targetArrowColor", { vp: "EDGE_TARGET_ARROW_UNSELECTED_PAINT", write: colorOut }],
300
+ ["curved", { vp: "EDGE_CURVED", write: desktopBool }],
301
+ ["label", { vp: "EDGE_LABEL", write: textOut }],
302
+ ["labelColor", { vp: "EDGE_LABEL_COLOR", write: colorOut }],
303
+ ["labelSize", { vp: "EDGE_LABEL_FONT_SIZE", write: desktopNumber }],
304
+ ["tooltip", { vp: "EDGE_TOOLTIP", write: textOut }],
305
+ ["visible", { vp: "EDGE_VISIBLE", write: desktopBool }],
306
+ ]),
307
+ },
308
+ cx2: {
309
+ node: new Map<string, Writer>([
310
+ ["fill", { vp: "NODE_BACKGROUND_COLOR", write: colorOut }],
311
+ ["fillOpacity", { vp: "NODE_BACKGROUND_OPACITY", write: cx2Number }],
312
+ ["borderColor", { vp: "NODE_BORDER_COLOR", write: colorOut }],
313
+ ["borderWidth", { vp: "NODE_BORDER_WIDTH", write: cx2Number }],
314
+ [
315
+ "borderStyle",
316
+ {
317
+ vp: "NODE_BORDER_STYLE",
318
+ write: enumOut(CX2_LINES, nearIn(CX2_LINES, NEAR_LINES), false, CY_OTHER_BORDERS),
319
+ },
320
+ ],
321
+ [
322
+ "shape",
323
+ {
324
+ vp: "NODE_SHAPE",
325
+ write: enumOut(lower(DESKTOP_SHAPES), nearIn(lower(DESKTOP_SHAPES), NEAR_SHAPES), false),
326
+ },
327
+ ],
328
+ ["width", { vp: "NODE_WIDTH", write: cx2Number }],
329
+ ["height", { vp: "NODE_HEIGHT", write: cx2Number }],
330
+ ["label", { vp: "NODE_LABEL", write: textOut }],
331
+ ["labelColor", { vp: "NODE_LABEL_COLOR", write: colorOut }],
332
+ ["labelSize", { vp: "NODE_LABEL_FONT_SIZE", write: cx2Number }],
333
+ ["labelPosition", { vp: "NODE_LABEL_POSITION", write: cx2Position }],
334
+ ["visible", { vp: "NODE_VISIBILITY", write: (v) => (v === true ? "element" : "none") }],
335
+ ]),
336
+ edge: new Map<string, Writer>([
337
+ ["color", { vp: "EDGE_LINE_COLOR", write: colorOut }],
338
+ ["opacity", { vp: "EDGE_OPACITY", write: cx2Number }],
339
+ ["width", { vp: "EDGE_WIDTH", write: cx2Number }],
340
+ [
341
+ "lineStyle",
342
+ {
343
+ vp: "EDGE_LINE_STYLE",
344
+ write: enumOut(CX2_LINES, nearIn(CX2_LINES, NEAR_LINES), false, CY_OTHER_LINES),
345
+ },
346
+ ],
347
+ [
348
+ "sourceArrow",
349
+ {
350
+ vp: "EDGE_SOURCE_ARROW_SHAPE",
351
+ write: enumOut(CX2_ARROWS, nearIn(CX2_ARROWS, NEAR_ARROWS), false, CY_OTHER_ARROWS),
352
+ },
353
+ ],
354
+ [
355
+ "targetArrow",
356
+ {
357
+ vp: "EDGE_TARGET_ARROW_SHAPE",
358
+ write: enumOut(CX2_ARROWS, nearIn(CX2_ARROWS, NEAR_ARROWS), false, CY_OTHER_ARROWS),
359
+ },
360
+ ],
361
+ ["sourceArrowColor", { vp: "EDGE_SOURCE_ARROW_COLOR", write: colorOut }],
362
+ ["targetArrowColor", { vp: "EDGE_TARGET_ARROW_COLOR", write: colorOut }],
363
+ ["curved", { vp: "EDGE_CURVED", write: (v) => v === true }],
364
+ ["label", { vp: "EDGE_LABEL", write: textOut }],
365
+ ["labelColor", { vp: "EDGE_LABEL_COLOR", write: colorOut }],
366
+ ["labelSize", { vp: "EDGE_LABEL_FONT_SIZE", write: cx2Number }],
367
+ ["visible", { vp: "EDGE_VISIBILITY", write: (v) => (v === true ? "element" : "none") }],
368
+ ]),
369
+ },
370
+ };
371
+
372
+ /**
373
+ * CX2's label position object.
374
+ * @param v - a canonical position
375
+ * @returns the object
376
+ */
377
+ function cx2Position(v: StyleValue): unknown {
378
+ const [h, vert] = Object.hasOwn(CX2_ANCHORS, String(v)) ? CX2_ANCHORS[String(v)] : ["center", "center"];
379
+ return {
380
+ HORIZONTAL_ALIGN: "center",
381
+ VERTICAL_ALIGN: "center",
382
+ HORIZONTAL_ANCHOR: h,
383
+ VERTICAL_ANCHOR: vert,
384
+ MARGIN_X: 0,
385
+ MARGIN_Y: 0,
386
+ JUSTIFICATION: "center",
387
+ };
388
+ }
389
+
390
+ /** The font properties, written together into one font face. */
391
+ const FONT_PROPS = ["labelFont", "labelWeight", "labelItalic"] as const;
392
+ const FONT_VP: Readonly<Record<StyleTarget, string>> = { node: "NODE_LABEL_FONT_FACE", edge: "EDGE_LABEL_FONT_FACE" };
393
+ const CSS_TO_JAVA: Readonly<Record<string, string>> = {
394
+ "sans-serif": "SansSerif",
395
+ serif: "Serif",
396
+ monospace: "Monospaced",
397
+ };
398
+
399
+ /**
400
+ * A font face in a dialect's spelling.
401
+ * @param dialect - the dialect
402
+ * @param family - the family, or null
403
+ * @param weight - the weight, or null
404
+ * @param italic - whether italic, or null
405
+ * @param approx - called when the family had to be shortened
406
+ * @returns the face
407
+ */
408
+ function fontOut(
409
+ dialect: CytoscapeDialect,
410
+ family: StyleValue | null,
411
+ weight: StyleValue | null,
412
+ italic: StyleValue | null,
413
+ approx: () => void,
414
+ ): unknown {
415
+ const bold = weight === "bold";
416
+ const it = italic === true;
417
+ const fam = family === null ? "sans-serif" : String(family);
418
+ if (dialect === "cx2") {
419
+ if (fontReadsAs(fam) !== fam) {
420
+ approx();
421
+ }
422
+ return { FONT_FAMILY: fam, FONT_STYLE: it ? "italic" : "normal", FONT_WEIGHT: bold ? "bold" : "normal" };
423
+ }
424
+ const first = fam.includes(",") ? fam.split(",")[0].trim() : fam;
425
+ const java = Object.hasOwn(CSS_TO_JAVA, first) ? CSS_TO_JAVA[first] : first;
426
+ if (fontReadsAs(java) !== fam) {
427
+ approx();
428
+ }
429
+ return `${java},${javaFontStyle(bold, it)},12`;
430
+ }
431
+
432
+ /**
433
+ * The style part of a Java font face.
434
+ * @param bold - whether bold
435
+ * @param italic - whether italic
436
+ * @returns `plain`, `bold`, `italic` or `bolditalic`
437
+ */
438
+ function javaFontStyle(bold: boolean, italic: boolean): string {
439
+ if (!bold && !italic) {
440
+ return "plain";
441
+ }
442
+ return (bold ? "bold" : "") + (italic ? "italic" : "");
443
+ }
444
+
445
+ /**
446
+ * The family a font face reads back as: the reader drops a face suffix (Dialog.plain is Dialog) and reads a Java
447
+ * logical name as the family Cytoscape draws it with (Dialog as sans-serif).
448
+ * @param name - the face
449
+ * @returns the family
450
+ */
451
+ function fontReadsAs(name: string): string {
452
+ const family = name.replace(/\.(?:bold|italic|plain|bolditalic)$/i, "");
453
+ return JAVA_FONTS.get(family.toLowerCase()) ?? family;
454
+ }
455
+
456
+ /**
457
+ * The default name of the column a mapping over a non-data column reads.
458
+ * @param target - nodes or edges
459
+ * @param ref - the column
460
+ * @returns the name
461
+ */
462
+ function defaultColumnName(target: StyleTarget, ref: ColumnRef): string {
463
+ if ("style" in ref) {
464
+ return `graphty.style.${target}.${ref.style}`;
465
+ }
466
+ return `graphty.style.${target}.${"computed" in ref ? ref.computed : (ref as { data: string }).data}`;
467
+ }
468
+
469
+ /** The kinds of value a Cytoscape passthrough reads the same way the neutral reading does. */
470
+ const PASSTHROUGH_SAFE: ReadonlySet<string> = new Set(["color", "number", "text", "boolean"]);
471
+
472
+ /**
473
+ * Turns neutral rules into a Cytoscape visual style. Rules over every element become defaults (a constant) and
474
+ * mappings (one per visual property: the topmost wins); a passthrough of a value Cytoscape spells differently (an
475
+ * opacity, a shape) becomes a discrete table over the values the graph holds. Every element whose value under the
476
+ * rules differs from what that style gives gets a bypass, so Cytoscape draws exactly what the rules give; each rule
477
+ * written that way is noted as `W_STYLE_LAYER_FLATTENED`. A property Cytoscape has no visual property for is noted as
478
+ * `W_STYLE_NOT_WRITTEN`, a value changed to the nearest Cytoscape has as `W_STYLE_APPROXIMATED`.
479
+ * @param extracted - the styles, for this graph
480
+ * @param snapshot - the graph
481
+ * @param options - the dialect, notes and column names
482
+ * @returns the style, the bypasses and the columns the writer adds
483
+ * @category Styles
484
+ */
485
+ export function emitCytoscapeStyle(
486
+ extracted: ExtractedStyles,
487
+ snapshot: GraphSnapshot,
488
+ options: CytoscapeEmitOptions,
489
+ ): CytoscapeEmitted {
490
+ const notes = options.notes ?? new StyleNotes();
491
+ const style = emptyCytoscapeStyle();
492
+ const bypasses = {
493
+ node: new Array<Map<string, unknown> | null>(snapshot.nodeCount).fill(null),
494
+ edge: new Array<Map<string, unknown> | null>(snapshot.edgeCount).fill(null),
495
+ };
496
+ const columns = {
497
+ node: new Map<string, readonly (StyleValue | null)[]>(),
498
+ edge: new Map<string, readonly (StyleValue | null)[]>(),
499
+ };
500
+ for (const target of ["node", "edge"] as const) {
501
+ emitTarget(targetEmit(target, extracted, snapshot, options, notes), style, bypasses[target], columns[target]);
502
+ }
503
+ const bg = extracted.styles.graph.background;
504
+ if (bg !== undefined) {
505
+ style.network.defaults.set(
506
+ options.dialect === "cx2" ? "NETWORK_BACKGROUND_COLOR" : "NETWORK_BACKGROUND_PAINT",
507
+ bg.toUpperCase(),
508
+ );
509
+ }
510
+ return { style, bypasses, columns };
511
+ }
512
+
513
+ /** The neutral style one target's Cytoscape style gives, being built. */
514
+ interface Model {
515
+ readonly set: Map<string, StyleValue>;
516
+ readonly map: Map<string, Mapping>;
517
+ readonly mappedBy: Map<string, string>;
518
+ }
519
+
520
+ /** What emitting one target reads. */
521
+ interface TargetEmit {
522
+ readonly target: StyleTarget;
523
+ readonly extracted: ExtractedStyles;
524
+ readonly snapshot: GraphSnapshot;
525
+ readonly options: CytoscapeEmitOptions;
526
+ readonly notes: StyleNotes;
527
+ /** The element count. */
528
+ readonly n: number;
529
+ readonly src: ReturnType<typeof refSource>;
530
+ /** The target's rules. */
531
+ readonly rules: readonly StyleRule[];
532
+ /** Every element's value of each property the rules write. */
533
+ readonly truth: ReadonlyMap<string, (StyleValue | null)[]>;
534
+ /** The approximation note of a property. */
535
+ readonly approx: (p: string) => () => void;
536
+ /** The approximation note of a default, counted over the elements that draw it. */
537
+ readonly approxDefault: (p: string, value: StyleValue) => () => void;
538
+ }
539
+
540
+ /**
541
+ * What emitting one target reads, worked out once.
542
+ * @param target - nodes or edges
543
+ * @param extracted - the styles
544
+ * @param snapshot - the graph
545
+ * @param options - the options
546
+ * @param notes - where losses are noted
547
+ * @returns the target's emission
548
+ */
549
+ function targetEmit(
550
+ target: StyleTarget,
551
+ extracted: ExtractedStyles,
552
+ snapshot: GraphSnapshot,
553
+ options: CytoscapeEmitOptions,
554
+ notes: StyleNotes,
555
+ ): TargetEmit {
556
+ const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
557
+ const src = refSource(target, n, extracted.columns[target], extracted.features[target], snapshot);
558
+ const rules = extracted.styles.rules.filter((r) => r.target === target);
559
+ const written = new Set(rules.flatMap((r) => [...Object.keys(r.set ?? {}), ...Object.keys(r.map ?? {})]));
560
+ const truth = new Map(Array.from(written, (p) => [p, resolveRules(rules, target, p, src)] as const));
561
+ return {
562
+ target,
563
+ extracted,
564
+ snapshot,
565
+ options,
566
+ notes,
567
+ n,
568
+ src,
569
+ rules,
570
+ truth,
571
+ approx: (p) => () => {
572
+ notes.add(STYLE_ISSUE.APPROXIMATED, target, p);
573
+ },
574
+ // a default written as an approximation changes every element that draws it
575
+ approxDefault: (p, value) => () => {
576
+ const count = (truth.get(p) ?? []).filter((v) => v === value).length;
577
+ notes.add(STYLE_ISSUE.APPROXIMATED, target, p, { count: Math.max(count, 1) });
578
+ },
579
+ };
580
+ }
581
+
582
+ /**
583
+ * Emits one target.
584
+ * @param e - the target's emission
585
+ * @param style - the style being written
586
+ * @param bypasses - the target's bypasses, filled in
587
+ * @param columns - the target's added columns, filled in
588
+ */
589
+ function emitTarget(
590
+ e: TargetEmit,
591
+ style: CytoscapeStyle,
592
+ bypasses: (Map<string, unknown> | null)[],
593
+ columns: Map<string, readonly (StyleValue | null)[]>,
594
+ ): void {
595
+ const { target, notes, truth } = e;
596
+ const writers = WRITERS[e.options.dialect][target];
597
+ for (const [p, values] of truth) {
598
+ if (!writers.has(p) && !(FONT_PROPS as readonly string[]).includes(p)) {
599
+ const count = values.filter((v) => v !== null).length;
600
+ notes.add(STYLE_ISSUE.NOT_WRITTEN, target, p, { count: Math.max(count, 1) });
601
+ }
602
+ }
603
+
604
+ // a rule written as per-element values is noted with the elements that carry one, once per property
605
+ const counted = new Set<string>();
606
+ const carrying = (p: string): number => {
607
+ if (counted.has(p)) {
608
+ return 0;
609
+ }
610
+ counted.add(p);
611
+ return Math.max((truth.get(p) ?? []).filter((v) => v !== null).length, 1);
612
+ };
613
+ const model = buildModel(target, e.rules, writers, notes, carrying);
614
+ const s = style[target];
615
+ s.dependencies.set(target === "node" ? "nodeSizeLocked" : "arrowColorMatchesEdge", false);
616
+ for (const [p, w] of writers) {
617
+ if (truth.has(p)) {
618
+ emitProperty(e, s, model, p, w, columns, carrying);
619
+ }
620
+ }
621
+ addBypasses(e, model, writers, bypasses);
622
+ emitFont(e, model, s, bypasses);
623
+ }
624
+
625
+ /**
626
+ * Emits one property the rules write: its default, and its mapping, or the mapping noted as flattened when
627
+ * Cytoscape cannot read the column it needs.
628
+ * @param e - the target's emission
629
+ * @param s - the target's style
630
+ * @param model - the model; a mapping not written is removed
631
+ * @param p - the property
632
+ * @param w - its writer
633
+ * @param columns - where added columns go
634
+ * @param carrying - how many elements carry a value of a property, once
635
+ */
636
+ function emitProperty(
637
+ e: TargetEmit,
638
+ s: CytoscapeStyle["node"],
639
+ model: Model,
640
+ p: string,
641
+ w: Writer,
642
+ columns: Map<string, readonly (StyleValue | null)[]>,
643
+ carrying: (p: string) => number,
644
+ ): void {
645
+ const set = model.set.get(p);
646
+ if (set !== undefined) {
647
+ s.defaults.set(w.vp, w.write(set, e.approxDefault(p, set)));
648
+ }
649
+ let m = model.map.get(p);
650
+ if (m === undefined) {
651
+ return;
652
+ }
653
+ if (e.options.dialect === "cx2" && m.kind === "continuous") {
654
+ m = cx2Points(m, e.approx(p));
655
+ model.map.set(p, m);
656
+ }
657
+ const cm = cytoscapeMapping(e, p, m, w, columns);
658
+ if (cm === null) {
659
+ e.notes.add(STYLE_ISSUE.LAYER_FLATTENED, e.target, p, { sample: model.mappedBy.get(p), count: carrying(p) });
660
+ model.map.delete(p);
661
+ } else {
662
+ s.mappings.set(w.vp, cm);
663
+ }
664
+ }
665
+
666
+ /**
667
+ * A bypass for every element the written style draws differently from the rules.
668
+ * @param e - the target's emission
669
+ * @param model - the defaults and mappings written
670
+ * @param writers - the dialect's writers
671
+ * @param bypasses - the bypasses, filled in
672
+ */
673
+ function addBypasses(
674
+ e: TargetEmit,
675
+ model: Model,
676
+ writers: ReadonlyMap<string, Writer>,
677
+ bypasses: (Map<string, unknown> | null)[],
678
+ ): void {
679
+ const { target } = e;
680
+ const modelRules: StyleRule[] = [
681
+ {
682
+ id: "defaults",
683
+ target,
684
+ role: "defaults",
685
+ origin: "declared",
686
+ where: { all: true },
687
+ set: Object.fromEntries(model.set),
688
+ },
689
+ {
690
+ id: "mappings",
691
+ target,
692
+ role: "mapping",
693
+ origin: "declared",
694
+ where: { all: true },
695
+ map: Object.fromEntries(model.map),
696
+ },
697
+ ];
698
+ for (const [p, w] of writers) {
699
+ const want = e.truth.get(p);
700
+ if (want === undefined) {
701
+ continue;
702
+ }
703
+ const drawn = resolveRules(modelRules, target, p, e.src);
704
+ for (let i = 0; i < e.n; i++) {
705
+ const v = want[i];
706
+ if (v !== null && v !== drawn[i]) {
707
+ bypasses[i] ??= new Map();
708
+ bypasses[i]?.set(w.vp, w.write(v, e.approx(p)));
709
+ }
710
+ }
711
+ }
712
+ }
713
+
714
+ /**
715
+ * The defaults and mappings of one target: rules over every element, bottom to top. A constant over every element
716
+ * hides everything beneath it; of two mappings of one property only the topmost is kept. Every rule that cannot be
717
+ * kept whole is noted as `W_STYLE_LAYER_FLATTENED`.
718
+ * @param target - nodes or edges
719
+ * @param rules - the target's rules
720
+ * @param writers - the dialect's writers
721
+ * @param notes - where flattened rules are noted
722
+ * @param carrying - how many elements carry a value of a property
723
+ * @returns the model
724
+ */
725
+ function buildModel(
726
+ target: StyleTarget,
727
+ rules: readonly StyleRule[],
728
+ writers: ReadonlyMap<string, Writer>,
729
+ notes: StyleNotes,
730
+ carrying: (p: string) => number,
731
+ ): Model {
732
+ const model: Model = { set: new Map(), map: new Map(), mappedBy: new Map() };
733
+ const flatten = (p: string, id: string): void => {
734
+ notes.add(STYLE_ISSUE.LAYER_FLATTENED, target, p, { sample: id, count: carrying(p) });
735
+ };
736
+ const holds = (p: string): boolean => writers.has(p) || (FONT_PROPS as readonly string[]).includes(p);
737
+ const applySet = (r: StyleRule, all: boolean): void => {
738
+ for (const [p, v] of Object.entries(r.set ?? {})) {
739
+ if (v === undefined || !holds(p)) {
740
+ continue;
741
+ }
742
+ if (!all) {
743
+ flatten(p, r.id);
744
+ continue;
745
+ }
746
+ model.set.set(p, v);
747
+ model.map.delete(p);
748
+ model.mappedBy.delete(p);
749
+ }
750
+ };
751
+ const applyMap = (r: StyleRule, all: boolean): void => {
752
+ for (const [p, m] of Object.entries(r.map ?? {})) {
753
+ if (m === undefined || !holds(p)) {
754
+ continue;
755
+ }
756
+ if (!all || (FONT_PROPS as readonly string[]).includes(p)) {
757
+ flatten(p, r.id);
758
+ continue;
759
+ }
760
+ const below = model.mappedBy.get(p);
761
+ if (below !== undefined) {
762
+ flatten(p, below);
763
+ }
764
+ model.map.set(p, m);
765
+ model.mappedBy.set(p, r.id);
766
+ }
767
+ };
768
+ for (const r of rules) {
769
+ const all = "all" in r.where && r.where.all;
770
+ applySet(r, all);
771
+ applyMap(r, all);
772
+ }
773
+ return model;
774
+ }
775
+
776
+ /**
777
+ * One neutral mapping as a Cytoscape mapping.
778
+ * @param e - the target's emission
779
+ * @param p - the property
780
+ * @param m - the mapping
781
+ * @param w - the property's writer
782
+ * @param columns - where added columns go
783
+ * @returns the mapping, or null when Cytoscape cannot read the column it needs
784
+ */
785
+ function cytoscapeMapping(
786
+ e: TargetEmit,
787
+ p: string,
788
+ m: Mapping,
789
+ w: Writer,
790
+ columns: Map<string, readonly (StyleValue | null)[]>,
791
+ ): CytoscapeMapping | null {
792
+ const column = mappingColumn(e, m.by, columns);
793
+ if (column === null) {
794
+ return null;
795
+ }
796
+ const approx = e.approx(p);
797
+ const write = (v: StyleValue): unknown => w.write(v, approx);
798
+ if (m.kind === "passthrough") {
799
+ return passthroughMapping(e, p, m.by, column, write);
800
+ }
801
+ if (m.kind === "discrete") {
802
+ return {
803
+ kind: "discrete",
804
+ column,
805
+ type: typeOf(m.table.map(([k]) => k)),
806
+ table: m.table.map(([k, v]) => [k, write(v)]),
807
+ };
808
+ }
809
+ if (m.kind === "continuous") {
810
+ const points: CytoscapeContinuousPoint[] = m.points.map((pt: ContinuousPoint) => ({
811
+ at: pt.at,
812
+ below: write(pt.below),
813
+ value: write(pt.value),
814
+ above: write(pt.above),
815
+ }));
816
+ return { kind: "continuous", column, type: "double", points };
817
+ }
818
+ return null;
819
+ }
820
+
821
+ /**
822
+ * The column a mapping reads, by its name in the file: a data column's own, else an added column of the style
823
+ * column's or the degree's values.
824
+ * @param e - the target's emission
825
+ * @param ref - the column
826
+ * @param columns - where added columns go
827
+ * @returns the name, or null when the values cannot be written (a degree of an edge)
828
+ */
829
+ function mappingColumn(
830
+ e: TargetEmit,
831
+ ref: ColumnRef,
832
+ columns: Map<string, readonly (StyleValue | null)[]>,
833
+ ): string | null {
834
+ if ("data" in ref) {
835
+ return ref.data;
836
+ }
837
+ const { target } = e;
838
+ const column = (e.options.columnName ?? defaultColumnName)(target, ref);
839
+ let values: readonly (StyleValue | null)[] | null = null;
840
+ if ("style" in ref) {
841
+ values = e.extracted.columns[target].get(ref.style) ?? null;
842
+ } else if ("computed" in ref && target === "node") {
843
+ values = degreeValues(e.snapshot, ref.computed);
844
+ }
845
+ if (values === null) {
846
+ return null;
847
+ }
848
+ columns.set(column, values);
849
+ return column;
850
+ }
851
+
852
+ /**
853
+ * A passthrough as Cytoscape reads it: a passthrough where Cytoscape reads the values as the neutral reading does,
854
+ * else a table over the values the graph holds, in Cytoscape's spelling.
855
+ * @param e - the target's emission
856
+ * @param p - the property
857
+ * @param by - the column
858
+ * @param column - its name in the file
859
+ * @param write - a value in Cytoscape's spelling
860
+ * @returns the mapping
861
+ */
862
+ function passthroughMapping(
863
+ e: TargetEmit,
864
+ p: string,
865
+ by: ColumnRef,
866
+ column: string,
867
+ write: (v: StyleValue) => unknown,
868
+ ): CytoscapeMapping {
869
+ const { target } = e;
870
+ const kind = STYLE_PROPERTIES[target].get(p) ?? "";
871
+ const cells = e.src.column(by) ?? [];
872
+ if (PASSTHROUGH_SAFE.has(kind)) {
873
+ return { kind: "passthrough", column, type: typeOf(Array.from(cells)) };
874
+ }
875
+ const read = "style" in by ? (v: unknown): StyleValue | null => (isScalar(v) ? v : null) : canonicalFor(target, p);
876
+ const seen = new Map<Scalar, unknown>();
877
+ for (let i = 0; i < cells.length; i++) {
878
+ const raw = cells[i];
879
+ const v = isScalar(raw) && !seen.has(raw) ? read(raw) : null;
880
+ if (v !== null) {
881
+ seen.set(raw as Scalar, write(v));
882
+ }
883
+ }
884
+ const table = Array.from(seen);
885
+ return { kind: "discrete", column, type: typeOf(table.map(([k]) => k)), table };
886
+ }
887
+
888
+ /**
889
+ * A continuous mapping as CX2 holds it: CX2's intervals give the value exactly at a point only as the value just
890
+ * below or just above it, so a third value becomes the one below (noted; the elements exactly at it get bypasses).
891
+ * @param m - the mapping
892
+ * @param approx - called when a point changes
893
+ * @returns the mapping CX2 reads back
894
+ */
895
+ function cx2Points(m: Extract<Mapping, { kind: "continuous" }>, approx: () => void): Mapping {
896
+ let changed = false;
897
+ const points = m.points.map((pt) => {
898
+ if (pt.value === pt.below || pt.value === pt.above) {
899
+ return pt;
900
+ }
901
+ changed = true;
902
+ return { ...pt, value: pt.below };
903
+ });
904
+ if (!changed) {
905
+ return m;
906
+ }
907
+ approx();
908
+ return { ...m, points };
909
+ }
910
+
911
+ /**
912
+ * Whether a value is a scalar.
913
+ * @param v - the value
914
+ * @returns true for a string, a finite number or a boolean
915
+ */
916
+ function isScalar(v: unknown): v is Scalar {
917
+ return typeof v === "string" || typeof v === "boolean" || (typeof v === "number" && Number.isFinite(v));
918
+ }
919
+
920
+ /**
921
+ * Cytoscape's attribute type of a set of values: `integer`, `double`, `boolean` or `string`.
922
+ * @param values - the values
923
+ * @returns the type
924
+ */
925
+ function typeOf(values: readonly unknown[]): string {
926
+ let numbers = 0;
927
+ let integers = 0;
928
+ let booleans = 0;
929
+ let all = 0;
930
+ for (const v of values) {
931
+ if (v === null || v === undefined) {
932
+ continue;
933
+ }
934
+ all++;
935
+ if (typeof v === "number") {
936
+ numbers++;
937
+ if (Number.isInteger(v)) {
938
+ integers++;
939
+ }
940
+ } else if (typeof v === "boolean") {
941
+ booleans++;
942
+ }
943
+ }
944
+ if (all > 0 && numbers === all) {
945
+ return integers === all ? "integer" : "double";
946
+ }
947
+ if (all > 0 && booleans === all) {
948
+ return "boolean";
949
+ }
950
+ return "string";
951
+ }
952
+
953
+ /**
954
+ * The font face: a default from the constant font properties, and a bypass for every element whose family, weight
955
+ * or italic differs from it.
956
+ * @param e - the target's emission
957
+ * @param model - the model
958
+ * @param s - the target's style
959
+ * @param bypasses - the bypasses
960
+ */
961
+ function emitFont(
962
+ e: TargetEmit,
963
+ model: Model,
964
+ s: CytoscapeStyle["node"],
965
+ bypasses: (Map<string, unknown> | null)[],
966
+ ): void {
967
+ const { target, truth, n, approx, approxDefault } = e;
968
+ const { dialect } = e.options;
969
+ if (!FONT_PROPS.some((p) => truth.has(p))) {
970
+ return;
971
+ }
972
+ const [fd, wd, id] = FONT_PROPS.map((p) => model.set.get(p) ?? null);
973
+ const vp = FONT_VP[target];
974
+ if (fd !== null || wd !== null || id !== null) {
975
+ s.defaults.set(
976
+ vp,
977
+ fontOut(dialect, fd, wd, id, fd === null ? approx("labelFont") : approxDefault("labelFont", fd)),
978
+ );
979
+ }
980
+ const [ft, wt, it] = FONT_PROPS.map((p) => truth.get(p) ?? null);
981
+ for (let i = 0; i < n; i++) {
982
+ const f = ft?.[i] ?? null;
983
+ const w = wt?.[i] ?? null;
984
+ const t = it?.[i] ?? null;
985
+ const differs = (f !== null && f !== fd) || (w !== null && w !== wd) || (t !== null && t !== id);
986
+ if (differs) {
987
+ bypasses[i] ??= new Map();
988
+ bypasses[i]?.set(vp, fontOut(dialect, f ?? fd, w ?? wd, t ?? id, approx("labelFont")));
989
+ }
990
+ }
991
+ }
992
+
993
+ // ------------------------------------------------------------------ the file forms
994
+
995
+ /**
996
+ * A raw value as text: `,` doubled inside a CX version 1 definition.
997
+ * @param v - the value
998
+ * @returns the text
999
+ */
1000
+ function defText(v: unknown): string {
1001
+ return String(v).replaceAll(",", ",,");
1002
+ }
1003
+
1004
+ /**
1005
+ * A mapping as a CX version 1 definition string.
1006
+ * @param m - the mapping
1007
+ * @returns the definition
1008
+ * @category Styles
1009
+ */
1010
+ export function cx1Definition(m: CytoscapeMapping): string {
1011
+ const parts = [`COL=${defText(m.column)}`, `T=${m.type ?? "string"}`];
1012
+ if (m.kind === "discrete") {
1013
+ m.table.forEach(([k, v], i) => {
1014
+ parts.push(`K=${i}=${defText(k)}`, `V=${i}=${defText(v)}`);
1015
+ });
1016
+ } else if (m.kind === "continuous") {
1017
+ m.points.forEach((p, i) => {
1018
+ parts.push(
1019
+ `L=${i}=${defText(p.below)}`,
1020
+ `E=${i}=${defText(p.value)}`,
1021
+ `G=${i}=${defText(p.above)}`,
1022
+ `OV=${i}=${p.at}`,
1023
+ );
1024
+ });
1025
+ }
1026
+ return parts.join(",");
1027
+ }
1028
+
1029
+ /**
1030
+ * A record of a Map with text keys.
1031
+ * @param map - the map
1032
+ * @param value - how each value is written
1033
+ * @returns the record
1034
+ */
1035
+ function recordOf<V>(map: ReadonlyMap<string, V>, value: (v: V) => unknown): Record<string, unknown> {
1036
+ const out: Record<string, unknown> = {};
1037
+ for (const [k, v] of map) {
1038
+ Object.defineProperty(out, k, { value: value(v), enumerable: true, writable: true, configurable: true });
1039
+ }
1040
+ return out;
1041
+ }
1042
+
1043
+ /**
1044
+ * A style (in the desktop dialect) as the default entries of a CX version 1 `cyVisualProperties` aspect: `network`,
1045
+ * `nodes:default` and `edges:default`, with every value as text and every mapping as a definition string. The
1046
+ * writer adds each element's bypasses as `nodes` / `edges` entries.
1047
+ * @param style - the style
1048
+ * @returns the entries
1049
+ * @category Styles
1050
+ */
1051
+ export function cytoscapeCxAspect(style: CytoscapeStyle): Record<string, unknown>[] {
1052
+ const entry = (of: string, t: CytoscapeStyle["node"]): Record<string, unknown> => ({
1053
+ properties_of: of,
1054
+ properties: recordOf(t.defaults, String),
1055
+ dependencies: recordOf(t.dependencies, String),
1056
+ mappings: recordOf(t.mappings, (m) => ({ type: m.kind.toUpperCase(), definition: cx1Definition(m) })),
1057
+ });
1058
+ return [
1059
+ { properties_of: "network", properties: recordOf(style.network.defaults, String) },
1060
+ entry("nodes:default", style.node),
1061
+ entry("edges:default", style.edge),
1062
+ ];
1063
+ }
1064
+
1065
+ /**
1066
+ * A continuous mapping's points as CX2 intervals: below the first point, between each two, above the last. The
1067
+ * value exactly at a point is the interval that includes it.
1068
+ * @param points - the points
1069
+ * @returns the intervals
1070
+ */
1071
+ function cx2Intervals(points: readonly CytoscapeContinuousPoint[]): Record<string, unknown>[] {
1072
+ const same = (a: unknown, b: unknown): boolean => JSON.stringify(a) === JSON.stringify(b);
1073
+ const out: Record<string, unknown>[] = [];
1074
+ for (let i = 0; i <= points.length; i++) {
1075
+ const lo = points.at(i - 1);
1076
+ const hi = points.at(i);
1077
+ const iv: Record<string, unknown> = {};
1078
+ if (i > 0 && lo !== undefined) {
1079
+ iv.min = lo.at;
1080
+ iv.minVPValue = lo.above;
1081
+ iv.includeMin = same(lo.value, lo.above);
1082
+ }
1083
+ if (i < points.length && hi !== undefined) {
1084
+ iv.max = hi.at;
1085
+ iv.maxVPValue = hi.below;
1086
+ iv.includeMax = !same(hi.value, hi.above) && same(hi.value, hi.below);
1087
+ }
1088
+ out.push(iv);
1089
+ }
1090
+ return out;
1091
+ }
1092
+
1093
+ /**
1094
+ * A style (in the CX2 dialect) as CX2's `visualProperties` and `visualEditorProperties` aspects. The writer adds the
1095
+ * bypasses as `nodeBypasses` / `edgeBypasses`.
1096
+ * @param style - the style
1097
+ * @returns the two aspects
1098
+ * @category Styles
1099
+ */
1100
+ export function cytoscapeCx2Aspects(style: CytoscapeStyle): {
1101
+ /** The `visualProperties` aspect. */
1102
+ visualProperties: Record<string, unknown>[];
1103
+ /** The `visualEditorProperties` aspect. */
1104
+ visualEditorProperties: Record<string, unknown>[];
1105
+ } {
1106
+ const mapping = (m: CytoscapeMapping): Record<string, unknown> => {
1107
+ if (m.kind === "passthrough") {
1108
+ return { type: "PASSTHROUGH", definition: { attribute: m.column } };
1109
+ }
1110
+ if (m.kind === "discrete") {
1111
+ return {
1112
+ type: "DISCRETE",
1113
+ definition: { attribute: m.column, map: m.table.map(([v, vp]) => ({ v, vp })) },
1114
+ };
1115
+ }
1116
+ return { type: "CONTINUOUS", definition: { attribute: m.column, map: cx2Intervals(m.points) } };
1117
+ };
1118
+ const id = (v: unknown): unknown => v;
1119
+ return {
1120
+ visualProperties: [
1121
+ {
1122
+ default: {
1123
+ network: recordOf(style.network.defaults, id),
1124
+ node: recordOf(style.node.defaults, id),
1125
+ edge: recordOf(style.edge.defaults, id),
1126
+ },
1127
+ nodeMapping: recordOf(style.node.mappings, mapping),
1128
+ edgeMapping: recordOf(style.edge.mappings, mapping),
1129
+ },
1130
+ ],
1131
+ visualEditorProperties: [
1132
+ {
1133
+ properties: {
1134
+ nodeSizeLocked: style.node.dependencies.get("nodeSizeLocked") === true,
1135
+ arrowColorMatchesEdge: style.edge.dependencies.get("arrowColorMatchesEdge") === true,
1136
+ },
1137
+ },
1138
+ ],
1139
+ };
1140
+ }
1141
+
1142
+ /**
1143
+ * An attribute pair.
1144
+ * @param name - the attribute
1145
+ * @param value - its value
1146
+ * @returns ` name="value"`, escaped
1147
+ */
1148
+ function attr(name: string, value: unknown): string {
1149
+ return ` ${name}="${escapeXmlAttribute(String(value))}"`;
1150
+ }
1151
+
1152
+ /**
1153
+ * A style (in the desktop dialect) as one `<visualStyle>` element of a session's `session_vizmap.xml`.
1154
+ * @param style - the style
1155
+ * @param name - the style's name
1156
+ * @returns the element's XML
1157
+ * @category Styles
1158
+ */
1159
+ export function cytoscapeVizmapXml(style: CytoscapeStyle, name: string): string {
1160
+ const lines = [`<visualStyle${attr("name", name)}>`];
1161
+ const section = (tag: string, defaults: ReadonlyMap<string, unknown>, t?: CytoscapeStyle["node"]): void => {
1162
+ lines.push(` <${tag}>`);
1163
+ for (const [dep, on] of t?.dependencies ?? []) {
1164
+ lines.push(` <dependency${attr("name", dep)}${attr("value", on)}/>`);
1165
+ }
1166
+ const vps = new Set([...defaults.keys(), ...(t?.mappings.keys() ?? [])]);
1167
+ for (const vp of vps) {
1168
+ const d = defaults.has(vp) ? attr("default", defaults.get(vp)) : "";
1169
+ const m = t?.mappings.get(vp);
1170
+ if (m === undefined) {
1171
+ lines.push(` <visualProperty${attr("name", vp)}${d}/>`);
1172
+ } else {
1173
+ lines.push(` <visualProperty${attr("name", vp)}${d}>`, ...mappingXml(m), " </visualProperty>");
1174
+ }
1175
+ }
1176
+ lines.push(` </${tag}>`);
1177
+ };
1178
+ section("network", style.network.defaults);
1179
+ section("node", style.node.defaults, style.node);
1180
+ section("edge", style.edge.defaults, style.edge);
1181
+ lines.push("</visualStyle>");
1182
+ return lines.join("\n");
1183
+ }
1184
+
1185
+ /**
1186
+ * A mapping as the lines of its `session_vizmap.xml` element.
1187
+ * @param m - the mapping
1188
+ * @returns the lines
1189
+ */
1190
+ function mappingXml(m: CytoscapeMapping): string[] {
1191
+ const head = `${attr("attributeName", m.column)}${attr("attributeType", m.kind === "continuous" ? "float" : (m.type ?? "string"))}`;
1192
+ if (m.kind === "passthrough") {
1193
+ return [` <passthroughMapping${head}/>`];
1194
+ }
1195
+ if (m.kind === "discrete") {
1196
+ return [
1197
+ ` <discreteMapping${head}>`,
1198
+ ...m.table.map(
1199
+ ([k, v]) => ` <discreteMappingEntry${attr("attributeValue", k)}${attr("value", v)}/>`,
1200
+ ),
1201
+ " </discreteMapping>",
1202
+ ];
1203
+ }
1204
+ return [
1205
+ ` <continuousMapping${head}>`,
1206
+ ...m.points.map(
1207
+ (p) =>
1208
+ ` <continuousMappingPoint${attr("attrValue", p.at)}${attr("lesserValue", p.below)}${attr("equalValue", p.value)}${attr("greaterValue", p.above)}/>`,
1209
+ ),
1210
+ " </continuousMapping>",
1211
+ ];
1212
+ }
1213
+
1214
+ // ------------------------------------------------------------------ per-element values
1215
+
1216
+ /**
1217
+ * The neutral properties a Cytoscape dialect holds per element: what a writer of resolved per-element values (XGMML
1218
+ * graphics, a session view's bypasses) can write.
1219
+ * @param target - nodes or edges
1220
+ * @param dialect - the spelling
1221
+ * @returns the properties
1222
+ */
1223
+ export function cytoscapeWritableProperties(target: StyleTarget, dialect: CytoscapeDialect): string[] {
1224
+ return [...WRITERS[dialect][target].keys(), ...FONT_PROPS];
1225
+ }
1226
+
1227
+ /**
1228
+ * One element's values as Cytoscape visual properties in a dialect's spelling, for a writer of per-element values.
1229
+ * The label font, weight and italic are written together as one font face.
1230
+ * @param target - nodes or edges
1231
+ * @param value - the element's value of a neutral property, or null
1232
+ * @param dialect - the spelling
1233
+ * @param approx - called with the property when a value is written as its nearest
1234
+ * @returns visual property name to raw value
1235
+ */
1236
+ export function cytoscapeElementProperties(
1237
+ target: StyleTarget,
1238
+ value: (property: string) => StyleValue | null,
1239
+ dialect: CytoscapeDialect,
1240
+ approx: (property: string) => void,
1241
+ ): Map<string, unknown> {
1242
+ const out = new Map<string, unknown>();
1243
+ for (const [p, w] of WRITERS[dialect][target]) {
1244
+ const v = value(p);
1245
+ if (v !== null) {
1246
+ out.set(
1247
+ w.vp,
1248
+ w.write(v, () => {
1249
+ approx(p);
1250
+ }),
1251
+ );
1252
+ }
1253
+ }
1254
+ const [f, wt, it] = FONT_PROPS.map((p) => value(p));
1255
+ if (f !== null || wt !== null || it !== null) {
1256
+ out.set(
1257
+ FONT_VP[target],
1258
+ fontOut(dialect, f, wt, it, () => {
1259
+ approx("labelFont");
1260
+ }),
1261
+ );
1262
+ }
1263
+ return out;
1264
+ }