@graphty/graph-io 0.3.42 → 0.3.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/approximations-3Q4TsdTh.js +40 -0
- package/dist/chunks/approximations-3Q4TsdTh.js.map +1 -0
- package/dist/chunks/block-JQiwViEt.js +2777 -0
- package/dist/chunks/block-JQiwViEt.js.map +1 -0
- package/dist/chunks/{choose-DvpbdRuz.js → choose-BemygfH8.js} +3 -2
- package/dist/chunks/{choose-DvpbdRuz.js.map → choose-BemygfH8.js.map} +1 -1
- package/dist/chunks/{constants-fT3E8JYY.js → constants-BFUR_CtU.js} +6 -2
- package/dist/chunks/constants-BFUR_CtU.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-DCwFLts5.js +3456 -0
- package/dist/chunks/emit-DCwFLts5.js.map +1 -0
- package/dist/chunks/{escape-B62ZGM_a.js → escape-B1rMftd9.js} +43 -20
- package/dist/chunks/escape-B1rMftd9.js.map +1 -0
- package/dist/chunks/{export-rKLeGQMo.js → export-D7IGzTG4.js} +196 -792
- package/dist/chunks/export-D7IGzTG4.js.map +1 -0
- package/dist/chunks/{exporter-BWwEuOPT.js → exporter-BtnqYuuj.js} +22 -12
- package/dist/chunks/exporter-BtnqYuuj.js.map +1 -0
- package/dist/chunks/{exporter-yLxKBvLE.js → exporter-ChPyhBOv.js} +4 -3
- package/dist/chunks/{exporter-yLxKBvLE.js.map → exporter-ChPyhBOv.js.map} +1 -1
- package/dist/chunks/{exporter-8hLNemFk.js → exporter-KEPxYUXD.js} +133 -74
- package/dist/chunks/exporter-KEPxYUXD.js.map +1 -0
- package/dist/chunks/{exporter-CpRyHZ3H.js → exporter-quPUCBiC.js} +1807 -288
- package/dist/chunks/exporter-quPUCBiC.js.map +1 -0
- package/dist/chunks/format-GDmO6m6Q.js +676 -0
- package/dist/chunks/format-GDmO6m6Q.js.map +1 -0
- package/dist/chunks/generic-BL06jrJf.js +1347 -0
- package/dist/chunks/generic-BL06jrJf.js.map +1 -0
- package/dist/chunks/{importer-WrGRZb0N.js → importer-B7r2owVf.js} +449 -102
- package/dist/chunks/importer-B7r2owVf.js.map +1 -0
- package/dist/chunks/{importer-Ht7LNg8L.js → importer-BFmOP96W.js} +116 -296
- package/dist/chunks/importer-BFmOP96W.js.map +1 -0
- package/dist/chunks/{importer-BbNb7eNg.js → importer-C2nzGhdV.js} +1390 -129
- package/dist/chunks/importer-C2nzGhdV.js.map +1 -0
- package/dist/chunks/{importer-Dz7tI8Sa.js → importer-CbX2_rNF.js} +617 -115
- package/dist/chunks/importer-CbX2_rNF.js.map +1 -0
- package/dist/chunks/{importer-D794XUTe.js → importer-CdzeN6X4.js} +31 -8
- package/dist/chunks/importer-CdzeN6X4.js.map +1 -0
- package/dist/chunks/{importer-xZ-3suY1.js → importer-ChKNC_GN.js} +791 -135
- package/dist/chunks/importer-ChKNC_GN.js.map +1 -0
- package/dist/chunks/{importer-C98dpSH4.js → importer-dwoYpSqb.js} +141 -82
- package/dist/chunks/importer-dwoYpSqb.js.map +1 -0
- package/dist/chunks/index-B9fdNpTA.js +4310 -0
- package/dist/chunks/index-B9fdNpTA.js.map +1 -0
- package/dist/chunks/{json-elements-DKSqpNW1.js → json-elements-C4FwgnH5.js} +123 -32
- package/dist/chunks/json-elements-C4FwgnH5.js.map +1 -0
- package/dist/chunks/lexicon-B-knUi-l.js +508 -0
- package/dist/chunks/lexicon-B-knUi-l.js.map +1 -0
- package/dist/chunks/{ontology-export-CtRXjg_i.js → ontology-export-BSpbY5FL.js} +3 -2
- package/dist/chunks/{ontology-export-CtRXjg_i.js.map → ontology-export-BSpbY5FL.js.map} +1 -1
- package/dist/chunks/{records-Cu4bxYOm.js → records-Nwz_rbNt.js} +2 -2
- package/dist/chunks/{records-Cu4bxYOm.js.map → records-Nwz_rbNt.js.map} +1 -1
- package/dist/chunks/styles-Brt9JMfs.js +690 -0
- package/dist/chunks/styles-Brt9JMfs.js.map +1 -0
- package/dist/chunks/styles-_N-C8S-U.js +2102 -0
- package/dist/chunks/styles-_N-C8S-U.js.map +1 -0
- package/dist/chunks/styles-yed-DAfcIkf7.js +842 -0
- package/dist/chunks/styles-yed-DAfcIkf7.js.map +1 -0
- package/dist/chunks/{text-DHOJXnED.js → text-CgzMqHQb.js} +2 -2
- package/dist/chunks/{text-DHOJXnED.js.map → text-CgzMqHQb.js.map} +1 -1
- package/dist/chunks/units-XMZFIFe1.js +19 -0
- package/dist/chunks/units-XMZFIFe1.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 +295 -414
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +8 -3031
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +354 -240
- 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 +1484 -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 +23 -7
- 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 +441 -265
- 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 +790 -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 +603 -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 +80 -0
- package/dist/src/styles/io/writer.d.ts.map +1 -0
- package/dist/src/styles/io/writer.js +596 -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 +1741 -0
- package/src/formats/gexf/exporter.ts +30 -13
- 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 +543 -323
- 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 +951 -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 +656 -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 +745 -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-CpRyHZ3H.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
|
@@ -79,6 +79,7 @@ import { readText, throwIfAborted } from "../../common/input.js";
|
|
|
79
79
|
import {
|
|
80
80
|
findDuplicateKeys,
|
|
81
81
|
flipY,
|
|
82
|
+
keysInTextOrder,
|
|
82
83
|
MAYBE_INEXACT_EXPONENT,
|
|
83
84
|
MAYBE_UNSAFE_INTEGER,
|
|
84
85
|
rewriteNumbers,
|
|
@@ -97,6 +98,7 @@ import { agree, plural } from "../../common/plural.js";
|
|
|
97
98
|
import { ImportReportBuilder } from "../../common/report.js";
|
|
98
99
|
import { weightFromValue } from "../../common/weights.js";
|
|
99
100
|
import { headBytes, sniffJsonDialectHead } from "../../sniff.js";
|
|
101
|
+
import { STYLE_BLOCK_NAME } from "../../styles/io/block.js";
|
|
100
102
|
import {
|
|
101
103
|
type CommonImportOptions,
|
|
102
104
|
type GraphChoiceOptions,
|
|
@@ -125,6 +127,8 @@ import {
|
|
|
125
127
|
SUFFIX,
|
|
126
128
|
} from "./dialect.js";
|
|
127
129
|
import { importObographs } from "./obographs.js";
|
|
130
|
+
import { jsonPositions } from "./positions.js";
|
|
131
|
+
import { jsonStyles, type JsonStyleSource } from "./styles.js";
|
|
128
132
|
|
|
129
133
|
/**
|
|
130
134
|
* The format-specific options of the JSON importer. `graphIndex` / `graphName` choose one graph of
|
|
@@ -386,6 +390,9 @@ const JGF_EDGE_KEYS: ReadonlySet<string> = new Set([
|
|
|
386
390
|
/** The JGF hyperedge keys that are not metadata. */
|
|
387
391
|
const JGF_HYPEREDGE_KEYS: ReadonlySet<string> = new Set([...JGF_EDGE_KEYS, "nodes"]);
|
|
388
392
|
|
|
393
|
+
/** A key JavaScript orders as an array index: digits with no leading zero, below 2^32 - 1. */
|
|
394
|
+
const ARRAY_INDEX = /^(?:0|[1-9]\d{0,9})$/;
|
|
395
|
+
|
|
389
396
|
/** The JGF node keys that are not metadata. */
|
|
390
397
|
const JGF_NODE_KEYS: ReadonlySet<string> = new Set(["label", "metadata"]);
|
|
391
398
|
|
|
@@ -395,6 +402,12 @@ const GRAPHOLOGY_NODE_KEYS: ReadonlySet<string> = new Set(["key", "attributes"])
|
|
|
395
402
|
/** The graphology edge keys that are not attributes. */
|
|
396
403
|
const GRAPHOLOGY_EDGE_KEYS: ReadonlySet<string> = new Set(["key", "source", "target", "attributes", "undirected"]);
|
|
397
404
|
|
|
405
|
+
/** The structural keys of a node in Gephi's JSON export. */
|
|
406
|
+
const GEPHI_NODE_KEYS: ReadonlySet<string> = new Set(["id", "attributes"]);
|
|
407
|
+
|
|
408
|
+
/** The structural keys of an edge in Gephi's JSON export. */
|
|
409
|
+
const GEPHI_EDGE_KEYS: ReadonlySet<string> = new Set([...GRAPHOLOGY_EDGE_KEYS, "id"]);
|
|
410
|
+
|
|
398
411
|
/** The vis.js endpoint keys. */
|
|
399
412
|
const VIS_SOURCE_KEYS: readonly string[] = Object.freeze(["from"]);
|
|
400
413
|
const VIS_TARGET_KEYS: readonly string[] = Object.freeze(["to"]);
|
|
@@ -897,6 +910,9 @@ export class ImportContext {
|
|
|
897
910
|
/** Whether the caller passed `defaultDirected` explicitly (then it beats the dialect's convention). */
|
|
898
911
|
readonly explicitDefaultDirected: boolean;
|
|
899
912
|
|
|
913
|
+
/** The document's text, for an order JSON.parse does not keep (a JGF nodes object's integer-like keys). */
|
|
914
|
+
text = "";
|
|
915
|
+
|
|
900
916
|
/** Nodes and edges pushed since the signal was last checked. */
|
|
901
917
|
private elementsSinceCheck = 0;
|
|
902
918
|
|
|
@@ -1098,6 +1114,34 @@ export class ImportContext {
|
|
|
1098
1114
|
/** Set when the document has no node section: every node comes from an edge (E_MISSING_SECTION says so). */
|
|
1099
1115
|
nodesFromEdges = false;
|
|
1100
1116
|
|
|
1117
|
+
/** What the dialect's reader keeps for the style reader (`report.keepForStyles()`), or null. */
|
|
1118
|
+
styleSource: JsonStyleSource | null = null;
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* A Cytoscape.js endpoint or parent id: Cytoscape.js holds every id as text, so an endpoint `0` names the node
|
|
1122
|
+
* `"0"` and an endpoint `"0"` the node `0` when the file has no node of the endpoint's own type.
|
|
1123
|
+
* @param raw - the JSON value
|
|
1124
|
+
* @param element - the element name for issues
|
|
1125
|
+
* @returns the id, or null when the value was reported
|
|
1126
|
+
*/
|
|
1127
|
+
textualId(raw: unknown, element: string): NodeId | null {
|
|
1128
|
+
const id = this.coerceId(raw, element);
|
|
1129
|
+
if (id === null || this.sink.indexOf(id) !== INVALID_INDEX) {
|
|
1130
|
+
return id;
|
|
1131
|
+
}
|
|
1132
|
+
let other: unknown = null;
|
|
1133
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
1134
|
+
other = String(raw);
|
|
1135
|
+
} else if (typeof raw === "string" && /^-?\d+(?:\.\d+)?$/.test(raw)) {
|
|
1136
|
+
other = Number(raw);
|
|
1137
|
+
}
|
|
1138
|
+
if (other === null) {
|
|
1139
|
+
return id;
|
|
1140
|
+
}
|
|
1141
|
+
const alt = this.ids.value(other);
|
|
1142
|
+
return this.sink.indexOf(alt) === INVALID_INDEX ? id : alt;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1101
1145
|
/** Dangling endpoints whose id, as the other JSON type, is a node (`"1"` next to the node 1). */
|
|
1102
1146
|
private readonly nearMatches: NodeId[] = [];
|
|
1103
1147
|
|
|
@@ -1280,8 +1324,11 @@ export class ImportContext {
|
|
|
1280
1324
|
* @param record - the element record
|
|
1281
1325
|
* @param dict - the nested attribute dict
|
|
1282
1326
|
* @param structural - the element keys that are not attributes
|
|
1283
|
-
* @param weightFrom - the weight key to skip in the dict, or null
|
|
1284
1327
|
* @param element - the element name for issues
|
|
1328
|
+
* @param how - `weightFrom`: the weight key to skip in the dict; `plain`: write the element-level keys under their
|
|
1329
|
+
* own names, not with the `#element` suffix
|
|
1330
|
+
* @param how.weightFrom - the weight key to skip in the dict, or null (the default)
|
|
1331
|
+
* @param how.plain - write the element-level keys under their own names (default false)
|
|
1285
1332
|
*/
|
|
1286
1333
|
writeNested(
|
|
1287
1334
|
writer: AttributeWriter,
|
|
@@ -1289,9 +1336,10 @@ export class ImportContext {
|
|
|
1289
1336
|
record: JsonRecord,
|
|
1290
1337
|
dict: JsonRecord,
|
|
1291
1338
|
structural: ReadonlySet<string>,
|
|
1292
|
-
weightFrom: string | null,
|
|
1293
1339
|
element: string,
|
|
1340
|
+
how: { readonly weightFrom?: string | null; readonly plain?: boolean } = {},
|
|
1294
1341
|
): void {
|
|
1342
|
+
const { weightFrom = null, plain = false } = how;
|
|
1295
1343
|
for (const key of Object.keys(dict)) {
|
|
1296
1344
|
if (key !== weightFrom) {
|
|
1297
1345
|
writer.write(row, key, dict[key], SUFFIX.data, element);
|
|
@@ -1299,7 +1347,7 @@ export class ImportContext {
|
|
|
1299
1347
|
}
|
|
1300
1348
|
for (const key of Object.keys(record)) {
|
|
1301
1349
|
if (!structural.has(key)) {
|
|
1302
|
-
writer.write(row, `${key}${SUFFIX.element}`, record[key], SUFFIX.data, element);
|
|
1350
|
+
writer.write(row, plain ? key : `${key}${SUFFIX.element}`, record[key], SUFFIX.data, element);
|
|
1303
1351
|
}
|
|
1304
1352
|
}
|
|
1305
1353
|
}
|
|
@@ -1687,7 +1735,8 @@ function arraySection(value: unknown, what: string, report: ImportReportBuilder)
|
|
|
1687
1735
|
if (!Array.isArray(value)) {
|
|
1688
1736
|
return report.fail(JSON_ISSUE.SHAPE, `${what} must be an array, found ${describe(value)}`, { element: what });
|
|
1689
1737
|
}
|
|
1690
|
-
|
|
1738
|
+
const items: readonly unknown[] = value;
|
|
1739
|
+
return items;
|
|
1691
1740
|
}
|
|
1692
1741
|
|
|
1693
1742
|
/**
|
|
@@ -1773,10 +1822,16 @@ function reportUnreadKeys(ctx: ImportContext, record: JsonRecord, known: Readonl
|
|
|
1773
1822
|
}
|
|
1774
1823
|
|
|
1775
1824
|
/** The keys of a vis.js document the reader reads. */
|
|
1776
|
-
const VIS_DOCUMENT_KEYS: ReadonlySet<string> = new Set(["nodes", "edges"]);
|
|
1825
|
+
const VIS_DOCUMENT_KEYS: ReadonlySet<string> = new Set(["nodes", "edges", STYLE_BLOCK_NAME]);
|
|
1777
1826
|
|
|
1778
1827
|
/** The keys of a graphology serialization the reader reads. */
|
|
1779
|
-
const GRAPHOLOGY_DOCUMENT_KEYS: ReadonlySet<string> = new Set([
|
|
1828
|
+
const GRAPHOLOGY_DOCUMENT_KEYS: ReadonlySet<string> = new Set([
|
|
1829
|
+
"nodes",
|
|
1830
|
+
"edges",
|
|
1831
|
+
"options",
|
|
1832
|
+
"attributes",
|
|
1833
|
+
STYLE_BLOCK_NAME,
|
|
1834
|
+
]);
|
|
1780
1835
|
|
|
1781
1836
|
/** The keys of a JGF graph object the reader reads. */
|
|
1782
1837
|
const JGF_GRAPH_KEYS: ReadonlySet<string> = new Set([
|
|
@@ -1820,10 +1875,61 @@ function isIndexBelow(value: unknown, bound: number): boolean {
|
|
|
1820
1875
|
*/
|
|
1821
1876
|
function importNodeLink(ctx: ImportContext, root: JsonRecord, dialect: "node-link" | "d3"): void {
|
|
1822
1877
|
const { report, json } = ctx;
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1878
|
+
const edgesKey = json.edgesKey ?? (hasKey(root, "edges") || !hasKey(root, "links") ? "edges" : "links");
|
|
1879
|
+
const { nodeList, edgeList } = nodeLinkSections(ctx, root, edgesKey);
|
|
1880
|
+
const directed = flagOf(root.directed, "directed", ctx.defaultDirected(dialect), report);
|
|
1881
|
+
const multigraph = hasKey(root, "multigraph") ? flagOf(root.multigraph, "multigraph", false, report) : null;
|
|
1882
|
+
ctx.setHeader(directed);
|
|
1883
|
+
ctx.writeGraphDict(root.graph, "graph");
|
|
1884
|
+
reportUnreadKeys(ctx, root, new Set(["nodes", edgesKey, "directed", "multigraph", "graph", STYLE_BLOCK_NAME]), "");
|
|
1885
|
+
// NetworkX's node_link_data writes all three keys; its drawing defaults then apply
|
|
1886
|
+
const networkx = hasKey(root, "directed") && hasKey(root, "multigraph") && hasKey(root, "graph");
|
|
1887
|
+
ctx.styleSource = { dialect, block: blockOf(root), networkx };
|
|
1888
|
+
ctx.sink.reserve(nodeList.length, edgeList.length);
|
|
1889
|
+
|
|
1890
|
+
const nodeIdKey = nodeLinkIdKey(ctx, nodeList);
|
|
1891
|
+
const positional = nodeIdKey === null;
|
|
1892
|
+
let indexLinks: boolean;
|
|
1893
|
+
if (positional) {
|
|
1894
|
+
indexLinks = true;
|
|
1895
|
+
} else if (json.indexLinks === "auto") {
|
|
1896
|
+
indexLinks = looksIndexLinked(nodeList, edgeList, nodeIdKey, json);
|
|
1897
|
+
} else {
|
|
1898
|
+
({ indexLinks } = json);
|
|
1826
1899
|
}
|
|
1900
|
+
if (indexLinks && !positional && json.indexLinks === "auto") {
|
|
1901
|
+
reportAmbiguousIndexLinks(ctx, nodeList, edgeList, nodeIdKey);
|
|
1902
|
+
}
|
|
1903
|
+
const positionIds = importNodeLinkNodes(ctx, nodeList, nodeIdKey, indexLinks);
|
|
1904
|
+
throwIfAborted(ctx.options.signal);
|
|
1905
|
+
const endpointKeys = importNodeLinkEdges(ctx, edgeList, edgesKey, positionIds, multigraph);
|
|
1906
|
+
ctx.setMeta(
|
|
1907
|
+
{
|
|
1908
|
+
dialect,
|
|
1909
|
+
edgesKey,
|
|
1910
|
+
nodeIdKey,
|
|
1911
|
+
indexLinks,
|
|
1912
|
+
sourceKey: endpointKeys.source ?? undefined,
|
|
1913
|
+
targetKey: endpointKeys.target ?? undefined,
|
|
1914
|
+
},
|
|
1915
|
+
{ declaredMultigraph: multigraph, ...ctx.weightOriginPatch() },
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
/**
|
|
1920
|
+
* The node and edge arrays of a node-link or d3 document; a missing one is reported (without nodes, nodes come from
|
|
1921
|
+
* the edges).
|
|
1922
|
+
* @param ctx - the context
|
|
1923
|
+
* @param root - the document
|
|
1924
|
+
* @param edgesKey - the key of the edges
|
|
1925
|
+
* @returns the arrays, empty when missing
|
|
1926
|
+
*/
|
|
1927
|
+
function nodeLinkSections(
|
|
1928
|
+
ctx: ImportContext,
|
|
1929
|
+
root: JsonRecord,
|
|
1930
|
+
edgesKey: string,
|
|
1931
|
+
): { nodeList: readonly unknown[]; edgeList: readonly unknown[] } {
|
|
1932
|
+
const { report } = ctx;
|
|
1827
1933
|
const nodes = arraySection(root.nodes, "nodes", report);
|
|
1828
1934
|
const edges = arraySection(root[edgesKey], edgesKey, report);
|
|
1829
1935
|
if (nodes === null) {
|
|
@@ -1843,101 +1949,100 @@ function importNodeLink(ctx: ImportContext, root: JsonRecord, dialect: "node-lin
|
|
|
1843
1949
|
{ element: edgesKey },
|
|
1844
1950
|
);
|
|
1845
1951
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
ctx.setHeader(directed);
|
|
1849
|
-
ctx.writeGraphDict(root.graph, "graph");
|
|
1850
|
-
reportUnreadKeys(ctx, root, new Set(["nodes", edgesKey, "directed", "multigraph", "graph"]), "");
|
|
1851
|
-
|
|
1852
|
-
const nodeList = nodes ?? [];
|
|
1853
|
-
const edgeList = edges ?? [];
|
|
1854
|
-
ctx.sink.reserve(nodeList.length, edgeList.length);
|
|
1952
|
+
return { nodeList: nodes ?? [], edgeList: edges ?? [] };
|
|
1953
|
+
}
|
|
1855
1954
|
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
nodeIdKey === "id"
|
|
1868
|
-
? 'nodeIdFrom "label": no node has a label or name key; ids are read from "id"'
|
|
1869
|
-
: 'nodeIdFrom "label": no node has a "label" key; ids are read from "name"',
|
|
1870
|
-
{ element: "nodeIdFrom" },
|
|
1871
|
-
);
|
|
1872
|
-
}
|
|
1873
|
-
if (nodeIdKey === null) {
|
|
1874
|
-
if (nodeList.length > 0) {
|
|
1875
|
-
positional = true;
|
|
1876
|
-
report.warning(
|
|
1877
|
-
"missing-value",
|
|
1878
|
-
JSON_ISSUE.POSITIONAL_NODES,
|
|
1879
|
-
"no node has an id or name key; array positions are the node ids",
|
|
1880
|
-
{ element: "nodes" },
|
|
1881
|
-
);
|
|
1882
|
-
} else {
|
|
1883
|
-
nodeIdKey = "id";
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1955
|
+
/**
|
|
1956
|
+
* The key node ids are read from: the caller's, else "id" when any node has it, else "name" (d3); null when the nodes
|
|
1957
|
+
* are positional (nodeIdFrom "index", or a d3 v3 file whose nodes carry no id at all).
|
|
1958
|
+
* @param ctx - the context
|
|
1959
|
+
* @param nodeList - the node records
|
|
1960
|
+
* @returns the key, or null for positional nodes
|
|
1961
|
+
*/
|
|
1962
|
+
function nodeLinkIdKey(ctx: ImportContext, nodeList: readonly unknown[]): string | null {
|
|
1963
|
+
const { report, json } = ctx;
|
|
1964
|
+
if (ctx.options.nodeIdFrom === "index") {
|
|
1965
|
+
return null;
|
|
1886
1966
|
}
|
|
1887
|
-
if (
|
|
1888
|
-
nodeIdKey
|
|
1967
|
+
if (json.nodeIdKey !== null) {
|
|
1968
|
+
return json.nodeIdKey;
|
|
1889
1969
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1970
|
+
const byLabel = ctx.options.nodeIdFrom === "label";
|
|
1971
|
+
const candidates = byLabel ? ["label", "name", "id"] : ["id", "name"];
|
|
1972
|
+
const found = candidates.find((key) => anyHas(nodeList, key)) ?? null;
|
|
1973
|
+
if (byLabel && (found === "id" || found === "name")) {
|
|
1974
|
+
report.warning(
|
|
1975
|
+
"unsupported",
|
|
1976
|
+
JSON_ISSUE.OPTION_IGNORED,
|
|
1977
|
+
found === "id"
|
|
1978
|
+
? 'nodeIdFrom "label": no node has a label or name key; ids are read from "id"'
|
|
1979
|
+
: 'nodeIdFrom "label": no node has a "label" key; ids are read from "name"',
|
|
1980
|
+
{ element: "nodeIdFrom" },
|
|
1981
|
+
);
|
|
1897
1982
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
reportAmbiguousIndexLinks(ctx, nodeList, edgeList, nodeIdKey);
|
|
1983
|
+
if (found !== null || nodeList.length === 0) {
|
|
1984
|
+
return found ?? "id";
|
|
1901
1985
|
}
|
|
1986
|
+
report.warning(
|
|
1987
|
+
"missing-value",
|
|
1988
|
+
JSON_ISSUE.POSITIONAL_NODES,
|
|
1989
|
+
"no node has an id or name key; array positions are the node ids",
|
|
1990
|
+
{ element: "nodes" },
|
|
1991
|
+
);
|
|
1992
|
+
return null;
|
|
1993
|
+
}
|
|
1902
1994
|
|
|
1995
|
+
/**
|
|
1996
|
+
* Read the node-link / d3 node records.
|
|
1997
|
+
* @param ctx - the context
|
|
1998
|
+
* @param nodeList - the node records
|
|
1999
|
+
* @param nodeIdKey - the key ids are read from, or null for positional nodes
|
|
2000
|
+
* @param indexLinks - whether edges name their endpoints by array position
|
|
2001
|
+
* @returns the ids by array position under index links (null where a node was not added), else null
|
|
2002
|
+
*/
|
|
2003
|
+
function importNodeLinkNodes(
|
|
2004
|
+
ctx: ImportContext,
|
|
2005
|
+
nodeList: readonly unknown[],
|
|
2006
|
+
nodeIdKey: string | null,
|
|
2007
|
+
indexLinks: boolean,
|
|
2008
|
+
): (NodeId | null)[] | null {
|
|
2009
|
+
const positionIds: (NodeId | null)[] | null = indexLinks ? [] : null;
|
|
1903
2010
|
for (let i = 0; i < nodeList.length; i++) {
|
|
1904
2011
|
const element = `nodes[${i}]`;
|
|
1905
2012
|
const record = nodeList[i];
|
|
1906
2013
|
let pushed: NodeId | null = null;
|
|
1907
|
-
if (
|
|
1908
|
-
ctx
|
|
2014
|
+
if (isJsonObject(record)) {
|
|
2015
|
+
pushed = pushNodeLinkNode(ctx, record, i, nodeIdKey);
|
|
1909
2016
|
} else {
|
|
1910
|
-
|
|
1911
|
-
if (nodeIdKey === null) {
|
|
1912
|
-
id = i;
|
|
1913
|
-
} else {
|
|
1914
|
-
id = ctx.coerceId(hasKey(record, nodeIdKey) ? record[nodeIdKey] : undefined, element);
|
|
1915
|
-
}
|
|
1916
|
-
if (id === null) {
|
|
1917
|
-
ctx.countSkipped("node");
|
|
1918
|
-
} else {
|
|
1919
|
-
const index = ctx.pushNode(id, element);
|
|
1920
|
-
if (index >= 0) {
|
|
1921
|
-
pushed = id;
|
|
1922
|
-
writeFlat(ctx.nodes, index, record, id, (key) => key !== nodeIdKey);
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
2017
|
+
ctx.badElement("node", element);
|
|
1925
2018
|
}
|
|
1926
2019
|
positionIds?.push(pushed);
|
|
1927
2020
|
}
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2021
|
+
return positionIds;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
/**
|
|
2025
|
+
* Add one node-link / d3 node and its other keys as attributes.
|
|
2026
|
+
* @param ctx - the context
|
|
2027
|
+
* @param record - the node record
|
|
2028
|
+
* @param i - its array position
|
|
2029
|
+
* @param nodeIdKey - the key its id is read from, or null for positional nodes
|
|
2030
|
+
* @returns its id, or null when it was not added
|
|
2031
|
+
*/
|
|
2032
|
+
function pushNodeLinkNode(ctx: ImportContext, record: JsonRecord, i: number, nodeIdKey: string | null): NodeId | null {
|
|
2033
|
+
const element = `nodes[${i}]`;
|
|
2034
|
+
const raw = nodeIdKey !== null && hasKey(record, nodeIdKey) ? record[nodeIdKey] : undefined;
|
|
2035
|
+
const id = nodeIdKey === null ? i : ctx.coerceId(raw, element);
|
|
2036
|
+
if (id === null) {
|
|
2037
|
+
ctx.countSkipped("node");
|
|
2038
|
+
return null;
|
|
2039
|
+
}
|
|
2040
|
+
const index = ctx.pushNode(id, element);
|
|
2041
|
+
if (index < 0) {
|
|
2042
|
+
return null;
|
|
2043
|
+
}
|
|
2044
|
+
writeFlat(ctx.nodes, index, record, id, (key) => key !== nodeIdKey);
|
|
2045
|
+
return id;
|
|
1941
2046
|
}
|
|
1942
2047
|
|
|
1943
2048
|
/**
|
|
@@ -2275,6 +2380,7 @@ function importVis(ctx: ImportContext, root: JsonRecord): void {
|
|
|
2275
2380
|
{ element: "arrows" },
|
|
2276
2381
|
);
|
|
2277
2382
|
}
|
|
2383
|
+
ctx.styleSource = { dialect: "vis", block: blockOf(root), options: root.options, groups: root.groups };
|
|
2278
2384
|
ctx.setMeta(
|
|
2279
2385
|
{ dialect: "vis", nodeIdKey, sourceKey: sourceKey ?? undefined, targetKey: targetKey ?? undefined },
|
|
2280
2386
|
ctx.weightOriginPatch(),
|
|
@@ -2681,58 +2787,34 @@ function importGraphology(ctx: ImportContext, root: JsonRecord): void {
|
|
|
2681
2787
|
});
|
|
2682
2788
|
}
|
|
2683
2789
|
const type = graphologyType(ctx, options);
|
|
2684
|
-
ctx.setHeader(
|
|
2790
|
+
ctx.setHeader(type === "mixed" ? mostlyDirected(ctx, edges) : type === "directed");
|
|
2685
2791
|
const multi = hasKey(options, "multi") ? flagOf(options.multi, "options.multi", false, report) : null;
|
|
2686
2792
|
const allowSelfLoops = hasKey(options, "allowSelfLoops")
|
|
2687
2793
|
? flagOf(options.allowSelfLoops, "options.allowSelfLoops", true, report)
|
|
2688
2794
|
: undefined;
|
|
2689
2795
|
ctx.writeGraphDict(root.attributes, "attributes");
|
|
2796
|
+
// Gephi's JSON export (for sigma.js) names nodes and edges by `id` and puts sigma's display keys (`label`, `x`,
|
|
2797
|
+
// `y`, `color`, `size`) beside `attributes`; they are read as attributes, not as unknown element keys
|
|
2798
|
+
const firstNode = nodes.find((n) => isJsonObject(n));
|
|
2799
|
+
const gephi = isJsonObject(firstNode) && !hasKey(firstNode, "key") && hasKey(firstNode, "id");
|
|
2800
|
+
ctx.styleSource = { dialect: "graphology", block: blockOf(root), gephi };
|
|
2690
2801
|
ctx.reportNodeIdFrom("graphology", "the node key");
|
|
2691
2802
|
ctx.sink.reserve(nodes.length, edges.length);
|
|
2692
2803
|
|
|
2693
|
-
|
|
2694
|
-
pushGraphologyNode(ctx, nodes[i], `nodes[${i}]`);
|
|
2695
|
-
}
|
|
2804
|
+
importGraphologyNodes(ctx, nodes, gephi);
|
|
2696
2805
|
throwIfAborted(ctx.options.signal);
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
ids: ctx.declareEdgeIds(edges, (edge) => edge.key, "key", true),
|
|
2700
|
-
type,
|
|
2701
|
-
allowSelfLoops,
|
|
2702
|
-
// what the edges do against the declared options: graphology itself refuses such a document
|
|
2703
|
-
violations: { selfLoops: 0, parallels: 0, flags: 0 },
|
|
2704
|
-
pairs: multi === false ? new Set<string>() : null,
|
|
2705
|
-
};
|
|
2706
|
-
for (let i = 0; i < edges.length; i++) {
|
|
2707
|
-
readGraphologyEdge(ctx, reading, edges[i], `edges[${i}]`);
|
|
2708
|
-
}
|
|
2709
|
-
reportGraphologyViolations(ctx, reading.violations, type);
|
|
2806
|
+
const violations = importGraphologyEdges(ctx, edges, { type, gephi, multi, allowSelfLoops });
|
|
2807
|
+
reportGraphologyViolations(ctx, violations, type);
|
|
2710
2808
|
ctx.setMeta({ dialect: "graphology", allowSelfLoops }, { declaredMultigraph: multi, ...ctx.weightOriginPatch() });
|
|
2711
2809
|
}
|
|
2712
2810
|
|
|
2713
|
-
/** The graph types a graphology document declares in `options.type`. */
|
|
2714
|
-
type GraphologyType = "directed" | "undirected" | "mixed";
|
|
2715
|
-
|
|
2716
|
-
/** The state of a graphology read, shared by its edges. */
|
|
2717
|
-
interface GraphologyReading {
|
|
2718
|
-
/** The edge id column, from declareEdgeIds(). */
|
|
2719
|
-
readonly ids: EdgeIdColumn | null;
|
|
2720
|
-
readonly type: GraphologyType;
|
|
2721
|
-
/** options.allowSelfLoops, or undefined when absent. */
|
|
2722
|
-
readonly allowSelfLoops: boolean | undefined;
|
|
2723
|
-
/** The edges that contradict the declared options (reportGraphologyViolations()). */
|
|
2724
|
-
readonly violations: { selfLoops: number; parallels: number; flags: number };
|
|
2725
|
-
/** The pairs seen so far under multi false, or null. */
|
|
2726
|
-
readonly pairs: Set<string> | null;
|
|
2727
|
-
}
|
|
2728
|
-
|
|
2729
2811
|
/**
|
|
2730
|
-
* A graphology document's `options.type
|
|
2812
|
+
* A graphology document's `options.type`: "mixed" when absent or not one of the three (reported).
|
|
2731
2813
|
* @param ctx - the context
|
|
2732
|
-
* @param options - the options
|
|
2814
|
+
* @param options - the document's options
|
|
2733
2815
|
* @returns the type
|
|
2734
2816
|
*/
|
|
2735
|
-
function graphologyType(ctx: ImportContext, options: JsonRecord):
|
|
2817
|
+
function graphologyType(ctx: ImportContext, options: JsonRecord): "directed" | "undirected" | "mixed" {
|
|
2736
2818
|
if (options.type === "directed" || options.type === "undirected" || options.type === "mixed") {
|
|
2737
2819
|
return options.type;
|
|
2738
2820
|
}
|
|
@@ -2748,127 +2830,170 @@ function graphologyType(ctx: ImportContext, options: JsonRecord): GraphologyType
|
|
|
2748
2830
|
}
|
|
2749
2831
|
|
|
2750
2832
|
/**
|
|
2751
|
-
* The header direction of a graphology
|
|
2752
|
-
* edge object says undirected (the dialect default when there is none).
|
|
2833
|
+
* The header direction of a mixed graphology graph: directed unless every edge is flagged undirected.
|
|
2753
2834
|
* @param ctx - the context
|
|
2754
|
-
* @param type - options.type
|
|
2755
2835
|
* @param edges - the edge records
|
|
2756
|
-
* @returns
|
|
2836
|
+
* @returns true for directed
|
|
2757
2837
|
*/
|
|
2758
|
-
function
|
|
2759
|
-
if (type !== "mixed") {
|
|
2760
|
-
return type === "directed";
|
|
2761
|
-
}
|
|
2838
|
+
function mostlyDirected(ctx: ImportContext, edges: readonly unknown[]): boolean {
|
|
2762
2839
|
const objects = edges.filter((edge) => isJsonObject(edge)) as JsonRecord[];
|
|
2763
2840
|
const undirectedEdges = objects.filter((edge) => edge.undirected === true).length;
|
|
2764
2841
|
return objects.length === 0 ? ctx.defaultDirected("graphology") : undirectedEdges < objects.length;
|
|
2765
2842
|
}
|
|
2766
2843
|
|
|
2767
2844
|
/**
|
|
2768
|
-
*
|
|
2845
|
+
* Read the graphology node records.
|
|
2769
2846
|
* @param ctx - the context
|
|
2770
|
-
* @param
|
|
2771
|
-
* @param
|
|
2847
|
+
* @param nodes - the node records
|
|
2848
|
+
* @param gephi - whether Gephi wrote them (ids under `id`, display keys beside the attributes)
|
|
2772
2849
|
*/
|
|
2773
|
-
function
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2850
|
+
function importGraphologyNodes(ctx: ImportContext, nodes: readonly unknown[], gephi: boolean): void {
|
|
2851
|
+
const nodeKey = gephi ? "id" : "key";
|
|
2852
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
2853
|
+
const element = `nodes[${i}]`;
|
|
2854
|
+
const record = nodes[i];
|
|
2855
|
+
if (!isJsonObject(record)) {
|
|
2856
|
+
ctx.badElement("node", element);
|
|
2857
|
+
continue;
|
|
2858
|
+
}
|
|
2859
|
+
const id = ctx.coerceId(hasKey(record, nodeKey) ? record[nodeKey] : undefined, element);
|
|
2860
|
+
if (id === null) {
|
|
2861
|
+
ctx.countSkipped("node");
|
|
2862
|
+
continue;
|
|
2863
|
+
}
|
|
2864
|
+
pushNestedNode(ctx, id, record, "attributes", gephi ? GEPHI_NODE_KEYS : GRAPHOLOGY_NODE_KEYS, element, gephi);
|
|
2777
2865
|
}
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
/** What a graphology document declares about its edges. */
|
|
2869
|
+
interface GraphologyEdgeOptions {
|
|
2870
|
+
/** `options.type`. */
|
|
2871
|
+
readonly type: "directed" | "undirected" | "mixed";
|
|
2872
|
+
/** Whether Gephi wrote the document. */
|
|
2873
|
+
readonly gephi: boolean;
|
|
2874
|
+
/** `options.multi`, or null when absent. */
|
|
2875
|
+
readonly multi: boolean | null;
|
|
2876
|
+
/** `options.allowSelfLoops`, or undefined when absent. */
|
|
2877
|
+
readonly allowSelfLoops: boolean | undefined;
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
/** How many edges contradict a graphology document's declared options. */
|
|
2881
|
+
interface GraphologyViolations {
|
|
2882
|
+
selfLoops: number;
|
|
2883
|
+
parallels: number;
|
|
2884
|
+
flags: number;
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
/**
|
|
2888
|
+
* Read the graphology edge records.
|
|
2889
|
+
* @param ctx - the context
|
|
2890
|
+
* @param edges - the edge records
|
|
2891
|
+
* @param declared - what the document declares
|
|
2892
|
+
* @returns the edges that contradict the declared options: graphology itself refuses such a document
|
|
2893
|
+
*/
|
|
2894
|
+
function importGraphologyEdges(
|
|
2895
|
+
ctx: ImportContext,
|
|
2896
|
+
edges: readonly unknown[],
|
|
2897
|
+
declared: GraphologyEdgeOptions,
|
|
2898
|
+
): GraphologyViolations {
|
|
2899
|
+
const { gephi } = declared;
|
|
2900
|
+
const ids = ctx.declareEdgeIds(edges, (edge) => (gephi && !hasKey(edge, "key") ? edge.id : edge.key), "key", true);
|
|
2901
|
+
const violations: GraphologyViolations = { selfLoops: 0, parallels: 0, flags: 0 };
|
|
2902
|
+
const pairs = declared.multi === false ? new Set<string>() : null;
|
|
2903
|
+
for (let i = 0; i < edges.length; i++) {
|
|
2904
|
+
const element = `edges[${i}]`;
|
|
2905
|
+
const record = graphologyEdgeRecord(ctx, edges[i], element);
|
|
2906
|
+
if (record === null) {
|
|
2907
|
+
continue;
|
|
2908
|
+
}
|
|
2909
|
+
try {
|
|
2910
|
+
const pushed = pushGraphologyEdge(ctx, record, element, ids, declared, violations);
|
|
2911
|
+
if (pushed !== null && pairs !== null) {
|
|
2912
|
+
violations.parallels += pairs.has(pushed) ? 1 : 0;
|
|
2913
|
+
pairs.add(pushed);
|
|
2914
|
+
}
|
|
2915
|
+
} catch (err) {
|
|
2916
|
+
ctx.skip(err, "edge", element);
|
|
2917
|
+
}
|
|
2782
2918
|
}
|
|
2783
|
-
|
|
2919
|
+
return violations;
|
|
2784
2920
|
}
|
|
2785
2921
|
|
|
2786
2922
|
/**
|
|
2787
|
-
*
|
|
2923
|
+
* One graphology edge record, when it is an object with both endpoints.
|
|
2788
2924
|
* @param ctx - the context
|
|
2789
|
-
* @param reading - the read's state
|
|
2790
2925
|
* @param record - the edge record
|
|
2791
2926
|
* @param element - the element name
|
|
2927
|
+
* @returns the record, or null when it was reported and is skipped
|
|
2792
2928
|
*/
|
|
2793
|
-
function
|
|
2929
|
+
function graphologyEdgeRecord(ctx: ImportContext, record: unknown, element: string): JsonRecord | null {
|
|
2794
2930
|
if (!isJsonObject(record)) {
|
|
2795
2931
|
ctx.badElement("edge", element);
|
|
2796
|
-
return;
|
|
2932
|
+
return null;
|
|
2797
2933
|
}
|
|
2798
2934
|
if (!present(record, "source") || !present(record, "target")) {
|
|
2799
2935
|
ctx.missingEndpoint(element, present(record, "source") ? "target" : "source");
|
|
2800
|
-
return;
|
|
2801
|
-
}
|
|
2802
|
-
try {
|
|
2803
|
-
const u = ctx.coerceId(record.source, `${element}.source`);
|
|
2804
|
-
const v = ctx.coerceId(record.target, `${element}.target`);
|
|
2805
|
-
if (u === null || v === null) {
|
|
2806
|
-
ctx.countSkipped("edge");
|
|
2807
|
-
return;
|
|
2808
|
-
}
|
|
2809
|
-
const kind = graphologyEdgeKind(ctx, reading, record, element);
|
|
2810
|
-
const attributes = graphologyEdgeAttributes(ctx, record, element);
|
|
2811
|
-
const idValue = ctx.edgeIdValue(reading.ids, record.key);
|
|
2812
|
-
const edge = ctx.pushEdge(u, v, kind, ctx.weightOf(attributes), element);
|
|
2813
|
-
ctx.setEdgeId(reading.ids, edge, idValue);
|
|
2814
|
-
ctx.writeNested(ctx.edges, edge, record, attributes, GRAPHOLOGY_EDGE_KEYS, ctx.options.weightFrom, element);
|
|
2815
|
-
if (reading.allowSelfLoops === false && u === v) {
|
|
2816
|
-
reading.violations.selfLoops++;
|
|
2817
|
-
}
|
|
2818
|
-
if (reading.pairs !== null) {
|
|
2819
|
-
const pair = `${kind} ${pairKey(u, v, kind === "directed")}`;
|
|
2820
|
-
reading.violations.parallels += reading.pairs.has(pair) ? 1 : 0;
|
|
2821
|
-
reading.pairs.add(pair);
|
|
2822
|
-
}
|
|
2823
|
-
} catch (err) {
|
|
2824
|
-
ctx.skip(err, "edge", element);
|
|
2936
|
+
return null;
|
|
2825
2937
|
}
|
|
2938
|
+
return record;
|
|
2826
2939
|
}
|
|
2827
2940
|
|
|
2828
2941
|
/**
|
|
2829
|
-
*
|
|
2830
|
-
* (a contrary flag is counted as a violation).
|
|
2942
|
+
* Add one graphology edge with its id and attributes.
|
|
2831
2943
|
* @param ctx - the context
|
|
2832
|
-
* @param reading - the read's state
|
|
2833
2944
|
* @param record - the edge record
|
|
2834
2945
|
* @param element - the element name
|
|
2835
|
-
* @
|
|
2946
|
+
* @param ids - the edge id column
|
|
2947
|
+
* @param declared - what the document declares
|
|
2948
|
+
* @param violations - counts the edges that contradict the declared options
|
|
2949
|
+
* @returns the edge's kind and endpoint pair, or null when it was skipped
|
|
2836
2950
|
*/
|
|
2837
|
-
function
|
|
2951
|
+
function pushGraphologyEdge(
|
|
2838
2952
|
ctx: ImportContext,
|
|
2839
|
-
reading: GraphologyReading,
|
|
2840
2953
|
record: JsonRecord,
|
|
2841
2954
|
element: string,
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2955
|
+
ids: Parameters<ImportContext["edgeIdValue"]>[0],
|
|
2956
|
+
declared: GraphologyEdgeOptions,
|
|
2957
|
+
violations: GraphologyViolations,
|
|
2958
|
+
): string | null {
|
|
2959
|
+
const { report } = ctx;
|
|
2960
|
+
const { type, gephi } = declared;
|
|
2961
|
+
const u = ctx.coerceId(record.source, `${element}.source`);
|
|
2962
|
+
const v = ctx.coerceId(record.target, `${element}.target`);
|
|
2963
|
+
if (u === null || v === null) {
|
|
2964
|
+
ctx.countSkipped("edge");
|
|
2965
|
+
return null;
|
|
2846
2966
|
}
|
|
2847
|
-
|
|
2848
|
-
|
|
2967
|
+
let kind: EdgeKind;
|
|
2968
|
+
if (type === "mixed") {
|
|
2969
|
+
kind = flagOf(record.undirected, `${element}.undirected`, false, report) ? "undirected" : "directed";
|
|
2970
|
+
} else {
|
|
2971
|
+
kind = type;
|
|
2972
|
+
if (typeof record.undirected === "boolean" && record.undirected !== (type === "undirected")) {
|
|
2973
|
+
violations.flags++;
|
|
2974
|
+
}
|
|
2849
2975
|
}
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
/**
|
|
2854
|
-
* The `attributes` object of a graphology edge; any other non-null value is reported and read as
|
|
2855
|
-
* no attributes.
|
|
2856
|
-
* @param ctx - the context
|
|
2857
|
-
* @param record - the edge record
|
|
2858
|
-
* @param element - the element name
|
|
2859
|
-
* @returns the attributes
|
|
2860
|
-
*/
|
|
2861
|
-
function graphologyEdgeAttributes(ctx: ImportContext, record: JsonRecord, element: string): JsonRecord {
|
|
2862
|
-
const { attributes } = record;
|
|
2863
|
-
if (attributes !== undefined && attributes !== null && !isJsonObject(attributes)) {
|
|
2864
|
-
ctx.report.error(
|
|
2976
|
+
const { attributes: rawAttributes } = record;
|
|
2977
|
+
if (rawAttributes !== undefined && rawAttributes !== null && !isJsonObject(rawAttributes)) {
|
|
2978
|
+
report.error(
|
|
2865
2979
|
"validation-error",
|
|
2866
2980
|
JSON_ISSUE.BAD_VALUE,
|
|
2867
|
-
`${element}: attributes must be an object, found ${describe(
|
|
2981
|
+
`${element}: attributes must be an object, found ${describe(rawAttributes)}`,
|
|
2868
2982
|
{ element },
|
|
2869
2983
|
);
|
|
2870
2984
|
}
|
|
2871
|
-
|
|
2985
|
+
const attributes = isJsonObject(rawAttributes) ? rawAttributes : {};
|
|
2986
|
+
const idValue = ctx.edgeIdValue(ids, record.key);
|
|
2987
|
+
const edge = ctx.pushEdge(u, v, kind, ctx.weightOf(attributes), element);
|
|
2988
|
+
ctx.setEdgeId(ids, edge, idValue);
|
|
2989
|
+
ctx.writeNested(ctx.edges, edge, record, attributes, gephi ? GEPHI_EDGE_KEYS : GRAPHOLOGY_EDGE_KEYS, element, {
|
|
2990
|
+
weightFrom: ctx.options.weightFrom,
|
|
2991
|
+
plain: gephi,
|
|
2992
|
+
});
|
|
2993
|
+
if (declared.allowSelfLoops === false && u === v) {
|
|
2994
|
+
violations.selfLoops++;
|
|
2995
|
+
}
|
|
2996
|
+
return `${kind} ${pairKey(u, v, kind === "directed")}`;
|
|
2872
2997
|
}
|
|
2873
2998
|
|
|
2874
2999
|
/**
|
|
@@ -2918,6 +3043,7 @@ function reportGraphologyViolations(
|
|
|
2918
3043
|
* @param dictKey - the key of the nested dict
|
|
2919
3044
|
* @param structural - the element keys that are not attributes
|
|
2920
3045
|
* @param element - the element name
|
|
3046
|
+
* @param plain - write the element-level keys under their own names
|
|
2921
3047
|
* @returns the node index, or -1 when skipped
|
|
2922
3048
|
*/
|
|
2923
3049
|
function pushNestedNode(
|
|
@@ -2927,6 +3053,7 @@ function pushNestedNode(
|
|
|
2927
3053
|
dictKey: string,
|
|
2928
3054
|
structural: ReadonlySet<string>,
|
|
2929
3055
|
element: string,
|
|
3056
|
+
plain = false,
|
|
2930
3057
|
): number {
|
|
2931
3058
|
const index = ctx.pushNode(id, element);
|
|
2932
3059
|
if (index < 0) {
|
|
@@ -2942,7 +3069,7 @@ function pushNestedNode(
|
|
|
2942
3069
|
{ element },
|
|
2943
3070
|
);
|
|
2944
3071
|
}
|
|
2945
|
-
ctx.writeNested(ctx.nodes, index, record, isJsonObject(dict) ? dict : {}, structural,
|
|
3072
|
+
ctx.writeNested(ctx.nodes, index, record, isJsonObject(dict) ? dict : {}, structural, element, { plain });
|
|
2946
3073
|
} catch (err) {
|
|
2947
3074
|
ctx.report.recordError(err, { element: String(id) });
|
|
2948
3075
|
}
|
|
@@ -2989,7 +3116,7 @@ function importJgf(ctx: ImportContext, root: JsonRecord): void {
|
|
|
2989
3116
|
anyHas(nodeRecords, "label"),
|
|
2990
3117
|
);
|
|
2991
3118
|
if (isJsonObject(nodesRaw)) {
|
|
2992
|
-
for (const key of
|
|
3119
|
+
for (const key of jgfNodeKeys(ctx, root, graph, nodesRaw)) {
|
|
2993
3120
|
pushJgfKeyedNode(ctx, key, nodesRaw[key], labelColumn);
|
|
2994
3121
|
}
|
|
2995
3122
|
} else {
|
|
@@ -3126,7 +3253,7 @@ function jgfEdgeWriter(ctx: ImportContext, edges: readonly unknown[], hyperedges
|
|
|
3126
3253
|
element,
|
|
3127
3254
|
});
|
|
3128
3255
|
}
|
|
3129
|
-
ctx.writeNested(ctx.edges, edge, record, dict, structural, ctx.options.weightFrom
|
|
3256
|
+
ctx.writeNested(ctx.edges, edge, record, dict, structural, element, { weightFrom: ctx.options.weightFrom });
|
|
3130
3257
|
};
|
|
3131
3258
|
}
|
|
3132
3259
|
|
|
@@ -3167,6 +3294,26 @@ function readJgfEdge(
|
|
|
3167
3294
|
}
|
|
3168
3295
|
}
|
|
3169
3296
|
|
|
3297
|
+
/**
|
|
3298
|
+
* The keys of a JGF nodes object in the file's order, which is the nodes' order: JSON.parse puts integer-like keys
|
|
3299
|
+
* first, so then they are read from the text.
|
|
3300
|
+
* @param ctx - the context, holding the text
|
|
3301
|
+
* @param root - the document
|
|
3302
|
+
* @param graph - the graph object read
|
|
3303
|
+
* @param nodes - its nodes object
|
|
3304
|
+
* @returns the keys
|
|
3305
|
+
*/
|
|
3306
|
+
function jgfNodeKeys(ctx: ImportContext, root: JsonRecord, graph: JsonRecord, nodes: JsonRecord): string[] {
|
|
3307
|
+
const keys = Object.keys(nodes);
|
|
3308
|
+
if (!keys.some((k) => ARRAY_INDEX.test(k))) {
|
|
3309
|
+
return keys;
|
|
3310
|
+
}
|
|
3311
|
+
const path = isJsonObject(root.graph)
|
|
3312
|
+
? ["graph", "nodes"]
|
|
3313
|
+
: ["graphs", (root.graphs as unknown[]).indexOf(graph), "nodes"];
|
|
3314
|
+
return keysInTextOrder(ctx.text, path) ?? keys;
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3170
3317
|
/**
|
|
3171
3318
|
* The graph object of a JGF document: `graph`, or the graph of `graphs` that graphIndex /
|
|
3172
3319
|
* graphName choose (chooseGraph()).
|
|
@@ -3344,7 +3491,7 @@ function pushJgfNode(
|
|
|
3344
3491
|
element,
|
|
3345
3492
|
});
|
|
3346
3493
|
}
|
|
3347
|
-
ctx.writeNested(ctx.nodes, index, record, isJsonObject(metadata) ? metadata : {}, JGF_NODE_KEYS,
|
|
3494
|
+
ctx.writeNested(ctx.nodes, index, record, isJsonObject(metadata) ? metadata : {}, JGF_NODE_KEYS, element);
|
|
3348
3495
|
} catch (err) {
|
|
3349
3496
|
ctx.report.recordError(err, { element: String(id) });
|
|
3350
3497
|
}
|
|
@@ -3561,69 +3708,63 @@ function distinctMembers(ctx: ImportContext, members: readonly NodeId[], element
|
|
|
3561
3708
|
function importCytoscape(ctx: ImportContext, root: unknown): void {
|
|
3562
3709
|
const { elements, extra } = cytoscapeRoot(ctx, root);
|
|
3563
3710
|
const { nodes, edges } = cytoscapeSections(ctx, elements);
|
|
3711
|
+
const generatedIds = cytoscapeGeneratedIds(nodes);
|
|
3712
|
+
ctx.styleSource = {
|
|
3713
|
+
dialect: "cytoscape",
|
|
3714
|
+
block: blockOf(root),
|
|
3715
|
+
elements: elements ?? [],
|
|
3716
|
+
style: isJsonObject(root) ? root.style : undefined,
|
|
3717
|
+
generatedBy: isJsonObject(root) && typeof root.generated_by === "string" ? root.generated_by : null,
|
|
3718
|
+
generatedIds,
|
|
3719
|
+
};
|
|
3564
3720
|
ctx.setHeader(ctx.defaultDirected("cytoscape"));
|
|
3565
3721
|
ctx.reportNodeIdFrom("cytoscape", "data.id");
|
|
3566
3722
|
ctx.sink.reserve(nodes.length, edges.length);
|
|
3567
3723
|
|
|
3568
|
-
const
|
|
3569
|
-
items.some((item) => isJsonObject(item) && isJsonObject(item.data) && item.data[key] !== undefined);
|
|
3570
|
-
const reading: CytoscapeNodeReading = {
|
|
3571
|
-
positionColumn: ctx.nodes.declareIf(
|
|
3572
|
-
{
|
|
3573
|
-
name: POSITION_COLUMN,
|
|
3574
|
-
dtype: "f64",
|
|
3575
|
-
components: 3,
|
|
3576
|
-
role: "position",
|
|
3577
|
-
mutable: true,
|
|
3578
|
-
nullable: true,
|
|
3579
|
-
// 3D layouts written by Cytoscape-compatible tools carry a z
|
|
3580
|
-
extra: { sourceDims: nodes.some(hasPositionZ) ? 3 : 2, units: "file" },
|
|
3581
|
-
origin: { format: "json", namespace: "cytoscape" },
|
|
3582
|
-
},
|
|
3583
|
-
anyHas(nodes, "position"),
|
|
3584
|
-
),
|
|
3585
|
-
classesColumn: ctx.nodes.declareIf(
|
|
3586
|
-
{ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true },
|
|
3587
|
-
anyHas(nodes, "classes"),
|
|
3588
|
-
),
|
|
3589
|
-
parents: [],
|
|
3590
|
-
point: [0, 0, 0],
|
|
3591
|
-
};
|
|
3592
|
-
const parentColumn = ctx.nodes.declareIf(
|
|
3593
|
-
{ name: PARENT_COLUMN, dtype: "u32", role: "parent", refersTo: "node", nullable: true },
|
|
3594
|
-
dataHas(nodes, "parent"),
|
|
3595
|
-
);
|
|
3724
|
+
const columns = cytoscapeNodeColumns(ctx, nodes);
|
|
3596
3725
|
const edgeClassesColumn = ctx.edges.declareIf(
|
|
3597
3726
|
{ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true },
|
|
3598
3727
|
anyHas(edges, "classes"),
|
|
3599
3728
|
);
|
|
3600
|
-
|
|
3729
|
+
const parents: CytoscapeParentRef[] = [];
|
|
3601
3730
|
for (let i = 0; i < nodes.length; i++) {
|
|
3602
|
-
|
|
3731
|
+
const element = `nodes[${i}]`;
|
|
3732
|
+
const record = nodes[i];
|
|
3733
|
+
if (isJsonObject(record) && isJsonObject(record.data)) {
|
|
3734
|
+
// Cytoscape.js gives a node without data.id an id of its own
|
|
3735
|
+
const raw = record.data.id === undefined ? generatedIds.get(record) : record.data.id;
|
|
3736
|
+
importCytoscapeNode(ctx, record, record.data, raw, element, columns, parents);
|
|
3737
|
+
} else {
|
|
3738
|
+
ctx.badElement("node", element, "an element with a data object");
|
|
3739
|
+
}
|
|
3603
3740
|
}
|
|
3604
|
-
resolveCytoscapeParents(ctx,
|
|
3741
|
+
resolveCytoscapeParents(ctx, parents, columns.parent);
|
|
3605
3742
|
throwIfAborted(ctx.options.signal);
|
|
3606
3743
|
|
|
3607
3744
|
importCytoscapeEdges(ctx, edges, edgeClassesColumn);
|
|
3608
3745
|
ctx.setMeta({ dialect: "cytoscape", cytoscape: extra }, ctx.weightOriginPatch());
|
|
3609
3746
|
}
|
|
3610
3747
|
|
|
3611
|
-
/**
|
|
3612
|
-
interface
|
|
3613
|
-
readonly
|
|
3614
|
-
readonly
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3748
|
+
/** A Cytoscape node's `data.parent`, resolved once every node is known. */
|
|
3749
|
+
interface CytoscapeParentRef {
|
|
3750
|
+
readonly index: number;
|
|
3751
|
+
readonly parent: NodeId;
|
|
3752
|
+
readonly element: string;
|
|
3753
|
+
}
|
|
3754
|
+
|
|
3755
|
+
/** The node columns a Cytoscape document fills besides its data. */
|
|
3756
|
+
interface CytoscapeNodeColumns {
|
|
3757
|
+
readonly position: ColumnHandle;
|
|
3758
|
+
readonly classes: ColumnHandle;
|
|
3759
|
+
readonly parent: ColumnHandle;
|
|
3619
3760
|
}
|
|
3620
3761
|
|
|
3621
3762
|
/**
|
|
3622
|
-
*
|
|
3623
|
-
*
|
|
3763
|
+
* A Cytoscape document's elements, and its other top-level keys (kept in meta); its `data` is written as graph
|
|
3764
|
+
* attributes.
|
|
3624
3765
|
* @param ctx - the context
|
|
3625
|
-
* @param root - the document
|
|
3626
|
-
* @returns the elements and the other
|
|
3766
|
+
* @param root - the document: an object, or an array of elements
|
|
3767
|
+
* @returns the elements and the other keys (undefined when there are none)
|
|
3627
3768
|
*/
|
|
3628
3769
|
function cytoscapeRoot(ctx: ImportContext, root: unknown): { elements: unknown; extra?: Record<string, unknown> } {
|
|
3629
3770
|
if (Array.isArray(root)) {
|
|
@@ -3635,32 +3776,73 @@ function cytoscapeRoot(ctx: ImportContext, root: unknown): { elements: unknown;
|
|
|
3635
3776
|
`a Cytoscape document must be an object or an array, found ${describe(root)}`,
|
|
3636
3777
|
);
|
|
3637
3778
|
}
|
|
3638
|
-
|
|
3779
|
+
const object = root as JsonRecord;
|
|
3780
|
+
ctx.writeGraphDict(object.data, "data");
|
|
3639
3781
|
const rest: Record<string, unknown> = {};
|
|
3640
|
-
for (const key of Object.keys(
|
|
3641
|
-
if (key !== "elements" && key !== "data") {
|
|
3642
|
-
rest[key] =
|
|
3782
|
+
for (const key of Object.keys(object)) {
|
|
3783
|
+
if (key !== "elements" && key !== "data" && key !== STYLE_BLOCK_NAME) {
|
|
3784
|
+
rest[key] = object[key];
|
|
3643
3785
|
}
|
|
3644
3786
|
}
|
|
3645
|
-
return Object.keys(rest).length > 0 ? { elements:
|
|
3787
|
+
return Object.keys(rest).length > 0 ? { elements: object.elements, extra: rest } : { elements: object.elements };
|
|
3646
3788
|
}
|
|
3647
3789
|
|
|
3648
3790
|
/**
|
|
3649
|
-
*
|
|
3650
|
-
*
|
|
3791
|
+
* Declare the position, classes and parent columns the node records use.
|
|
3792
|
+
* @param ctx - the context
|
|
3793
|
+
* @param nodes - the node records
|
|
3794
|
+
* @returns the columns
|
|
3795
|
+
*/
|
|
3796
|
+
function cytoscapeNodeColumns(ctx: ImportContext, nodes: readonly unknown[]): CytoscapeNodeColumns {
|
|
3797
|
+
const dataHas = (items: readonly unknown[], key: string): boolean =>
|
|
3798
|
+
items.some((item) => isJsonObject(item) && isJsonObject(item.data) && item.data[key] !== undefined);
|
|
3799
|
+
return {
|
|
3800
|
+
position: ctx.nodes.declareIf(
|
|
3801
|
+
{
|
|
3802
|
+
name: POSITION_COLUMN,
|
|
3803
|
+
dtype: "f64",
|
|
3804
|
+
components: 3,
|
|
3805
|
+
role: "position",
|
|
3806
|
+
mutable: true,
|
|
3807
|
+
nullable: true,
|
|
3808
|
+
// 3D layouts written by Cytoscape-compatible tools carry a z
|
|
3809
|
+
extra: { sourceDims: nodes.some(hasPositionZ) ? 3 : 2, units: "file" },
|
|
3810
|
+
origin: { format: "json", namespace: "cytoscape" },
|
|
3811
|
+
},
|
|
3812
|
+
anyHas(nodes, "position"),
|
|
3813
|
+
),
|
|
3814
|
+
classes: ctx.nodes.declareIf(
|
|
3815
|
+
{ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true },
|
|
3816
|
+
anyHas(nodes, "classes"),
|
|
3817
|
+
),
|
|
3818
|
+
parent: ctx.nodes.declareIf(
|
|
3819
|
+
{ name: PARENT_COLUMN, dtype: "u32", role: "parent", refersTo: "node", nullable: true },
|
|
3820
|
+
dataHas(nodes, "parent"),
|
|
3821
|
+
),
|
|
3822
|
+
};
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
/**
|
|
3826
|
+
* Add one Cytoscape node: its data as attributes (its parent kept for later), its position, classes and element keys.
|
|
3651
3827
|
* @param ctx - the context
|
|
3652
|
-
* @param reading - the read's state
|
|
3653
3828
|
* @param record - the element
|
|
3829
|
+
* @param data - its data object
|
|
3830
|
+
* @param raw - its raw id
|
|
3654
3831
|
* @param element - the element name
|
|
3832
|
+
* @param columns - the node columns
|
|
3833
|
+
* @param parents - receives its parent link
|
|
3655
3834
|
*/
|
|
3656
|
-
function
|
|
3835
|
+
function importCytoscapeNode(
|
|
3836
|
+
ctx: ImportContext,
|
|
3837
|
+
record: JsonRecord,
|
|
3838
|
+
data: JsonRecord,
|
|
3839
|
+
raw: unknown,
|
|
3840
|
+
element: string,
|
|
3841
|
+
columns: CytoscapeNodeColumns,
|
|
3842
|
+
parents: CytoscapeParentRef[],
|
|
3843
|
+
): void {
|
|
3657
3844
|
const { report } = ctx;
|
|
3658
|
-
|
|
3659
|
-
ctx.badElement("node", element, "an element with a data object");
|
|
3660
|
-
return;
|
|
3661
|
-
}
|
|
3662
|
-
const { data } = record;
|
|
3663
|
-
const id = ctx.coerceId(hasKey(data, "id") ? data.id : undefined, element);
|
|
3845
|
+
const id = ctx.coerceId(raw, element);
|
|
3664
3846
|
if (id === null) {
|
|
3665
3847
|
ctx.countSkipped("node");
|
|
3666
3848
|
return;
|
|
@@ -3678,9 +3860,18 @@ function readCytoscapeNode(ctx: ImportContext, reading: CytoscapeNodeReading, re
|
|
|
3678
3860
|
);
|
|
3679
3861
|
}
|
|
3680
3862
|
try {
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3863
|
+
for (const key of Object.keys(data).filter((k) => k !== "id")) {
|
|
3864
|
+
if (key !== "parent") {
|
|
3865
|
+
ctx.nodes.write(index, key, data[key], SUFFIX.data, element);
|
|
3866
|
+
continue;
|
|
3867
|
+
}
|
|
3868
|
+
const parent = data.parent === null ? null : ctx.coerceId(data.parent, `${element}.data.parent`);
|
|
3869
|
+
if (parent !== null) {
|
|
3870
|
+
parents.push({ index, parent, element });
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
writeCytoscapePosition(ctx, columns.position, index, record.position, element);
|
|
3874
|
+
writeClasses(ctx, ctx.nodes, columns.classes, index, record.classes, element);
|
|
3684
3875
|
writeElementKeys(ctx.nodes, index, record, element);
|
|
3685
3876
|
} catch (err) {
|
|
3686
3877
|
report.recordError(err, { element: String(id) });
|
|
@@ -3688,51 +3879,17 @@ function readCytoscapeNode(ctx: ImportContext, reading: CytoscapeNodeReading, re
|
|
|
3688
3879
|
}
|
|
3689
3880
|
|
|
3690
3881
|
/**
|
|
3691
|
-
* Write a Cytoscape node's
|
|
3692
|
-
*
|
|
3693
|
-
* @param reading - the read's state
|
|
3694
|
-
* @param index - the node index
|
|
3695
|
-
* @param data - the element's data object
|
|
3696
|
-
* @param element - the element name
|
|
3697
|
-
*/
|
|
3698
|
-
function writeCytoscapeData(
|
|
3699
|
-
ctx: ImportContext,
|
|
3700
|
-
reading: CytoscapeNodeReading,
|
|
3701
|
-
index: number,
|
|
3702
|
-
data: JsonRecord,
|
|
3703
|
-
element: string,
|
|
3704
|
-
): void {
|
|
3705
|
-
for (const key of Object.keys(data)) {
|
|
3706
|
-
if (key === "id") {
|
|
3707
|
-
continue;
|
|
3708
|
-
}
|
|
3709
|
-
if (key !== "parent") {
|
|
3710
|
-
ctx.nodes.write(index, key, data[key], SUFFIX.data, element);
|
|
3711
|
-
continue;
|
|
3712
|
-
}
|
|
3713
|
-
const raw = data.parent;
|
|
3714
|
-
if (raw === undefined || raw === null) {
|
|
3715
|
-
continue;
|
|
3716
|
-
}
|
|
3717
|
-
const parent = ctx.coerceId(raw, `${element}.data.parent`);
|
|
3718
|
-
if (parent !== null) {
|
|
3719
|
-
reading.parents.push({ index, parent, element });
|
|
3720
|
-
}
|
|
3721
|
-
}
|
|
3722
|
-
}
|
|
3723
|
-
|
|
3724
|
-
/**
|
|
3725
|
-
* Write a Cytoscape node's `position` ({ x, y, z? }, y flipped to grow upward); any other value is
|
|
3726
|
-
* reported.
|
|
3882
|
+
* Write a Cytoscape node's position, y flipped (Cytoscape's y grows downward; positions are stored y-up and the
|
|
3883
|
+
* exporter flips back).
|
|
3727
3884
|
* @param ctx - the context
|
|
3728
|
-
* @param
|
|
3729
|
-
* @param index - the node
|
|
3885
|
+
* @param column - the position column
|
|
3886
|
+
* @param index - the node
|
|
3730
3887
|
* @param position - the element's position
|
|
3731
3888
|
* @param element - the element name
|
|
3732
3889
|
*/
|
|
3733
3890
|
function writeCytoscapePosition(
|
|
3734
3891
|
ctx: ImportContext,
|
|
3735
|
-
|
|
3892
|
+
column: ColumnHandle,
|
|
3736
3893
|
index: number,
|
|
3737
3894
|
position: unknown,
|
|
3738
3895
|
element: string,
|
|
@@ -3742,12 +3899,7 @@ function writeCytoscapePosition(
|
|
|
3742
3899
|
}
|
|
3743
3900
|
const z = isJsonObject(position) ? (position.z ?? 0) : 0;
|
|
3744
3901
|
if (isJsonObject(position) && isCoordinate(position.x) && isCoordinate(position.y) && isCoordinate(z)) {
|
|
3745
|
-
|
|
3746
|
-
point[0] = position.x;
|
|
3747
|
-
// Cytoscape's y grows downward; positions are stored y-up (the exporter flips back)
|
|
3748
|
-
point[1] = flipY(position.y);
|
|
3749
|
-
point[2] = z;
|
|
3750
|
-
ctx.nodes.set(reading.positionColumn, index, point);
|
|
3902
|
+
ctx.nodes.set(column, index, [position.x, flipY(position.y), z]);
|
|
3751
3903
|
} else {
|
|
3752
3904
|
ctx.report.error(
|
|
3753
3905
|
"validation-error",
|
|
@@ -3783,7 +3935,11 @@ function resolveCytoscapeParents(
|
|
|
3783
3935
|
// the last link per child (a duplicate node's later parent wins), child -> its link
|
|
3784
3936
|
const linkOf = new Map<number, ParentLink>();
|
|
3785
3937
|
for (let order = 0; order < parents.length; order++) {
|
|
3786
|
-
const { index,
|
|
3938
|
+
const { index, element } = parents[order];
|
|
3939
|
+
let { parent } = parents[order];
|
|
3940
|
+
if (ctx.sink.indexOf(parent) === INVALID_INDEX) {
|
|
3941
|
+
parent = ctx.textualId(parent, element) ?? parent;
|
|
3942
|
+
}
|
|
3787
3943
|
const parentIndex = ctx.sink.indexOf(parent);
|
|
3788
3944
|
if (parentIndex === INVALID_INDEX) {
|
|
3789
3945
|
report.error(
|
|
@@ -3897,8 +4053,8 @@ function importCytoscapeEdge(
|
|
|
3897
4053
|
): void {
|
|
3898
4054
|
const { ids, kind, edgeClassesColumn } = shared;
|
|
3899
4055
|
try {
|
|
3900
|
-
const u = ctx.
|
|
3901
|
-
const v = ctx.
|
|
4056
|
+
const u = ctx.textualId(data.source, `${element}.data.source`);
|
|
4057
|
+
const v = ctx.textualId(data.target, `${element}.data.target`);
|
|
3902
4058
|
if (u === null || v === null) {
|
|
3903
4059
|
ctx.countSkipped("edge");
|
|
3904
4060
|
return;
|
|
@@ -3963,6 +4119,49 @@ function cytoscapeSections(
|
|
|
3963
4119
|
return report.fail(JSON_ISSUE.SHAPE, `elements must be an object or an array, found ${describe(elements)}`);
|
|
3964
4120
|
}
|
|
3965
4121
|
|
|
4122
|
+
/**
|
|
4123
|
+
* The ids Cytoscape.js would give the nodes without a `data.id`: `n<k>` by their place among the nodes, made unique
|
|
4124
|
+
* against every id the file states.
|
|
4125
|
+
* @param nodes - the node elements
|
|
4126
|
+
* @returns the generated id of each node element without one
|
|
4127
|
+
*/
|
|
4128
|
+
function cytoscapeGeneratedIds(nodes: readonly unknown[]): Map<object, string> {
|
|
4129
|
+
const out = new Map<object, string>();
|
|
4130
|
+
const missing = nodes.filter(
|
|
4131
|
+
(n): n is JsonRecord => isJsonObject(n) && isJsonObject(n.data) && n.data.id === undefined,
|
|
4132
|
+
);
|
|
4133
|
+
if (missing.length === 0) {
|
|
4134
|
+
return out;
|
|
4135
|
+
}
|
|
4136
|
+
const taken = new Set<string>();
|
|
4137
|
+
for (const n of nodes) {
|
|
4138
|
+
const id = isJsonObject(n) && isJsonObject(n.data) ? n.data.id : undefined;
|
|
4139
|
+
if (typeof id === "string" || typeof id === "number") {
|
|
4140
|
+
taken.add(String(id));
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
for (let k = 0; k < missing.length; k++) {
|
|
4144
|
+
let id = `n${String(k)}`;
|
|
4145
|
+
let j = 2;
|
|
4146
|
+
while (taken.has(id)) {
|
|
4147
|
+
id = `n${String(k)}_${String(j)}`;
|
|
4148
|
+
j++;
|
|
4149
|
+
}
|
|
4150
|
+
taken.add(id);
|
|
4151
|
+
out.set(missing[k], id);
|
|
4152
|
+
}
|
|
4153
|
+
return out;
|
|
4154
|
+
}
|
|
4155
|
+
|
|
4156
|
+
/**
|
|
4157
|
+
* A `graphty.styles` block a document holds as a top-level key, or null.
|
|
4158
|
+
* @param root - the document
|
|
4159
|
+
* @returns the block's text
|
|
4160
|
+
*/
|
|
4161
|
+
function blockOf(root: unknown): string | null {
|
|
4162
|
+
return isJsonObject(root) && typeof root[STYLE_BLOCK_NAME] === "string" ? root[STYLE_BLOCK_NAME] : null;
|
|
4163
|
+
}
|
|
4164
|
+
|
|
3966
4165
|
/**
|
|
3967
4166
|
* Whether a Cytoscape element's position has a numeric z.
|
|
3968
4167
|
* @param element - the element
|
|
@@ -4073,6 +4272,7 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
4073
4272
|
"typedefs",
|
|
4074
4273
|
]),
|
|
4075
4274
|
extensions: Object.freeze([".json"]),
|
|
4275
|
+
positions: jsonPositions,
|
|
4076
4276
|
mimeTypes: Object.freeze(["application/json"]),
|
|
4077
4277
|
|
|
4078
4278
|
/**
|
|
@@ -4112,7 +4312,7 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
4112
4312
|
const report = new ImportReportBuilder("json", resolved.errorLimit);
|
|
4113
4313
|
const text = await readText(input, report, { ...resolved, bomlessUtf16: true });
|
|
4114
4314
|
const { root, dialect } = documentOf(parseDocument(text, report), json, report);
|
|
4115
|
-
readGraph(root, dialect, sink, report, resolved, json, options);
|
|
4315
|
+
readGraph({ root, dialect, text }, sink, report, resolved, json, options);
|
|
4116
4316
|
return report.finish();
|
|
4117
4317
|
},
|
|
4118
4318
|
|
|
@@ -4158,17 +4358,35 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
4158
4358
|
const reports: ImportReport[] = [];
|
|
4159
4359
|
for (let i = 0; i < Math.max(graphs, 1); i++) {
|
|
4160
4360
|
const report = i === 0 ? first : new ImportReportBuilder("json", resolved.errorLimit);
|
|
4161
|
-
readGraph(
|
|
4361
|
+
readGraph(
|
|
4362
|
+
{ root, dialect, text },
|
|
4363
|
+
sinkFor(i),
|
|
4364
|
+
report,
|
|
4365
|
+
resolved,
|
|
4366
|
+
{ ...json, graphIndex: i, all: true },
|
|
4367
|
+
options,
|
|
4368
|
+
);
|
|
4162
4369
|
reports.push(report.finish());
|
|
4163
4370
|
}
|
|
4164
4371
|
return reports;
|
|
4165
4372
|
},
|
|
4373
|
+
|
|
4374
|
+
/**
|
|
4375
|
+
* The document's styles, read by its dialect's reader (Cytoscape.js stylesheets, graphology and vis.js keys,
|
|
4376
|
+
* NetworkX's defaults); a `graphty.styles` block gives back the rules graphty wrote.
|
|
4377
|
+
* @param snapshot - the graph the import built
|
|
4378
|
+
* @param context - the work budget and what the import kept
|
|
4379
|
+
* @returns the styles, or null when the dialect has none
|
|
4380
|
+
*/
|
|
4381
|
+
styles: jsonStyles,
|
|
4166
4382
|
});
|
|
4167
4383
|
|
|
4168
4384
|
/**
|
|
4169
4385
|
* Read one graph of a parsed document into a sink.
|
|
4170
|
-
* @param
|
|
4171
|
-
* @param
|
|
4386
|
+
* @param doc - the parsed document, its dialect and its text
|
|
4387
|
+
* @param doc.root - the parsed document
|
|
4388
|
+
* @param doc.dialect - its dialect
|
|
4389
|
+
* @param doc.text - the document's text
|
|
4172
4390
|
* @param sink - the sink
|
|
4173
4391
|
* @param report - the graph's report
|
|
4174
4392
|
* @param resolved - the resolved common options
|
|
@@ -4176,19 +4394,21 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
4176
4394
|
* @param options - the caller's options, for the sink and unused-option checks
|
|
4177
4395
|
*/
|
|
4178
4396
|
function readGraph(
|
|
4179
|
-
root: unknown,
|
|
4180
|
-
dialect: JsonImportDialect,
|
|
4397
|
+
doc: { readonly root: unknown; readonly dialect: JsonImportDialect; readonly text: string },
|
|
4181
4398
|
sink: GraphSink,
|
|
4182
4399
|
report: ImportReportBuilder,
|
|
4183
4400
|
resolved: ResolvedImportOptions,
|
|
4184
4401
|
json: ResolvedJsonOptions,
|
|
4185
4402
|
options: (JsonImportOptions & CommonImportOptions) | undefined,
|
|
4186
4403
|
): void {
|
|
4404
|
+
const { root, dialect, text } = doc;
|
|
4187
4405
|
const ctx = new ImportContext(sink, report, resolved, json, options?.defaultDirected !== undefined);
|
|
4406
|
+
ctx.text = text;
|
|
4188
4407
|
// the obographs reader refuses missing endpoints itself, so addMissingNodes false holds on any sink
|
|
4189
4408
|
reportSinkOptions(sink, options, report, dialect === "obographs");
|
|
4190
4409
|
reportUnusedOptions(options, report, USED_OPTIONS);
|
|
4191
4410
|
readDialect(ctx, root, dialect);
|
|
4411
|
+
report.keepForStyles(ctx.styleSource ?? { dialect, block: blockOf(root) });
|
|
4192
4412
|
ctx.reportDangling();
|
|
4193
4413
|
ctx.nodes.reportNullOnly();
|
|
4194
4414
|
ctx.edges.reportNullOnly();
|