@graphty/graph-io 0.3.48 → 0.3.50

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 (600) hide show
  1. package/dist/chunks/{approximations-C2riR4NT.js → approximations-BRXsll0T.js} +2 -2
  2. package/dist/chunks/{approximations-C2riR4NT.js.map → approximations-BRXsll0T.js.map} +1 -1
  3. package/dist/chunks/bends-BGXjv_U4.js +129 -0
  4. package/dist/chunks/bends-BGXjv_U4.js.map +1 -0
  5. package/dist/chunks/{block-Cu22ewuS.js → block-yl0f_qLO.js} +931 -164
  6. package/dist/chunks/block-yl0f_qLO.js.map +1 -0
  7. package/dist/chunks/choose-CczZJaX_.js +717 -0
  8. package/dist/chunks/choose-CczZJaX_.js.map +1 -0
  9. package/dist/chunks/{codes-CV_Bhcyi.js → codes-BE2xir2k.js} +6 -2
  10. package/dist/chunks/codes-BE2xir2k.js.map +1 -0
  11. package/dist/chunks/{constants-D0LqcsLJ.js → constants-CKkYLPtt.js} +2 -2
  12. package/dist/chunks/{constants-D0LqcsLJ.js.map → constants-CKkYLPtt.js.map} +1 -1
  13. package/dist/chunks/{context-cgmmOxZ2.js → context-DizmppHy.js} +5 -4
  14. package/dist/chunks/{context-cgmmOxZ2.js.map → context-DizmppHy.js.map} +1 -1
  15. package/dist/chunks/{convert-BcQ8_jMo.js → convert-Ct2zelbQ.js} +7 -2
  16. package/dist/chunks/convert-Ct2zelbQ.js.map +1 -0
  17. package/dist/chunks/{credentials-BiQsnMTb.js → credentials-CmvJXYeH.js} +67 -16
  18. package/dist/chunks/credentials-CmvJXYeH.js.map +1 -0
  19. package/dist/chunks/{cx-export-BEeF6ymo.js → cx-export-Bt6NoqTQ.js} +3 -3
  20. package/dist/chunks/{cx-export-BEeF6ymo.js.map → cx-export-Bt6NoqTQ.js.map} +1 -1
  21. package/dist/chunks/descriptor-B28U25x4.js +60 -0
  22. package/dist/chunks/descriptor-B28U25x4.js.map +1 -0
  23. package/dist/chunks/{descriptor-BJ-W0Jd-.js → descriptor-Dhez_Cew.js} +11 -2
  24. package/dist/chunks/descriptor-Dhez_Cew.js.map +1 -0
  25. package/dist/chunks/{emit-CD8xnY8s.js → emit-7Jkpg0yd.js} +1788 -133
  26. package/dist/chunks/emit-7Jkpg0yd.js.map +1 -0
  27. package/dist/chunks/{escape-DH2Egt-l.js → escape-CuCIzNMB.js} +8 -8
  28. package/dist/chunks/{escape-DH2Egt-l.js.map → escape-CuCIzNMB.js.map} +1 -1
  29. package/dist/chunks/{export-CZ7VRm4I.js → export-Cha4dRFz.js} +45 -387
  30. package/dist/chunks/export-Cha4dRFz.js.map +1 -0
  31. package/dist/chunks/{exporter-ChjL5fmP.js → exporter-1Ajud15J.js} +9 -8
  32. package/dist/chunks/{exporter-ChjL5fmP.js.map → exporter-1Ajud15J.js.map} +1 -1
  33. package/dist/chunks/{exporter-BIdaoIgh.js → exporter-B6yvL3wy.js} +7 -7
  34. package/dist/chunks/{exporter-BIdaoIgh.js.map → exporter-B6yvL3wy.js.map} +1 -1
  35. package/dist/chunks/{exporter-Dcw7Ldtq.js → exporter-BP6otMO6.js} +6 -6
  36. package/dist/chunks/{exporter-Dcw7Ldtq.js.map → exporter-BP6otMO6.js.map} +1 -1
  37. package/dist/chunks/{exporter-DtVU80Fa.js → exporter-CA5q8ROP.js} +9 -8
  38. package/dist/chunks/{exporter-DtVU80Fa.js.map → exporter-CA5q8ROP.js.map} +1 -1
  39. package/dist/chunks/{exporter-bxxjOrDZ.js → exporter-CQQZFJ2v.js} +1284 -156
  40. package/dist/chunks/exporter-CQQZFJ2v.js.map +1 -0
  41. package/dist/chunks/format-BXiAb-1h.js +408 -0
  42. package/dist/chunks/format-BXiAb-1h.js.map +1 -0
  43. package/dist/chunks/{generic-CP6YD34u.js → generic-Dc21MAdg.js} +387 -31
  44. package/dist/chunks/generic-Dc21MAdg.js.map +1 -0
  45. package/dist/chunks/{importer-B87_fmVx.js → importer-C6NVpCXp.js} +327 -84
  46. package/dist/chunks/importer-C6NVpCXp.js.map +1 -0
  47. package/dist/chunks/{importer-DPZDN40-.js → importer-CKFe4q54.js} +25 -17
  48. package/dist/chunks/importer-CKFe4q54.js.map +1 -0
  49. package/dist/chunks/{importer-Bji2MeWr.js → importer-CKbUbsJA.js} +13 -10
  50. package/dist/chunks/{importer-Bji2MeWr.js.map → importer-CKbUbsJA.js.map} +1 -1
  51. package/dist/chunks/{importer-CB_zV5_L.js → importer-ClCsdgTJ.js} +208 -1104
  52. package/dist/chunks/importer-ClCsdgTJ.js.map +1 -0
  53. package/dist/chunks/{importer-e3huMZbm.js → importer-CyVZTrf0.js} +14 -10
  54. package/dist/chunks/importer-CyVZTrf0.js.map +1 -0
  55. package/dist/chunks/{importer-CVT-JyDE.js → importer-DgG4v1Ms.js} +1537 -355
  56. package/dist/chunks/importer-DgG4v1Ms.js.map +1 -0
  57. package/dist/chunks/{importer-Cyy6UseH.js → importer-asFUrIsj.js} +66 -13
  58. package/dist/chunks/importer-asFUrIsj.js.map +1 -0
  59. package/dist/chunks/{input-Ccg9I22A.js → input-CAxt_aV-.js} +4 -4
  60. package/dist/chunks/{input-Ccg9I22A.js.map → input-CAxt_aV-.js.map} +1 -1
  61. package/dist/chunks/{json-elements-By172SVq.js → json-elements-Cq76WjnZ.js} +4 -4
  62. package/dist/chunks/{json-elements-By172SVq.js.map → json-elements-Cq76WjnZ.js.map} +1 -1
  63. package/dist/chunks/{lexicon-u2SI5xcx.js → lexicon-IVRq2Uoo.js} +282 -74
  64. package/dist/chunks/lexicon-IVRq2Uoo.js.map +1 -0
  65. package/dist/chunks/{ontology-export-BwgMyuF0.js → ontology-export-OMo91mb0.js} +4 -4
  66. package/dist/chunks/{ontology-export-BwgMyuF0.js.map → ontology-export-OMo91mb0.js.map} +1 -1
  67. package/dist/chunks/{records-Cgvxz-Vx.js → records-NJiYvekn.js} +2 -2
  68. package/dist/chunks/{records-Cgvxz-Vx.js.map → records-NJiYvekn.js.map} +1 -1
  69. package/dist/chunks/{report-CgdsL9Hb.js → report-CwMfYa9J.js} +23 -23
  70. package/dist/chunks/{report-CgdsL9Hb.js.map → report-CwMfYa9J.js.map} +1 -1
  71. package/dist/chunks/round-trip-Cy3_SHRZ.js +79 -0
  72. package/dist/chunks/round-trip-Cy3_SHRZ.js.map +1 -0
  73. package/dist/chunks/{sigv4-LkAzsLAu.js → sigv4-BXwVbza3.js} +5 -5
  74. package/dist/chunks/sigv4-BXwVbza3.js.map +1 -0
  75. package/dist/chunks/{styles-CaABMoO1.js → styles-CAVmBLgp.js} +1237 -138
  76. package/dist/chunks/styles-CAVmBLgp.js.map +1 -0
  77. package/dist/chunks/styles-CPczw1Jz.js +1928 -0
  78. package/dist/chunks/styles-CPczw1Jz.js.map +1 -0
  79. package/dist/chunks/styles-boK6Ln54.js +2847 -0
  80. package/dist/chunks/styles-boK6Ln54.js.map +1 -0
  81. package/dist/chunks/{styles-yed-Cs1nmrjn.js → styles-yed-XeFBQ6VS.js} +506 -175
  82. package/dist/chunks/styles-yed-XeFBQ6VS.js.map +1 -0
  83. package/dist/chunks/{text-BuKQVMz2.js → text-ystE2bYi.js} +2 -2
  84. package/dist/chunks/{text-BuKQVMz2.js.map → text-ystE2bYi.js.map} +1 -1
  85. package/dist/csv.js +7 -7
  86. package/dist/cx.js +2 -2
  87. package/dist/cx2.js +2 -2
  88. package/dist/cys.js +1 -1
  89. package/dist/dot.js +8 -4
  90. package/dist/dot.js.map +1 -1
  91. package/dist/gexf.js +42 -12
  92. package/dist/gexf.js.map +1 -1
  93. package/dist/gml.js +848 -194
  94. package/dist/gml.js.map +1 -1
  95. package/dist/graph-io.js +230 -193
  96. package/dist/graph-io.js.map +1 -1
  97. package/dist/graphml.js +3 -3
  98. package/dist/json.js +3 -2
  99. package/dist/neo4j.js +6 -6
  100. package/dist/obo.js +1 -1
  101. package/dist/pajek.js +1 -1
  102. package/dist/positions.js +45 -3
  103. package/dist/positions.js.map +1 -1
  104. package/dist/sources-cypher.js +141 -23
  105. package/dist/sources-cypher.js.map +1 -1
  106. package/dist/sources-ndex.js +2 -2
  107. package/dist/sources-rest.js +32 -6
  108. package/dist/sources-rest.js.map +1 -1
  109. package/dist/sources-sparql.d.ts +1 -0
  110. package/dist/sources-sparql.js +439 -0
  111. package/dist/sources-sparql.js.map +1 -0
  112. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  113. package/dist/src/formats/cx/importer.js +5 -1
  114. package/dist/src/formats/cx/importer.js.map +1 -1
  115. package/dist/src/formats/cx/styles.d.ts +24 -1
  116. package/dist/src/formats/cx/styles.d.ts.map +1 -1
  117. package/dist/src/formats/cx/styles.js +117 -11
  118. package/dist/src/formats/cx/styles.js.map +1 -1
  119. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  120. package/dist/src/formats/cx2/importer.js +1 -0
  121. package/dist/src/formats/cx2/importer.js.map +1 -1
  122. package/dist/src/formats/cx2/styles.d.ts.map +1 -1
  123. package/dist/src/formats/cx2/styles.js +3 -1
  124. package/dist/src/formats/cx2/styles.js.map +1 -1
  125. package/dist/src/formats/cys/importer.d.ts.map +1 -1
  126. package/dist/src/formats/cys/importer.js +1 -0
  127. package/dist/src/formats/cys/importer.js.map +1 -1
  128. package/dist/src/formats/cys/styles.d.ts +2 -0
  129. package/dist/src/formats/cys/styles.d.ts.map +1 -1
  130. package/dist/src/formats/cys/styles.js +15 -7
  131. package/dist/src/formats/cys/styles.js.map +1 -1
  132. package/dist/src/formats/dot/drawing.d.ts +98 -0
  133. package/dist/src/formats/dot/drawing.d.ts.map +1 -0
  134. package/dist/src/formats/dot/drawing.js +734 -0
  135. package/dist/src/formats/dot/drawing.js.map +1 -0
  136. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  137. package/dist/src/formats/dot/exporter.js +34 -2
  138. package/dist/src/formats/dot/exporter.js.map +1 -1
  139. package/dist/src/formats/dot/importer.d.ts +6 -1
  140. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  141. package/dist/src/formats/dot/importer.js +170 -33
  142. package/dist/src/formats/dot/importer.js.map +1 -1
  143. package/dist/src/formats/dot/index.d.ts +3 -1
  144. package/dist/src/formats/dot/index.d.ts.map +1 -1
  145. package/dist/src/formats/dot/index.js +2 -1
  146. package/dist/src/formats/dot/index.js.map +1 -1
  147. package/dist/src/formats/dot/label-places.d.ts +41 -0
  148. package/dist/src/formats/dot/label-places.d.ts.map +1 -0
  149. package/dist/src/formats/dot/label-places.js +185 -0
  150. package/dist/src/formats/dot/label-places.js.map +1 -0
  151. package/dist/src/formats/dot/names.d.ts +13 -0
  152. package/dist/src/formats/dot/names.d.ts.map +1 -1
  153. package/dist/src/formats/dot/names.js +13 -0
  154. package/dist/src/formats/dot/names.js.map +1 -1
  155. package/dist/src/formats/dot/record.d.ts +50 -0
  156. package/dist/src/formats/dot/record.d.ts.map +1 -0
  157. package/dist/src/formats/dot/record.js +275 -0
  158. package/dist/src/formats/dot/record.js.map +1 -0
  159. package/dist/src/formats/dot/styles.d.ts +72 -2
  160. package/dist/src/formats/dot/styles.d.ts.map +1 -1
  161. package/dist/src/formats/dot/styles.js +1500 -119
  162. package/dist/src/formats/dot/styles.js.map +1 -1
  163. package/dist/src/formats/gexf/dialog-metrics.d.ts +15 -0
  164. package/dist/src/formats/gexf/dialog-metrics.d.ts.map +1 -0
  165. package/dist/src/formats/gexf/dialog-metrics.js +59 -0
  166. package/dist/src/formats/gexf/dialog-metrics.js.map +1 -0
  167. package/dist/src/formats/gexf/importer.d.ts +7 -0
  168. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  169. package/dist/src/formats/gexf/importer.js +41 -5
  170. package/dist/src/formats/gexf/importer.js.map +1 -1
  171. package/dist/src/formats/gexf/preview.d.ts +77 -0
  172. package/dist/src/formats/gexf/preview.d.ts.map +1 -0
  173. package/dist/src/formats/gexf/preview.js +191 -0
  174. package/dist/src/formats/gexf/preview.js.map +1 -0
  175. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  176. package/dist/src/formats/gexf/schema.js +2 -1
  177. package/dist/src/formats/gexf/schema.js.map +1 -1
  178. package/dist/src/formats/gexf/styles.d.ts +58 -31
  179. package/dist/src/formats/gexf/styles.d.ts.map +1 -1
  180. package/dist/src/formats/gexf/styles.js +848 -225
  181. package/dist/src/formats/gexf/styles.js.map +1 -1
  182. package/dist/src/formats/gml/importer.d.ts +5 -2
  183. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  184. package/dist/src/formats/gml/importer.js +258 -71
  185. package/dist/src/formats/gml/importer.js.map +1 -1
  186. package/dist/src/formats/gml/index.d.ts +1 -1
  187. package/dist/src/formats/gml/index.d.ts.map +1 -1
  188. package/dist/src/formats/gml/index.js +1 -1
  189. package/dist/src/formats/gml/index.js.map +1 -1
  190. package/dist/src/formats/gml/positions.d.ts +7 -0
  191. package/dist/src/formats/gml/positions.d.ts.map +1 -1
  192. package/dist/src/formats/gml/positions.js +29 -7
  193. package/dist/src/formats/gml/positions.js.map +1 -1
  194. package/dist/src/formats/gml/styles.d.ts.map +1 -1
  195. package/dist/src/formats/gml/styles.js +754 -102
  196. package/dist/src/formats/gml/styles.js.map +1 -1
  197. package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -1
  198. package/dist/src/formats/graphml/styles-plain.js +6 -10
  199. package/dist/src/formats/graphml/styles-plain.js.map +1 -1
  200. package/dist/src/formats/graphml/styles-yed.d.ts +21 -15
  201. package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -1
  202. package/dist/src/formats/graphml/styles-yed.js +695 -164
  203. package/dist/src/formats/graphml/styles-yed.js.map +1 -1
  204. package/dist/src/formats/graphml/styles-yfiles3.d.ts +1 -1
  205. package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -1
  206. package/dist/src/formats/graphml/styles-yfiles3.js +431 -64
  207. package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -1
  208. package/dist/src/formats/graphml/yfiles.d.ts +3 -3
  209. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  210. package/dist/src/formats/graphml/yfiles.js +16 -6
  211. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  212. package/dist/src/formats/json/dialect.d.ts +3 -2
  213. package/dist/src/formats/json/dialect.d.ts.map +1 -1
  214. package/dist/src/formats/json/dialect.js +39 -5
  215. package/dist/src/formats/json/dialect.js.map +1 -1
  216. package/dist/src/formats/json/importer.d.ts +13 -2
  217. package/dist/src/formats/json/importer.d.ts.map +1 -1
  218. package/dist/src/formats/json/importer.js +42 -6
  219. package/dist/src/formats/json/importer.js.map +1 -1
  220. package/dist/src/formats/json/index.d.ts +1 -1
  221. package/dist/src/formats/json/index.d.ts.map +1 -1
  222. package/dist/src/formats/json/index.js +1 -1
  223. package/dist/src/formats/json/index.js.map +1 -1
  224. package/dist/src/formats/json/positions.d.ts +5 -2
  225. package/dist/src/formats/json/positions.d.ts.map +1 -1
  226. package/dist/src/formats/json/positions.js +20 -4
  227. package/dist/src/formats/json/positions.js.map +1 -1
  228. package/dist/src/formats/json/styles.d.ts +12 -1
  229. package/dist/src/formats/json/styles.d.ts.map +1 -1
  230. package/dist/src/formats/json/styles.js +1843 -136
  231. package/dist/src/formats/json/styles.js.map +1 -1
  232. package/dist/src/formats/pajek/draw.d.ts +104 -0
  233. package/dist/src/formats/pajek/draw.d.ts.map +1 -0
  234. package/dist/src/formats/pajek/draw.js +382 -0
  235. package/dist/src/formats/pajek/draw.js.map +1 -0
  236. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  237. package/dist/src/formats/pajek/exporter.js +70 -51
  238. package/dist/src/formats/pajek/exporter.js.map +1 -1
  239. package/dist/src/formats/pajek/importer.d.ts +9 -0
  240. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  241. package/dist/src/formats/pajek/importer.js +233 -102
  242. package/dist/src/formats/pajek/importer.js.map +1 -1
  243. package/dist/src/formats/pajek/index.d.ts +1 -0
  244. package/dist/src/formats/pajek/index.d.ts.map +1 -1
  245. package/dist/src/formats/pajek/index.js.map +1 -1
  246. package/dist/src/formats/pajek/styles.d.ts +15 -7
  247. package/dist/src/formats/pajek/styles.d.ts.map +1 -1
  248. package/dist/src/formats/pajek/styles.js +1395 -114
  249. package/dist/src/formats/pajek/styles.js.map +1 -1
  250. package/dist/src/formats/pajek/syntax.d.ts +10 -1
  251. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  252. package/dist/src/formats/pajek/syntax.js +26 -2
  253. package/dist/src/formats/pajek/syntax.js.map +1 -1
  254. package/dist/src/formats/xgmml/emit.d.ts +1 -0
  255. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  256. package/dist/src/formats/xgmml/emit.js +65 -5
  257. package/dist/src/formats/xgmml/emit.js.map +1 -1
  258. package/dist/src/formats/xgmml/styles.d.ts +23 -3
  259. package/dist/src/formats/xgmml/styles.d.ts.map +1 -1
  260. package/dist/src/formats/xgmml/styles.js +277 -60
  261. package/dist/src/formats/xgmml/styles.js.map +1 -1
  262. package/dist/src/index.d.ts +6 -5
  263. package/dist/src/index.d.ts.map +1 -1
  264. package/dist/src/index.js +6 -5
  265. package/dist/src/index.js.map +1 -1
  266. package/dist/src/positions/convert.d.ts.map +1 -1
  267. package/dist/src/positions/convert.js +11 -1
  268. package/dist/src/positions/convert.js.map +1 -1
  269. package/dist/src/positions/extract.d.ts.map +1 -1
  270. package/dist/src/positions/extract.js +53 -1
  271. package/dist/src/positions/extract.js.map +1 -1
  272. package/dist/src/positions/index.d.ts +1 -1
  273. package/dist/src/positions/index.d.ts.map +1 -1
  274. package/dist/src/positions/types.d.ts +59 -0
  275. package/dist/src/positions/types.d.ts.map +1 -1
  276. package/dist/src/sources/builtins.d.ts +1 -1
  277. package/dist/src/sources/builtins.d.ts.map +1 -1
  278. package/dist/src/sources/builtins.js +8 -2
  279. package/dist/src/sources/builtins.js.map +1 -1
  280. package/dist/src/sources/context.d.ts.map +1 -1
  281. package/dist/src/sources/context.js +1 -0
  282. package/dist/src/sources/context.js.map +1 -1
  283. package/dist/src/sources/credentials.d.ts +50 -1
  284. package/dist/src/sources/credentials.d.ts.map +1 -1
  285. package/dist/src/sources/credentials.js +69 -0
  286. package/dist/src/sources/credentials.js.map +1 -1
  287. package/dist/src/sources/cypher/descriptor.d.ts.map +1 -1
  288. package/dist/src/sources/cypher/descriptor.js +5 -0
  289. package/dist/src/sources/cypher/descriptor.js.map +1 -1
  290. package/dist/src/sources/cypher/index.d.ts.map +1 -1
  291. package/dist/src/sources/cypher/index.js +125 -18
  292. package/dist/src/sources/cypher/index.js.map +1 -1
  293. package/dist/src/sources/cypher/params.d.ts +1 -1
  294. package/dist/src/sources/cypher/params.d.ts.map +1 -1
  295. package/dist/src/sources/cypher/query-builder.d.ts +17 -0
  296. package/dist/src/sources/cypher/query-builder.d.ts.map +1 -1
  297. package/dist/src/sources/cypher/query-builder.js +31 -0
  298. package/dist/src/sources/cypher/query-builder.js.map +1 -1
  299. package/dist/src/sources/ids.d.ts +5 -0
  300. package/dist/src/sources/ids.d.ts.map +1 -1
  301. package/dist/src/sources/ids.js +5 -0
  302. package/dist/src/sources/ids.js.map +1 -1
  303. package/dist/src/sources/params.d.ts +3 -0
  304. package/dist/src/sources/params.d.ts.map +1 -1
  305. package/dist/src/sources/rest/descriptor.d.ts.map +1 -1
  306. package/dist/src/sources/rest/descriptor.js +9 -0
  307. package/dist/src/sources/rest/descriptor.js.map +1 -1
  308. package/dist/src/sources/rest/openalex.d.ts +4 -2
  309. package/dist/src/sources/rest/openalex.d.ts.map +1 -1
  310. package/dist/src/sources/rest/openalex.js +4 -2
  311. package/dist/src/sources/rest/openalex.js.map +1 -1
  312. package/dist/src/sources/rest/source.d.ts.map +1 -1
  313. package/dist/src/sources/rest/source.js +38 -0
  314. package/dist/src/sources/rest/source.js.map +1 -1
  315. package/dist/src/sources/run.d.ts +2 -0
  316. package/dist/src/sources/run.d.ts.map +1 -1
  317. package/dist/src/sources/run.js +30 -3
  318. package/dist/src/sources/run.js.map +1 -1
  319. package/dist/src/sources/sigv4.d.ts +5 -3
  320. package/dist/src/sources/sigv4.d.ts.map +1 -1
  321. package/dist/src/sources/sigv4.js +6 -4
  322. package/dist/src/sources/sigv4.js.map +1 -1
  323. package/dist/src/sources/sparql/descriptor.d.ts +10 -0
  324. package/dist/src/sources/sparql/descriptor.d.ts.map +1 -0
  325. package/dist/src/sources/sparql/descriptor.js +64 -0
  326. package/dist/src/sources/sparql/descriptor.js.map +1 -0
  327. package/dist/src/sources/sparql/index.d.ts +28 -0
  328. package/dist/src/sources/sparql/index.d.ts.map +1 -0
  329. package/dist/src/sources/sparql/index.js +327 -0
  330. package/dist/src/sources/sparql/index.js.map +1 -0
  331. package/dist/src/sources/sparql/ntriples.d.ts +69 -0
  332. package/dist/src/sources/sparql/ntriples.d.ts.map +1 -0
  333. package/dist/src/sources/sparql/ntriples.js +180 -0
  334. package/dist/src/sources/sparql/ntriples.js.map +1 -0
  335. package/dist/src/sources/sparql/params.d.ts +27 -0
  336. package/dist/src/sources/sparql/params.d.ts.map +1 -0
  337. package/dist/src/sources/sparql/params.js +5 -0
  338. package/dist/src/sources/sparql/params.js.map +1 -0
  339. package/dist/src/sources/sparql/query.d.ts +73 -0
  340. package/dist/src/sources/sparql/query.d.ts.map +1 -0
  341. package/dist/src/sources/sparql/query.js +164 -0
  342. package/dist/src/sources/sparql/query.js.map +1 -0
  343. package/dist/src/sources/types.d.ts +60 -0
  344. package/dist/src/sources/types.d.ts.map +1 -1
  345. package/dist/src/styles/annotations.d.ts +61 -5
  346. package/dist/src/styles/annotations.d.ts.map +1 -1
  347. package/dist/src/styles/annotations.js +130 -22
  348. package/dist/src/styles/annotations.js.map +1 -1
  349. package/dist/src/styles/canon/bends.d.ts +46 -0
  350. package/dist/src/styles/canon/bends.d.ts.map +1 -1
  351. package/dist/src/styles/canon/bends.js +123 -0
  352. package/dist/src/styles/canon/bends.js.map +1 -1
  353. package/dist/src/styles/canon/color-tables.d.ts +6 -1
  354. package/dist/src/styles/canon/color-tables.d.ts.map +1 -1
  355. package/dist/src/styles/canon/color-tables.js +10 -4
  356. package/dist/src/styles/canon/color-tables.js.map +1 -1
  357. package/dist/src/styles/canon/color.d.ts +3 -1
  358. package/dist/src/styles/canon/color.d.ts.map +1 -1
  359. package/dist/src/styles/canon/color.js +103 -3
  360. package/dist/src/styles/canon/color.js.map +1 -1
  361. package/dist/src/styles/canon/enums.d.ts +2 -2
  362. package/dist/src/styles/canon/enums.d.ts.map +1 -1
  363. package/dist/src/styles/canon/enums.js +89 -7
  364. package/dist/src/styles/canon/enums.js.map +1 -1
  365. package/dist/src/styles/canon/graphviz.d.ts +12 -3
  366. package/dist/src/styles/canon/graphviz.d.ts.map +1 -1
  367. package/dist/src/styles/canon/graphviz.js +57 -9
  368. package/dist/src/styles/canon/graphviz.js.map +1 -1
  369. package/dist/src/styles/canon/number.d.ts +8 -0
  370. package/dist/src/styles/canon/number.d.ts.map +1 -1
  371. package/dist/src/styles/canon/number.js +18 -0
  372. package/dist/src/styles/canon/number.js.map +1 -1
  373. package/dist/src/styles/canon/text.d.ts +29 -2
  374. package/dist/src/styles/canon/text.d.ts.map +1 -1
  375. package/dist/src/styles/canon/text.js +156 -16
  376. package/dist/src/styles/canon/text.js.map +1 -1
  377. package/dist/src/styles/codes.d.ts +2 -0
  378. package/dist/src/styles/codes.d.ts.map +1 -1
  379. package/dist/src/styles/codes.js +2 -0
  380. package/dist/src/styles/codes.js.map +1 -1
  381. package/dist/src/styles/cyjs/cascade.d.ts.map +1 -1
  382. package/dist/src/styles/cyjs/cascade.js +1139 -79
  383. package/dist/src/styles/cyjs/cascade.js.map +1 -1
  384. package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -1
  385. package/dist/src/styles/cyjs/defaults-table.js +4 -0
  386. package/dist/src/styles/cyjs/defaults-table.js.map +1 -1
  387. package/dist/src/styles/cyjs/emit.d.ts.map +1 -1
  388. package/dist/src/styles/cyjs/emit.js +232 -10
  389. package/dist/src/styles/cyjs/emit.js.map +1 -1
  390. package/dist/src/styles/cyjs/face.d.ts +20 -0
  391. package/dist/src/styles/cyjs/face.d.ts.map +1 -0
  392. package/dist/src/styles/cyjs/face.js +332 -0
  393. package/dist/src/styles/cyjs/face.js.map +1 -0
  394. package/dist/src/styles/cyjs/mappers.d.ts +44 -1
  395. package/dist/src/styles/cyjs/mappers.d.ts.map +1 -1
  396. package/dist/src/styles/cyjs/mappers.js +475 -26
  397. package/dist/src/styles/cyjs/mappers.js.map +1 -1
  398. package/dist/src/styles/cyjs/routes.d.ts +53 -0
  399. package/dist/src/styles/cyjs/routes.d.ts.map +1 -0
  400. package/dist/src/styles/cyjs/routes.js +360 -0
  401. package/dist/src/styles/cyjs/routes.js.map +1 -0
  402. package/dist/src/styles/cytoscape/charts.d.ts +23 -0
  403. package/dist/src/styles/cytoscape/charts.d.ts.map +1 -0
  404. package/dist/src/styles/cytoscape/charts.js +560 -0
  405. package/dist/src/styles/cytoscape/charts.js.map +1 -0
  406. package/dist/src/styles/cytoscape/emit.d.ts.map +1 -1
  407. package/dist/src/styles/cytoscape/emit.js +156 -48
  408. package/dist/src/styles/cytoscape/emit.js.map +1 -1
  409. package/dist/src/styles/cytoscape/lexicon.d.ts +91 -3
  410. package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -1
  411. package/dist/src/styles/cytoscape/lexicon.js +387 -77
  412. package/dist/src/styles/cytoscape/lexicon.js.map +1 -1
  413. package/dist/src/styles/cytoscape/mappings.d.ts +30 -0
  414. package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -1
  415. package/dist/src/styles/cytoscape/mappings.js +92 -0
  416. package/dist/src/styles/cytoscape/mappings.js.map +1 -1
  417. package/dist/src/styles/cytoscape/resolve.d.ts +42 -2
  418. package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -1
  419. package/dist/src/styles/cytoscape/resolve.js +623 -17
  420. package/dist/src/styles/cytoscape/resolve.js.map +1 -1
  421. package/dist/src/styles/defaults.d.ts +46 -26
  422. package/dist/src/styles/defaults.d.ts.map +1 -1
  423. package/dist/src/styles/defaults.js +308 -71
  424. package/dist/src/styles/defaults.js.map +1 -1
  425. package/dist/src/styles/index.d.ts +4 -3
  426. package/dist/src/styles/index.d.ts.map +1 -1
  427. package/dist/src/styles/index.js +3 -2
  428. package/dist/src/styles/index.js.map +1 -1
  429. package/dist/src/styles/internal.d.ts +1 -0
  430. package/dist/src/styles/internal.d.ts.map +1 -1
  431. package/dist/src/styles/internal.js +1 -0
  432. package/dist/src/styles/internal.js.map +1 -1
  433. package/dist/src/styles/io/generic.d.ts.map +1 -1
  434. package/dist/src/styles/io/generic.js +56 -16
  435. package/dist/src/styles/io/generic.js.map +1 -1
  436. package/dist/src/styles/io/round-trip.d.ts +56 -0
  437. package/dist/src/styles/io/round-trip.d.ts.map +1 -0
  438. package/dist/src/styles/io/round-trip.js +138 -0
  439. package/dist/src/styles/io/round-trip.js.map +1 -0
  440. package/dist/src/styles/io/writer.d.ts.map +1 -1
  441. package/dist/src/styles/io/writer.js +6 -1
  442. package/dist/src/styles/io/writer.js.map +1 -1
  443. package/dist/src/styles/notes.d.ts.map +1 -1
  444. package/dist/src/styles/notes.js +1 -0
  445. package/dist/src/styles/notes.js.map +1 -1
  446. package/dist/src/styles/plan/features.d.ts +1 -1
  447. package/dist/src/styles/plan/features.d.ts.map +1 -1
  448. package/dist/src/styles/plan/features.js +22 -2
  449. package/dist/src/styles/plan/features.js.map +1 -1
  450. package/dist/src/styles/plan/index.d.ts +1 -1
  451. package/dist/src/styles/plan/index.d.ts.map +1 -1
  452. package/dist/src/styles/plan/index.js +1 -1
  453. package/dist/src/styles/plan/index.js.map +1 -1
  454. package/dist/src/styles/plan/plan.d.ts.map +1 -1
  455. package/dist/src/styles/plan/plan.js +61 -4
  456. package/dist/src/styles/plan/plan.js.map +1 -1
  457. package/dist/src/styles/plan/resolve.d.ts +11 -0
  458. package/dist/src/styles/plan/resolve.d.ts.map +1 -1
  459. package/dist/src/styles/plan/resolve.js +40 -4
  460. package/dist/src/styles/plan/resolve.js.map +1 -1
  461. package/dist/src/styles/slots.d.ts +14 -0
  462. package/dist/src/styles/slots.d.ts.map +1 -0
  463. package/dist/src/styles/slots.js +20 -0
  464. package/dist/src/styles/slots.js.map +1 -0
  465. package/dist/src/styles/types.d.ts +687 -6
  466. package/dist/src/styles/types.d.ts.map +1 -1
  467. package/dist/src/styles/vocabulary.d.ts +3 -2
  468. package/dist/src/styles/vocabulary.d.ts.map +1 -1
  469. package/dist/src/styles/vocabulary.js +224 -0
  470. package/dist/src/styles/vocabulary.js.map +1 -1
  471. package/dist/styles.js +30 -239
  472. package/dist/styles.js.map +1 -1
  473. package/dist/xgmml.js +3 -3
  474. package/package.json +27 -7
  475. package/src/formats/cx/importer.ts +6 -1
  476. package/src/formats/cx/styles.ts +143 -9
  477. package/src/formats/cx2/importer.ts +1 -0
  478. package/src/formats/cx2/styles.ts +3 -1
  479. package/src/formats/cys/importer.ts +1 -0
  480. package/src/formats/cys/styles.ts +27 -5
  481. package/src/formats/dot/drawing.ts +799 -0
  482. package/src/formats/dot/exporter.ts +37 -1
  483. package/src/formats/dot/importer.ts +192 -36
  484. package/src/formats/dot/index.ts +3 -1
  485. package/src/formats/dot/label-places.ts +241 -0
  486. package/src/formats/dot/names.ts +15 -0
  487. package/src/formats/dot/record.ts +336 -0
  488. package/src/formats/dot/styles.ts +1702 -149
  489. package/src/formats/gexf/dialog-metrics.ts +61 -0
  490. package/src/formats/gexf/importer.ts +50 -5
  491. package/src/formats/gexf/preview.ts +217 -0
  492. package/src/formats/gexf/schema.ts +2 -1
  493. package/src/formats/gexf/styles.ts +1099 -245
  494. package/src/formats/gml/importer.ts +291 -76
  495. package/src/formats/gml/index.ts +1 -1
  496. package/src/formats/gml/positions.ts +29 -7
  497. package/src/formats/gml/styles.ts +852 -110
  498. package/src/formats/graphml/styles-plain.ts +6 -15
  499. package/src/formats/graphml/styles-yed.ts +765 -176
  500. package/src/formats/graphml/styles-yfiles3.ts +487 -63
  501. package/src/formats/graphml/yfiles.ts +19 -6
  502. package/src/formats/json/dialect.ts +41 -5
  503. package/src/formats/json/importer.ts +59 -7
  504. package/src/formats/json/index.ts +1 -0
  505. package/src/formats/json/positions.ts +22 -5
  506. package/src/formats/json/styles.ts +2123 -171
  507. package/src/formats/pajek/draw.ts +469 -0
  508. package/src/formats/pajek/exporter.ts +81 -49
  509. package/src/formats/pajek/importer.ts +278 -103
  510. package/src/formats/pajek/index.ts +1 -0
  511. package/src/formats/pajek/styles.ts +1506 -133
  512. package/src/formats/pajek/syntax.ts +27 -2
  513. package/src/formats/xgmml/emit.ts +73 -5
  514. package/src/formats/xgmml/styles.ts +330 -71
  515. package/src/index.ts +12 -1
  516. package/src/positions/convert.ts +12 -1
  517. package/src/positions/extract.ts +55 -1
  518. package/src/positions/index.ts +1 -0
  519. package/src/positions/types.ts +60 -0
  520. package/src/sources/builtins.ts +8 -1
  521. package/src/sources/context.ts +1 -0
  522. package/src/sources/credentials.ts +90 -1
  523. package/src/sources/cypher/descriptor.ts +7 -1
  524. package/src/sources/cypher/index.ts +149 -18
  525. package/src/sources/cypher/params.ts +1 -1
  526. package/src/sources/cypher/query-builder.ts +33 -0
  527. package/src/sources/ids.ts +6 -0
  528. package/src/sources/params.ts +3 -0
  529. package/src/sources/rest/descriptor.ts +9 -0
  530. package/src/sources/rest/openalex.ts +4 -2
  531. package/src/sources/rest/source.ts +41 -0
  532. package/src/sources/run.ts +35 -3
  533. package/src/sources/sigv4.ts +6 -4
  534. package/src/sources/sparql/descriptor.ts +67 -0
  535. package/src/sources/sparql/index.ts +371 -0
  536. package/src/sources/sparql/ntriples.ts +219 -0
  537. package/src/sources/sparql/params.ts +28 -0
  538. package/src/sources/sparql/query.ts +216 -0
  539. package/src/sources/types.ts +68 -0
  540. package/src/styles/annotations.ts +201 -27
  541. package/src/styles/canon/bends.ts +137 -0
  542. package/src/styles/canon/color-tables.ts +10 -4
  543. package/src/styles/canon/color.ts +105 -3
  544. package/src/styles/canon/enums.ts +107 -8
  545. package/src/styles/canon/graphviz.ts +71 -10
  546. package/src/styles/canon/number.ts +19 -0
  547. package/src/styles/canon/text.ts +187 -16
  548. package/src/styles/codes.ts +2 -0
  549. package/src/styles/cyjs/cascade.ts +1303 -84
  550. package/src/styles/cyjs/defaults-table.ts +4 -0
  551. package/src/styles/cyjs/emit.ts +268 -14
  552. package/src/styles/cyjs/face.ts +372 -0
  553. package/src/styles/cyjs/mappers.ts +506 -27
  554. package/src/styles/cyjs/routes.ts +432 -0
  555. package/src/styles/cytoscape/charts.ts +684 -0
  556. package/src/styles/cytoscape/emit.ts +170 -48
  557. package/src/styles/cytoscape/lexicon.ts +431 -78
  558. package/src/styles/cytoscape/mappings.ts +108 -0
  559. package/src/styles/cytoscape/resolve.ts +741 -17
  560. package/src/styles/defaults.ts +417 -108
  561. package/src/styles/index.ts +5 -0
  562. package/src/styles/internal.ts +1 -0
  563. package/src/styles/io/generic.ts +79 -23
  564. package/src/styles/io/round-trip.ts +202 -0
  565. package/src/styles/io/writer.ts +6 -1
  566. package/src/styles/notes.ts +1 -0
  567. package/src/styles/plan/features.ts +23 -2
  568. package/src/styles/plan/index.ts +1 -1
  569. package/src/styles/plan/plan.ts +69 -5
  570. package/src/styles/plan/resolve.ts +47 -4
  571. package/src/styles/slots.ts +25 -0
  572. package/src/styles/types.ts +698 -5
  573. package/src/styles/vocabulary.ts +232 -2
  574. package/dist/chunks/block-Cu22ewuS.js.map +0 -1
  575. package/dist/chunks/choose-Cdx6Enl2.js +0 -57
  576. package/dist/chunks/choose-Cdx6Enl2.js.map +0 -1
  577. package/dist/chunks/codes-CV_Bhcyi.js.map +0 -1
  578. package/dist/chunks/convert-BcQ8_jMo.js.map +0 -1
  579. package/dist/chunks/credentials-BiQsnMTb.js.map +0 -1
  580. package/dist/chunks/descriptor-BJ-W0Jd-.js.map +0 -1
  581. package/dist/chunks/emit-CD8xnY8s.js.map +0 -1
  582. package/dist/chunks/export-CZ7VRm4I.js.map +0 -1
  583. package/dist/chunks/exporter-bxxjOrDZ.js.map +0 -1
  584. package/dist/chunks/format-BMS-1Nxb.js +0 -64
  585. package/dist/chunks/format-BMS-1Nxb.js.map +0 -1
  586. package/dist/chunks/generic-CP6YD34u.js.map +0 -1
  587. package/dist/chunks/importer-B87_fmVx.js.map +0 -1
  588. package/dist/chunks/importer-CB_zV5_L.js.map +0 -1
  589. package/dist/chunks/importer-CVT-JyDE.js.map +0 -1
  590. package/dist/chunks/importer-Cyy6UseH.js.map +0 -1
  591. package/dist/chunks/importer-DPZDN40-.js.map +0 -1
  592. package/dist/chunks/importer-e3huMZbm.js.map +0 -1
  593. package/dist/chunks/lexicon-u2SI5xcx.js.map +0 -1
  594. package/dist/chunks/sigv4-LkAzsLAu.js.map +0 -1
  595. package/dist/chunks/styles-CaABMoO1.js.map +0 -1
  596. package/dist/chunks/styles-P_gmFAOi.js +0 -770
  597. package/dist/chunks/styles-P_gmFAOi.js.map +0 -1
  598. package/dist/chunks/styles-yed-Cs1nmrjn.js.map +0 -1
  599. package/dist/chunks/writer-DvHTl36J.js +0 -449
  600. package/dist/chunks/writer-DvHTl36J.js.map +0 -1
