@graphty/graph-io 0.3.43 → 0.3.45
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.
- package/dist/chunks/approximations-DJMHdDvv.js +40 -0
- package/dist/chunks/approximations-DJMHdDvv.js.map +1 -0
- package/dist/chunks/block-CPCHkX42.js +2777 -0
- package/dist/chunks/block-CPCHkX42.js.map +1 -0
- package/dist/chunks/{choose-DvpbdRuz.js → choose--jM0yYCo.js} +3 -2
- package/dist/chunks/{choose-DvpbdRuz.js.map → choose--jM0yYCo.js.map} +1 -1
- package/dist/chunks/{constants-fT3E8JYY.js → constants-BKwqelYD.js} +6 -2
- package/dist/chunks/constants-BKwqelYD.js.map +1 -0
- package/dist/chunks/convert-BcQ8_jMo.js +59 -0
- package/dist/chunks/convert-BcQ8_jMo.js.map +1 -0
- package/dist/chunks/emit-DZy6JgdA.js +3456 -0
- package/dist/chunks/emit-DZy6JgdA.js.map +1 -0
- package/dist/chunks/{escape-B62ZGM_a.js → escape-CgzxiYPg.js} +44 -21
- package/dist/chunks/escape-CgzxiYPg.js.map +1 -0
- package/dist/chunks/{export-rKLeGQMo.js → export-BvqAXCKh.js} +196 -792
- package/dist/chunks/export-BvqAXCKh.js.map +1 -0
- package/dist/chunks/{exporter-yLxKBvLE.js → exporter--Mk9gXCJ.js} +4 -3
- package/dist/chunks/{exporter-yLxKBvLE.js.map → exporter--Mk9gXCJ.js.map} +1 -1
- package/dist/chunks/{exporter-RVtXVetw.js → exporter-C-9cgZKa.js} +1805 -287
- package/dist/chunks/exporter-C-9cgZKa.js.map +1 -0
- package/dist/chunks/{exporter-8hLNemFk.js → exporter-DFqJhBnj.js} +133 -74
- package/dist/chunks/exporter-DFqJhBnj.js.map +1 -0
- package/dist/chunks/{exporter-BWwEuOPT.js → exporter-DteVeJki.js} +22 -12
- package/dist/chunks/exporter-DteVeJki.js.map +1 -0
- package/dist/chunks/format-BpQBx8MZ.js +676 -0
- package/dist/chunks/format-BpQBx8MZ.js.map +1 -0
- package/dist/chunks/generic-BGO71QBv.js +1347 -0
- package/dist/chunks/generic-BGO71QBv.js.map +1 -0
- package/dist/chunks/{importer-C98dpSH4.js → importer-B-9Rc1zO.js} +141 -82
- package/dist/chunks/importer-B-9Rc1zO.js.map +1 -0
- package/dist/chunks/{importer-D794XUTe.js → importer-BVVR740x.js} +31 -8
- package/dist/chunks/importer-BVVR740x.js.map +1 -0
- package/dist/chunks/{importer-WrGRZb0N.js → importer-C0AjA3Ax.js} +449 -102
- package/dist/chunks/importer-C0AjA3Ax.js.map +1 -0
- package/dist/chunks/{importer-BbNb7eNg.js → importer-C7pvJe1L.js} +1398 -129
- package/dist/chunks/importer-C7pvJe1L.js.map +1 -0
- package/dist/chunks/{importer-xZ-3suY1.js → importer-ClwoVx3O.js} +791 -135
- package/dist/chunks/importer-ClwoVx3O.js.map +1 -0
- package/dist/chunks/{importer-Ht7LNg8L.js → importer-D0sC_6bO.js} +116 -296
- package/dist/chunks/importer-D0sC_6bO.js.map +1 -0
- package/dist/chunks/{importer-Dz7tI8Sa.js → importer-EHQk1jbM.js} +617 -115
- package/dist/chunks/importer-EHQk1jbM.js.map +1 -0
- package/dist/chunks/{json-elements-DKSqpNW1.js → json-elements-DJZRZ6jd.js} +123 -32
- package/dist/chunks/json-elements-DJZRZ6jd.js.map +1 -0
- package/dist/chunks/lexicon-WMsNddyU.js +509 -0
- package/dist/chunks/lexicon-WMsNddyU.js.map +1 -0
- package/dist/chunks/{ontology-export-CtRXjg_i.js → ontology-export-G4MqRdux.js} +3 -2
- package/dist/chunks/{ontology-export-CtRXjg_i.js.map → ontology-export-G4MqRdux.js.map} +1 -1
- package/dist/chunks/{records-Cu4bxYOm.js → records-CiaWZrFH.js} +2 -2
- package/dist/chunks/{records-Cu4bxYOm.js.map → records-CiaWZrFH.js.map} +1 -1
- package/dist/chunks/styles-DvoFBKCK.js +690 -0
- package/dist/chunks/styles-DvoFBKCK.js.map +1 -0
- package/dist/chunks/styles-yed-Cz9rLBCP.js +842 -0
- package/dist/chunks/styles-yed-Cz9rLBCP.js.map +1 -0
- package/dist/chunks/styles-zNTmz5R7.js +2102 -0
- package/dist/chunks/styles-zNTmz5R7.js.map +1 -0
- package/dist/chunks/{text-DHOJXnED.js → text-CzJNFOS9.js} +2 -2
- package/dist/chunks/{text-DHOJXnED.js.map → text-CzJNFOS9.js.map} +1 -1
- package/dist/chunks/units-XMZFIFe1.js +19 -0
- package/dist/chunks/units-XMZFIFe1.js.map +1 -0
- package/dist/chunks/writer-gQ8kx99H.js +448 -0
- package/dist/chunks/writer-gQ8kx99H.js.map +1 -0
- package/dist/csv.js +5 -4
- package/dist/csv.js.map +1 -1
- package/dist/cx.js +1 -1
- package/dist/cx2.js +2 -2
- package/dist/cys.js +1 -1
- package/dist/dot.js +5 -2
- package/dist/dot.js.map +1 -1
- package/dist/gexf.js +292 -412
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +927 -74
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +351 -236
- package/dist/graph-io.js.map +1 -1
- package/dist/graphml.js +4 -2
- package/dist/graphml.js.map +1 -1
- package/dist/json.js +1 -1
- package/dist/neo4j.js +4 -3
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.js +1 -1
- package/dist/pajek.js +4 -2
- package/dist/positions.d.ts +1 -0
- package/dist/positions.js +427 -0
- package/dist/positions.js.map +1 -0
- package/dist/src/common/codes.d.ts +6 -0
- package/dist/src/common/codes.d.ts.map +1 -1
- package/dist/src/common/codes.js +6 -0
- package/dist/src/common/codes.js.map +1 -1
- package/dist/src/common/escape.d.ts.map +1 -1
- package/dist/src/common/escape.js +4 -2
- package/dist/src/common/escape.js.map +1 -1
- package/dist/src/common/export.d.ts +10 -0
- package/dist/src/common/export.d.ts.map +1 -1
- package/dist/src/common/export.js +123 -100
- package/dist/src/common/export.js.map +1 -1
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +8 -1
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/json-elements.d.ts +12 -9
- package/dist/src/common/json-elements.d.ts.map +1 -1
- package/dist/src/common/json-elements.js +160 -37
- package/dist/src/common/json-elements.js.map +1 -1
- package/dist/src/common/notes.d.ts +42 -0
- package/dist/src/common/notes.d.ts.map +1 -0
- package/dist/src/common/notes.js +72 -0
- package/dist/src/common/notes.js.map +1 -0
- package/dist/src/common/report.d.ts +18 -0
- package/dist/src/common/report.d.ts.map +1 -1
- package/dist/src/common/report.js +36 -1
- package/dist/src/common/report.js.map +1 -1
- package/dist/src/common/units.d.ts +24 -0
- package/dist/src/common/units.d.ts.map +1 -0
- package/dist/src/common/units.js +26 -0
- package/dist/src/common/units.js.map +1 -0
- package/dist/src/common/xml.d.ts +18 -0
- package/dist/src/common/xml.d.ts.map +1 -1
- package/dist/src/common/xml.js +42 -5
- package/dist/src/common/xml.js.map +1 -1
- package/dist/src/formats/cx/exporter.d.ts +6 -0
- package/dist/src/formats/cx/exporter.d.ts.map +1 -1
- package/dist/src/formats/cx/exporter.js +71 -8
- package/dist/src/formats/cx/exporter.js.map +1 -1
- package/dist/src/formats/cx/importer.d.ts +5 -2
- package/dist/src/formats/cx/importer.d.ts.map +1 -1
- package/dist/src/formats/cx/importer.js +102 -72
- package/dist/src/formats/cx/importer.js.map +1 -1
- package/dist/src/formats/cx/styles.d.ts +102 -0
- package/dist/src/formats/cx/styles.d.ts.map +1 -0
- package/dist/src/formats/cx/styles.js +225 -0
- package/dist/src/formats/cx/styles.js.map +1 -0
- package/dist/src/formats/cx2/exporter.d.ts +5 -0
- package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
- package/dist/src/formats/cx2/exporter.js +181 -71
- package/dist/src/formats/cx2/exporter.js.map +1 -1
- package/dist/src/formats/cx2/importer.d.ts +4 -1
- package/dist/src/formats/cx2/importer.d.ts.map +1 -1
- package/dist/src/formats/cx2/importer.js +19 -1
- package/dist/src/formats/cx2/importer.js.map +1 -1
- package/dist/src/formats/cx2/styles.d.ts +18 -0
- package/dist/src/formats/cx2/styles.d.ts.map +1 -0
- package/dist/src/formats/cx2/styles.js +24 -0
- package/dist/src/formats/cx2/styles.js.map +1 -0
- package/dist/src/formats/cys/constants.d.ts +2 -0
- package/dist/src/formats/cys/constants.d.ts.map +1 -1
- package/dist/src/formats/cys/constants.js +2 -0
- package/dist/src/formats/cys/constants.js.map +1 -1
- package/dist/src/formats/cys/exporter.d.ts +6 -2
- package/dist/src/formats/cys/exporter.d.ts.map +1 -1
- package/dist/src/formats/cys/exporter.js +215 -53
- package/dist/src/formats/cys/exporter.js.map +1 -1
- package/dist/src/formats/cys/importer.d.ts +2 -1
- package/dist/src/formats/cys/importer.d.ts.map +1 -1
- package/dist/src/formats/cys/importer.js +119 -6
- package/dist/src/formats/cys/importer.js.map +1 -1
- package/dist/src/formats/cys/session.d.ts +2 -0
- package/dist/src/formats/cys/session.d.ts.map +1 -1
- package/dist/src/formats/cys/session.js +5 -0
- package/dist/src/formats/cys/session.js.map +1 -1
- package/dist/src/formats/cys/styles.d.ts +38 -0
- package/dist/src/formats/cys/styles.d.ts.map +1 -0
- package/dist/src/formats/cys/styles.js +213 -0
- package/dist/src/formats/cys/styles.js.map +1 -0
- package/dist/src/formats/cys/tables.d.ts +8 -3
- package/dist/src/formats/cys/tables.d.ts.map +1 -1
- package/dist/src/formats/cys/tables.js +88 -34
- package/dist/src/formats/cys/tables.js.map +1 -1
- package/dist/src/formats/dot/exporter.d.ts +2 -0
- package/dist/src/formats/dot/exporter.d.ts.map +1 -1
- package/dist/src/formats/dot/exporter.js +195 -81
- package/dist/src/formats/dot/exporter.js.map +1 -1
- package/dist/src/formats/dot/importer.d.ts.map +1 -1
- package/dist/src/formats/dot/importer.js +100 -20
- package/dist/src/formats/dot/importer.js.map +1 -1
- package/dist/src/formats/dot/index.d.ts +1 -0
- package/dist/src/formats/dot/index.d.ts.map +1 -1
- package/dist/src/formats/dot/index.js +1 -0
- package/dist/src/formats/dot/index.js.map +1 -1
- package/dist/src/formats/dot/positions.d.ts +14 -0
- package/dist/src/formats/dot/positions.d.ts.map +1 -0
- package/dist/src/formats/dot/positions.js +70 -0
- package/dist/src/formats/dot/positions.js.map +1 -0
- package/dist/src/formats/dot/styles.d.ts +82 -0
- package/dist/src/formats/dot/styles.d.ts.map +1 -0
- package/dist/src/formats/dot/styles.js +1496 -0
- package/dist/src/formats/dot/styles.js.map +1 -0
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
- package/dist/src/formats/gexf/exporter.js +20 -5
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts +8 -1
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +251 -131
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gexf/index.d.ts +8 -0
- package/dist/src/formats/gexf/index.d.ts.map +1 -1
- package/dist/src/formats/gexf/index.js +10 -2
- package/dist/src/formats/gexf/index.js.map +1 -1
- package/dist/src/formats/gexf/schema.d.ts +5 -0
- package/dist/src/formats/gexf/schema.d.ts.map +1 -1
- package/dist/src/formats/gexf/schema.js +5 -0
- package/dist/src/formats/gexf/schema.js.map +1 -1
- package/dist/src/formats/gexf/styles.d.ts +74 -0
- package/dist/src/formats/gexf/styles.d.ts.map +1 -0
- package/dist/src/formats/gexf/styles.js +604 -0
- package/dist/src/formats/gexf/styles.js.map +1 -0
- package/dist/src/formats/gml/exporter.d.ts +1 -5
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +19 -8
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +4 -0
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/index.d.ts +2 -1
- package/dist/src/formats/gml/index.d.ts.map +1 -1
- package/dist/src/formats/gml/index.js +2 -1
- package/dist/src/formats/gml/index.js.map +1 -1
- package/dist/src/formats/gml/positions.d.ts +14 -0
- package/dist/src/formats/gml/positions.d.ts.map +1 -0
- package/dist/src/formats/gml/positions.js +46 -0
- package/dist/src/formats/gml/positions.js.map +1 -0
- package/dist/src/formats/gml/styles.d.ts +59 -0
- package/dist/src/formats/gml/styles.d.ts.map +1 -0
- package/dist/src/formats/gml/styles.js +1056 -0
- package/dist/src/formats/gml/styles.js.map +1 -0
- package/dist/src/formats/gml/syntax.d.ts +11 -0
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +151 -62
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/constants.d.ts +4 -0
- package/dist/src/formats/graphml/constants.d.ts.map +1 -1
- package/dist/src/formats/graphml/constants.js +5 -1
- package/dist/src/formats/graphml/constants.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +133 -65
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts +4 -1
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +100 -44
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/index.d.ts +1 -0
- package/dist/src/formats/graphml/index.d.ts.map +1 -1
- package/dist/src/formats/graphml/index.js +1 -0
- package/dist/src/formats/graphml/index.js.map +1 -1
- package/dist/src/formats/graphml/styles-plain.d.ts +19 -0
- package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -0
- package/dist/src/formats/graphml/styles-plain.js +187 -0
- package/dist/src/formats/graphml/styles-plain.js.map +1 -0
- package/dist/src/formats/graphml/styles-yed.d.ts +179 -0
- package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -0
- package/dist/src/formats/graphml/styles-yed.js +1086 -0
- package/dist/src/formats/graphml/styles-yed.js.map +1 -0
- package/dist/src/formats/graphml/styles-yfiles3.d.ts +19 -0
- package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -0
- package/dist/src/formats/graphml/styles-yfiles3.js +508 -0
- package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -0
- package/dist/src/formats/graphml/tree.d.ts.map +1 -1
- package/dist/src/formats/graphml/tree.js +34 -19
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts +16 -2
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +83 -12
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts +5 -0
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +68 -25
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts +22 -3
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +439 -264
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/positions.d.ts +16 -0
- package/dist/src/formats/json/positions.d.ts.map +1 -0
- package/dist/src/formats/json/positions.js +44 -0
- package/dist/src/formats/json/positions.js.map +1 -0
- package/dist/src/formats/json/styles.d.ts +102 -0
- package/dist/src/formats/json/styles.d.ts.map +1 -0
- package/dist/src/formats/json/styles.js +1753 -0
- package/dist/src/formats/json/styles.js.map +1 -0
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +2 -1
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts +2 -0
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +142 -76
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts +4 -0
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +60 -6
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/index.d.ts +2 -0
- package/dist/src/formats/pajek/index.d.ts.map +1 -1
- package/dist/src/formats/pajek/index.js +2 -0
- package/dist/src/formats/pajek/index.js.map +1 -1
- package/dist/src/formats/pajek/styles.d.ts +50 -0
- package/dist/src/formats/pajek/styles.d.ts.map +1 -0
- package/dist/src/formats/pajek/styles.js +569 -0
- package/dist/src/formats/pajek/styles.js.map +1 -0
- package/dist/src/formats/pajek/syntax.d.ts +6 -0
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +6 -0
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
- package/dist/src/formats/xgmml/columns.js +34 -9
- package/dist/src/formats/xgmml/columns.js.map +1 -1
- package/dist/src/formats/xgmml/constants.d.ts +2 -0
- package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
- package/dist/src/formats/xgmml/constants.js +3 -1
- package/dist/src/formats/xgmml/constants.js.map +1 -1
- package/dist/src/formats/xgmml/document.d.ts +12 -8
- package/dist/src/formats/xgmml/document.d.ts.map +1 -1
- package/dist/src/formats/xgmml/document.js +56 -13
- package/dist/src/formats/xgmml/document.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +12 -6
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts +3 -1
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +22 -9
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts +2 -1
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +31 -24
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/formats/xgmml/styles.d.ts +104 -0
- package/dist/src/formats/xgmml/styles.d.ts.map +1 -0
- package/dist/src/formats/xgmml/styles.js +310 -0
- package/dist/src/formats/xgmml/styles.js.map +1 -0
- package/dist/src/index.d.ts +9 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -7
- package/dist/src/index.js.map +1 -1
- package/dist/src/positions/codes.d.ts +31 -0
- package/dist/src/positions/codes.d.ts.map +1 -0
- package/dist/src/positions/codes.js +26 -0
- package/dist/src/positions/codes.js.map +1 -0
- package/dist/src/positions/convert.d.ts +58 -0
- package/dist/src/positions/convert.d.ts.map +1 -0
- package/dist/src/positions/convert.js +107 -0
- package/dist/src/positions/convert.js.map +1 -0
- package/dist/src/positions/extract.d.ts +27 -0
- package/dist/src/positions/extract.d.ts.map +1 -0
- package/dist/src/positions/extract.js +526 -0
- package/dist/src/positions/extract.js.map +1 -0
- package/dist/src/positions/index.d.ts +10 -0
- package/dist/src/positions/index.d.ts.map +1 -0
- package/dist/src/positions/index.js +9 -0
- package/dist/src/positions/index.js.map +1 -0
- package/dist/src/positions/types.d.ts +208 -0
- package/dist/src/positions/types.d.ts.map +1 -0
- package/dist/src/positions/types.js +10 -0
- package/dist/src/positions/types.js.map +1 -0
- package/dist/src/registry.d.ts +30 -0
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +130 -9
- package/dist/src/registry.js.map +1 -1
- package/dist/src/styles/annotations.d.ts +132 -0
- package/dist/src/styles/annotations.d.ts.map +1 -0
- package/dist/src/styles/annotations.js +320 -0
- package/dist/src/styles/annotations.js.map +1 -0
- package/dist/src/styles/budget.d.ts +45 -0
- package/dist/src/styles/budget.d.ts.map +1 -0
- package/dist/src/styles/budget.js +58 -0
- package/dist/src/styles/budget.js.map +1 -0
- package/dist/src/styles/canon/bends.d.ts +32 -0
- package/dist/src/styles/canon/bends.d.ts.map +1 -0
- package/dist/src/styles/canon/bends.js +74 -0
- package/dist/src/styles/canon/bends.js.map +1 -0
- package/dist/src/styles/canon/color-tables.d.ts +22 -0
- package/dist/src/styles/canon/color-tables.d.ts.map +1 -0
- package/dist/src/styles/canon/color-tables.js +369 -0
- package/dist/src/styles/canon/color-tables.js.map +1 -0
- package/dist/src/styles/canon/color.d.ts +66 -0
- package/dist/src/styles/canon/color.d.ts.map +1 -0
- package/dist/src/styles/canon/color.js +548 -0
- package/dist/src/styles/canon/color.js.map +1 -0
- package/dist/src/styles/canon/enums.d.ts +25 -0
- package/dist/src/styles/canon/enums.d.ts.map +1 -0
- package/dist/src/styles/canon/enums.js +187 -0
- package/dist/src/styles/canon/enums.js.map +1 -0
- package/dist/src/styles/canon/graphviz.d.ts +48 -0
- package/dist/src/styles/canon/graphviz.d.ts.map +1 -0
- package/dist/src/styles/canon/graphviz.js +160 -0
- package/dist/src/styles/canon/graphviz.js.map +1 -0
- package/dist/src/styles/canon/index.d.ts +13 -0
- package/dist/src/styles/canon/index.d.ts.map +1 -0
- package/dist/src/styles/canon/index.js +13 -0
- package/dist/src/styles/canon/index.js.map +1 -0
- package/dist/src/styles/canon/number.d.ts +59 -0
- package/dist/src/styles/canon/number.d.ts.map +1 -0
- package/dist/src/styles/canon/number.js +142 -0
- package/dist/src/styles/canon/number.js.map +1 -0
- package/dist/src/styles/canon/site.d.ts +51 -0
- package/dist/src/styles/canon/site.d.ts.map +1 -0
- package/dist/src/styles/canon/site.js +63 -0
- package/dist/src/styles/canon/site.js.map +1 -0
- package/dist/src/styles/canon/text.d.ts +61 -0
- package/dist/src/styles/canon/text.d.ts.map +1 -0
- package/dist/src/styles/canon/text.js +199 -0
- package/dist/src/styles/canon/text.js.map +1 -0
- package/dist/src/styles/codes.d.ts +95 -0
- package/dist/src/styles/codes.d.ts.map +1 -0
- package/dist/src/styles/codes.js +97 -0
- package/dist/src/styles/codes.js.map +1 -0
- package/dist/src/styles/cyjs/cascade.d.ts +134 -0
- package/dist/src/styles/cyjs/cascade.d.ts.map +1 -0
- package/dist/src/styles/cyjs/cascade.js +2252 -0
- package/dist/src/styles/cyjs/cascade.js.map +1 -0
- package/dist/src/styles/cyjs/defaults-table.d.ts +25 -0
- package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -0
- package/dist/src/styles/cyjs/defaults-table.js +76 -0
- package/dist/src/styles/cyjs/defaults-table.js.map +1 -0
- package/dist/src/styles/cyjs/emit.d.ts +82 -0
- package/dist/src/styles/cyjs/emit.d.ts.map +1 -0
- package/dist/src/styles/cyjs/emit.js +791 -0
- package/dist/src/styles/cyjs/emit.js.map +1 -0
- package/dist/src/styles/cyjs/index.d.ts +13 -0
- package/dist/src/styles/cyjs/index.d.ts.map +1 -0
- package/dist/src/styles/cyjs/index.js +13 -0
- package/dist/src/styles/cyjs/index.js.map +1 -0
- package/dist/src/styles/cyjs/live.d.ts +46 -0
- package/dist/src/styles/cyjs/live.d.ts.map +1 -0
- package/dist/src/styles/cyjs/live.js +108 -0
- package/dist/src/styles/cyjs/live.js.map +1 -0
- package/dist/src/styles/cyjs/mappers.d.ts +147 -0
- package/dist/src/styles/cyjs/mappers.d.ts.map +1 -0
- package/dist/src/styles/cyjs/mappers.js +744 -0
- package/dist/src/styles/cyjs/mappers.js.map +1 -0
- package/dist/src/styles/cyjs/selector.d.ts +113 -0
- package/dist/src/styles/cyjs/selector.d.ts.map +1 -0
- package/dist/src/styles/cyjs/selector.js +611 -0
- package/dist/src/styles/cyjs/selector.js.map +1 -0
- package/dist/src/styles/cyjs/stylesheet-text.d.ts +37 -0
- package/dist/src/styles/cyjs/stylesheet-text.d.ts.map +1 -0
- package/dist/src/styles/cyjs/stylesheet-text.js +181 -0
- package/dist/src/styles/cyjs/stylesheet-text.js.map +1 -0
- package/dist/src/styles/cytoscape/emit.d.ts +125 -0
- package/dist/src/styles/cytoscape/emit.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/emit.js +1025 -0
- package/dist/src/styles/cytoscape/emit.js.map +1 -0
- package/dist/src/styles/cytoscape/index.d.ts +10 -0
- package/dist/src/styles/cytoscape/index.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/index.js +10 -0
- package/dist/src/styles/cytoscape/index.js.map +1 -0
- package/dist/src/styles/cytoscape/lexicon.d.ts +49 -0
- package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/lexicon.js +605 -0
- package/dist/src/styles/cytoscape/lexicon.js.map +1 -0
- package/dist/src/styles/cytoscape/mappings.d.ts +171 -0
- package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/mappings.js +460 -0
- package/dist/src/styles/cytoscape/mappings.js.map +1 -0
- package/dist/src/styles/cytoscape/resolve.d.ts +137 -0
- package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/resolve.js +381 -0
- package/dist/src/styles/cytoscape/resolve.js.map +1 -0
- package/dist/src/styles/cytoscape/translate.d.ts +50 -0
- package/dist/src/styles/cytoscape/translate.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/translate.js +156 -0
- package/dist/src/styles/cytoscape/translate.js.map +1 -0
- package/dist/src/styles/defaults.d.ts +90 -0
- package/dist/src/styles/defaults.d.ts.map +1 -0
- package/dist/src/styles/defaults.js +414 -0
- package/dist/src/styles/defaults.js.map +1 -0
- package/dist/src/styles/index.d.ts +15 -0
- package/dist/src/styles/index.d.ts.map +1 -0
- package/dist/src/styles/index.js +14 -0
- package/dist/src/styles/index.js.map +1 -0
- package/dist/src/styles/internal.d.ts +17 -0
- package/dist/src/styles/internal.d.ts.map +1 -0
- package/dist/src/styles/internal.js +16 -0
- package/dist/src/styles/internal.js.map +1 -0
- package/dist/src/styles/io/approximations.d.ts +24 -0
- package/dist/src/styles/io/approximations.d.ts.map +1 -0
- package/dist/src/styles/io/approximations.js +51 -0
- package/dist/src/styles/io/approximations.js.map +1 -0
- package/dist/src/styles/io/block.d.ts +166 -0
- package/dist/src/styles/io/block.d.ts.map +1 -0
- package/dist/src/styles/io/block.js +930 -0
- package/dist/src/styles/io/block.js.map +1 -0
- package/dist/src/styles/io/generic.d.ts +88 -0
- package/dist/src/styles/io/generic.d.ts.map +1 -0
- package/dist/src/styles/io/generic.js +516 -0
- package/dist/src/styles/io/generic.js.map +1 -0
- package/dist/src/styles/io/index.d.ts +8 -0
- package/dist/src/styles/io/index.d.ts.map +1 -0
- package/dist/src/styles/io/index.js +7 -0
- package/dist/src/styles/io/index.js.map +1 -0
- package/dist/src/styles/io/writer.d.ts +89 -0
- package/dist/src/styles/io/writer.d.ts.map +1 -0
- package/dist/src/styles/io/writer.js +626 -0
- package/dist/src/styles/io/writer.js.map +1 -0
- package/dist/src/styles/notes.d.ts +47 -0
- package/dist/src/styles/notes.d.ts.map +1 -0
- package/dist/src/styles/notes.js +140 -0
- package/dist/src/styles/notes.js.map +1 -0
- package/dist/src/styles/plan/classes.d.ts +31 -0
- package/dist/src/styles/plan/classes.d.ts.map +1 -0
- package/dist/src/styles/plan/classes.js +139 -0
- package/dist/src/styles/plan/classes.js.map +1 -0
- package/dist/src/styles/plan/continuous.d.ts +32 -0
- package/dist/src/styles/plan/continuous.d.ts.map +1 -0
- package/dist/src/styles/plan/continuous.js +165 -0
- package/dist/src/styles/plan/continuous.js.map +1 -0
- package/dist/src/styles/plan/discrete.d.ts +39 -0
- package/dist/src/styles/plan/discrete.d.ts.map +1 -0
- package/dist/src/styles/plan/discrete.js +127 -0
- package/dist/src/styles/plan/discrete.js.map +1 -0
- package/dist/src/styles/plan/families.d.ts +31 -0
- package/dist/src/styles/plan/families.d.ts.map +1 -0
- package/dist/src/styles/plan/families.js +95 -0
- package/dist/src/styles/plan/families.js.map +1 -0
- package/dist/src/styles/plan/features.d.ts +58 -0
- package/dist/src/styles/plan/features.d.ts.map +1 -0
- package/dist/src/styles/plan/features.js +185 -0
- package/dist/src/styles/plan/features.js.map +1 -0
- package/dist/src/styles/plan/index.d.ts +7 -0
- package/dist/src/styles/plan/index.d.ts.map +1 -0
- package/dist/src/styles/plan/index.js +7 -0
- package/dist/src/styles/plan/index.js.map +1 -0
- package/dist/src/styles/plan/plan.d.ts +71 -0
- package/dist/src/styles/plan/plan.d.ts.map +1 -0
- package/dist/src/styles/plan/plan.js +893 -0
- package/dist/src/styles/plan/plan.js.map +1 -0
- package/dist/src/styles/plan/resolve.d.ts +137 -0
- package/dist/src/styles/plan/resolve.d.ts.map +1 -0
- package/dist/src/styles/plan/resolve.js +587 -0
- package/dist/src/styles/plan/resolve.js.map +1 -0
- package/dist/src/styles/types.d.ts +702 -0
- package/dist/src/styles/types.d.ts.map +1 -0
- package/dist/src/styles/types.js +10 -0
- package/dist/src/styles/types.js.map +1 -0
- package/dist/src/styles/vocabulary.d.ts +41 -0
- package/dist/src/styles/vocabulary.d.ts.map +1 -0
- package/dist/src/styles/vocabulary.js +140 -0
- package/dist/src/styles/vocabulary.js.map +1 -0
- package/dist/src/types.d.ts +64 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/styles.d.ts +1 -0
- package/dist/styles.js +322 -0
- package/dist/styles.js.map +1 -0
- package/dist/xgmml.js +5 -4
- package/dist/xgmml.js.map +1 -1
- package/package.json +13 -2
- package/src/common/codes.ts +7 -0
- package/src/common/escape.ts +5 -2
- package/src/common/export.ts +179 -140
- package/src/common/input.ts +8 -4
- package/src/common/json-elements.ts +169 -38
- package/src/common/notes.ts +94 -0
- package/src/common/report.ts +41 -1
- package/src/common/units.ts +42 -0
- package/src/common/xml.ts +45 -5
- package/src/formats/cx/exporter.ts +95 -11
- package/src/formats/cx/importer.ts +126 -90
- package/src/formats/cx/styles.ts +304 -0
- package/src/formats/cx2/exporter.ts +234 -77
- package/src/formats/cx2/importer.ts +19 -1
- package/src/formats/cx2/styles.ts +29 -0
- package/src/formats/cys/constants.ts +3 -0
- package/src/formats/cys/exporter.ts +246 -60
- package/src/formats/cys/importer.ts +125 -9
- package/src/formats/cys/session.ts +6 -0
- package/src/formats/cys/styles.ts +254 -0
- package/src/formats/cys/tables.ts +117 -48
- package/src/formats/dot/exporter.ts +262 -126
- package/src/formats/dot/importer.ts +111 -20
- package/src/formats/dot/index.ts +1 -0
- package/src/formats/dot/positions.ts +77 -0
- package/src/formats/dot/styles.ts +1755 -0
- package/src/formats/gexf/exporter.ts +27 -11
- package/src/formats/gexf/importer.ts +308 -158
- package/src/formats/gexf/index.ts +10 -0
- package/src/formats/gexf/schema.ts +5 -0
- package/src/formats/gexf/styles.ts +719 -0
- package/src/formats/gml/exporter.ts +33 -14
- package/src/formats/gml/importer.ts +4 -0
- package/src/formats/gml/index.ts +2 -1
- package/src/formats/gml/positions.ts +52 -0
- package/src/formats/gml/styles.ts +1248 -0
- package/src/formats/gml/syntax.ts +173 -62
- package/src/formats/graphml/constants.ts +5 -0
- package/src/formats/graphml/exporter.ts +163 -71
- package/src/formats/graphml/importer.ts +119 -45
- package/src/formats/graphml/index.ts +1 -0
- package/src/formats/graphml/styles-plain.ts +236 -0
- package/src/formats/graphml/styles-yed.ts +1221 -0
- package/src/formats/graphml/styles-yfiles3.ts +573 -0
- package/src/formats/graphml/tree.ts +36 -20
- package/src/formats/graphml/yfiles.ts +89 -13
- package/src/formats/json/exporter.ts +76 -26
- package/src/formats/json/importer.ts +541 -322
- package/src/formats/json/positions.ts +50 -0
- package/src/formats/json/styles.ts +2082 -0
- package/src/formats/neo4j/exporter.ts +2 -1
- package/src/formats/pajek/exporter.ts +195 -106
- package/src/formats/pajek/importer.ts +78 -9
- package/src/formats/pajek/index.ts +2 -0
- package/src/formats/pajek/styles.ts +685 -0
- package/src/formats/pajek/syntax.ts +7 -0
- package/src/formats/xgmml/columns.ts +39 -9
- package/src/formats/xgmml/constants.ts +3 -0
- package/src/formats/xgmml/document.ts +54 -12
- package/src/formats/xgmml/emit.ts +11 -6
- package/src/formats/xgmml/exporter.ts +32 -13
- package/src/formats/xgmml/importer.ts +43 -29
- package/src/formats/xgmml/styles.ts +400 -0
- package/src/index.ts +16 -0
- package/src/positions/codes.ts +32 -0
- package/src/positions/convert.ts +123 -0
- package/src/positions/extract.ts +619 -0
- package/src/positions/index.ts +23 -0
- package/src/positions/types.ts +221 -0
- package/src/registry.ts +179 -8
- package/src/styles/annotations.ts +478 -0
- package/src/styles/budget.ts +60 -0
- package/src/styles/canon/bends.ts +84 -0
- package/src/styles/canon/color-tables.ts +386 -0
- package/src/styles/canon/color.ts +624 -0
- package/src/styles/canon/enums.ts +211 -0
- package/src/styles/canon/graphviz.ts +189 -0
- package/src/styles/canon/index.ts +13 -0
- package/src/styles/canon/number.ts +152 -0
- package/src/styles/canon/site.ts +87 -0
- package/src/styles/canon/text.ts +221 -0
- package/src/styles/codes.ts +104 -0
- package/src/styles/cyjs/cascade.ts +2670 -0
- package/src/styles/cyjs/defaults-table.ts +82 -0
- package/src/styles/cyjs/emit.ts +952 -0
- package/src/styles/cyjs/index.ts +37 -0
- package/src/styles/cyjs/live.ts +147 -0
- package/src/styles/cyjs/mappers.ts +879 -0
- package/src/styles/cyjs/selector.ts +778 -0
- package/src/styles/cyjs/stylesheet-text.ts +214 -0
- package/src/styles/cytoscape/emit.ts +1264 -0
- package/src/styles/cytoscape/index.ts +40 -0
- package/src/styles/cytoscape/lexicon.ts +658 -0
- package/src/styles/cytoscape/mappings.ts +603 -0
- package/src/styles/cytoscape/resolve.ts +521 -0
- package/src/styles/cytoscape/translate.ts +237 -0
- package/src/styles/defaults.ts +504 -0
- package/src/styles/index.ts +57 -0
- package/src/styles/internal.ts +17 -0
- package/src/styles/io/approximations.ts +47 -0
- package/src/styles/io/block.ts +1138 -0
- package/src/styles/io/generic.ts +687 -0
- package/src/styles/io/index.ts +18 -0
- package/src/styles/io/writer.ts +780 -0
- package/src/styles/notes.ts +165 -0
- package/src/styles/plan/classes.ts +166 -0
- package/src/styles/plan/continuous.ts +218 -0
- package/src/styles/plan/discrete.ts +165 -0
- package/src/styles/plan/families.ts +115 -0
- package/src/styles/plan/features.ts +234 -0
- package/src/styles/plan/index.ts +7 -0
- package/src/styles/plan/plan.ts +1129 -0
- package/src/styles/plan/resolve.ts +685 -0
- package/src/styles/types.ts +814 -0
- package/src/styles/vocabulary.ts +172 -0
- package/src/types.ts +76 -1
- package/dist/chunks/constants-fT3E8JYY.js.map +0 -1
- package/dist/chunks/escape-B62ZGM_a.js.map +0 -1
- package/dist/chunks/export-rKLeGQMo.js.map +0 -1
- package/dist/chunks/exporter-8hLNemFk.js.map +0 -1
- package/dist/chunks/exporter-BWwEuOPT.js.map +0 -1
- package/dist/chunks/exporter-RVtXVetw.js.map +0 -1
- package/dist/chunks/importer-BbNb7eNg.js.map +0 -1
- package/dist/chunks/importer-C98dpSH4.js.map +0 -1
- package/dist/chunks/importer-D794XUTe.js.map +0 -1
- package/dist/chunks/importer-Dz7tI8Sa.js.map +0 -1
- package/dist/chunks/importer-Ht7LNg8L.js.map +0 -1
- package/dist/chunks/importer-WrGRZb0N.js.map +0 -1
- package/dist/chunks/importer-xZ-3suY1.js.map +0 -1
- package/dist/chunks/json-elements-DKSqpNW1.js.map +0 -1
|
@@ -0,0 +1,1753 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON styles: each dialect read the way the program that draws it draws it, and a graph's styles written back into
|
|
3
|
+
* each dialect's own style keys.
|
|
4
|
+
*
|
|
5
|
+
* - Cytoscape.js: the stylesheet (an array or a CSS-like text) over the raw element JSON, through graph-io's
|
|
6
|
+
* Cytoscape.js engine, with Cytoscape.js's defaults beneath it; an element's own `style` is a bypass. Cytoscape
|
|
7
|
+
* desktop's export without a stylesheet gets Cytoscape desktop's default style instead.
|
|
8
|
+
* - graphology: the keys sigma.js draws (`color`, `size`, `label`, `hidden`), with sigma's defaults. Gephi's JSON
|
|
9
|
+
* export for sigma.js is read the same way.
|
|
10
|
+
* - vis.js: the keys vis-network draws, `options.nodes` and `options.edges` as declared defaults and
|
|
11
|
+
* `options.groups` as group rules, with vis-network's defaults, its palette for groups the file does not define,
|
|
12
|
+
* and an edge without a color of its own in its source node's border color.
|
|
13
|
+
* - NetworkX node-link: no style keys, NetworkX's drawing defaults.
|
|
14
|
+
*
|
|
15
|
+
* Writing: Cytoscape.js gets a stylesheet with the element data its selectors read and a bypass where no rule gives
|
|
16
|
+
* the value; graphology and vis.js get their per-element keys; node-link, d3 and JGF only the `graphty.styles` block
|
|
17
|
+
* and the tagged per-element values. The block is a graph attribute where the dialect has graph attributes
|
|
18
|
+
* (node-link, JGF, graphology) and a top-level key elsewhere (d3, vis.js, Cytoscape.js).
|
|
19
|
+
*/
|
|
20
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
21
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
22
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
23
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
24
|
+
};
|
|
25
|
+
var _Replaced_counts;
|
|
26
|
+
import { canonColor } from "../../styles/canon/color.js";
|
|
27
|
+
import { canonEnum } from "../../styles/canon/enums.js";
|
|
28
|
+
import { canonBoolean, canonNumber, canonOpacity } from "../../styles/canon/number.js";
|
|
29
|
+
import { canonText } from "../../styles/canon/text.js";
|
|
30
|
+
import { STYLE_ISSUE } from "../../styles/codes.js";
|
|
31
|
+
import { cytoscapeJsStyles } from "../../styles/cyjs/cascade.js";
|
|
32
|
+
import { cyjsHoldsExactly, CYTOSCAPE_IMPORT_KEY, toCytoscapeStylesheet, } from "../../styles/cyjs/emit.js";
|
|
33
|
+
import { CYJS_EDGE_PROPERTIES, CYJS_NODE_PROPERTIES } from "../../styles/cyjs/mappers.js";
|
|
34
|
+
import { toolDefaults } from "../../styles/defaults.js";
|
|
35
|
+
import { StyleApproximations } from "../../styles/io/approximations.js";
|
|
36
|
+
import { blockStyles, parseStyleBlock, STYLE_BLOCK_NAME } from "../../styles/io/block.js";
|
|
37
|
+
import { StyleNotes } from "../../styles/notes.js";
|
|
38
|
+
import { styleFingerprint } from "../../styles/plan/features.js";
|
|
39
|
+
import { plainCell, resolveStyles } from "../../styles/plan/resolve.js";
|
|
40
|
+
import { isJsonObject } from "./dialect.js";
|
|
41
|
+
const TARGETS = ["node", "edge"];
|
|
42
|
+
// ============================================================ reading
|
|
43
|
+
/**
|
|
44
|
+
* The styles of a JSON document, read by its dialect's reader from what the import kept.
|
|
45
|
+
* @param snapshot - the graph the import built
|
|
46
|
+
* @param context - the work budget and what the import kept
|
|
47
|
+
* @returns the styles, or null when the dialect has none (a block held as a graph attribute is read by graph-io)
|
|
48
|
+
*/
|
|
49
|
+
export function jsonStyles(snapshot, context) {
|
|
50
|
+
const src = context.kept;
|
|
51
|
+
if (!isJsonObject(src)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
switch (src.dialect) {
|
|
55
|
+
case "cytoscape":
|
|
56
|
+
return cytoscapeStyles(snapshot, src, context);
|
|
57
|
+
case "graphology":
|
|
58
|
+
return graphologyStyles(snapshot, src, context);
|
|
59
|
+
case "vis":
|
|
60
|
+
return visStyles(snapshot, src, context);
|
|
61
|
+
case "node-link":
|
|
62
|
+
if (src.networkx === true) {
|
|
63
|
+
return networkxStyles(snapshot);
|
|
64
|
+
}
|
|
65
|
+
return src.block === null ? null : { canonical: true, block: src.block };
|
|
66
|
+
default:
|
|
67
|
+
return src.block === null ? null : { canonical: true, block: src.block };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Cytoscape.js: the stylesheet over the raw elements. A file graphty wrote is read without Cytoscape.js's own default
|
|
72
|
+
* rules (its block holds graphty's defaults); Cytoscape desktop's export without a stylesheet gets Cytoscape desktop's
|
|
73
|
+
* default style.
|
|
74
|
+
* @param snapshot - the graph
|
|
75
|
+
* @param src - what the import kept
|
|
76
|
+
* @param context - the work budget
|
|
77
|
+
* @returns the styles
|
|
78
|
+
*/
|
|
79
|
+
function cytoscapeStyles(snapshot, src, context) {
|
|
80
|
+
const sheetless = (src.style === undefined || src.style === null) && src.generatedBy?.startsWith("cytoscape-") === true;
|
|
81
|
+
// a file graphty wrote: its stylesheet states graphty's styles, beneath which graphty draws its own defaults
|
|
82
|
+
const graphty = src.block !== null && parseStyleBlock(src.block, styleFingerprint(snapshot)).ok;
|
|
83
|
+
const read = cytoscapeJsStyles({
|
|
84
|
+
elements: withGeneratedIds(src.elements, src.generatedIds),
|
|
85
|
+
style: sheetless ? [] : (src.style ?? []),
|
|
86
|
+
snapshot,
|
|
87
|
+
toolDefaults: !graphty && !sheetless,
|
|
88
|
+
budget: context.budget,
|
|
89
|
+
signal: context.signal,
|
|
90
|
+
});
|
|
91
|
+
if (!("canonical" in read)) {
|
|
92
|
+
return read;
|
|
93
|
+
}
|
|
94
|
+
let tool = "cytoscape-js";
|
|
95
|
+
if (graphty) {
|
|
96
|
+
tool = null;
|
|
97
|
+
exactFromBlock(snapshot, src.block, { node: (read.node?.values ?? {}), edge: (read.edge?.values ?? {}) }, CYJS_WRITTEN, cyjsHoldsExactly);
|
|
98
|
+
}
|
|
99
|
+
else if (sheetless) {
|
|
100
|
+
tool = "cytoscape";
|
|
101
|
+
}
|
|
102
|
+
return { ...read, tool, block: src.block };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Cytoscape.js trims every text it draws: a label with blanks around it reads back trimmed.
|
|
106
|
+
* @param property - the text property
|
|
107
|
+
* @returns what a written value reads back as
|
|
108
|
+
*/
|
|
109
|
+
const trimmed = (property) => (want) => {
|
|
110
|
+
const v = want(property);
|
|
111
|
+
return typeof v === "string" ? v.trim() : null;
|
|
112
|
+
};
|
|
113
|
+
/** The text properties Cytoscape.js trims. */
|
|
114
|
+
const CYJS_TEXT = {
|
|
115
|
+
node: ["label"],
|
|
116
|
+
edge: ["label", "sourceLabel", "targetLabel"],
|
|
117
|
+
};
|
|
118
|
+
const CYJS_WRITTEN = {
|
|
119
|
+
node: Object.fromEntries(CYJS_TEXT.node.map((p) => [p, trimmed(p)])),
|
|
120
|
+
edge: {
|
|
121
|
+
...Object.fromEntries(CYJS_TEXT.edge.map((p) => [p, trimmed(p)])),
|
|
122
|
+
// Cytoscape.js draws a loop curved whatever its curve style says
|
|
123
|
+
curved: (want, e, snapshot) => snapshot.edgeSource(e) === snapshot.edgeTarget(e) && want("curved") !== null ? true : want("curved"),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* The elements with the ids graph-io gave the nodes that have none, so the engine matches them to the graph.
|
|
128
|
+
* @param elements - the elements as the file holds them
|
|
129
|
+
* @param ids - the generated ids
|
|
130
|
+
* @returns the elements, copied only where an id is added
|
|
131
|
+
*/
|
|
132
|
+
function withGeneratedIds(elements, ids) {
|
|
133
|
+
if (ids === undefined || ids.size === 0) {
|
|
134
|
+
return elements;
|
|
135
|
+
}
|
|
136
|
+
const patch = (e) => {
|
|
137
|
+
const id = isJsonObject(e) ? ids.get(e) : undefined;
|
|
138
|
+
return id === undefined || !isJsonObject(e) ? e : { ...e, data: { ...e.data, id } };
|
|
139
|
+
};
|
|
140
|
+
if (Array.isArray(elements)) {
|
|
141
|
+
return elements.map(patch);
|
|
142
|
+
}
|
|
143
|
+
if (isJsonObject(elements) && Array.isArray(elements.nodes)) {
|
|
144
|
+
return { ...elements, nodes: elements.nodes.map(patch) };
|
|
145
|
+
}
|
|
146
|
+
return elements;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* NetworkX node-link: no style keys, NetworkX's drawing defaults, and an arrow at the target of every directed edge.
|
|
150
|
+
* @param snapshot - the graph
|
|
151
|
+
* @returns the styles
|
|
152
|
+
*/
|
|
153
|
+
function networkxStyles(snapshot) {
|
|
154
|
+
return {
|
|
155
|
+
canonical: true,
|
|
156
|
+
dialect: null,
|
|
157
|
+
tool: "networkx",
|
|
158
|
+
units: "px",
|
|
159
|
+
node: { values: {} },
|
|
160
|
+
edge: directedArrows(snapshot),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The edge output marking directed edges with the tool's target arrow.
|
|
165
|
+
* @param snapshot - the graph
|
|
166
|
+
* @returns the edge output
|
|
167
|
+
*/
|
|
168
|
+
function directedArrows(snapshot) {
|
|
169
|
+
if (!snapshot.directed || snapshot.edgeCount === 0) {
|
|
170
|
+
return { values: {}, rules: [], columns: new Map() };
|
|
171
|
+
}
|
|
172
|
+
const col = snapshot.edges.byRole("directed");
|
|
173
|
+
const flags = Array.from({ length: snapshot.edgeCount }, (_v, e) => col === null || plainCell(col.value(e)) !== false ? true : null);
|
|
174
|
+
return {
|
|
175
|
+
values: {},
|
|
176
|
+
rules: [
|
|
177
|
+
{
|
|
178
|
+
id: "edge.tool.arrow",
|
|
179
|
+
target: "edge",
|
|
180
|
+
role: "group",
|
|
181
|
+
origin: "tool",
|
|
182
|
+
where: { has: { style: "directed" } },
|
|
183
|
+
set: { targetArrow: "triangle" },
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
columns: new Map([["directed", flags]]),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* One element's values of a column, with a reader per value.
|
|
191
|
+
* @param snapshot - the graph
|
|
192
|
+
* @param target - nodes or edges
|
|
193
|
+
* @param name - the column
|
|
194
|
+
* @returns the cell reader, or null when the graph has no such column
|
|
195
|
+
*/
|
|
196
|
+
function cells(snapshot, target, name) {
|
|
197
|
+
const table = target === "node" ? snapshot.nodes : snapshot.edges;
|
|
198
|
+
return table.has(name) ? (i) => plainCell(table.value(name, i)) : null;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* A values table that makes each property's column on first use.
|
|
202
|
+
* @param n - the element count
|
|
203
|
+
* @returns the values and a setter
|
|
204
|
+
*/
|
|
205
|
+
function valuesOf(n) {
|
|
206
|
+
const values = {};
|
|
207
|
+
return {
|
|
208
|
+
values,
|
|
209
|
+
put: (p, i, v) => {
|
|
210
|
+
if (v !== null) {
|
|
211
|
+
setValue(values, p, n, i, v);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Sets one element's value of a property, making the property's column on first use.
|
|
218
|
+
* @param values - the values per property
|
|
219
|
+
* @param property - the property
|
|
220
|
+
* @param n - the element count
|
|
221
|
+
* @param i - the element
|
|
222
|
+
* @param v - the value; null clears it
|
|
223
|
+
*/
|
|
224
|
+
function setValue(values, property, n, i, v) {
|
|
225
|
+
values[property] ?? (values[property] = new Array(n).fill(null));
|
|
226
|
+
values[property][i] = v;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* graphology: the keys sigma.js draws, with sigma's defaults.
|
|
230
|
+
* @param snapshot - the graph
|
|
231
|
+
* @param src - what the import kept
|
|
232
|
+
* @param context - the work budget
|
|
233
|
+
* @returns the styles
|
|
234
|
+
*/
|
|
235
|
+
function graphologyStyles(snapshot, src, context) {
|
|
236
|
+
const notes = new StyleNotes();
|
|
237
|
+
const out = { node: {}, edge: {} };
|
|
238
|
+
const readFrom = { node: [], edge: [] };
|
|
239
|
+
for (const target of TARGETS) {
|
|
240
|
+
const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
|
|
241
|
+
const { values, put } = valuesOf(n);
|
|
242
|
+
const keys = {
|
|
243
|
+
color: cells(snapshot, target, "color"),
|
|
244
|
+
size: cells(snapshot, target, "size"),
|
|
245
|
+
label: cells(snapshot, target, "label"),
|
|
246
|
+
hidden: cells(snapshot, target, "hidden"),
|
|
247
|
+
};
|
|
248
|
+
readFrom[target].push(...Object.entries(keys).flatMap(([name, c]) => (c === null ? [] : [name])));
|
|
249
|
+
for (let i = 0; i < n; i++) {
|
|
250
|
+
const site = (property) => ({
|
|
251
|
+
notes,
|
|
252
|
+
target,
|
|
253
|
+
property,
|
|
254
|
+
element: i,
|
|
255
|
+
budget: context.budget,
|
|
256
|
+
});
|
|
257
|
+
readSigmaRow(keys, target, i, put, site);
|
|
258
|
+
}
|
|
259
|
+
out[target] = values;
|
|
260
|
+
}
|
|
261
|
+
exactFromBlock(snapshot, blockText(snapshot, src), out, GRAPHOLOGY_WRITTEN);
|
|
262
|
+
return {
|
|
263
|
+
canonical: true,
|
|
264
|
+
dialect: "graphology",
|
|
265
|
+
tool: "sigma",
|
|
266
|
+
opacityInColor: true,
|
|
267
|
+
units: "relative",
|
|
268
|
+
node: { values: out.node },
|
|
269
|
+
edge: { values: out.edge },
|
|
270
|
+
notes: notes.list(),
|
|
271
|
+
readFrom,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Reads one element's sigma.js keys.
|
|
276
|
+
* @param keys - the cell readers
|
|
277
|
+
* @param target - nodes or edges
|
|
278
|
+
* @param i - the element
|
|
279
|
+
* @param put - the setter
|
|
280
|
+
* @param site - where a value of a property was read
|
|
281
|
+
*/
|
|
282
|
+
function readSigmaRow(keys, target, i, put, site) {
|
|
283
|
+
const { color, size, label, hidden } = keys;
|
|
284
|
+
if (color !== null) {
|
|
285
|
+
const [fill, opacity] = target === "node" ? ["fill", "fillOpacity"] : ["color", "opacity"];
|
|
286
|
+
putColor(put, fill, opacity, i, color(i), site(fill));
|
|
287
|
+
}
|
|
288
|
+
if (size !== null) {
|
|
289
|
+
const v = canonNumber(size(i), site("width"));
|
|
290
|
+
put("width", i, v);
|
|
291
|
+
if (target === "node") {
|
|
292
|
+
put("height", i, v);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (label !== null) {
|
|
296
|
+
put("label", i, canonText(label(i), site("label")));
|
|
297
|
+
}
|
|
298
|
+
if (hidden !== null && canonBoolean(hidden(i), site("visible")) === true) {
|
|
299
|
+
put("visible", i, false);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* A CSS color into a property and its opacity.
|
|
304
|
+
* @param put - the setter
|
|
305
|
+
* @param property - the color property
|
|
306
|
+
* @param opacity - its opacity property, or null when it has none
|
|
307
|
+
* @param i - the element
|
|
308
|
+
* @param raw - the value
|
|
309
|
+
* @param site - where it was read
|
|
310
|
+
*/
|
|
311
|
+
function putColor(put, property, opacity, i, raw, site) {
|
|
312
|
+
if (raw === null || raw === undefined || raw === "") {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const c = canonColor(raw, "css", site);
|
|
316
|
+
if (c === null) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
put(property, i, c.color);
|
|
320
|
+
if (c.alpha < 1) {
|
|
321
|
+
if (opacity === null) {
|
|
322
|
+
site.notes.add(STYLE_ISSUE.ALPHA_DROPPED, site.target, property, { sample: raw, element: i });
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
put(opacity, i, c.alpha);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* The text of the block a document holds, as a top-level key or as a graph attribute, or null.
|
|
331
|
+
* @param snapshot - the graph
|
|
332
|
+
* @param src - what the import kept
|
|
333
|
+
* @returns the block's text
|
|
334
|
+
*/
|
|
335
|
+
function blockText(snapshot, src) {
|
|
336
|
+
if (src.block !== null) {
|
|
337
|
+
return src.block;
|
|
338
|
+
}
|
|
339
|
+
if (!snapshot.graph.has(STYLE_BLOCK_NAME)) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
const v = plainCell(snapshot.graph.value(STYLE_BLOCK_NAME, 0));
|
|
343
|
+
return typeof v === "string" ? v : null;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* For a file graphty wrote: where a native value is what the writer writes for the block's value (a width and a
|
|
347
|
+
* height written as one size, a shape written as the nearest one the dialect has), the block's exact value is the
|
|
348
|
+
* truth, so a re-import gives the same rules. A value another program changed stays the truth.
|
|
349
|
+
* @param snapshot - the graph
|
|
350
|
+
* @param text - the block's text, or null
|
|
351
|
+
* @param values - the native values per target, changed in place
|
|
352
|
+
* @param written - what the writer writes for each approximated property
|
|
353
|
+
* @param holdsExactly - whether the dialect holds a value as it is; where it does not, the native value tells
|
|
354
|
+
* nothing about the block's (the writer never wrote it), so the block's value is the truth
|
|
355
|
+
*/
|
|
356
|
+
function exactFromBlock(snapshot, text, values, written, holdsExactly) {
|
|
357
|
+
if (text === null) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const parsed = parseStyleBlock(text, styleFingerprint(snapshot));
|
|
361
|
+
const ex = parsed.ok ? blockStyles(parsed.block, snapshot, "json") : null;
|
|
362
|
+
if (ex === null) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
for (const target of TARGETS) {
|
|
366
|
+
const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
|
|
367
|
+
const cache = new Map();
|
|
368
|
+
const resolved = (p) => {
|
|
369
|
+
let r = cache.get(p);
|
|
370
|
+
if (r === undefined) {
|
|
371
|
+
r = resolveStyles(ex, target, p, snapshot);
|
|
372
|
+
cache.set(p, r);
|
|
373
|
+
}
|
|
374
|
+
return r;
|
|
375
|
+
};
|
|
376
|
+
if (holdsExactly !== undefined) {
|
|
377
|
+
const properties = new Set(ex.styles.rules
|
|
378
|
+
.filter((r) => r.target === target)
|
|
379
|
+
.flatMap((r) => [...Object.keys(r.set ?? {}), ...Object.keys(r.map ?? {})]));
|
|
380
|
+
for (const property of properties) {
|
|
381
|
+
const held = (w) => holdsExactly(target, property, w);
|
|
382
|
+
takeUnheld(values[target], property, resolved(property), n, held);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
for (const [property, write] of Object.entries(written[target] ?? {})) {
|
|
386
|
+
takeWritten(values[target], property, n, resolved, write, snapshot);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Takes the block's value of a property wherever the native value is what the writer writes for the block's values.
|
|
392
|
+
* @param column - the native values of the target, changed in place
|
|
393
|
+
* @param property - the property
|
|
394
|
+
* @param n - the element count
|
|
395
|
+
* @param resolved - the block's values of a property
|
|
396
|
+
* @param write - what the writer writes for an element, from the block's values
|
|
397
|
+
* @param snapshot - the graph
|
|
398
|
+
*/
|
|
399
|
+
function takeWritten(column, property, n, resolved, write, snapshot) {
|
|
400
|
+
const want = resolved(property);
|
|
401
|
+
for (let i = 0; i < n; i++) {
|
|
402
|
+
const w = want[i];
|
|
403
|
+
const native = column[property]?.[i] ?? null;
|
|
404
|
+
// a native value the writer made from the block's other values (vis.js's one size for a width) is the
|
|
405
|
+
// block's own value, none included
|
|
406
|
+
if (native !== w && native !== null && native === write((p) => resolved(p)[i], i, snapshot)) {
|
|
407
|
+
setValue(column, property, n, i, w);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Takes the block's value of a property wherever the dialect does not hold it exactly.
|
|
413
|
+
* @param values - the native values of the target, changed in place
|
|
414
|
+
* @param property - the property
|
|
415
|
+
* @param want - the block's values
|
|
416
|
+
* @param n - the element count
|
|
417
|
+
* @param held - whether the dialect holds a value exactly
|
|
418
|
+
*/
|
|
419
|
+
function takeUnheld(values, property, want, n, held) {
|
|
420
|
+
for (let i = 0; i < n; i++) {
|
|
421
|
+
const w = want[i];
|
|
422
|
+
if (w !== null && !held(w)) {
|
|
423
|
+
setValue(values, property, n, i, w);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* One size for a width and a height: the larger, as graphology and vis.js write it.
|
|
429
|
+
* @param want - the block's values of the element
|
|
430
|
+
* @returns the size
|
|
431
|
+
*/
|
|
432
|
+
const oneSize = (want) => {
|
|
433
|
+
const w = want("width");
|
|
434
|
+
const h = want("height");
|
|
435
|
+
return typeof w === "number" && typeof h === "number" ? Math.max(w, h) : (w ?? h);
|
|
436
|
+
};
|
|
437
|
+
const GRAPHOLOGY_WRITTEN = { node: { width: oneSize, height: oneSize } };
|
|
438
|
+
// ------------------------------------------------------------ vis.js
|
|
439
|
+
/** vis-network's node shapes that take their size from `size` (the others are sized by their label). */
|
|
440
|
+
const VIS_SIZED = new Set([
|
|
441
|
+
"image",
|
|
442
|
+
"circularImage",
|
|
443
|
+
"diamond",
|
|
444
|
+
"dot",
|
|
445
|
+
"star",
|
|
446
|
+
"triangle",
|
|
447
|
+
"triangleDown",
|
|
448
|
+
"hexagon",
|
|
449
|
+
"square",
|
|
450
|
+
"icon",
|
|
451
|
+
]);
|
|
452
|
+
/**
|
|
453
|
+
* vis-network's palette for a group the file does not define, in the order groups first appear (vis-network
|
|
454
|
+
* 10.1.2, `Groups._defaultGroups`): border, background.
|
|
455
|
+
*/
|
|
456
|
+
const VIS_GROUP_PALETTE = [
|
|
457
|
+
["#2B7CE9", "#97C2FC"],
|
|
458
|
+
["#FFA500", "#FFFF00"],
|
|
459
|
+
["#FA0A10", "#FB7E81"],
|
|
460
|
+
["#41A906", "#7BE141"],
|
|
461
|
+
["#E129F0", "#EB7DF4"],
|
|
462
|
+
["#7C29F0", "#AD85E4"],
|
|
463
|
+
["#C37F00", "#FFA807"],
|
|
464
|
+
["#4220FB", "#6E6EFD"],
|
|
465
|
+
["#FD5A77", "#FFC0CB"],
|
|
466
|
+
["#4AD63A", "#C2FABC"],
|
|
467
|
+
["#990000", "#EE0000"],
|
|
468
|
+
["#FF6000", "#FF6000"],
|
|
469
|
+
["#97C2FC", "#2B7CE9"],
|
|
470
|
+
["#399605", "#255C03"],
|
|
471
|
+
["#B70054", "#FF007E"],
|
|
472
|
+
["#AD85E4", "#7C29F0"],
|
|
473
|
+
["#4557FA", "#000EA1"],
|
|
474
|
+
["#FFC0CB", "#FD5A77"],
|
|
475
|
+
["#C2FABC", "#74D66A"],
|
|
476
|
+
["#EE0000", "#990000"],
|
|
477
|
+
];
|
|
478
|
+
/** vis-network's edge color when an edge inherits none (vis-network 10.1.2, `color.color`). */
|
|
479
|
+
const VIS_EDGE_COLOR = "#848484";
|
|
480
|
+
/**
|
|
481
|
+
* vis-network's reading of a node color given as text (vis-util `parseColor`): a hex or `rgb()` color gets a border
|
|
482
|
+
* 20% darker and 25% more saturated; any other text is both.
|
|
483
|
+
* @param text - the color
|
|
484
|
+
* @returns the background and the border
|
|
485
|
+
*/
|
|
486
|
+
function visParseColor(text) {
|
|
487
|
+
let hex = text;
|
|
488
|
+
const rgb = /^rgb\( *(\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3}) *\)$/i.exec(text);
|
|
489
|
+
if (rgb?.slice(1).every((c) => Number(c) <= 255)) {
|
|
490
|
+
hex = `#${((1 << 24) + (Number(rgb[1]) << 16) + (Number(rgb[2]) << 8) + Number(rgb[3])).toString(16).slice(1)}`;
|
|
491
|
+
}
|
|
492
|
+
if (!/^#([0-9a-f]{6}|[0-9a-f]{3})$/i.test(hex)) {
|
|
493
|
+
return { background: text, border: text };
|
|
494
|
+
}
|
|
495
|
+
const full = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
|
|
496
|
+
return { background: full, border: visBorder(full) };
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* vis-util's border of a background color: 20% darker and 25% more saturated, through HSV.
|
|
500
|
+
* @param full - the background, `#rrggbb`
|
|
501
|
+
* @returns the border, `#rrggbb`
|
|
502
|
+
*/
|
|
503
|
+
function visBorder(full) {
|
|
504
|
+
const n = Number.parseInt(full.slice(1), 16);
|
|
505
|
+
const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255].map((c) => c / 255);
|
|
506
|
+
const max = Math.max(r, g, b);
|
|
507
|
+
const min = Math.min(r, g, b);
|
|
508
|
+
let h = 0;
|
|
509
|
+
let s = 0;
|
|
510
|
+
if (max !== min) {
|
|
511
|
+
let d = b - r;
|
|
512
|
+
let k = 5;
|
|
513
|
+
if (r === min) {
|
|
514
|
+
d = g - b;
|
|
515
|
+
k = 3;
|
|
516
|
+
}
|
|
517
|
+
else if (b === min) {
|
|
518
|
+
d = r - g;
|
|
519
|
+
k = 1;
|
|
520
|
+
}
|
|
521
|
+
h = (60 * (k - d / (max - min))) / 360;
|
|
522
|
+
s = (max - min) / max;
|
|
523
|
+
}
|
|
524
|
+
const sv = Math.min(1, s * 1.25);
|
|
525
|
+
const vv = max * 0.8;
|
|
526
|
+
const i = Math.floor(h * 6);
|
|
527
|
+
const f = h * 6 - i;
|
|
528
|
+
const p = vv * (1 - sv);
|
|
529
|
+
const q = vv * (1 - f * sv);
|
|
530
|
+
const t = vv * (1 - (1 - f) * sv);
|
|
531
|
+
const rgbOut = [
|
|
532
|
+
[vv, t, p],
|
|
533
|
+
[q, vv, p],
|
|
534
|
+
[p, vv, t],
|
|
535
|
+
[p, q, vv],
|
|
536
|
+
[t, p, vv],
|
|
537
|
+
[vv, p, q],
|
|
538
|
+
][i % 6].map((c) => Math.floor(c * 255));
|
|
539
|
+
return `#${((1 << 24) + (rgbOut[0] << 16) + (rgbOut[1] << 8) + rgbOut[2]).toString(16).slice(1)}`;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* The vis.js options of one node or edge record (or of `options.nodes`, `options.edges`, a group) as a layer.
|
|
543
|
+
* @param o - the options
|
|
544
|
+
* @param target - nodes or edges
|
|
545
|
+
* @returns the layer
|
|
546
|
+
*/
|
|
547
|
+
function visLayer(o, target) {
|
|
548
|
+
if (!isJsonObject(o)) {
|
|
549
|
+
return {};
|
|
550
|
+
}
|
|
551
|
+
const layer = {};
|
|
552
|
+
const set = (k, v) => {
|
|
553
|
+
if (v !== undefined && v !== null) {
|
|
554
|
+
layer[k] = v;
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
if (target === "node") {
|
|
558
|
+
visNodeLayer(o, set);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
visEdgeLayer(o, set);
|
|
562
|
+
}
|
|
563
|
+
set("hidden", o.hidden);
|
|
564
|
+
set("label", o.label);
|
|
565
|
+
set("title", o.title);
|
|
566
|
+
visFontLayer(o.font, set);
|
|
567
|
+
return layer;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* The node keys of a vis.js options object: a color given as text has vis-util's darker border.
|
|
571
|
+
* @param o - the options
|
|
572
|
+
* @param set - the layer's setter
|
|
573
|
+
*/
|
|
574
|
+
function visNodeLayer(o, set) {
|
|
575
|
+
const { color } = o;
|
|
576
|
+
if (typeof color === "string") {
|
|
577
|
+
const parsed = visParseColor(color.trim());
|
|
578
|
+
set("fill", parsed.background);
|
|
579
|
+
set("border", parsed.border);
|
|
580
|
+
}
|
|
581
|
+
else if (isJsonObject(color)) {
|
|
582
|
+
set("fill", color.background);
|
|
583
|
+
set("border", color.border);
|
|
584
|
+
}
|
|
585
|
+
set("shape", o.shape);
|
|
586
|
+
set("size", o.size);
|
|
587
|
+
set("borderWidth", o.borderWidth);
|
|
588
|
+
set("opacity", o.opacity);
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* The edge keys of a vis.js options object: a color of its own stops the edge inheriting its node's.
|
|
592
|
+
* @param o - the options
|
|
593
|
+
* @param set - the layer's setter
|
|
594
|
+
*/
|
|
595
|
+
function visEdgeLayer(o, set) {
|
|
596
|
+
const { color } = o;
|
|
597
|
+
if (typeof color === "string") {
|
|
598
|
+
set("edgeColor", color);
|
|
599
|
+
set("inherit", false);
|
|
600
|
+
}
|
|
601
|
+
else if (isJsonObject(color)) {
|
|
602
|
+
set("edgeColor", color.color);
|
|
603
|
+
set("edgeOpacity", color.opacity);
|
|
604
|
+
const own = color.color !== undefined || color.highlight !== undefined || color.hover !== undefined;
|
|
605
|
+
set("inherit", own ? false : color.inherit);
|
|
606
|
+
}
|
|
607
|
+
set("width", o.width);
|
|
608
|
+
set("dashes", o.dashes);
|
|
609
|
+
set("arrows", o.arrows);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* The font keys of a vis.js options object: an object, or text like `"14px arial red"`.
|
|
613
|
+
* @param font - the `font` value
|
|
614
|
+
* @param set - the layer's setter
|
|
615
|
+
*/
|
|
616
|
+
function visFontLayer(font, set) {
|
|
617
|
+
if (isJsonObject(font)) {
|
|
618
|
+
set("fontColor", font.color);
|
|
619
|
+
set("fontSize", font.size);
|
|
620
|
+
set("fontFace", font.face);
|
|
621
|
+
}
|
|
622
|
+
else if (typeof font === "string") {
|
|
623
|
+
const m = /^\s*(\d+(?:\.\d+)?)(?:px)?\s+(\S+)(?:\s+(\S+))?/.exec(font.slice(0, 256));
|
|
624
|
+
if (m !== null) {
|
|
625
|
+
set("fontSize", Number(m[1]));
|
|
626
|
+
set("fontFace", m[2]);
|
|
627
|
+
set("fontColor", m[3]);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* The canonical values of one vis.js layer.
|
|
633
|
+
* @param layer - the layer
|
|
634
|
+
* @param target - nodes or edges
|
|
635
|
+
* @param site - where its values are read
|
|
636
|
+
* @param sized - nodes: whether the shape takes its size from `size`
|
|
637
|
+
* @returns the values
|
|
638
|
+
*/
|
|
639
|
+
function visValues(layer, target, site, sized) {
|
|
640
|
+
const out = {};
|
|
641
|
+
const put = (p, v) => {
|
|
642
|
+
if (v !== null) {
|
|
643
|
+
out[p] = v;
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
const color = (p, opacity, raw) => {
|
|
647
|
+
if (raw === undefined || raw === null || raw === "") {
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const c = canonColor(raw, "vis", site(p));
|
|
651
|
+
if (c !== null) {
|
|
652
|
+
put(p, c.color);
|
|
653
|
+
if (c.alpha < 1 && opacity !== null) {
|
|
654
|
+
put(opacity, c.alpha);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
if (target === "node") {
|
|
659
|
+
color("fill", "fillOpacity", layer.fill);
|
|
660
|
+
color("borderColor", null, layer.border);
|
|
661
|
+
visNodeValues(layer, sized, put, site);
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
color("color", "opacity", layer.edgeColor);
|
|
665
|
+
visEdgeValues(layer, put, site);
|
|
666
|
+
}
|
|
667
|
+
if (layer.hidden !== undefined && canonBoolean(layer.hidden, site("visible")) === true) {
|
|
668
|
+
put("visible", false);
|
|
669
|
+
}
|
|
670
|
+
if (layer.label !== undefined) {
|
|
671
|
+
put("label", canonText(layer.label, site("label")));
|
|
672
|
+
}
|
|
673
|
+
if (layer.title !== undefined) {
|
|
674
|
+
put("tooltip", canonText(layer.title, site("tooltip")));
|
|
675
|
+
}
|
|
676
|
+
color("labelColor", null, layer.fontColor);
|
|
677
|
+
if (layer.fontSize !== undefined) {
|
|
678
|
+
put("labelSize", canonNumber(layer.fontSize, site("labelSize")));
|
|
679
|
+
}
|
|
680
|
+
if (layer.fontFace !== undefined) {
|
|
681
|
+
put("labelFont", canonText(layer.fontFace, site("labelFont")));
|
|
682
|
+
}
|
|
683
|
+
return out;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* The node values of a vis.js layer besides its colors.
|
|
687
|
+
* @param layer - the layer
|
|
688
|
+
* @param sized - whether the shape takes its size from `size`
|
|
689
|
+
* @param put - the row's setter
|
|
690
|
+
* @param site - where its values are read
|
|
691
|
+
*/
|
|
692
|
+
function visNodeValues(layer, sized, put, site) {
|
|
693
|
+
if (layer.shape !== undefined) {
|
|
694
|
+
put("shape", canonEnum("shape", layer.shape, "vis", site("shape")));
|
|
695
|
+
}
|
|
696
|
+
if (sized && layer.size !== undefined) {
|
|
697
|
+
const v = canonNumber(layer.size, site("width"));
|
|
698
|
+
put("width", v);
|
|
699
|
+
put("height", v);
|
|
700
|
+
}
|
|
701
|
+
if (layer.borderWidth !== undefined) {
|
|
702
|
+
put("borderWidth", canonNumber(layer.borderWidth, site("borderWidth")));
|
|
703
|
+
}
|
|
704
|
+
if (layer.opacity !== undefined) {
|
|
705
|
+
put("opacity", canonOpacity(layer.opacity, "vis", site("opacity")));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* The edge values of a vis.js layer besides its color.
|
|
710
|
+
* @param layer - the layer
|
|
711
|
+
* @param put - the row's setter
|
|
712
|
+
* @param site - where its values are read
|
|
713
|
+
*/
|
|
714
|
+
function visEdgeValues(layer, put, site) {
|
|
715
|
+
if (layer.edgeOpacity !== undefined) {
|
|
716
|
+
put("opacity", canonOpacity(layer.edgeOpacity, "vis", site("opacity")));
|
|
717
|
+
}
|
|
718
|
+
if (layer.width !== undefined) {
|
|
719
|
+
put("width", canonNumber(layer.width, site("width")));
|
|
720
|
+
}
|
|
721
|
+
if (layer.dashes !== undefined) {
|
|
722
|
+
put("lineStyle", layer.dashes === false || layer.dashes === "false" ? "solid" : "dashed");
|
|
723
|
+
}
|
|
724
|
+
if (layer.arrows !== undefined) {
|
|
725
|
+
const [to, from] = visArrows(layer.arrows, site);
|
|
726
|
+
put("targetArrow", to);
|
|
727
|
+
put("sourceArrow", from);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* vis.js `arrows` as the target and source arrows: `"to"`, `"to, from"`, or `{ to: true | { enabled, type } }`.
|
|
732
|
+
* @param arrows - the value
|
|
733
|
+
* @param site - where it is read
|
|
734
|
+
* @returns the target arrow and the source arrow
|
|
735
|
+
*/
|
|
736
|
+
function visArrows(arrows, site) {
|
|
737
|
+
const end = (name, property) => {
|
|
738
|
+
if (typeof arrows === "string") {
|
|
739
|
+
return new RegExp(String.raw `\b${name}\b`).test(arrows.slice(0, 256)) ? "triangle" : "none";
|
|
740
|
+
}
|
|
741
|
+
if (!isJsonObject(arrows)) {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
const a = arrows[name];
|
|
745
|
+
if (a === true) {
|
|
746
|
+
return "triangle";
|
|
747
|
+
}
|
|
748
|
+
if (!isJsonObject(a) || a.enabled === false) {
|
|
749
|
+
return "none";
|
|
750
|
+
}
|
|
751
|
+
return canonEnum("arrow", a.type ?? "arrow", "vis", site(property));
|
|
752
|
+
};
|
|
753
|
+
return [end("to", "targetArrow"), end("from", "sourceArrow")];
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* vis.js: the per-element keys over the file's groups and `options.nodes` / `options.edges`, beneath them
|
|
757
|
+
* vis-network's defaults. The defaults and groups the file declares become declared rules; a group the file does not
|
|
758
|
+
* define gets vis-network's palette, as a rule of the tool's; an edge without a color of its own takes its source
|
|
759
|
+
* node's border color (its target's under `inherit: "to"`).
|
|
760
|
+
* @param snapshot - the graph
|
|
761
|
+
* @param src - what the import kept
|
|
762
|
+
* @param context - the work budget
|
|
763
|
+
* @returns the styles
|
|
764
|
+
*/
|
|
765
|
+
function visStyles(snapshot, src, context) {
|
|
766
|
+
const notes = new StyleNotes();
|
|
767
|
+
const options = isJsonObject(src.options) ? src.options : {};
|
|
768
|
+
const n = snapshot.nodeCount;
|
|
769
|
+
const ruleSite = (target) => (property) => ({ notes, target, property });
|
|
770
|
+
const nodeRules = [];
|
|
771
|
+
const nodeColumns = new Map();
|
|
772
|
+
// the node defaults and groups the file declares
|
|
773
|
+
const nodeDefaults = visLayer(options.nodes, "node");
|
|
774
|
+
const groupCol = cells(snapshot, "node", "group");
|
|
775
|
+
const groupOf = (i) => {
|
|
776
|
+
const g = groupCol?.(i);
|
|
777
|
+
return typeof g === "string" || typeof g === "number" ? String(g) : null;
|
|
778
|
+
};
|
|
779
|
+
const { groupLayers, paletteRows } = visGroupLayers(n, groupOf, visGroupDefs(src, options));
|
|
780
|
+
context.budget.spend("node", n);
|
|
781
|
+
// what vis-network draws per node: its own keys over its group over options.nodes
|
|
782
|
+
const node = valuesOf(n);
|
|
783
|
+
const records = nodeRecords(snapshot);
|
|
784
|
+
const rawShape = (layer) => (typeof layer.shape === "string" ? layer.shape : null);
|
|
785
|
+
for (let i = 0; i < n; i++) {
|
|
786
|
+
const g = groupOf(i);
|
|
787
|
+
const own = records(i);
|
|
788
|
+
const group = g === null ? {} : (groupLayers.get(g) ?? {});
|
|
789
|
+
const shape = rawShape(own) ?? rawShape(group) ?? rawShape(nodeDefaults) ?? "ellipse";
|
|
790
|
+
const site = (property) => ({
|
|
791
|
+
notes,
|
|
792
|
+
target: "node",
|
|
793
|
+
property,
|
|
794
|
+
element: i,
|
|
795
|
+
budget: context.budget,
|
|
796
|
+
});
|
|
797
|
+
const merged = { ...nodeDefaults, ...group, ...own };
|
|
798
|
+
for (const [p, v] of Object.entries(visValues(merged, "node", site, VIS_SIZED.has(shape)))) {
|
|
799
|
+
node.put(p, i, v);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
const declaredNode = visValues(nodeDefaults, "node", ruleSite("node"), true);
|
|
803
|
+
if (Object.keys(declaredNode).length > 0) {
|
|
804
|
+
nodeRules.push(rule("node.vis.defaults", "node", "defaults", "declared", { all: true }, declaredNode));
|
|
805
|
+
}
|
|
806
|
+
let k = 0;
|
|
807
|
+
for (const [g, layer] of groupLayers) {
|
|
808
|
+
const set = paletteRows.get(g) ?? visValues(layer, "node", ruleSite("node"), true);
|
|
809
|
+
if (Object.keys(set).length === 0) {
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
const key = `group.${String(k)}`;
|
|
813
|
+
nodeColumns.set(key, Array.from({ length: n }, (_v, i) => (groupOf(i) === g ? true : null)));
|
|
814
|
+
const origin = paletteRows.has(g) ? "tool" : "declared";
|
|
815
|
+
const r = rule(`node.vis.group.${String(k++)}`, "node", "group", origin, { has: { style: key } }, set);
|
|
816
|
+
nodeRules.push({ ...r, facts: { names: [[key, g]] } });
|
|
817
|
+
}
|
|
818
|
+
const toolNode = toolDefaults("vis-network")?.node;
|
|
819
|
+
const borderOf = (i) => node.values.borderColor?.[i] ?? toolNode?.borderColor?.value ?? "#2b7ce9";
|
|
820
|
+
const edge = visEdges(snapshot, visLayer(options.edges, "edge"), borderOf, notes, context);
|
|
821
|
+
const values = { node: node.values, edge: edge.values };
|
|
822
|
+
exactFromBlock(snapshot, src.block, values, VIS_WRITTEN);
|
|
823
|
+
return {
|
|
824
|
+
canonical: true,
|
|
825
|
+
dialect: "vis",
|
|
826
|
+
tool: "vis-network",
|
|
827
|
+
opacityInColor: true,
|
|
828
|
+
units: "px",
|
|
829
|
+
node: { values: values.node, rules: nodeRules, columns: nodeColumns },
|
|
830
|
+
edge: { values: values.edge, rules: edge.rules, columns: edge.columns },
|
|
831
|
+
notes: notes.list(),
|
|
832
|
+
block: src.block,
|
|
833
|
+
readFrom: {
|
|
834
|
+
node: VIS_KEYS.node.filter((x) => snapshot.nodes.has(x)),
|
|
835
|
+
edge: VIS_KEYS.edge.filter((x) => snapshot.edges.has(x)),
|
|
836
|
+
},
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* vis.js: the group definitions, from `options.groups` or a top-level `groups`.
|
|
841
|
+
* @param src - what the import kept
|
|
842
|
+
* @param options - the file's `options`
|
|
843
|
+
* @returns the definitions by group name
|
|
844
|
+
*/
|
|
845
|
+
function visGroupDefs(src, options) {
|
|
846
|
+
if (isJsonObject(options.groups)) {
|
|
847
|
+
return options.groups;
|
|
848
|
+
}
|
|
849
|
+
return isJsonObject(src.groups) ? src.groups : {};
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* vis.js: each group the nodes name, in the order first named, with its layer; a group the file does not define gets
|
|
853
|
+
* vis-network's palette.
|
|
854
|
+
* @param n - the node count
|
|
855
|
+
* @param groupOf - each node's group
|
|
856
|
+
* @param groupDefs - the group definitions
|
|
857
|
+
* @returns the layers by group, and the palette values of the groups that took them
|
|
858
|
+
*/
|
|
859
|
+
function visGroupLayers(n, groupOf, groupDefs) {
|
|
860
|
+
const groupLayers = new Map();
|
|
861
|
+
const paletteRows = new Map();
|
|
862
|
+
let palette = 0;
|
|
863
|
+
for (let i = 0; i < n; i++) {
|
|
864
|
+
const g = groupOf(i);
|
|
865
|
+
if (g === null || groupLayers.has(g)) {
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
if (Object.hasOwn(groupDefs, g)) {
|
|
869
|
+
groupLayers.set(g, visLayer(groupDefs[g], "node"));
|
|
870
|
+
}
|
|
871
|
+
else {
|
|
872
|
+
const [border, background] = VIS_GROUP_PALETTE[palette++ % VIS_GROUP_PALETTE.length];
|
|
873
|
+
groupLayers.set(g, { fill: background, border });
|
|
874
|
+
paletteRows.set(g, { fill: background.toLowerCase(), borderColor: border.toLowerCase() });
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return { groupLayers, paletteRows };
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* vis.js edges: their own keys over `options.edges`; an edge without a color of its own takes its source node's
|
|
881
|
+
* border color (its target's under `inherit: "to"`).
|
|
882
|
+
* @param snapshot - the graph
|
|
883
|
+
* @param edgeDefaults - `options.edges`
|
|
884
|
+
* @param borderOf - each node's drawn border color
|
|
885
|
+
* @param notes - where problems are noted
|
|
886
|
+
* @param context - the work budget
|
|
887
|
+
* @returns the edge values, rules and style columns
|
|
888
|
+
*/
|
|
889
|
+
function visEdges(snapshot, edgeDefaults, borderOf, notes, context) {
|
|
890
|
+
const m = snapshot.edgeCount;
|
|
891
|
+
const edge = valuesOf(m);
|
|
892
|
+
const edgeRecords = elementRecords(snapshot, "edge");
|
|
893
|
+
const inherited = new Array(m).fill(null);
|
|
894
|
+
let inheriting = 0;
|
|
895
|
+
for (let e = 0; e < m; e++) {
|
|
896
|
+
const own = edgeRecords(e);
|
|
897
|
+
const merged = { ...edgeDefaults, ...own };
|
|
898
|
+
const site = (property) => ({
|
|
899
|
+
notes,
|
|
900
|
+
target: "edge",
|
|
901
|
+
property,
|
|
902
|
+
element: e,
|
|
903
|
+
budget: context.budget,
|
|
904
|
+
});
|
|
905
|
+
const vals = visValues(merged, "edge", site, true);
|
|
906
|
+
for (const [p, v] of Object.entries(vals)) {
|
|
907
|
+
edge.put(p, e, v);
|
|
908
|
+
}
|
|
909
|
+
const inherit = own.inherit ?? edgeDefaults.inherit ?? "from";
|
|
910
|
+
if (inherit === false || inherit === "false") {
|
|
911
|
+
if (vals.color === undefined) {
|
|
912
|
+
edge.put("color", e, VIS_EDGE_COLOR);
|
|
913
|
+
}
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
if (inherit === "both") {
|
|
917
|
+
notes.add(STYLE_ISSUE.GRADIENT, "edge", "color", { sample: "inherit both", element: e });
|
|
918
|
+
}
|
|
919
|
+
inherited[e] = borderOf(inherit === "to" ? snapshot.edgeTarget(e) : snapshot.edgeSource(e));
|
|
920
|
+
inheriting++;
|
|
921
|
+
}
|
|
922
|
+
context.budget.spend("edge", m);
|
|
923
|
+
const edgeRules = [];
|
|
924
|
+
const edgeColumns = new Map();
|
|
925
|
+
const declaredEdge = visValues(edgeDefaults, "edge", (property) => ({ notes, target: "edge", property }), true);
|
|
926
|
+
if (Object.keys(declaredEdge).length > 0) {
|
|
927
|
+
edgeRules.push(rule("edge.vis.defaults", "edge", "defaults", "declared", { all: true }, declaredEdge));
|
|
928
|
+
}
|
|
929
|
+
if (inheriting > 0) {
|
|
930
|
+
// vis-network draws such an edge in its source node's color. graphty-element cannot bind an edge's color to
|
|
931
|
+
// its source node's drawn color yet, so the color is frozen here; replace this column with that binding once
|
|
932
|
+
// the element has it (tracking issue "graphty-element: bind an edge's color to its source node's drawn color").
|
|
933
|
+
edgeColumns.set("source-color", inherited);
|
|
934
|
+
edgeRules.push({
|
|
935
|
+
id: "edge.tool.source-color",
|
|
936
|
+
target: "edge",
|
|
937
|
+
role: "passthrough",
|
|
938
|
+
origin: "tool",
|
|
939
|
+
where: { has: { style: "source-color" } },
|
|
940
|
+
map: { color: { kind: "passthrough", by: { style: "source-color" } } },
|
|
941
|
+
facts: { elements: inheriting, reason: "SOURCE_NODE_COLOR" },
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
return { values: edge.values, rules: edgeRules, columns: edgeColumns };
|
|
945
|
+
}
|
|
946
|
+
/** The vis.js keys the reader reads, per target. */
|
|
947
|
+
const VIS_KEYS = {
|
|
948
|
+
node: ["color", "shape", "size", "borderWidth", "opacity", "hidden", "label", "title", "font"],
|
|
949
|
+
edge: ["color", "width", "dashes", "arrows", "hidden", "label", "title", "font"],
|
|
950
|
+
};
|
|
951
|
+
/**
|
|
952
|
+
* Each node's own vis.js keys as a layer.
|
|
953
|
+
* @param snapshot - the graph
|
|
954
|
+
* @returns the layer of a node
|
|
955
|
+
*/
|
|
956
|
+
function nodeRecords(snapshot) {
|
|
957
|
+
return elementRecords(snapshot, "node");
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Each element's own vis.js keys as a layer, read from the graph's columns.
|
|
961
|
+
* @param snapshot - the graph
|
|
962
|
+
* @param target - nodes or edges
|
|
963
|
+
* @returns the layer of an element
|
|
964
|
+
*/
|
|
965
|
+
function elementRecords(snapshot, target) {
|
|
966
|
+
const readers = VIS_KEYS[target]
|
|
967
|
+
.map((name) => [name, cells(snapshot, target, name)])
|
|
968
|
+
.filter((x) => x[1] !== null);
|
|
969
|
+
return (i) => {
|
|
970
|
+
const record = {};
|
|
971
|
+
for (const [name, read] of readers) {
|
|
972
|
+
const v = read(i);
|
|
973
|
+
if (v !== null) {
|
|
974
|
+
record[name] = v;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return visLayer(record, target);
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* A rule.
|
|
982
|
+
* @param id - its id
|
|
983
|
+
* @param target - nodes or edges
|
|
984
|
+
* @param role - what it is
|
|
985
|
+
* @param origin - where it came from
|
|
986
|
+
* @param where - which elements
|
|
987
|
+
* @param set - the values
|
|
988
|
+
* @returns the rule
|
|
989
|
+
*/
|
|
990
|
+
function rule(id, target, role, origin, where, set) {
|
|
991
|
+
const values = {};
|
|
992
|
+
for (const [p, v] of Object.entries(set)) {
|
|
993
|
+
if (v !== null) {
|
|
994
|
+
values[p] = v;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
return { id, target, role, origin, where, set: values };
|
|
998
|
+
}
|
|
999
|
+
// ============================================================ writing
|
|
1000
|
+
/** The neutral node shape to vis-network's shape; the boolean says whether it is exact. */
|
|
1001
|
+
const VIS_SHAPES = new Map([
|
|
1002
|
+
["ellipse", ["dot", true]],
|
|
1003
|
+
["rectangle", ["square", true]],
|
|
1004
|
+
["round-rectangle", ["box", true]],
|
|
1005
|
+
["triangle", ["triangle", true]],
|
|
1006
|
+
["diamond", ["diamond", true]],
|
|
1007
|
+
["star", ["star", true]],
|
|
1008
|
+
["hexagon", ["hexagon", true]],
|
|
1009
|
+
["none", ["text", true]],
|
|
1010
|
+
["pentagon", ["hexagon", false]],
|
|
1011
|
+
["octagon", ["hexagon", false]],
|
|
1012
|
+
["parallelogram", ["box", false]],
|
|
1013
|
+
["trapezoid", ["box", false]],
|
|
1014
|
+
["vee", ["triangle", false]],
|
|
1015
|
+
]);
|
|
1016
|
+
/** The neutral arrow to vis-network's arrow type; the boolean says whether it is exact. */
|
|
1017
|
+
const VIS_ARROW_TYPES = new Map([
|
|
1018
|
+
["triangle", ["arrow", true]],
|
|
1019
|
+
["tee", ["bar", true]],
|
|
1020
|
+
["circle", ["circle", true]],
|
|
1021
|
+
["square", ["box", true]],
|
|
1022
|
+
["crow", ["crow", true]],
|
|
1023
|
+
["diamond", ["diamond", true]],
|
|
1024
|
+
["vee", ["vee", true]],
|
|
1025
|
+
["inverted", ["inv_triangle", true]],
|
|
1026
|
+
["triangle-open", ["arrow", false]],
|
|
1027
|
+
["diamond-open", ["diamond", false]],
|
|
1028
|
+
["circle-open", ["circle", false]],
|
|
1029
|
+
["half", ["arrow", false]],
|
|
1030
|
+
]);
|
|
1031
|
+
/**
|
|
1032
|
+
* A neutral shape as vis-network's: the shape, and whether it is exact.
|
|
1033
|
+
* @param shape - the neutral shape
|
|
1034
|
+
* @returns the vis-network shape and whether it is exact
|
|
1035
|
+
*/
|
|
1036
|
+
function visShape(shape) {
|
|
1037
|
+
const known = VIS_SHAPES.get(shape);
|
|
1038
|
+
if (known !== undefined) {
|
|
1039
|
+
return known;
|
|
1040
|
+
}
|
|
1041
|
+
return shape.startsWith("other:") ? [shape.slice(6), true] : ["dot", false];
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* A neutral arrow as vis-network's arrow type: the type (null for none), and whether it is exact.
|
|
1045
|
+
* @param arrow - the neutral arrow
|
|
1046
|
+
* @returns the type and whether it is exact
|
|
1047
|
+
*/
|
|
1048
|
+
function visArrowType(arrow) {
|
|
1049
|
+
if (arrow === "none") {
|
|
1050
|
+
return [null, true];
|
|
1051
|
+
}
|
|
1052
|
+
const known = VIS_ARROW_TYPES.get(arrow);
|
|
1053
|
+
if (known !== undefined) {
|
|
1054
|
+
return known;
|
|
1055
|
+
}
|
|
1056
|
+
return arrow.startsWith("other:") ? [arrow.slice(6), true] : ["arrow", false];
|
|
1057
|
+
}
|
|
1058
|
+
/** What reading a written vis.js file gives back for the values vis.js holds only approximately. */
|
|
1059
|
+
/**
|
|
1060
|
+
* A node size as a written vis.js file gives it back: one size for both, and none for a shape vis-network sizes by
|
|
1061
|
+
* its label.
|
|
1062
|
+
* @param want - the block's values of the element
|
|
1063
|
+
* @returns the size read back
|
|
1064
|
+
*/
|
|
1065
|
+
const visSize = (want) => {
|
|
1066
|
+
const s = want("shape");
|
|
1067
|
+
const shape = typeof s === "string" ? visShape(s)[0] : "ellipse";
|
|
1068
|
+
return VIS_SIZED.has(shape) ? oneSize(want) : null;
|
|
1069
|
+
};
|
|
1070
|
+
const VIS_WRITTEN = {
|
|
1071
|
+
node: {
|
|
1072
|
+
width: visSize,
|
|
1073
|
+
height: visSize,
|
|
1074
|
+
shape: (want) => {
|
|
1075
|
+
const s = want("shape");
|
|
1076
|
+
if (typeof s !== "string") {
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
const site = { notes: new StyleNotes(), target: "node", property: "shape" };
|
|
1080
|
+
return canonEnum("shape", visShape(s)[0], "vis", site);
|
|
1081
|
+
},
|
|
1082
|
+
},
|
|
1083
|
+
edge: {
|
|
1084
|
+
lineStyle: (want) => {
|
|
1085
|
+
const s = want("lineStyle");
|
|
1086
|
+
if (s === null) {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
return s === "solid" ? "solid" : "dashed";
|
|
1090
|
+
},
|
|
1091
|
+
targetArrow: (want) => arrowBack(want("targetArrow")),
|
|
1092
|
+
sourceArrow: (want) => arrowBack(want("sourceArrow")),
|
|
1093
|
+
},
|
|
1094
|
+
};
|
|
1095
|
+
/**
|
|
1096
|
+
* What a written arrow reads back as.
|
|
1097
|
+
* @param a - the neutral arrow
|
|
1098
|
+
* @returns the arrow read back
|
|
1099
|
+
*/
|
|
1100
|
+
function arrowBack(a) {
|
|
1101
|
+
if (typeof a !== "string") {
|
|
1102
|
+
return null;
|
|
1103
|
+
}
|
|
1104
|
+
const [type] = visArrowType(a);
|
|
1105
|
+
const site = { notes: new StyleNotes(), target: "edge", property: "targetArrow" };
|
|
1106
|
+
return type === null ? "none" : canonEnum("arrow", type, "vis", site);
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* The neutral properties Cytoscape.js holds natively, per target (what the stylesheet emitter writes).
|
|
1110
|
+
* @param map - the emitter's Cytoscape.js properties
|
|
1111
|
+
* @param extra - the neutral properties written by a rule of their own
|
|
1112
|
+
* @returns the properties
|
|
1113
|
+
*/
|
|
1114
|
+
function cyjsProperties(map, extra) {
|
|
1115
|
+
const out = new Set();
|
|
1116
|
+
for (const spec of map.values()) {
|
|
1117
|
+
if (!spec.property.startsWith("_")) {
|
|
1118
|
+
out.add(spec.property);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
for (const p of extra) {
|
|
1122
|
+
out.add(p);
|
|
1123
|
+
}
|
|
1124
|
+
return [...out];
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* The neutral properties each style-writing dialect holds natively. Not part of the published entry points: the
|
|
1128
|
+
* documentation's per-dialect tables read it, through a dynamic import knip cannot follow (hence public).
|
|
1129
|
+
* @public
|
|
1130
|
+
*/
|
|
1131
|
+
export const NATIVE = {
|
|
1132
|
+
cytoscape: {
|
|
1133
|
+
node: cyjsProperties(CYJS_NODE_PROPERTIES, ["labelPosition", "labelItalic", "visible"]),
|
|
1134
|
+
edge: cyjsProperties(CYJS_EDGE_PROPERTIES, ["curved", "labelItalic", "visible"]),
|
|
1135
|
+
},
|
|
1136
|
+
graphology: {
|
|
1137
|
+
node: ["fill", "fillOpacity", "width", "height", "label", "visible"],
|
|
1138
|
+
edge: ["color", "opacity", "width", "label", "visible"],
|
|
1139
|
+
},
|
|
1140
|
+
vis: {
|
|
1141
|
+
node: [
|
|
1142
|
+
"fill",
|
|
1143
|
+
"fillOpacity",
|
|
1144
|
+
"borderColor",
|
|
1145
|
+
"borderWidth",
|
|
1146
|
+
"shape",
|
|
1147
|
+
"width",
|
|
1148
|
+
"height",
|
|
1149
|
+
"opacity",
|
|
1150
|
+
"label",
|
|
1151
|
+
"tooltip",
|
|
1152
|
+
"labelColor",
|
|
1153
|
+
"labelSize",
|
|
1154
|
+
"labelFont",
|
|
1155
|
+
"visible",
|
|
1156
|
+
],
|
|
1157
|
+
edge: [
|
|
1158
|
+
"color",
|
|
1159
|
+
"opacity",
|
|
1160
|
+
"width",
|
|
1161
|
+
"lineStyle",
|
|
1162
|
+
"targetArrow",
|
|
1163
|
+
"sourceArrow",
|
|
1164
|
+
"label",
|
|
1165
|
+
"tooltip",
|
|
1166
|
+
"labelColor",
|
|
1167
|
+
"labelSize",
|
|
1168
|
+
"labelFont",
|
|
1169
|
+
"visible",
|
|
1170
|
+
],
|
|
1171
|
+
},
|
|
1172
|
+
};
|
|
1173
|
+
/**
|
|
1174
|
+
* What the JSON writer holds natively, over every dialect: Cytoscape.js's stylesheet, vis.js's and graphology's
|
|
1175
|
+
* per-element keys. A dialect that holds less (node-link, d3, JGF hold none) reports the rest from `check()`. The
|
|
1176
|
+
* block goes where each dialect keeps extra data.
|
|
1177
|
+
* @category Built-in formats
|
|
1178
|
+
*/
|
|
1179
|
+
export const JSON_STYLE_CAPABILITIES = Object.freeze({
|
|
1180
|
+
node: Object.freeze([
|
|
1181
|
+
...new Set([...NATIVE.cytoscape.node, ...NATIVE.vis.node, ...NATIVE.graphology.node]),
|
|
1182
|
+
]),
|
|
1183
|
+
edge: Object.freeze([
|
|
1184
|
+
...new Set([...NATIVE.cytoscape.edge, ...NATIVE.vis.edge, ...NATIVE.graphology.edge]),
|
|
1185
|
+
]),
|
|
1186
|
+
block: "native",
|
|
1187
|
+
});
|
|
1188
|
+
/** The graph attribute the block is written as, in the dialects with graph attributes. */
|
|
1189
|
+
const BLOCK_AS_ATTRIBUTE = new Set(["node-link", "jgf", "graphology"]);
|
|
1190
|
+
/**
|
|
1191
|
+
* Works out a JSON export with styles for one dialect.
|
|
1192
|
+
* @param snapshot - the graph handed to the exporter
|
|
1193
|
+
* @param dialect - the dialect written
|
|
1194
|
+
* @param input - the styles, or undefined when none are written
|
|
1195
|
+
* @returns the styled export, or null without styles
|
|
1196
|
+
*/
|
|
1197
|
+
export function jsonStyledExport(snapshot, dialect, input) {
|
|
1198
|
+
if (input === undefined) {
|
|
1199
|
+
return null;
|
|
1200
|
+
}
|
|
1201
|
+
const native = dialect === "cytoscape" || dialect === "graphology" || dialect === "vis" ? NATIVE[dialect] : null;
|
|
1202
|
+
const notes = [...notWritten(input, dialect, native), ...rulesNotKept(snapshot, dialect, input)];
|
|
1203
|
+
if (dialect === "obographs") {
|
|
1204
|
+
return { snapshot, topLevelBlock: null, stylesheet: null, groups: null, notes };
|
|
1205
|
+
}
|
|
1206
|
+
const out = snapshot.withColumns();
|
|
1207
|
+
const read = dataColumnsRead(input.rules.styles.rules);
|
|
1208
|
+
for (const [table, target] of [
|
|
1209
|
+
[out.nodes, "node"],
|
|
1210
|
+
[out.edges, "edge"],
|
|
1211
|
+
]) {
|
|
1212
|
+
removeEarlierStyleKeys(table, dialect, read[target]);
|
|
1213
|
+
for (const [name, col] of input.tags[target]) {
|
|
1214
|
+
setColumn(table, name, col);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
if (out.graph.has(STYLE_BLOCK_NAME)) {
|
|
1218
|
+
out.graph.remove(STYLE_BLOCK_NAME);
|
|
1219
|
+
}
|
|
1220
|
+
if (BLOCK_AS_ATTRIBUTE.has(dialect)) {
|
|
1221
|
+
out.graph.set(STYLE_BLOCK_NAME, [input.block], { dtype: "string" });
|
|
1222
|
+
}
|
|
1223
|
+
let stylesheet = null;
|
|
1224
|
+
const approximated = new StyleApproximations();
|
|
1225
|
+
const replaced = new Replaced(out.nodes);
|
|
1226
|
+
if (dialect === "cytoscape") {
|
|
1227
|
+
const emitted = writeCytoscapeStyles(out, input);
|
|
1228
|
+
({ stylesheet } = emitted);
|
|
1229
|
+
notes.push(...emitted.notes.map(lossOf));
|
|
1230
|
+
addUntrimmedText(input, approximated);
|
|
1231
|
+
}
|
|
1232
|
+
else if (dialect === "graphology") {
|
|
1233
|
+
writeGraphology(out, input, approximated, replaced);
|
|
1234
|
+
}
|
|
1235
|
+
else if (dialect === "vis") {
|
|
1236
|
+
writeVis(out, input, approximated, replaced);
|
|
1237
|
+
}
|
|
1238
|
+
notes.push(...approximated.notes(`the ${dialect} dialect of JSON`), ...replaced.notes(dialect));
|
|
1239
|
+
return {
|
|
1240
|
+
snapshot: out,
|
|
1241
|
+
topLevelBlock: BLOCK_AS_ATTRIBUTE.has(dialect) ? null : input.block,
|
|
1242
|
+
stylesheet,
|
|
1243
|
+
groups: dialect === "vis" ? visGroups(out) : null,
|
|
1244
|
+
notes,
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* W_STYLE_RULES_NOT_KEPT for a dialect that cannot give the rules back: JGF when it changes the node ids' type or
|
|
1249
|
+
* order, OBO Graphs always.
|
|
1250
|
+
* @param snapshot - the graph handed to the exporter
|
|
1251
|
+
* @param dialect - the dialect written
|
|
1252
|
+
* @param input - the styles
|
|
1253
|
+
* @returns the notes
|
|
1254
|
+
*/
|
|
1255
|
+
function rulesNotKept(snapshot, dialect, input) {
|
|
1256
|
+
let message = null;
|
|
1257
|
+
if (dialect === "jgf" && !jgfKeepsNodes(snapshot)) {
|
|
1258
|
+
message =
|
|
1259
|
+
"the style rules are not kept: JGF changes the node ids' type or order (number ids become text, integer-like ids are read first), so the graphty.styles block no longer matches the graph read back";
|
|
1260
|
+
}
|
|
1261
|
+
else if (dialect === "obographs") {
|
|
1262
|
+
message = "the style rules are not kept: OBO Graphs has no place for them";
|
|
1263
|
+
}
|
|
1264
|
+
if (message === null) {
|
|
1265
|
+
return [];
|
|
1266
|
+
}
|
|
1267
|
+
return [
|
|
1268
|
+
Object.freeze({
|
|
1269
|
+
code: STYLE_ISSUE.RULES_NOT_KEPT,
|
|
1270
|
+
message,
|
|
1271
|
+
column: null,
|
|
1272
|
+
count: input.rules.styles.rules.length,
|
|
1273
|
+
}),
|
|
1274
|
+
];
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Removes the style values an earlier write left in the data, unless the styles themselves read them as data, and,
|
|
1278
|
+
* for Cytoscape.js, the element style an import kept.
|
|
1279
|
+
* @param table - the node or edge table, changed in place
|
|
1280
|
+
* @param dialect - the dialect written
|
|
1281
|
+
* @param read - the data columns the target's rules read
|
|
1282
|
+
*/
|
|
1283
|
+
function removeEarlierStyleKeys(table, dialect, read) {
|
|
1284
|
+
for (const name of table.names()) {
|
|
1285
|
+
const styleKey = name.startsWith("graphty.style.") || name.startsWith("graphty_style_") || name === CYTOSCAPE_IMPORT_KEY;
|
|
1286
|
+
if (styleKey && !read.has(name)) {
|
|
1287
|
+
table.remove(name);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
if (dialect === "cytoscape") {
|
|
1291
|
+
for (const name of ["style#element", "css#element"].filter((c) => table.has(c))) {
|
|
1292
|
+
table.remove(name);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Writes the Cytoscape.js element data and bypasses the stylesheet reads.
|
|
1298
|
+
* @param out - the graph written, changed in place
|
|
1299
|
+
* @param input - the styles
|
|
1300
|
+
* @returns what toCytoscapeStylesheet() emitted
|
|
1301
|
+
*/
|
|
1302
|
+
function writeCytoscapeStyles(out, input) {
|
|
1303
|
+
// the element data the stylesheet is checked against is the data written: without the style values of an
|
|
1304
|
+
// earlier write, which are not written again
|
|
1305
|
+
const features = {
|
|
1306
|
+
node: input.rules.features.node.filter((f) => !("data" in f.ref) || out.nodes.has(f.ref.data)),
|
|
1307
|
+
edge: input.rules.features.edge.filter((f) => !("data" in f.ref) || out.edges.has(f.ref.data)),
|
|
1308
|
+
};
|
|
1309
|
+
const emitted = toCytoscapeStylesheet({ ...input.rules, features });
|
|
1310
|
+
for (const [table, target] of [
|
|
1311
|
+
[out.nodes, "node"],
|
|
1312
|
+
[out.edges, "edge"],
|
|
1313
|
+
]) {
|
|
1314
|
+
const rows = emitted.elementData[target];
|
|
1315
|
+
const keys = new Set(rows.flatMap((row) => Object.keys(row ?? {})));
|
|
1316
|
+
for (const key of keys) {
|
|
1317
|
+
setColumn(table, key, rows.map((row) => row?.[key] ?? null));
|
|
1318
|
+
}
|
|
1319
|
+
const bypass = emitted.bypass[target].map((b) => (b === null || Object.keys(b).length === 0 ? null : b));
|
|
1320
|
+
if (bypass.some((b) => b !== null)) {
|
|
1321
|
+
setColumn(table, "style#element", bypass);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return emitted;
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Counts as approximated the text values with leading or trailing white space, which Cytoscape.js trims.
|
|
1328
|
+
* @param input - the styles
|
|
1329
|
+
* @param approximated - where approximations are counted
|
|
1330
|
+
*/
|
|
1331
|
+
function addUntrimmedText(input, approximated) {
|
|
1332
|
+
for (const target of TARGETS) {
|
|
1333
|
+
for (const property of CYJS_TEXT[target]) {
|
|
1334
|
+
for (const v of input.picture(target, property)) {
|
|
1335
|
+
if (typeof v === "string" && v.trim() !== v) {
|
|
1336
|
+
approximated.add(target, property, JSON.stringify(v));
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* vis.js: one empty group definition per value of the nodes' `group` key, so vis-network draws none of its palette.
|
|
1344
|
+
* @param snapshot - the graph written
|
|
1345
|
+
* @returns the definitions, or null when no node names a group
|
|
1346
|
+
*/
|
|
1347
|
+
function visGroups(snapshot) {
|
|
1348
|
+
const column = snapshot.nodes.get("group");
|
|
1349
|
+
if (column === null || column === undefined) {
|
|
1350
|
+
return null;
|
|
1351
|
+
}
|
|
1352
|
+
const groups = new Map();
|
|
1353
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
1354
|
+
const value = column.isSet(i) ? column.value(i) : null;
|
|
1355
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
1356
|
+
groups.set(String(value), {});
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
return groups.size === 0 ? null : Object.fromEntries(groups);
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* The data columns rules read, per target.
|
|
1363
|
+
* @param rules - the rules
|
|
1364
|
+
* @returns the column names
|
|
1365
|
+
*/
|
|
1366
|
+
function dataColumnsRead(rules) {
|
|
1367
|
+
const out = { node: new Set(), edge: new Set() };
|
|
1368
|
+
for (const rule of rules) {
|
|
1369
|
+
const refs = [
|
|
1370
|
+
...predicateRefs(rule.where),
|
|
1371
|
+
...Object.values(rule.map ?? {}).flatMap((m) => (m === undefined ? [] : [m.by])),
|
|
1372
|
+
];
|
|
1373
|
+
for (const ref of refs) {
|
|
1374
|
+
if ("data" in ref) {
|
|
1375
|
+
out[rule.target].add(ref.data);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
return out;
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* The columns a predicate tests.
|
|
1383
|
+
* @param where - the predicate
|
|
1384
|
+
* @returns the column references
|
|
1385
|
+
*/
|
|
1386
|
+
function predicateRefs(where) {
|
|
1387
|
+
const refs = [];
|
|
1388
|
+
const stack = [where];
|
|
1389
|
+
while (stack.length > 0) {
|
|
1390
|
+
const p = stack.pop();
|
|
1391
|
+
if ("has" in p) {
|
|
1392
|
+
refs.push(p.has);
|
|
1393
|
+
}
|
|
1394
|
+
else if ("eq" in p) {
|
|
1395
|
+
refs.push(p.eq[0]);
|
|
1396
|
+
}
|
|
1397
|
+
else if ("in" in p) {
|
|
1398
|
+
refs.push(p.in[0]);
|
|
1399
|
+
}
|
|
1400
|
+
else if ("and" in p) {
|
|
1401
|
+
stack.push(...p.and);
|
|
1402
|
+
}
|
|
1403
|
+
else if ("or" in p) {
|
|
1404
|
+
stack.push(...p.or);
|
|
1405
|
+
}
|
|
1406
|
+
else if ("not" in p) {
|
|
1407
|
+
stack.push(p.not);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
return refs;
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Whether JGF reads the nodes back with the same ids in the same order: no number ids (JGF keys nodes by text), and
|
|
1414
|
+
* the integer-like ids (which JSON parsers enumerate first, ascending) already first and ascending.
|
|
1415
|
+
* @param snapshot - the graph
|
|
1416
|
+
* @returns true when the block's fingerprint survives
|
|
1417
|
+
*/
|
|
1418
|
+
function jgfKeepsNodes(snapshot) {
|
|
1419
|
+
let last = -1;
|
|
1420
|
+
let other = false;
|
|
1421
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
1422
|
+
const id = snapshot.ids.idOf(i);
|
|
1423
|
+
if (typeof id === "number") {
|
|
1424
|
+
return false;
|
|
1425
|
+
}
|
|
1426
|
+
if (/^(0|[1-9]\d*)$/.test(id) && Number(id) < 4294967295) {
|
|
1427
|
+
if (other || Number(id) <= last) {
|
|
1428
|
+
return false;
|
|
1429
|
+
}
|
|
1430
|
+
last = Number(id);
|
|
1431
|
+
}
|
|
1432
|
+
else {
|
|
1433
|
+
other = true;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
return true;
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* W_STYLE_NOT_WRITTEN for each property the styles use that the dialect does not hold but some other dialect does
|
|
1440
|
+
* (graph-io reports the properties no dialect holds).
|
|
1441
|
+
* @param input - the styles
|
|
1442
|
+
* @param dialect - the dialect
|
|
1443
|
+
* @param native - what it holds, or null for none
|
|
1444
|
+
* @returns the notes
|
|
1445
|
+
*/
|
|
1446
|
+
function notWritten(input, dialect, native) {
|
|
1447
|
+
const out = [];
|
|
1448
|
+
for (const target of TARGETS) {
|
|
1449
|
+
const all = new Set(JSON_STYLE_CAPABILITIES[target]);
|
|
1450
|
+
const held = new Set(native?.[target] ?? []);
|
|
1451
|
+
const used = new Set();
|
|
1452
|
+
for (const r of input.rules.styles.rules) {
|
|
1453
|
+
if (r.target === target) {
|
|
1454
|
+
for (const p of [...Object.keys(r.set ?? {}), ...Object.keys(r.map ?? {})]) {
|
|
1455
|
+
used.add(p);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
for (const property of used) {
|
|
1460
|
+
if (!all.has(property) || held.has(property)) {
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
const count = input.picture(target, property).filter((v) => v !== null).length;
|
|
1464
|
+
out.push(Object.freeze({
|
|
1465
|
+
code: STYLE_ISSUE.NOT_WRITTEN,
|
|
1466
|
+
message: `${target} ${property} is not written: the ${dialect} dialect of JSON has no place for it`,
|
|
1467
|
+
column: null,
|
|
1468
|
+
count,
|
|
1469
|
+
style: Object.freeze({ target, property: property }),
|
|
1470
|
+
}));
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return out;
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* A style note as a loss note.
|
|
1477
|
+
* @param n - the note
|
|
1478
|
+
* @returns the loss note
|
|
1479
|
+
*/
|
|
1480
|
+
function lossOf(n) {
|
|
1481
|
+
return Object.freeze({
|
|
1482
|
+
code: n.code,
|
|
1483
|
+
message: n.message,
|
|
1484
|
+
column: null,
|
|
1485
|
+
count: n.params.count,
|
|
1486
|
+
style: n.params.target === null || n.params.property === null
|
|
1487
|
+
? undefined
|
|
1488
|
+
: Object.freeze({ target: n.params.target, property: n.params.property }),
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
/**
|
|
1492
|
+
* Sets a column, with the dtype its values have: numbers, booleans, text, else JSON.
|
|
1493
|
+
* @param table - the table
|
|
1494
|
+
* @param name - the column
|
|
1495
|
+
* @param values - the values by row, null where unset
|
|
1496
|
+
*/
|
|
1497
|
+
function setColumn(table, name, values) {
|
|
1498
|
+
const kinds = new Set(values.filter((v) => v !== null && v !== undefined).map((v) => typeof v));
|
|
1499
|
+
let dtype = "json";
|
|
1500
|
+
if (kinds.size === 1) {
|
|
1501
|
+
if (kinds.has("number")) {
|
|
1502
|
+
dtype = "f64";
|
|
1503
|
+
}
|
|
1504
|
+
else if (kinds.has("boolean")) {
|
|
1505
|
+
dtype = "bool";
|
|
1506
|
+
}
|
|
1507
|
+
else if (kinds.has("string")) {
|
|
1508
|
+
dtype = "string";
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
// an unset row is undefined: null is a value in a json column
|
|
1512
|
+
table.set(name, values.map((v) => (v === null ? undefined : v)), { dtype });
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Writes a picture into a style key: every element gets the picture's value, and an element the picture gives none
|
|
1516
|
+
* loses the key, so a program reading the file draws what graphty draws. A value the column held before that differs
|
|
1517
|
+
* is counted, for the loss note.
|
|
1518
|
+
* @param table - the table
|
|
1519
|
+
* @param name - the column
|
|
1520
|
+
* @param values - the picture, null where it has no value
|
|
1521
|
+
* @param replaced - counts the values replaced, per column
|
|
1522
|
+
*/
|
|
1523
|
+
function writeKey(table, name, values, replaced) {
|
|
1524
|
+
const old = table.get(name);
|
|
1525
|
+
let changed = 0;
|
|
1526
|
+
if (old !== null) {
|
|
1527
|
+
for (let i = 0; i < values.length; i++) {
|
|
1528
|
+
const before = plainCell(old.value(i));
|
|
1529
|
+
const after = values[i] ?? null;
|
|
1530
|
+
if (before !== null && JSON.stringify(before) !== JSON.stringify(after)) {
|
|
1531
|
+
changed++;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
if (changed > 0) {
|
|
1536
|
+
replaced.add(table === replaced.nodes ? "node" : "edge", name, changed);
|
|
1537
|
+
}
|
|
1538
|
+
if (values.every((v) => v === null || v === undefined)) {
|
|
1539
|
+
if (old !== null) {
|
|
1540
|
+
table.remove(name);
|
|
1541
|
+
}
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
setColumn(table, name, values);
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* The code of the note that a data value held under a style key is replaced by what graphty draws.
|
|
1548
|
+
* @category Built-in formats
|
|
1549
|
+
*/
|
|
1550
|
+
export const STYLE_KEY_REPLACED_CODE = "W_JSON_STYLE_KEY_REPLACED";
|
|
1551
|
+
/** Counts the data values a style key's picture replaces, per target and key. */
|
|
1552
|
+
class Replaced {
|
|
1553
|
+
/**
|
|
1554
|
+
* Counts the replaced values of one export.
|
|
1555
|
+
* @param nodes - the node table being written, which tells node columns from edge columns
|
|
1556
|
+
*/
|
|
1557
|
+
constructor(nodes) {
|
|
1558
|
+
this.nodes = nodes;
|
|
1559
|
+
_Replaced_counts.set(this, new Map());
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Counts replaced values.
|
|
1563
|
+
* @param target - nodes or edges
|
|
1564
|
+
* @param key - the key
|
|
1565
|
+
* @param count - how many
|
|
1566
|
+
*/
|
|
1567
|
+
add(target, key, count) {
|
|
1568
|
+
__classPrivateFieldGet(this, _Replaced_counts, "f").set(`${target}\u0000${key}`, (__classPrivateFieldGet(this, _Replaced_counts, "f").get(`${target}\u0000${key}`) ?? 0) + count);
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* The notes, one per key.
|
|
1572
|
+
* @param dialect - the dialect, for the message
|
|
1573
|
+
* @returns the notes
|
|
1574
|
+
*/
|
|
1575
|
+
notes(dialect) {
|
|
1576
|
+
return Array.from(__classPrivateFieldGet(this, _Replaced_counts, "f"), ([key, count]) => {
|
|
1577
|
+
const [target, name] = key.split("\u0000");
|
|
1578
|
+
return Object.freeze({
|
|
1579
|
+
code: STYLE_KEY_REPLACED_CODE,
|
|
1580
|
+
message: `${target} column "${name}" is the ${dialect} dialect's style key: ${String(count)} ${target}${count === 1 ? "" : "s"} get what graphty draws instead of the value the column held`,
|
|
1581
|
+
column: name,
|
|
1582
|
+
count,
|
|
1583
|
+
});
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
_Replaced_counts = new WeakMap();
|
|
1588
|
+
/**
|
|
1589
|
+
* A color with its alpha as CSS text: `#rrggbb`, or `rgba()` below full opacity.
|
|
1590
|
+
* @param color - the color
|
|
1591
|
+
* @param alpha - the opacity, or null
|
|
1592
|
+
* @returns the text, or null
|
|
1593
|
+
*/
|
|
1594
|
+
function cssColor(color, alpha) {
|
|
1595
|
+
if (typeof color !== "string") {
|
|
1596
|
+
return null;
|
|
1597
|
+
}
|
|
1598
|
+
if (typeof alpha !== "number" || alpha >= 1) {
|
|
1599
|
+
return color;
|
|
1600
|
+
}
|
|
1601
|
+
const n = Number.parseInt(color.slice(1), 16);
|
|
1602
|
+
return `rgba(${String((n >> 16) & 255)},${String((n >> 8) & 255)},${String(n & 255)},${String(alpha)})`;
|
|
1603
|
+
}
|
|
1604
|
+
/**
|
|
1605
|
+
* One size for a node's width and height, noting a node whose two differ.
|
|
1606
|
+
* @param width - the widths
|
|
1607
|
+
* @param height - the heights
|
|
1608
|
+
* @param approximated - where approximations are counted
|
|
1609
|
+
* @returns the sizes
|
|
1610
|
+
*/
|
|
1611
|
+
function nodeSizes(width, height, approximated) {
|
|
1612
|
+
return width.map((_v, i) => {
|
|
1613
|
+
const w = width[i];
|
|
1614
|
+
const h = height[i];
|
|
1615
|
+
if (typeof w === "number" && typeof h === "number" && w !== h) {
|
|
1616
|
+
approximated.add("node", w > h ? "height" : "width", `${String(w)} x ${String(h)}`);
|
|
1617
|
+
return Math.max(w, h);
|
|
1618
|
+
}
|
|
1619
|
+
if (typeof w === "number") {
|
|
1620
|
+
return w;
|
|
1621
|
+
}
|
|
1622
|
+
return typeof h === "number" ? h : null;
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* graphology: sigma.js's keys from the picture.
|
|
1627
|
+
* @param out - the graph being written
|
|
1628
|
+
* @param input - the styles
|
|
1629
|
+
* @param approximated - where approximations are counted
|
|
1630
|
+
* @param replaced - where replaced data values are counted
|
|
1631
|
+
*/
|
|
1632
|
+
function writeGraphology(out, input, approximated, replaced) {
|
|
1633
|
+
const p = (target, property) => input.picture(target, property);
|
|
1634
|
+
for (const [table, target] of [
|
|
1635
|
+
[out.nodes, "node"],
|
|
1636
|
+
[out.edges, "edge"],
|
|
1637
|
+
]) {
|
|
1638
|
+
const [fill, opacity] = target === "node" ? ["fill", "fillOpacity"] : ["color", "opacity"];
|
|
1639
|
+
const colors = p(target, fill);
|
|
1640
|
+
const alphas = p(target, opacity);
|
|
1641
|
+
writeKey(table, "color", colors.map((c, i) => cssColor(c, alphas[i])), replaced);
|
|
1642
|
+
writeKey(table, "size", target === "node" ? nodeSizes(p("node", "width"), p("node", "height"), approximated) : p("edge", "width"), replaced);
|
|
1643
|
+
writeKey(table, "label", p(target, "label"), replaced);
|
|
1644
|
+
writeKey(table, "hidden", p(target, "visible").map((v) => (v === false ? true : null)), replaced);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* vis.js: vis-network's keys from the picture.
|
|
1649
|
+
* @param out - the graph being written
|
|
1650
|
+
* @param input - the styles
|
|
1651
|
+
* @param approximated - where approximations are counted
|
|
1652
|
+
* @param replaced - where replaced data values are counted
|
|
1653
|
+
*/
|
|
1654
|
+
function writeVis(out, input, approximated, replaced) {
|
|
1655
|
+
const p = (target, property) => input.picture(target, property);
|
|
1656
|
+
const font = (target) => {
|
|
1657
|
+
const color = p(target, "labelColor");
|
|
1658
|
+
const size = p(target, "labelSize");
|
|
1659
|
+
const face = p(target, "labelFont");
|
|
1660
|
+
return color.map((_v, i) => {
|
|
1661
|
+
const f = {};
|
|
1662
|
+
if (color[i] !== null) {
|
|
1663
|
+
f.color = color[i];
|
|
1664
|
+
}
|
|
1665
|
+
if (size[i] !== null) {
|
|
1666
|
+
f.size = size[i];
|
|
1667
|
+
}
|
|
1668
|
+
if (face[i] !== null) {
|
|
1669
|
+
f.face = face[i];
|
|
1670
|
+
}
|
|
1671
|
+
return Object.keys(f).length === 0 ? null : f;
|
|
1672
|
+
});
|
|
1673
|
+
};
|
|
1674
|
+
const label = (table, target) => {
|
|
1675
|
+
writeKey(table, "label", p(target, "label"), replaced);
|
|
1676
|
+
writeKey(table, "title", p(target, "tooltip"), replaced);
|
|
1677
|
+
writeKey(table, "font", font(target), replaced);
|
|
1678
|
+
writeKey(table, "hidden", p(target, "visible").map((v) => (v === false ? true : null)), replaced);
|
|
1679
|
+
};
|
|
1680
|
+
const fill = p("node", "fill");
|
|
1681
|
+
const fillOpacity = p("node", "fillOpacity");
|
|
1682
|
+
const border = p("node", "borderColor");
|
|
1683
|
+
writeKey(out.nodes, "color", fill.map((_v, i) => {
|
|
1684
|
+
const c = {};
|
|
1685
|
+
const bg = cssColor(fill[i], fillOpacity[i]);
|
|
1686
|
+
if (bg !== null) {
|
|
1687
|
+
c.background = bg;
|
|
1688
|
+
}
|
|
1689
|
+
if (typeof border[i] === "string") {
|
|
1690
|
+
c.border = border[i];
|
|
1691
|
+
}
|
|
1692
|
+
return Object.keys(c).length === 0 ? null : c;
|
|
1693
|
+
}), replaced);
|
|
1694
|
+
const sizes = nodeSizes(p("node", "width"), p("node", "height"), approximated);
|
|
1695
|
+
const shapes = p("node", "shape").map((s, i) => {
|
|
1696
|
+
const [shape, exact] = typeof s === "string" ? visShape(s) : [null, true];
|
|
1697
|
+
if (!exact) {
|
|
1698
|
+
approximated.add("node", "shape", s);
|
|
1699
|
+
}
|
|
1700
|
+
if (sizes[i] !== null && !VIS_SIZED.has(shape ?? "ellipse")) {
|
|
1701
|
+
approximated.add("node", "width", `${String(s ?? "ellipse")} sized by its label`);
|
|
1702
|
+
}
|
|
1703
|
+
return shape;
|
|
1704
|
+
});
|
|
1705
|
+
writeKey(out.nodes, "shape", shapes, replaced);
|
|
1706
|
+
writeKey(out.nodes, "size", sizes, replaced);
|
|
1707
|
+
writeKey(out.nodes, "borderWidth", p("node", "borderWidth"), replaced);
|
|
1708
|
+
writeKey(out.nodes, "opacity", p("node", "opacity"), replaced);
|
|
1709
|
+
label(out.nodes, "node");
|
|
1710
|
+
const color = p("edge", "color");
|
|
1711
|
+
const opacity = p("edge", "opacity");
|
|
1712
|
+
writeKey(out.edges, "color", color.map((_v, e) => {
|
|
1713
|
+
const c = {};
|
|
1714
|
+
if (typeof color[e] === "string") {
|
|
1715
|
+
c.color = color[e];
|
|
1716
|
+
}
|
|
1717
|
+
if (typeof opacity[e] === "number") {
|
|
1718
|
+
c.opacity = opacity[e];
|
|
1719
|
+
}
|
|
1720
|
+
return Object.keys(c).length === 0 ? null : c;
|
|
1721
|
+
}), replaced);
|
|
1722
|
+
writeKey(out.edges, "width", p("edge", "width"), replaced);
|
|
1723
|
+
writeKey(out.edges, "dashes", p("edge", "lineStyle").map((s) => {
|
|
1724
|
+
if (typeof s !== "string") {
|
|
1725
|
+
return null;
|
|
1726
|
+
}
|
|
1727
|
+
if (s !== "solid" && s !== "dashed") {
|
|
1728
|
+
approximated.add("edge", "lineStyle", s);
|
|
1729
|
+
}
|
|
1730
|
+
return s !== "solid";
|
|
1731
|
+
}), replaced);
|
|
1732
|
+
const to = p("edge", "targetArrow");
|
|
1733
|
+
const from = p("edge", "sourceArrow");
|
|
1734
|
+
writeKey(out.edges, "arrows", to.map((_v, e) => {
|
|
1735
|
+
const a = {};
|
|
1736
|
+
for (const [end, value, property] of [
|
|
1737
|
+
["to", to[e], "targetArrow"],
|
|
1738
|
+
["from", from[e], "sourceArrow"],
|
|
1739
|
+
]) {
|
|
1740
|
+
if (typeof value !== "string") {
|
|
1741
|
+
continue;
|
|
1742
|
+
}
|
|
1743
|
+
const [type, exact] = visArrowType(value);
|
|
1744
|
+
if (!exact) {
|
|
1745
|
+
approximated.add("edge", property, value);
|
|
1746
|
+
}
|
|
1747
|
+
a[end] = type === null ? { enabled: false } : { enabled: true, type };
|
|
1748
|
+
}
|
|
1749
|
+
return Object.keys(a).length === 0 ? null : a;
|
|
1750
|
+
}), replaced);
|
|
1751
|
+
label(out.edges, "edge");
|
|
1752
|
+
}
|
|
1753
|
+
//# sourceMappingURL=styles.js.map
|