@@ -4,15 +4,93 @@
4
4
  * `arrowColorMatchesEdge` makes the edge's paint drive the line and both arrows). Every pass is linear in the number
5
5
  * of elements plus the size of the style, and spends from the extraction's one budget.
6
6
  */
7
+ import { num6 } from "../canon/number.js";
8
+ import { noteAt } from "../canon/site.js";
7
9
  import { STYLE_ISSUE } from "../codes.js";
8
10
  import { toolDefaults } from "../defaults.js";
9
11
  import { StyleNotes } from "../notes.js";
10
12
  import { discreteLookup, evalContinuous, numberOf } from "../plan/resolve.js";
11
13
  import { isStyleValue } from "../vocabulary.js";
12
- import { colorParser, CYTOSCAPE_LEXICON, isUncarriedVp, NODE_SIZE_VP, sizeParser, vpStep, } from "./lexicon.js";
14
+ import { readCytoscapeCharts } from "./charts.js";
15
+ import { arrowGap, arrowProportion, arrowSizeParser, colorParser, CYTOSCAPE_LEXICON, cytoscapeLabelBase, cytoscapeLabelMove, cytoscapeLabelPlace, halfBack, isUncarriedVp, NODE_SIZE_VP, opacityParser, sizeParser, vpStep, } from "./lexicon.js";
13
16
  import { emptyCytoscapeStyle, } from "./mappings.js";
17
+ /** How closely desktop stacks the edges it fans, 0..1: its anchors are twice this times the node size apart. */
18
+ const EDGE_STACKING_DENSITY_VP = "EDGE_STACKING_DENSITY";
19
+ /** How desktop draws parallel edges: AUTO_BEND (the default) fans them, HAYSTACK draws them straight. */
20
+ const EDGE_STACKING_VP = "EDGE_STACKING";
21
+ /** Whether an edge's label is turned along its line. */
22
+ const AUTOROTATE_VP = "EDGE_LABEL_AUTOROTATE";
23
+ /**
24
+ * A map without one key.
25
+ * @param m - the map
26
+ * @param key - the key left out
27
+ * @returns a copy without it
28
+ */
29
+ function withoutKey(m, key) {
30
+ return new Map([...m].filter(([k]) => k !== key));
31
+ }
32
+ /** Desktop's `EDGE_STACKING_DENSITY` default. */
33
+ const EDGE_STACKING_DENSITY_DEFAULT = 0.5;
34
+ /**
35
+ * A graph's edge ends for `CytoscapeElements.ends`, with each node's larger side from the nodes' resolved `width`
36
+ * and `height`: what desktop spaces the edges it fans between two nodes by.
37
+ * @param snapshot - the graph
38
+ * @param nodes - the nodes' values per property
39
+ * @returns the ends
40
+ */
41
+ export function edgeEnds(snapshot, nodes) {
42
+ const nodeSize = Array.from({ length: snapshot.nodeCount }, (_, i) => {
43
+ const sides = [nodes.width?.[i], nodes.height?.[i]].filter((v) => typeof v === "number");
44
+ return sides.length === 0 ? null : Math.max(...sides);
45
+ });
46
+ const nodeBox = Array.from({ length: snapshot.nodeCount }, (_, i) => {
47
+ const [w, h, b] = [nodes.width?.[i], nodes.height?.[i], nodes.borderWidth?.[i]];
48
+ return typeof w === "number" && typeof h === "number" ? [w, h, typeof b === "number" ? b : 0] : null;
49
+ });
50
+ const position = snapshot.nodes.byRole("position");
51
+ // stored y up unless the column says otherwise; the label places are worked out y down, as Cytoscape draws
52
+ const down = position?.meta.extra?.yAxis === "down";
53
+ const nodeAt = Array.from({ length: snapshot.nodeCount }, (_, i) => {
54
+ const v = position?.isSet(i) === true ? position.value(i) : null;
55
+ return v === null ? null : [v[0], down ? v[1] : -v[1]];
56
+ });
57
+ const { src, dst } = snapshot.edgeList();
58
+ return { source: src, target: dst, nodeSize, nodeBox, nodeAt };
59
+ }
14
60
  /** Locked node sizes with no `NODE_SIZE` anywhere draw at Cytoscape's lexicon default. */
15
61
  const NODE_SIZE_FALLBACK = 50;
62
+ /**
63
+ * The visual property whose bypass each one takes when an element does not bypass it itself: Cytoscape desktop locks
64
+ * a bypassed parent's children too, so a session's `NODE_PAINT` bypass paints the fill and the border, a `NODE_SIZE`
65
+ * bypass sizes the node whatever `nodeSizeLocked` says, and an `EDGE_PAINT` or `EDGE_UNSELECTED_PAINT` bypass paints
66
+ * the line and its arrows (Cytoscape 3.10.5's SVG of the session specimen sheets `node-fills-borders`,
67
+ * `node-sizes-opacity` and `edges-lines`).
68
+ */
69
+ const LOCK_PARENT = new Map([
70
+ ["NODE_FILL_COLOR", "NODE_PAINT"],
71
+ ["NODE_BORDER_PAINT", "NODE_PAINT"],
72
+ ["NODE_WIDTH", "NODE_SIZE"],
73
+ ["NODE_HEIGHT", "NODE_SIZE"],
74
+ ["EDGE_UNSELECTED_PAINT", "EDGE_PAINT"],
75
+ ["EDGE_STROKE_UNSELECTED_PAINT", "EDGE_UNSELECTED_PAINT"],
76
+ ["EDGE_SOURCE_ARROW_UNSELECTED_PAINT", "EDGE_UNSELECTED_PAINT"],
77
+ ["EDGE_TARGET_ARROW_UNSELECTED_PAINT", "EDGE_UNSELECTED_PAINT"],
78
+ ]);
79
+ /**
80
+ * An element's bypass of a visual property: its own, else the nearest bypassed parent's (`LOCK_PARENT`).
81
+ * @param b - the element's bypasses
82
+ * @param vp - the visual property
83
+ * @returns the bypass value, or undefined
84
+ */
85
+ function lockedValue(b, vp) {
86
+ for (let p = vp; p !== undefined; p = LOCK_PARENT.get(p)) {
87
+ const v = b.get(p);
88
+ if (v !== undefined && v !== null) {
89
+ return v;
90
+ }
91
+ }
92
+ return undefined;
93
+ }
16
94
  /**
17
95
  * Which visual properties a style or its bypasses mention.
18
96
  * @param style - the target's style
@@ -33,6 +111,12 @@ export function vpUsage(style, bypasses) {
33
111
  bypassed.add(to);
34
112
  }
35
113
  }
114
+ // a bypassed parent bypasses its children (LOCK_PARENT lists a parent's own entry before its children's)
115
+ for (const [child, parent] of LOCK_PARENT) {
116
+ if (bypassed.has(parent)) {
117
+ bypassed.add(child);
118
+ }
119
+ }
36
120
  const used = new Set([...style.defaults.keys(), ...style.mappings.keys(), ...bypassed]);
37
121
  return { used, bypassed };
38
122
  }
@@ -57,7 +141,8 @@ export function chooseVps(target, style, used) {
57
141
  chosen.delete("height");
58
142
  }
59
143
  if (target === "edge" && style.dependencies.get("arrowColorMatchesEdge") === true) {
60
- if (used.has("EDGE_UNSELECTED_PAINT")) {
144
+ // the line is EDGE_UNSELECTED_PAINT's, not the stroke's, even where the style sets only the stroke
145
+ if (used.has("EDGE_UNSELECTED_PAINT") || used.has("EDGE_STROKE_UNSELECTED_PAINT")) {
61
146
  chosen.set("color", ["EDGE_UNSELECTED_PAINT", colorParser]);
62
147
  }
63
148
  chosen.delete("sourceArrowColor");
@@ -175,7 +260,7 @@ function resolver(target, style, elements, notes, budget) {
175
260
  return b.get(from);
176
261
  }
177
262
  }
178
- return undefined;
263
+ return lockedValue(b, vp);
179
264
  };
180
265
  // a mapping parsed once per visual property and neutral property
181
266
  const mappingOf = (vp, parse, property) => {
@@ -212,7 +297,7 @@ function resolver(target, style, elements, notes, budget) {
212
297
  /**
213
298
  * Every element's value per neutral property under a Cytoscape style, the way Cytoscape draws it: per visual
214
299
  * property a bypass, else the mapping's value, else the default; `nodeSizeLocked` and `arrowColorMatchesEdge`
215
- * applied. `EDGE_CURVED` only rounds an edge's bends, so an edge without bends is `curved: false`. Visual properties
300
+ * applied (under `nodeSizeLocked` a node's own `NODE_WIDTH` or `NODE_HEIGHT` bypass still sets that dimension). `EDGE_CURVED` only rounds an edge's bends, so an edge without bends is `curved: false` unless desktop fans it out of a bundle of parallel edges (given `elements.ends`). Visual properties
216
301
  * no neutral property carries are noted as `W_STYLE_NOT_CARRIED`.
217
302
  * @param target - nodes or edges
218
303
  * @param style - the style
@@ -227,7 +312,7 @@ export function resolveCytoscape(target, style, elements, options = {}) {
227
312
  const n = elements.count;
228
313
  const { used, bypassed } = vpUsage(s, elements.bypasses ?? []);
229
314
  const chosen = chooseVps(target, s, used);
230
- const { one } = resolver(target, s, elements, notes, options.budget);
315
+ const { one, bypass } = resolver(target, s, elements, notes, options.budget);
231
316
  const out = {};
232
317
  const column = (vp, parse, property) => {
233
318
  options.budget?.spend(target, n);
@@ -241,30 +326,509 @@ export function resolveCytoscape(target, style, elements, options = {}) {
241
326
  out[prop] = column(vp, parse, prop);
242
327
  }
243
328
  if (target === "node" && s.dependencies.get("nodeSizeLocked") === true) {
329
+ // a node's own width or height still wins over the locked size: Cytoscape draws it
244
330
  const size = column(NODE_SIZE_VP, sizeParser, "width").map((v) => v ?? NODE_SIZE_FALLBACK);
245
- out.width = size;
246
- out.height = size.slice();
331
+ const own = (vp, property) => size.map((v, i) => {
332
+ const b = bypass(i, vp);
333
+ const parsed = b === undefined || b === null ? null : sizeParser(b, { notes, target, property, element: i });
334
+ return parsed ?? v;
335
+ });
336
+ out.width = own("NODE_WIDTH", "width");
337
+ out.height = own("NODE_HEIGHT", "height");
338
+ }
339
+ labelDress(target, out, one, n, elements.ends);
340
+ if (target === "node") {
341
+ squashHexagons(out);
342
+ // the value as written: a chart is text, or an object in CX2; a size a number or its text
343
+ const raw = (v) => {
344
+ if (typeof v === "string" || typeof v === "number") {
345
+ return String(v);
346
+ }
347
+ return typeof v === "object" && v !== null ? JSON.stringify(v) : null;
348
+ };
349
+ readCytoscapeCharts(out, n, (slot, i) => {
350
+ const v = one(slot, raw, "pieColors", i);
351
+ return typeof v === "string" && v.startsWith("{") ? JSON.parse(v) : v;
352
+ }, (column, i) => elements.value(column, i), s.dependencies.get("nodeCustomGraphicsSizeSync") !== false);
353
+ }
354
+ if (target === "edge" &&
355
+ s.dependencies.get("arrowColorMatchesEdge") === true &&
356
+ !used.has("EDGE_UNSELECTED_PAINT") &&
357
+ used.has("EDGE_STROKE_UNSELECTED_PAINT")) {
358
+ // the dependency draws the line and the arrows in EDGE_UNSELECTED_PAINT, Cytoscape's own when the style has
359
+ // none: the stroke's default and its mapping are not drawn, only an element's own bypass of the stroke (below;
360
+ // Cytoscape 3.10.5 on specimen-mappings-bypass draws a passthrough of the stroke #404040, as its XGMML says)
361
+ const paint = (toolDefaults("cytoscape-fallbacks")?.edge.color?.value ?? null);
362
+ out.color = Array.from({ length: n }, (_, i) => one("EDGE_UNSELECTED_PAINT", colorParser, "color", i) ?? paint);
247
363
  }
248
364
  if (target === "edge" && s.dependencies.get("arrowColorMatchesEdge") === true && out.color !== undefined) {
249
- out.sourceArrowColor = out.color.slice();
250
- out.targetArrowColor = out.color.slice();
365
+ // CX2's EDGE_LINE_COLOR bypass is the stroke's alone (its writer spells a desktop EDGE_UNSELECTED_PAINT
366
+ // bypass out as the line and both arrow colors): the arrows keep the style's line color (Cytoscape 3.10.5's
367
+ // SVG of the CX2 specimen-mappings-bypass, bypass-over-edge-passthrough, draws its arrow #505050)
368
+ const lineVp = chosen.get("color")?.[0];
369
+ const unbypassed = resolver(target, s, { ...elements, bypasses: [] }, notes, options.budget).one;
370
+ const arrows = lineVp === "EDGE_LINE_COLOR"
371
+ ? Array.from({ length: n }, (_, i) => unbypassed(lineVp, colorParser, "color", i))
372
+ : out.color;
373
+ out.sourceArrowColor = arrows.slice();
374
+ out.targetArrowColor = arrows.slice();
375
+ // the dependency sets the line and the arrows from EDGE_UNSELECTED_PAINT; an element's own bypass of one of
376
+ // them still wins for that one (Cytoscape draws a bypassed EDGE_STROKE_UNSELECTED_PAINT line in its color)
377
+ const own = [
378
+ ["color", "EDGE_STROKE_UNSELECTED_PAINT"],
379
+ ["sourceArrowColor", "EDGE_SOURCE_ARROW_UNSELECTED_PAINT"],
380
+ ["targetArrowColor", "EDGE_TARGET_ARROW_UNSELECTED_PAINT"],
381
+ ["sourceArrowColor", "EDGE_SOURCE_ARROW_COLOR"],
382
+ ["targetArrowColor", "EDGE_TARGET_ARROW_COLOR"],
383
+ ];
384
+ for (const [prop, vp] of own) {
385
+ out[prop] = (out[prop] ?? []).map((v, i) => {
386
+ const b = bypass(i, vp);
387
+ const site = { notes, target, property: prop, element: i };
388
+ return (b === undefined || b === null ? null : colorParser(b, site)) ?? v;
389
+ });
390
+ }
251
391
  }
252
392
  noteUncarried(target, s, used, bypassed, notes);
253
- fillToolDefaults(target, out, n, options.defaults ?? null);
254
393
  if (target === "edge") {
255
- straightenBendless(out, one);
394
+ const bends = column(EDGE_BEND_VP, bendsParser, "bends");
395
+ if (bends.some((v) => v !== null)) {
396
+ out.bends = bends;
397
+ }
398
+ // before the tool's defaults: an edge the style leaves unset is curved as Cytoscape's EDGE_CURVED default is
399
+ const curvedAsSet = out.curved?.slice();
400
+ straightenBendless(out, one, n, elements.ends);
401
+ loopHandles(out, one, curvedAsSet, elements.ends);
402
+ arrowProportions(out, column);
256
403
  }
404
+ fillToolDefaults(target, out, n, options.defaults ?? null);
257
405
  return out;
258
406
  }
259
407
  /**
260
- * Marks straight every curved edge without bends: `EDGE_CURVED` only rounds the bends an edge has.
408
+ * A visual property's value as written, a CX2 object as its JSON.
409
+ * @param v - the value
410
+ * @returns its text, or null
411
+ */
412
+ const rawText = (v) => {
413
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
414
+ return String(v);
415
+ }
416
+ return typeof v === "object" && v !== null ? JSON.stringify(v) : null;
417
+ };
418
+ /**
419
+ * Reads what Cytoscape draws of each element's label beyond its words, font and color: the box behind it (drawn only
420
+ * with a `*_LABEL_BACKGROUND_SHAPE` other than NONE, the default, in its color and transparency), where it is placed
421
+ * (`*_LABEL_POSITION` read whole: which point of the label's box is pinned to which point of the node, or of the
422
+ * edge's line, and the offset) and how its lines are justified. Each column is kept only where some element needs it;
423
+ * an edge's place is kept whenever it has one, since Cytoscape centers an edge's label on its line.
424
+ * @param target - nodes or edges
261
425
  * @param out - the values per property, changed in place
262
426
  * @param one - reads one visual property of one element
427
+ * @param n - the element count
428
+ * @param ends - the edges' ends and their nodes' centers, when known
429
+ */
430
+ function labelDress(target, out, one, n, ends) {
431
+ const vp = (name) => `${target === "node" ? "NODE" : "EDGE"}_LABEL_${name}`;
432
+ const columns = {};
433
+ const put = (property, i, v) => {
434
+ let column = columns[property];
435
+ if (column === undefined) {
436
+ column = new Array(n).fill(null);
437
+ columns[property] = column;
438
+ }
439
+ column[i] = v;
440
+ };
441
+ for (let i = 0; i < n; i++) {
442
+ labelBox(vp, one, i, put);
443
+ // read whole, quietly (the lexicon's reading of the same value notes a bad one), under a name of its own: a
444
+ // mapping is parsed once per visual property and neutral property
445
+ const placed = one(vp("POSITION"), rawText, "labelPlace", i);
446
+ const raw = typeof placed === "string" && placed.startsWith("{") ? JSON.parse(placed) : placed;
447
+ const place = cytoscapeLabelPlace(raw, { notes: new StyleNotes(), target, property: "labelPosition" });
448
+ if (place !== null) {
449
+ placeLabel(target, place, i, out, put, ends);
450
+ }
451
+ if (target === "edge") {
452
+ autorotate(one, i, out, n, ends);
453
+ }
454
+ }
455
+ const kept = (property, v) => v !== null && v !== 0 && (v !== "center" || (property === "labelPosition" && target === "edge"));
456
+ for (const [property, values] of Object.entries(columns)) {
457
+ if (values.some((v) => kept(property, v))) {
458
+ out[property] = values;
459
+ }
460
+ }
461
+ }
462
+ /**
463
+ * The box Cytoscape draws behind one label, when its shape is not NONE: its color (`#B6B6B6` unset, the lexicon's
464
+ * default), its opacity, and for ROUND_RECTANGLE its corners, rounded an eighth of the box's height: 1.455 on the
465
+ * 11.64-tall box of a 10 px label (xgmml-specimen-labels.svg, label-background-shape-round-rectangle), so 0.1455 of the
466
+ * label's size. ponytail: one line's height; a box of several lines is rounded as one line's.
467
+ * @param vp - names a label visual property of the target
468
+ * @param one - reads one visual property of one element
469
+ * @param i - the element
470
+ * @param put - sets one element's value of a property
471
+ */
472
+ function labelBox(vp, one, i, put) {
473
+ const shape = one(vp("BACKGROUND_SHAPE"), rawText, "labelBackgroundShape", i);
474
+ if (typeof shape !== "string" || shape.trim().toUpperCase() === "NONE") {
475
+ return;
476
+ }
477
+ put("labelBackground", i, one(vp("BACKGROUND_COLOR"), colorParser, "labelBackground", i) ?? "#b6b6b6");
478
+ const [t, o] = [vp("BACKGROUND_TRANSPARENCY"), vp("BACKGROUND_OPACITY")];
479
+ const opacity = one(t, opacityParser(t), "labelBackgroundOpacity", i) ?? one(o, opacityParser(o), "labelBackgroundOpacity", i);
480
+ put("labelBackgroundOpacity", i, opacity ?? 1);
481
+ // CX2 spells it round-rectangle
482
+ if (shape.trim().toUpperCase().replace("-", "_") === "ROUND_RECTANGLE") {
483
+ const size = one(vp("FONT_SIZE"), sizeParser, "labelSize", i);
484
+ // desktop's default label sizes: 12 on a node, 10 on an edge
485
+ const defaultSize = vp("FONT_SIZE").startsWith("EDGE") ? 10 : 12;
486
+ put("labelCornerRadius", i, num6(LABEL_CORNER_PER_SIZE * (typeof size === "number" ? size : defaultSize)));
487
+ }
488
+ }
489
+ /** How far Cytoscape rounds a ROUND_RECTANGLE label box's corners, as a fraction of the label's font size. */
490
+ const LABEL_CORNER_PER_SIZE = 0.1455;
491
+ /**
492
+ * Where one label is placed, from its Cytoscape label position: its neutral `labelPosition`, offsets, shifts and
493
+ * line justification. A node's label is pinned against the node's box. An edge's is pinned to a point of its drawn
494
+ * line, the middle unless the anchor's horizontal letter names an end (W the source's, E the target's; its vertical
495
+ * letter changes nothing, measured on Cytoscape 3.10.5's SVG), from the line's middle, where it is centered.
496
+ * @param target - nodes or edges
497
+ * @param place - the label position
498
+ * @param i - the element
499
+ * @param out - the values per property (the node sizes are read)
500
+ * @param put - sets one element's value of a property
501
+ * @param ends - the edges' ends and their nodes' centers, when known
502
+ */
503
+ function placeLabel(target, place, i, out, put, ends) {
504
+ let move;
505
+ if (target === "node") {
506
+ const [w, h] = [out.width?.[i], out.height?.[i]];
507
+ move = cytoscapeLabelMove(place, typeof w === "number" ? w : 0, typeof h === "number" ? h : 0);
508
+ put("labelPosition", i, cytoscapeLabelBase(place));
509
+ }
510
+ else {
511
+ const [ax, ay] = lineEnd(ends, i, place.node[0]);
512
+ move = {
513
+ offset: [ax + place.offset[0], ay + place.offset[1]],
514
+ shift: [halfBack(place.label[0]), halfBack(place.label[1])],
515
+ };
516
+ put("labelPosition", i, "center");
517
+ }
518
+ put("labelOffsetX", i, num6(move.offset[0]));
519
+ put("labelOffsetY", i, num6(move.offset[1]));
520
+ put("labelShiftX", i, move.shift[0]);
521
+ put("labelShiftY", i, move.shift[1]);
522
+ put("labelTextAlign", i, place.justify);
523
+ }
524
+ /**
525
+ * Turns an edge's label along its line under `EDGE_LABEL_AUTOROTATE`, as Cytoscape draws it: the line's angle from
526
+ * its source's center to its target's (kept between -90 and 90 so the words read left to right), plus its
527
+ * `EDGE_LABEL_ROTATION`.
528
+ * ponytail: the angle is the file's node places at import; a layout that moves the nodes leaves it as it was, until
529
+ * the neutral styles can say "along the line"
530
+ * @param one - reads one visual property of one element
531
+ * @param e - the edge
532
+ * @param out - the values per property, changed in place
533
+ * @param n - the edge count
534
+ * @param ends - the edges' ends and their nodes' centers
263
535
  */
264
- function straightenBendless(out, one) {
265
- const { curved } = out;
266
- if (curved !== undefined) {
267
- out.curved = curved.map((c, i) => (c === true ? one(EDGE_BEND_VP, bendParser, "curved", i) === true : c));
536
+ function autorotate(one, e, out, n, ends) {
537
+ const on = one(AUTOROTATE_VP, rawText, "labelRotation", e);
538
+ const [si, ti] = [ends?.source[e], ends?.target[e]];
539
+ const [s, t] = [si === undefined ? null : ends?.nodeAt?.[si], ti === undefined ? null : ends?.nodeAt?.[ti]];
540
+ if (String(on).toLowerCase() !== "true" || !s || !t || (s[0] === t[0] && s[1] === t[1])) {
541
+ return;
542
+ }
543
+ const a = (Math.atan2(t[1] - s[1], t[0] - s[0]) * 180) / Math.PI;
544
+ let upright = a;
545
+ if (a > 90) {
546
+ upright = a - 180;
547
+ }
548
+ else if (a <= -90) {
549
+ upright = a + 180;
550
+ }
551
+ out.labelRotation ?? (out.labelRotation = new Array(n).fill(null));
552
+ const column = out.labelRotation;
553
+ const own = column[e];
554
+ column[e] = num6((((upright + (typeof own === "number" ? own : 0)) % 360) + 360) % 360);
555
+ }
556
+ /**
557
+ * How far from the middle of an edge's drawn line one of its ends is, on screen (y down): the line between its two
558
+ * nodes' centers, cut where it meets each node's ellipse; 0 for the middle, or where the centers are not known.
559
+ * @param ends - the edges' ends, their nodes' centers and boxes
560
+ * @param e - the edge
561
+ * @param side - -1 for the source's end, 1 for the target's, 0 for the middle
562
+ * @returns the offset from the line's middle
563
+ */
564
+ function lineEnd(ends, e, side) {
565
+ const [si, ti] = [ends?.source[e], ends?.target[e]];
566
+ const [s, t] = [si === undefined ? null : ends?.nodeAt?.[si], ti === undefined ? null : ends?.nodeAt?.[ti]];
567
+ if (side === 0 || s === null || s === undefined || t === null || t === undefined) {
568
+ return [0, 0];
569
+ }
570
+ const length = Math.hypot(t[0] - s[0], t[1] - s[1]);
571
+ if (length === 0) {
572
+ return [0, 0];
573
+ }
574
+ const [ux, uy] = [(t[0] - s[0]) / length, (t[1] - s[1]) / length];
575
+ // where the line leaves a node's ellipse, along it from the center
576
+ const reach = (node) => {
577
+ const box = node === undefined ? null : ends?.nodeBox?.[node];
578
+ if (box === null || box === undefined) {
579
+ return 0;
580
+ }
581
+ const [a, b] = [box[0] / 2, box[1] / 2];
582
+ return a === 0 || b === 0 ? 0 : (a * b) / Math.hypot(b * ux, a * uy);
583
+ };
584
+ const from = reach(si);
585
+ const to = length - reach(ti);
586
+ // the drawn line's middle and the end asked for, both measured along the line from the source's center
587
+ const along = (side < 0 ? from : to) - (from + to) / 2;
588
+ return [ux * along, uy * along];
589
+ }
590
+ /** How tall a regular hexagon is for its width. */
591
+ const HEXAGON_ASPECT = Math.sqrt(3) / 2;
592
+ /**
593
+ * Gives every hexagon node the height Cytoscape desktop draws it at. Its HEXAGON is a regular hexagon squashed to
594
+ * the node's box rather than one filling it: the corners reach the box's sides, the flat top and bottom only
595
+ * `sqrt(3) / 2` of the way up and down, so a 40 x 40 node with a border of 1 is a 39 x 33.8 path (its border is
596
+ * inside its size, `borderPosition` inside). The neutral hexagon fills its box, so the node is that much shorter.
597
+ * @param out - the node values per property, changed in place
598
+ */
599
+ function squashHexagons(out) {
600
+ const { shape, height } = out;
601
+ if (shape === undefined || height === undefined || !shape.includes("hexagon")) {
602
+ return;
603
+ }
604
+ const border = out.borderWidth;
605
+ out.height = height.map((h, i) => {
606
+ if (shape[i] !== "hexagon" || typeof h !== "number") {
607
+ return h;
608
+ }
609
+ const b = border?.[i];
610
+ const w = typeof b === "number" ? Math.min(b, h) : 0;
611
+ return (h - w) * HEXAGON_ASPECT + w;
612
+ });
613
+ }
614
+ /**
615
+ * What Cytoscape draws of each end's arrow beyond its shape and the one neutral `arrowSize`: the source end's own
616
+ * size where it differs from the target's (an unset size is Cytoscape's 6), and the length and width of the short and
617
+ * crossed arrows against their neutral shape's, and how far off its node each arrow stands. Each is kept only where
618
+ * some edge needs it.
619
+ * @param out - the values per property, changed in place
620
+ * @param column - reads one visual property of every element
621
+ */
622
+ function arrowProportions(out, column) {
623
+ const size = out.arrowSize;
624
+ const own = column("EDGE_SOURCE_ARROW_SIZE", arrowSizeParser, "sourceArrowSize").map((v) => v ?? 1);
625
+ if (own.some((v, i) => v !== (size?.[i] ?? 1))) {
626
+ out.sourceArrowSize = own;
627
+ }
628
+ for (const [prop, vp, side] of [
629
+ ["sourceArrowLength", "EDGE_SOURCE_ARROW_SHAPE", "length"],
630
+ ["targetArrowLength", "EDGE_TARGET_ARROW_SHAPE", "length"],
631
+ ["sourceArrowWidth", "EDGE_SOURCE_ARROW_SHAPE", "width"],
632
+ ["targetArrowWidth", "EDGE_TARGET_ARROW_SHAPE", "width"],
633
+ ]) {
634
+ const values = column(vp, arrowProportion(side), prop).map((v) => (v === 1 ? null : v));
635
+ if (values.some((v) => v !== null)) {
636
+ out[prop] = values;
637
+ }
638
+ }
639
+ // the arrows desktop draws standing off their node (T, the half circles, HALF_TOP, the squares)
640
+ for (const [prop, vp] of [
641
+ ["sourceArrowGap", "EDGE_SOURCE_ARROW_SHAPE"],
642
+ ["targetArrowGap", "EDGE_TARGET_ARROW_SHAPE"],
643
+ ]) {
644
+ const values = column(vp, arrowGap, prop).map((v) => (v === 0 ? null : v));
645
+ if (values.some((v) => v !== null)) {
646
+ out[prop] = values;
647
+ }
648
+ }
649
+ }
650
+ /**
651
+ * Marks straight every curved edge without bends, except where Cytoscape desktop fans it: `EDGE_CURVED` only
652
+ * rounds the bends an edge has, and the bends Cytoscape adds itself. Among the edges joining the same two nodes
653
+ * (either way round, not loops), it draws the last it created straight and bends each earlier one without bends
654
+ * of its own round an anchor beside the line (`DEdgeDetails.getAnchors`, `EDGE_STACKING` `AUTO_BEND`, the default;
655
+ * it walks the pair's edges newest first, measured on Cytoscape 3.10.5's SVG of edges joining two nodes both ways)
656
+ * -- unless the last has bends, when every anchor falls on the line. Its anchors are the larger side of the two
657
+ * nodes times twice `EDGE_STACKING_DENSITY` apart, so the curves' middles half that: each fanned edge's
658
+ * `fanSpacing`, when the node sizes are known. A fanned edge is drawn curved even under `EDGE_CURVED` false, and an
659
+ * edge whose `EDGE_STACKING` is HAYSTACK is neither fanned nor counted: desktop draws it straight (both measured on
660
+ * Cytoscape 3.10.5's SVG of the edges-lines specimen sheet).
661
+ * @param out - the values per property, changed in place
662
+ * @param one - reads one visual property of one element
663
+ * @param n - the edge count
664
+ * @param ends - each edge's source and target node index, in creation order, when known
665
+ */
666
+ function straightenBendless(out, one, n, ends) {
667
+ // an edge the style leaves unset takes EDGE_CURVED's default, true
668
+ const curved = out.curved ?? new Array(n).fill(null);
669
+ const bent = (i) => one(EDGE_BEND_VP, bendParser, "curved", i) === true;
670
+ const haystack = (i) => String(one(EDGE_STACKING_VP, rawText, "fanned", i) ?? "").toUpperCase() === "HAYSTACK";
671
+ // each fanned edge's place in its pair's walk (1 for the first fanned)
672
+ const place = ends === undefined ? new Map() : fanPlaces(ends, curved.length, bent, haystack);
673
+ const fanned = new Set(place.keys());
674
+ const rounds = (i) => fanned.has(i) || bent(i);
675
+ const rounded = curved.map((c, i) => {
676
+ if (c === null) {
677
+ return rounds(i) ? true : null;
678
+ }
679
+ return c === true || fanned.has(i) ? rounds(i) : c;
680
+ });
681
+ if (out.curved !== undefined || rounded.includes(true)) {
682
+ out.curved = rounded;
683
+ }
684
+ if (ends?.nodeSize === undefined || fanned.size === 0) {
685
+ return;
686
+ }
687
+ const { source, target, nodeSize } = ends;
688
+ out.fanSpacing = curved.map((_, i) => {
689
+ const size = Math.max(nodeSize[source[i]] ?? Number.NaN, nodeSize[target[i]] ?? Number.NaN);
690
+ if (out.curved?.[i] !== true || !fanned.has(i) || bent(i) || !Number.isFinite(size)) {
691
+ return null;
692
+ }
693
+ const read = one(EDGE_STACKING_DENSITY_VP, sizeParser, "fanSpacing", i);
694
+ const density = Math.min(1, typeof read === "number" ? read : EDGE_STACKING_DENSITY_DEFAULT);
695
+ return size * density;
696
+ });
697
+ fanAnchors(out, ends, place);
698
+ }
699
+ /**
700
+ * The edges desktop fans, each with its place in its node pair's walk (1 for the first fanned). Desktop walks a node
701
+ * pair's edges newest first (its adjacency lists take each new edge at the head); the first it meets is drawn
702
+ * straight, and each later one fanned unless that first one has bends. Loops and haystack edges are left out.
703
+ * @param ends - each edge's source and target node index, and the order they were created in
704
+ * @param count - the edge count
705
+ * @param bent - whether an edge has bends of its own
706
+ * @param haystack - whether an edge is a haystack edge
707
+ * @returns each fanned edge's place, in walk order
708
+ */
709
+ function fanPlaces(ends, count, bent, haystack) {
710
+ const place = new Map();
711
+ const first = new Map();
712
+ for (const i of newestFirst(count, ends.created?.edge)) {
713
+ const [s, t] = [ends.source[i], ends.target[i]];
714
+ if (s === t || haystack(i)) {
715
+ continue;
716
+ }
717
+ const key = `${Math.min(s, t)},${Math.max(s, t)}`;
718
+ const f = first.get(key);
719
+ if (f === undefined) {
720
+ first.set(key, { f: i, k: 0 });
721
+ }
722
+ else if (!bent(f.f)) {
723
+ f.k++;
724
+ place.set(i, f.k);
725
+ }
726
+ }
727
+ return place;
728
+ }
729
+ /**
730
+ * The edges newest first: by the order they were created in when it is known, else last index first.
731
+ * @param count - the edge count
732
+ * @param created - each edge's place in the creation order, when known
733
+ * @returns the edge indices in that order
734
+ */
735
+ function newestFirst(count, created) {
736
+ const walk = Array.from({ length: count }, (_, i) => i);
737
+ walk.sort(created === undefined ? (a, b) => b - a : (a, b) => created[b] - created[a]);
738
+ return walk;
739
+ }
740
+ /**
741
+ * Gives each fanned edge its place in desktop's fan as a bend, when the order desktop created the nodes in is known:
742
+ * the fan's side is then desktop's, not graphty's. Desktop measures each pair's fan from the node whose view it made
743
+ * first, and it makes views newest node first (`DEdgeDetails.getAnchors`: the pair's lower view SUID is its source;
744
+ * `SimpleNetwork.getNodeList` lists the newest node first); the `k`-th fanned edge bows `ceil(k / 2)` fan spacings
745
+ * off the middle of the line, to the right of the fan's direction (y down) for odd `k`, to the left for even. The
746
+ * bend is the control point of a quadratic Bezier whose middle lies there, twice as far off.
747
+ * @param out - the values per property, with `fanSpacing` set; changed in place
748
+ * @param ends - the edges' ends, their nodes' centers and creation order
749
+ * @param place - each fanned edge's place in its pair's walk, from 1
750
+ */
751
+ function fanAnchors(out, ends, place) {
752
+ const { created, nodeAt, source, target } = ends;
753
+ const spacing = out.fanSpacing?.slice();
754
+ if (created === undefined || nodeAt === undefined || spacing === undefined) {
755
+ return;
756
+ }
757
+ const bends = out.bends?.slice() ?? new Array(source.length).fill(null);
758
+ const bendCurve = out.bendCurve?.slice() ?? new Array(source.length).fill(null);
759
+ let changed = false;
760
+ for (const [i, k] of place) {
761
+ const [s, t] = [source[i], target[i]];
762
+ const [ps, pt, f] = [nodeAt[s], nodeAt[t], spacing[i]];
763
+ if (ps === null || ps === undefined || pt === null || pt === undefined || typeof f !== "number") {
764
+ continue;
765
+ }
766
+ const len = Math.hypot(pt[0] - ps[0], pt[1] - ps[1]);
767
+ if (len === 0) {
768
+ continue;
769
+ }
770
+ // the fan's direction: from the node desktop made a view of first, the one created last
771
+ const sign = created.node[s] > created.node[t] ? 1 : -1;
772
+ const [nx, ny] = [(sign * (pt[0] - ps[0])) / len, (sign * (pt[1] - ps[1])) / len];
773
+ const offset = Math.ceil(k / 2) * (k % 2 === 0 ? 1 : -1) * 2 * f;
774
+ const [vx, vy] = [offset * ny, -offset * nx];
775
+ // to the left of source -> target, y up, as a fraction of the length
776
+ const across = (vx * (pt[1] - ps[1]) - vy * (pt[0] - ps[0])) / (len * len);
777
+ bends[i] = `0.5,${String(Math.round(across * 1e6) / 1e6 || 0)}`;
778
+ bendCurve[i] = "bezier";
779
+ spacing[i] = null;
780
+ changed = true;
781
+ }
782
+ if (changed) {
783
+ out.bends = bends;
784
+ out.bendCurve = bendCurve;
785
+ out.fanSpacing = spacing;
786
+ }
787
+ }
788
+ /**
789
+ * Gives each self-loop without bends of its own the two handles Cytoscape desktop draws it round
790
+ * (`DEdgeDetails.getAnchors`): one left of the node's center and one above it, each `(i + 3) / 2` of the node's
791
+ * size times twice `EDGE_STACKING_DENSITY` away, where the size is the larger side of the node with its border
792
+ * (its extents in desktop's spatial index) and `i` counts the node's earlier loops without bends, newest first as
793
+ * desktop walks them. A loop is curved through them unless `EDGE_CURVED` says false. Measured on Cytoscape
794
+ * 3.10.5's SVG of the edges-lines specimen sheet (within 0.3 px of its loops).
795
+ * @param out - the values per property, changed in place
796
+ * @param one - reads one visual property of one element
797
+ * @param curvedAsSet - each edge's `curved` as the style set it, before bendless edges were straightened
798
+ * @param ends - each edge's source and target node index, in creation order, and each node's box, when known
799
+ */
800
+ function loopHandles(out, one, curvedAsSet, ends) {
801
+ const box = ends?.nodeBox;
802
+ if (ends === undefined || box === undefined) {
803
+ return;
804
+ }
805
+ const bends = out.bends?.slice() ?? new Array(ends.source.length).fill(null);
806
+ const curved = out.curved?.slice() ?? new Array(ends.source.length).fill(null);
807
+ const earlier = new Map();
808
+ const fraction = (v) => String(Math.round(v * 1e4) / 1e4 || 0);
809
+ let changed = false;
810
+ for (let i = ends.source.length - 1; i >= 0; i--) {
811
+ const node = ends.source[i];
812
+ const b = box[node];
813
+ if (node !== ends.target[i] || bends[i] !== null) {
814
+ continue;
815
+ }
816
+ const rank = earlier.get(node) ?? 0;
817
+ earlier.set(node, rank + 1);
818
+ if (b === null || b === undefined || b[0] <= 0 || b[1] <= 0) {
819
+ continue;
820
+ }
821
+ const read = one(EDGE_STACKING_DENSITY_VP, sizeParser, "fanSpacing", i);
822
+ const density = Math.min(1, typeof read === "number" ? read : EDGE_STACKING_DENSITY_DEFAULT);
823
+ const reach = (((rank + 3) * (Math.max(b[0], b[1]) + b[2])) / 2) * 2 * density;
824
+ // as `x,y` fractions of the node's half width and half height, y up: left of the center, then above it
825
+ bends[i] = `${fraction(-reach / (b[0] / 2))},0 0,${fraction(reach / (b[1] / 2))}`;
826
+ curved[i] = curvedAsSet?.[i] !== false;
827
+ changed = true;
828
+ }
829
+ if (changed) {
830
+ out.bends = bends;
831
+ out.curved = curved;
268
832
  }
269
833
  }
270
834
  /** An edge's bend handles. */
@@ -281,6 +845,40 @@ export const bendParser = (raw) => {
281
845
  }
282
846
  return Array.isArray(raw) ? raw.length > 0 : raw !== null && raw !== undefined && typeof raw === "object";
283
847
  };
848
+ /**
849
+ * An `EDGE_BEND` value as the neutral `bends`: Cytoscape keeps each handle as `cos,sin,ratio`, the handle at the
850
+ * source center plus `ratio` times the source-to-target vector turned by the angle (y down), handles separated by
851
+ * `|`. Relative to the same line, y up, that is `ratio * cos` along it and `-ratio * sin` across it.
852
+ * @param input - the value: an `EDGE_BEND` text, or CX2's `EDGE_CONTROL_POINTS` objects
853
+ * @param site - where it was read
854
+ * @returns the bends, or null for an edge without handles or a value that is not handles
855
+ */
856
+ const bendsParser = (input, site) => {
857
+ // CX2's EDGE_CONTROL_POINTS: one object per handle, whose `sin` Cytoscape fills with EDGE_BEND's first number and
858
+ // `cos` with its second (it reads them back the same way: specimen-edges-lines bend-one-handle bends)
859
+ const raw = Array.isArray(input)
860
+ ? input
861
+ .map((h) => {
862
+ const o = (h ?? {});
863
+ return `${Number(o.sin)},${Number(o.cos)},${Number(o.ratio)}`;
864
+ })
865
+ .join("|")
866
+ : input;
867
+ if (typeof raw !== "string" || !bendParser(raw, site)) {
868
+ return null;
869
+ }
870
+ const points = [];
871
+ for (const handle of raw.trim().split("|")) {
872
+ const [c, sin, ratio] = handle.split(",").map((t) => Number(t.trim()));
873
+ if (![c, sin, ratio].every((v) => Number.isFinite(v))) {
874
+ noteAt(site, STYLE_ISSUE.BAD_VALUE, raw.slice(0, 64));
875
+ return null;
876
+ }
877
+ const fraction = (v) => String(Math.round(v * 1e4) / 1e4 || 0);
878
+ points.push(`${fraction(ratio * c)},${fraction(-ratio * sin)}`);
879
+ }
880
+ return points.join(" ");
881
+ };
284
882
  /** Default values that mean "nothing drawn": a VP left at one of these is not worth a note. */
285
883
  const INERT_DEFAULT = /NullCustomGraphics|^255$|^0(?:\.0+)?$|^$|^C,C,c,0\.00,0\.00$|^false$/;
286
884
  /**
@@ -357,7 +955,15 @@ export function cytoscapeBackground(style, notes) {
357
955
  export function resolveCytoscapeBypasses(target, maps, options = {}) {
358
956
  const notes = options.notes ?? new StyleNotes();
359
957
  const style = emptyCytoscapeStyle();
360
- const elements = { count: maps.length, value: () => null, bypasses: maps };
958
+ const elements = {
959
+ count: maps.length,
960
+ value: options.value ?? (() => null),
961
+ // Cytoscape 3.10.5 draws an XGMML edge's EDGE_LABEL_AUTOROTATE true level, where it turns the same edge read
962
+ // from CX, CX2 or a session along its line (the label-autorotate-true edge of the edge-labels and
963
+ // mappings-bypass-p13 sheets in its SVG): its XGMML reader does not apply it, so neither does graph-io
964
+ bypasses: maps.map((m) => (m?.has(AUTOROTATE_VP) === true ? withoutKey(m, AUTOROTATE_VP) : m)),
965
+ ends: options.ends,
966
+ };
361
967
  const out = resolveCytoscape(target, style, elements, { notes, budget: options.budget });
362
968
  if (target === "node" && out.width === undefined) {
363
969
  const size = maps.map((m) => {