@graphty/graph-io 0.3.43 → 0.3.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-RVtXVetw.js → exporter-quPUCBiC.js} +1805 -287
- 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 +292 -412
- 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 +20 -5
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts +8 -1
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +251 -131
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gexf/index.d.ts +8 -0
- package/dist/src/formats/gexf/index.d.ts.map +1 -1
- package/dist/src/formats/gexf/index.js +10 -2
- package/dist/src/formats/gexf/index.js.map +1 -1
- package/dist/src/formats/gexf/schema.d.ts +5 -0
- package/dist/src/formats/gexf/schema.d.ts.map +1 -1
- package/dist/src/formats/gexf/schema.js +5 -0
- package/dist/src/formats/gexf/schema.js.map +1 -1
- package/dist/src/formats/gexf/styles.d.ts +74 -0
- package/dist/src/formats/gexf/styles.d.ts.map +1 -0
- package/dist/src/formats/gexf/styles.js +604 -0
- package/dist/src/formats/gexf/styles.js.map +1 -0
- package/dist/src/formats/gml/exporter.d.ts +1 -5
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +19 -8
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +4 -0
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/index.d.ts +2 -1
- package/dist/src/formats/gml/index.d.ts.map +1 -1
- package/dist/src/formats/gml/index.js +2 -1
- package/dist/src/formats/gml/index.js.map +1 -1
- package/dist/src/formats/gml/positions.d.ts +14 -0
- package/dist/src/formats/gml/positions.d.ts.map +1 -0
- package/dist/src/formats/gml/positions.js +46 -0
- package/dist/src/formats/gml/positions.js.map +1 -0
- package/dist/src/formats/gml/styles.d.ts +59 -0
- package/dist/src/formats/gml/styles.d.ts.map +1 -0
- package/dist/src/formats/gml/styles.js +1056 -0
- package/dist/src/formats/gml/styles.js.map +1 -0
- package/dist/src/formats/gml/syntax.d.ts +11 -0
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +151 -62
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/constants.d.ts +4 -0
- package/dist/src/formats/graphml/constants.d.ts.map +1 -1
- package/dist/src/formats/graphml/constants.js +5 -1
- package/dist/src/formats/graphml/constants.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +133 -65
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts +4 -1
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +100 -44
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/index.d.ts +1 -0
- package/dist/src/formats/graphml/index.d.ts.map +1 -1
- package/dist/src/formats/graphml/index.js +1 -0
- package/dist/src/formats/graphml/index.js.map +1 -1
- package/dist/src/formats/graphml/styles-plain.d.ts +19 -0
- package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -0
- package/dist/src/formats/graphml/styles-plain.js +187 -0
- package/dist/src/formats/graphml/styles-plain.js.map +1 -0
- package/dist/src/formats/graphml/styles-yed.d.ts +179 -0
- package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -0
- package/dist/src/formats/graphml/styles-yed.js +1086 -0
- package/dist/src/formats/graphml/styles-yed.js.map +1 -0
- package/dist/src/formats/graphml/styles-yfiles3.d.ts +19 -0
- package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -0
- package/dist/src/formats/graphml/styles-yfiles3.js +508 -0
- package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -0
- package/dist/src/formats/graphml/tree.d.ts.map +1 -1
- package/dist/src/formats/graphml/tree.js +34 -19
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts +16 -2
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +83 -12
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts +5 -0
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +68 -25
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts +22 -3
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +439 -264
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/positions.d.ts +16 -0
- package/dist/src/formats/json/positions.d.ts.map +1 -0
- package/dist/src/formats/json/positions.js +44 -0
- package/dist/src/formats/json/positions.js.map +1 -0
- package/dist/src/formats/json/styles.d.ts +102 -0
- package/dist/src/formats/json/styles.d.ts.map +1 -0
- package/dist/src/formats/json/styles.js +1753 -0
- package/dist/src/formats/json/styles.js.map +1 -0
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +2 -1
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts +2 -0
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +142 -76
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts +4 -0
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +60 -6
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/index.d.ts +2 -0
- package/dist/src/formats/pajek/index.d.ts.map +1 -1
- package/dist/src/formats/pajek/index.js +2 -0
- package/dist/src/formats/pajek/index.js.map +1 -1
- package/dist/src/formats/pajek/styles.d.ts +50 -0
- package/dist/src/formats/pajek/styles.d.ts.map +1 -0
- package/dist/src/formats/pajek/styles.js +569 -0
- package/dist/src/formats/pajek/styles.js.map +1 -0
- package/dist/src/formats/pajek/syntax.d.ts +6 -0
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +6 -0
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
- package/dist/src/formats/xgmml/columns.js +34 -9
- package/dist/src/formats/xgmml/columns.js.map +1 -1
- package/dist/src/formats/xgmml/constants.d.ts +2 -0
- package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
- package/dist/src/formats/xgmml/constants.js +3 -1
- package/dist/src/formats/xgmml/constants.js.map +1 -1
- package/dist/src/formats/xgmml/document.d.ts +12 -8
- package/dist/src/formats/xgmml/document.d.ts.map +1 -1
- package/dist/src/formats/xgmml/document.js +56 -13
- package/dist/src/formats/xgmml/document.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +12 -6
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts +3 -1
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +22 -9
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts +2 -1
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +31 -24
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/formats/xgmml/styles.d.ts +104 -0
- package/dist/src/formats/xgmml/styles.d.ts.map +1 -0
- package/dist/src/formats/xgmml/styles.js +310 -0
- package/dist/src/formats/xgmml/styles.js.map +1 -0
- package/dist/src/index.d.ts +9 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -7
- package/dist/src/index.js.map +1 -1
- package/dist/src/positions/codes.d.ts +31 -0
- package/dist/src/positions/codes.d.ts.map +1 -0
- package/dist/src/positions/codes.js +26 -0
- package/dist/src/positions/codes.js.map +1 -0
- package/dist/src/positions/convert.d.ts +58 -0
- package/dist/src/positions/convert.d.ts.map +1 -0
- package/dist/src/positions/convert.js +107 -0
- package/dist/src/positions/convert.js.map +1 -0
- package/dist/src/positions/extract.d.ts +27 -0
- package/dist/src/positions/extract.d.ts.map +1 -0
- package/dist/src/positions/extract.js +526 -0
- package/dist/src/positions/extract.js.map +1 -0
- package/dist/src/positions/index.d.ts +10 -0
- package/dist/src/positions/index.d.ts.map +1 -0
- package/dist/src/positions/index.js +9 -0
- package/dist/src/positions/index.js.map +1 -0
- package/dist/src/positions/types.d.ts +208 -0
- package/dist/src/positions/types.d.ts.map +1 -0
- package/dist/src/positions/types.js +10 -0
- package/dist/src/positions/types.js.map +1 -0
- package/dist/src/registry.d.ts +30 -0
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +130 -9
- package/dist/src/registry.js.map +1 -1
- package/dist/src/styles/annotations.d.ts +132 -0
- package/dist/src/styles/annotations.d.ts.map +1 -0
- package/dist/src/styles/annotations.js +320 -0
- package/dist/src/styles/annotations.js.map +1 -0
- package/dist/src/styles/budget.d.ts +45 -0
- package/dist/src/styles/budget.d.ts.map +1 -0
- package/dist/src/styles/budget.js +58 -0
- package/dist/src/styles/budget.js.map +1 -0
- package/dist/src/styles/canon/bends.d.ts +32 -0
- package/dist/src/styles/canon/bends.d.ts.map +1 -0
- package/dist/src/styles/canon/bends.js +74 -0
- package/dist/src/styles/canon/bends.js.map +1 -0
- package/dist/src/styles/canon/color-tables.d.ts +22 -0
- package/dist/src/styles/canon/color-tables.d.ts.map +1 -0
- package/dist/src/styles/canon/color-tables.js +369 -0
- package/dist/src/styles/canon/color-tables.js.map +1 -0
- package/dist/src/styles/canon/color.d.ts +66 -0
- package/dist/src/styles/canon/color.d.ts.map +1 -0
- package/dist/src/styles/canon/color.js +548 -0
- package/dist/src/styles/canon/color.js.map +1 -0
- package/dist/src/styles/canon/enums.d.ts +25 -0
- package/dist/src/styles/canon/enums.d.ts.map +1 -0
- package/dist/src/styles/canon/enums.js +187 -0
- package/dist/src/styles/canon/enums.js.map +1 -0
- package/dist/src/styles/canon/graphviz.d.ts +48 -0
- package/dist/src/styles/canon/graphviz.d.ts.map +1 -0
- package/dist/src/styles/canon/graphviz.js +160 -0
- package/dist/src/styles/canon/graphviz.js.map +1 -0
- package/dist/src/styles/canon/index.d.ts +13 -0
- package/dist/src/styles/canon/index.d.ts.map +1 -0
- package/dist/src/styles/canon/index.js +13 -0
- package/dist/src/styles/canon/index.js.map +1 -0
- package/dist/src/styles/canon/number.d.ts +59 -0
- package/dist/src/styles/canon/number.d.ts.map +1 -0
- package/dist/src/styles/canon/number.js +142 -0
- package/dist/src/styles/canon/number.js.map +1 -0
- package/dist/src/styles/canon/site.d.ts +51 -0
- package/dist/src/styles/canon/site.d.ts.map +1 -0
- package/dist/src/styles/canon/site.js +63 -0
- package/dist/src/styles/canon/site.js.map +1 -0
- package/dist/src/styles/canon/text.d.ts +61 -0
- package/dist/src/styles/canon/text.d.ts.map +1 -0
- package/dist/src/styles/canon/text.js +199 -0
- package/dist/src/styles/canon/text.js.map +1 -0
- package/dist/src/styles/codes.d.ts +95 -0
- package/dist/src/styles/codes.d.ts.map +1 -0
- package/dist/src/styles/codes.js +97 -0
- package/dist/src/styles/codes.js.map +1 -0
- package/dist/src/styles/cyjs/cascade.d.ts +134 -0
- package/dist/src/styles/cyjs/cascade.d.ts.map +1 -0
- package/dist/src/styles/cyjs/cascade.js +2252 -0
- package/dist/src/styles/cyjs/cascade.js.map +1 -0
- package/dist/src/styles/cyjs/defaults-table.d.ts +25 -0
- package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -0
- package/dist/src/styles/cyjs/defaults-table.js +76 -0
- package/dist/src/styles/cyjs/defaults-table.js.map +1 -0
- package/dist/src/styles/cyjs/emit.d.ts +82 -0
- package/dist/src/styles/cyjs/emit.d.ts.map +1 -0
- package/dist/src/styles/cyjs/emit.js +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 +27 -11
- package/src/formats/gexf/importer.ts +308 -158
- package/src/formats/gexf/index.ts +10 -0
- package/src/formats/gexf/schema.ts +5 -0
- package/src/formats/gexf/styles.ts +719 -0
- package/src/formats/gml/exporter.ts +33 -14
- package/src/formats/gml/importer.ts +4 -0
- package/src/formats/gml/index.ts +2 -1
- package/src/formats/gml/positions.ts +52 -0
- package/src/formats/gml/styles.ts +1248 -0
- package/src/formats/gml/syntax.ts +173 -62
- package/src/formats/graphml/constants.ts +5 -0
- package/src/formats/graphml/exporter.ts +163 -71
- package/src/formats/graphml/importer.ts +119 -45
- package/src/formats/graphml/index.ts +1 -0
- package/src/formats/graphml/styles-plain.ts +236 -0
- package/src/formats/graphml/styles-yed.ts +1221 -0
- package/src/formats/graphml/styles-yfiles3.ts +573 -0
- package/src/formats/graphml/tree.ts +36 -20
- package/src/formats/graphml/yfiles.ts +89 -13
- package/src/formats/json/exporter.ts +76 -26
- package/src/formats/json/importer.ts +541 -322
- package/src/formats/json/positions.ts +50 -0
- package/src/formats/json/styles.ts +2082 -0
- package/src/formats/neo4j/exporter.ts +2 -1
- package/src/formats/pajek/exporter.ts +195 -106
- package/src/formats/pajek/importer.ts +78 -9
- package/src/formats/pajek/index.ts +2 -0
- package/src/formats/pajek/styles.ts +685 -0
- package/src/formats/pajek/syntax.ts +7 -0
- package/src/formats/xgmml/columns.ts +39 -9
- package/src/formats/xgmml/constants.ts +3 -0
- package/src/formats/xgmml/document.ts +54 -12
- package/src/formats/xgmml/emit.ts +11 -6
- package/src/formats/xgmml/exporter.ts +32 -13
- package/src/formats/xgmml/importer.ts +43 -29
- package/src/formats/xgmml/styles.ts +400 -0
- package/src/index.ts +16 -0
- package/src/positions/codes.ts +32 -0
- package/src/positions/convert.ts +123 -0
- package/src/positions/extract.ts +619 -0
- package/src/positions/index.ts +23 -0
- package/src/positions/types.ts +221 -0
- package/src/registry.ts +179 -8
- package/src/styles/annotations.ts +478 -0
- package/src/styles/budget.ts +60 -0
- package/src/styles/canon/bends.ts +84 -0
- package/src/styles/canon/color-tables.ts +386 -0
- package/src/styles/canon/color.ts +624 -0
- package/src/styles/canon/enums.ts +211 -0
- package/src/styles/canon/graphviz.ts +189 -0
- package/src/styles/canon/index.ts +13 -0
- package/src/styles/canon/number.ts +152 -0
- package/src/styles/canon/site.ts +87 -0
- package/src/styles/canon/text.ts +221 -0
- package/src/styles/codes.ts +104 -0
- package/src/styles/cyjs/cascade.ts +2670 -0
- package/src/styles/cyjs/defaults-table.ts +82 -0
- package/src/styles/cyjs/emit.ts +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-RVtXVetw.js.map +0 -1
- package/dist/chunks/importer-BbNb7eNg.js.map +0 -1
- package/dist/chunks/importer-C98dpSH4.js.map +0 -1
- package/dist/chunks/importer-D794XUTe.js.map +0 -1
- package/dist/chunks/importer-Dz7tI8Sa.js.map +0 -1
- package/dist/chunks/importer-Ht7LNg8L.js.map +0 -1
- package/dist/chunks/importer-WrGRZb0N.js.map +0 -1
- package/dist/chunks/importer-xZ-3suY1.js.map +0 -1
- package/dist/chunks/json-elements-DKSqpNW1.js.map +0 -1
|
@@ -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
|
}
|
|
@@ -1774,10 +1822,16 @@ function reportUnreadKeys(ctx: ImportContext, record: JsonRecord, known: Readonl
|
|
|
1774
1822
|
}
|
|
1775
1823
|
|
|
1776
1824
|
/** The keys of a vis.js document the reader reads. */
|
|
1777
|
-
const VIS_DOCUMENT_KEYS: ReadonlySet<string> = new Set(["nodes", "edges"]);
|
|
1825
|
+
const VIS_DOCUMENT_KEYS: ReadonlySet<string> = new Set(["nodes", "edges", STYLE_BLOCK_NAME]);
|
|
1778
1826
|
|
|
1779
1827
|
/** The keys of a graphology serialization the reader reads. */
|
|
1780
|
-
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
|
+
]);
|
|
1781
1835
|
|
|
1782
1836
|
/** The keys of a JGF graph object the reader reads. */
|
|
1783
1837
|
const JGF_GRAPH_KEYS: ReadonlySet<string> = new Set([
|
|
@@ -1821,10 +1875,61 @@ function isIndexBelow(value: unknown, bound: number): boolean {
|
|
|
1821
1875
|
*/
|
|
1822
1876
|
function importNodeLink(ctx: ImportContext, root: JsonRecord, dialect: "node-link" | "d3"): void {
|
|
1823
1877
|
const { report, json } = ctx;
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
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);
|
|
1899
|
+
}
|
|
1900
|
+
if (indexLinks && !positional && json.indexLinks === "auto") {
|
|
1901
|
+
reportAmbiguousIndexLinks(ctx, nodeList, edgeList, nodeIdKey);
|
|
1827
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;
|
|
1828
1933
|
const nodes = arraySection(root.nodes, "nodes", report);
|
|
1829
1934
|
const edges = arraySection(root[edgesKey], edgesKey, report);
|
|
1830
1935
|
if (nodes === null) {
|
|
@@ -1844,101 +1949,100 @@ function importNodeLink(ctx: ImportContext, root: JsonRecord, dialect: "node-lin
|
|
|
1844
1949
|
{ element: edgesKey },
|
|
1845
1950
|
);
|
|
1846
1951
|
}
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
ctx.setHeader(directed);
|
|
1850
|
-
ctx.writeGraphDict(root.graph, "graph");
|
|
1851
|
-
reportUnreadKeys(ctx, root, new Set(["nodes", edgesKey, "directed", "multigraph", "graph"]), "");
|
|
1852
|
-
|
|
1853
|
-
const nodeList = nodes ?? [];
|
|
1854
|
-
const edgeList = edges ?? [];
|
|
1855
|
-
ctx.sink.reserve(nodeList.length, edgeList.length);
|
|
1952
|
+
return { nodeList: nodes ?? [], edgeList: edges ?? [] };
|
|
1953
|
+
}
|
|
1856
1954
|
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
nodeIdKey === "id"
|
|
1869
|
-
? 'nodeIdFrom "label": no node has a label or name key; ids are read from "id"'
|
|
1870
|
-
: 'nodeIdFrom "label": no node has a "label" key; ids are read from "name"',
|
|
1871
|
-
{ element: "nodeIdFrom" },
|
|
1872
|
-
);
|
|
1873
|
-
}
|
|
1874
|
-
if (nodeIdKey === null) {
|
|
1875
|
-
if (nodeList.length > 0) {
|
|
1876
|
-
positional = true;
|
|
1877
|
-
report.warning(
|
|
1878
|
-
"missing-value",
|
|
1879
|
-
JSON_ISSUE.POSITIONAL_NODES,
|
|
1880
|
-
"no node has an id or name key; array positions are the node ids",
|
|
1881
|
-
{ element: "nodes" },
|
|
1882
|
-
);
|
|
1883
|
-
} else {
|
|
1884
|
-
nodeIdKey = "id";
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
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;
|
|
1887
1966
|
}
|
|
1888
|
-
if (
|
|
1889
|
-
nodeIdKey
|
|
1967
|
+
if (json.nodeIdKey !== null) {
|
|
1968
|
+
return json.nodeIdKey;
|
|
1890
1969
|
}
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
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
|
+
);
|
|
1898
1982
|
}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
reportAmbiguousIndexLinks(ctx, nodeList, edgeList, nodeIdKey);
|
|
1983
|
+
if (found !== null || nodeList.length === 0) {
|
|
1984
|
+
return found ?? "id";
|
|
1902
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
|
+
}
|
|
1903
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;
|
|
1904
2010
|
for (let i = 0; i < nodeList.length; i++) {
|
|
1905
2011
|
const element = `nodes[${i}]`;
|
|
1906
2012
|
const record = nodeList[i];
|
|
1907
2013
|
let pushed: NodeId | null = null;
|
|
1908
|
-
if (
|
|
1909
|
-
ctx
|
|
2014
|
+
if (isJsonObject(record)) {
|
|
2015
|
+
pushed = pushNodeLinkNode(ctx, record, i, nodeIdKey);
|
|
1910
2016
|
} else {
|
|
1911
|
-
|
|
1912
|
-
if (nodeIdKey === null) {
|
|
1913
|
-
id = i;
|
|
1914
|
-
} else {
|
|
1915
|
-
id = ctx.coerceId(hasKey(record, nodeIdKey) ? record[nodeIdKey] : undefined, element);
|
|
1916
|
-
}
|
|
1917
|
-
if (id === null) {
|
|
1918
|
-
ctx.countSkipped("node");
|
|
1919
|
-
} else {
|
|
1920
|
-
const index = ctx.pushNode(id, element);
|
|
1921
|
-
if (index >= 0) {
|
|
1922
|
-
pushed = id;
|
|
1923
|
-
writeFlat(ctx.nodes, index, record, id, (key) => key !== nodeIdKey);
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
2017
|
+
ctx.badElement("node", element);
|
|
1926
2018
|
}
|
|
1927
2019
|
positionIds?.push(pushed);
|
|
1928
2020
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
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;
|
|
1942
2046
|
}
|
|
1943
2047
|
|
|
1944
2048
|
/**
|
|
@@ -2276,6 +2380,7 @@ function importVis(ctx: ImportContext, root: JsonRecord): void {
|
|
|
2276
2380
|
{ element: "arrows" },
|
|
2277
2381
|
);
|
|
2278
2382
|
}
|
|
2383
|
+
ctx.styleSource = { dialect: "vis", block: blockOf(root), options: root.options, groups: root.groups };
|
|
2279
2384
|
ctx.setMeta(
|
|
2280
2385
|
{ dialect: "vis", nodeIdKey, sourceKey: sourceKey ?? undefined, targetKey: targetKey ?? undefined },
|
|
2281
2386
|
ctx.weightOriginPatch(),
|
|
@@ -2682,58 +2787,34 @@ function importGraphology(ctx: ImportContext, root: JsonRecord): void {
|
|
|
2682
2787
|
});
|
|
2683
2788
|
}
|
|
2684
2789
|
const type = graphologyType(ctx, options);
|
|
2685
|
-
ctx.setHeader(
|
|
2790
|
+
ctx.setHeader(type === "mixed" ? mostlyDirected(ctx, edges) : type === "directed");
|
|
2686
2791
|
const multi = hasKey(options, "multi") ? flagOf(options.multi, "options.multi", false, report) : null;
|
|
2687
2792
|
const allowSelfLoops = hasKey(options, "allowSelfLoops")
|
|
2688
2793
|
? flagOf(options.allowSelfLoops, "options.allowSelfLoops", true, report)
|
|
2689
2794
|
: undefined;
|
|
2690
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 };
|
|
2691
2801
|
ctx.reportNodeIdFrom("graphology", "the node key");
|
|
2692
2802
|
ctx.sink.reserve(nodes.length, edges.length);
|
|
2693
2803
|
|
|
2694
|
-
|
|
2695
|
-
pushGraphologyNode(ctx, nodes[i], `nodes[${i}]`);
|
|
2696
|
-
}
|
|
2804
|
+
importGraphologyNodes(ctx, nodes, gephi);
|
|
2697
2805
|
throwIfAborted(ctx.options.signal);
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
ids: ctx.declareEdgeIds(edges, (edge) => edge.key, "key", true),
|
|
2701
|
-
type,
|
|
2702
|
-
allowSelfLoops,
|
|
2703
|
-
// what the edges do against the declared options: graphology itself refuses such a document
|
|
2704
|
-
violations: { selfLoops: 0, parallels: 0, flags: 0 },
|
|
2705
|
-
pairs: multi === false ? new Set<string>() : null,
|
|
2706
|
-
};
|
|
2707
|
-
for (let i = 0; i < edges.length; i++) {
|
|
2708
|
-
readGraphologyEdge(ctx, reading, edges[i], `edges[${i}]`);
|
|
2709
|
-
}
|
|
2710
|
-
reportGraphologyViolations(ctx, reading.violations, type);
|
|
2806
|
+
const violations = importGraphologyEdges(ctx, edges, { type, gephi, multi, allowSelfLoops });
|
|
2807
|
+
reportGraphologyViolations(ctx, violations, type);
|
|
2711
2808
|
ctx.setMeta({ dialect: "graphology", allowSelfLoops }, { declaredMultigraph: multi, ...ctx.weightOriginPatch() });
|
|
2712
2809
|
}
|
|
2713
2810
|
|
|
2714
|
-
/** The graph types a graphology document declares in `options.type`. */
|
|
2715
|
-
type GraphologyType = "directed" | "undirected" | "mixed";
|
|
2716
|
-
|
|
2717
|
-
/** The state of a graphology read, shared by its edges. */
|
|
2718
|
-
interface GraphologyReading {
|
|
2719
|
-
/** The edge id column, from declareEdgeIds(). */
|
|
2720
|
-
readonly ids: EdgeIdColumn | null;
|
|
2721
|
-
readonly type: GraphologyType;
|
|
2722
|
-
/** options.allowSelfLoops, or undefined when absent. */
|
|
2723
|
-
readonly allowSelfLoops: boolean | undefined;
|
|
2724
|
-
/** The edges that contradict the declared options (reportGraphologyViolations()). */
|
|
2725
|
-
readonly violations: { selfLoops: number; parallels: number; flags: number };
|
|
2726
|
-
/** The pairs seen so far under multi false, or null. */
|
|
2727
|
-
readonly pairs: Set<string> | null;
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
2811
|
/**
|
|
2731
|
-
* A graphology document's `options.type
|
|
2812
|
+
* A graphology document's `options.type`: "mixed" when absent or not one of the three (reported).
|
|
2732
2813
|
* @param ctx - the context
|
|
2733
|
-
* @param options - the options
|
|
2814
|
+
* @param options - the document's options
|
|
2734
2815
|
* @returns the type
|
|
2735
2816
|
*/
|
|
2736
|
-
function graphologyType(ctx: ImportContext, options: JsonRecord):
|
|
2817
|
+
function graphologyType(ctx: ImportContext, options: JsonRecord): "directed" | "undirected" | "mixed" {
|
|
2737
2818
|
if (options.type === "directed" || options.type === "undirected" || options.type === "mixed") {
|
|
2738
2819
|
return options.type;
|
|
2739
2820
|
}
|
|
@@ -2749,127 +2830,170 @@ function graphologyType(ctx: ImportContext, options: JsonRecord): GraphologyType
|
|
|
2749
2830
|
}
|
|
2750
2831
|
|
|
2751
2832
|
/**
|
|
2752
|
-
* The header direction of a graphology
|
|
2753
|
-
* 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.
|
|
2754
2834
|
* @param ctx - the context
|
|
2755
|
-
* @param type - options.type
|
|
2756
2835
|
* @param edges - the edge records
|
|
2757
|
-
* @returns
|
|
2836
|
+
* @returns true for directed
|
|
2758
2837
|
*/
|
|
2759
|
-
function
|
|
2760
|
-
if (type !== "mixed") {
|
|
2761
|
-
return type === "directed";
|
|
2762
|
-
}
|
|
2838
|
+
function mostlyDirected(ctx: ImportContext, edges: readonly unknown[]): boolean {
|
|
2763
2839
|
const objects = edges.filter((edge) => isJsonObject(edge)) as JsonRecord[];
|
|
2764
2840
|
const undirectedEdges = objects.filter((edge) => edge.undirected === true).length;
|
|
2765
2841
|
return objects.length === 0 ? ctx.defaultDirected("graphology") : undirectedEdges < objects.length;
|
|
2766
2842
|
}
|
|
2767
2843
|
|
|
2768
2844
|
/**
|
|
2769
|
-
*
|
|
2845
|
+
* Read the graphology node records.
|
|
2770
2846
|
* @param ctx - the context
|
|
2771
|
-
* @param
|
|
2772
|
-
* @param
|
|
2847
|
+
* @param nodes - the node records
|
|
2848
|
+
* @param gephi - whether Gephi wrote them (ids under `id`, display keys beside the attributes)
|
|
2773
2849
|
*/
|
|
2774
|
-
function
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
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);
|
|
2778
2865
|
}
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
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
|
+
}
|
|
2783
2918
|
}
|
|
2784
|
-
|
|
2919
|
+
return violations;
|
|
2785
2920
|
}
|
|
2786
2921
|
|
|
2787
2922
|
/**
|
|
2788
|
-
*
|
|
2923
|
+
* One graphology edge record, when it is an object with both endpoints.
|
|
2789
2924
|
* @param ctx - the context
|
|
2790
|
-
* @param reading - the read's state
|
|
2791
2925
|
* @param record - the edge record
|
|
2792
2926
|
* @param element - the element name
|
|
2927
|
+
* @returns the record, or null when it was reported and is skipped
|
|
2793
2928
|
*/
|
|
2794
|
-
function
|
|
2929
|
+
function graphologyEdgeRecord(ctx: ImportContext, record: unknown, element: string): JsonRecord | null {
|
|
2795
2930
|
if (!isJsonObject(record)) {
|
|
2796
2931
|
ctx.badElement("edge", element);
|
|
2797
|
-
return;
|
|
2932
|
+
return null;
|
|
2798
2933
|
}
|
|
2799
2934
|
if (!present(record, "source") || !present(record, "target")) {
|
|
2800
2935
|
ctx.missingEndpoint(element, present(record, "source") ? "target" : "source");
|
|
2801
|
-
return;
|
|
2802
|
-
}
|
|
2803
|
-
try {
|
|
2804
|
-
const u = ctx.coerceId(record.source, `${element}.source`);
|
|
2805
|
-
const v = ctx.coerceId(record.target, `${element}.target`);
|
|
2806
|
-
if (u === null || v === null) {
|
|
2807
|
-
ctx.countSkipped("edge");
|
|
2808
|
-
return;
|
|
2809
|
-
}
|
|
2810
|
-
const kind = graphologyEdgeKind(ctx, reading, record, element);
|
|
2811
|
-
const attributes = graphologyEdgeAttributes(ctx, record, element);
|
|
2812
|
-
const idValue = ctx.edgeIdValue(reading.ids, record.key);
|
|
2813
|
-
const edge = ctx.pushEdge(u, v, kind, ctx.weightOf(attributes), element);
|
|
2814
|
-
ctx.setEdgeId(reading.ids, edge, idValue);
|
|
2815
|
-
ctx.writeNested(ctx.edges, edge, record, attributes, GRAPHOLOGY_EDGE_KEYS, ctx.options.weightFrom, element);
|
|
2816
|
-
if (reading.allowSelfLoops === false && u === v) {
|
|
2817
|
-
reading.violations.selfLoops++;
|
|
2818
|
-
}
|
|
2819
|
-
if (reading.pairs !== null) {
|
|
2820
|
-
const pair = `${kind} ${pairKey(u, v, kind === "directed")}`;
|
|
2821
|
-
reading.violations.parallels += reading.pairs.has(pair) ? 1 : 0;
|
|
2822
|
-
reading.pairs.add(pair);
|
|
2823
|
-
}
|
|
2824
|
-
} catch (err) {
|
|
2825
|
-
ctx.skip(err, "edge", element);
|
|
2936
|
+
return null;
|
|
2826
2937
|
}
|
|
2938
|
+
return record;
|
|
2827
2939
|
}
|
|
2828
2940
|
|
|
2829
2941
|
/**
|
|
2830
|
-
*
|
|
2831
|
-
* (a contrary flag is counted as a violation).
|
|
2942
|
+
* Add one graphology edge with its id and attributes.
|
|
2832
2943
|
* @param ctx - the context
|
|
2833
|
-
* @param reading - the read's state
|
|
2834
2944
|
* @param record - the edge record
|
|
2835
2945
|
* @param element - the element name
|
|
2836
|
-
* @
|
|
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
|
|
2837
2950
|
*/
|
|
2838
|
-
function
|
|
2951
|
+
function pushGraphologyEdge(
|
|
2839
2952
|
ctx: ImportContext,
|
|
2840
|
-
reading: GraphologyReading,
|
|
2841
2953
|
record: JsonRecord,
|
|
2842
2954
|
element: string,
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
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;
|
|
2847
2966
|
}
|
|
2848
|
-
|
|
2849
|
-
|
|
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
|
+
}
|
|
2850
2975
|
}
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
/**
|
|
2855
|
-
* The `attributes` object of a graphology edge; any other non-null value is reported and read as
|
|
2856
|
-
* no attributes.
|
|
2857
|
-
* @param ctx - the context
|
|
2858
|
-
* @param record - the edge record
|
|
2859
|
-
* @param element - the element name
|
|
2860
|
-
* @returns the attributes
|
|
2861
|
-
*/
|
|
2862
|
-
function graphologyEdgeAttributes(ctx: ImportContext, record: JsonRecord, element: string): JsonRecord {
|
|
2863
|
-
const { attributes } = record;
|
|
2864
|
-
if (attributes !== undefined && attributes !== null && !isJsonObject(attributes)) {
|
|
2865
|
-
ctx.report.error(
|
|
2976
|
+
const { attributes: rawAttributes } = record;
|
|
2977
|
+
if (rawAttributes !== undefined && rawAttributes !== null && !isJsonObject(rawAttributes)) {
|
|
2978
|
+
report.error(
|
|
2866
2979
|
"validation-error",
|
|
2867
2980
|
JSON_ISSUE.BAD_VALUE,
|
|
2868
|
-
`${element}: attributes must be an object, found ${describe(
|
|
2981
|
+
`${element}: attributes must be an object, found ${describe(rawAttributes)}`,
|
|
2869
2982
|
{ element },
|
|
2870
2983
|
);
|
|
2871
2984
|
}
|
|
2872
|
-
|
|
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")}`;
|
|
2873
2997
|
}
|
|
2874
2998
|
|
|
2875
2999
|
/**
|
|
@@ -2919,6 +3043,7 @@ function reportGraphologyViolations(
|
|
|
2919
3043
|
* @param dictKey - the key of the nested dict
|
|
2920
3044
|
* @param structural - the element keys that are not attributes
|
|
2921
3045
|
* @param element - the element name
|
|
3046
|
+
* @param plain - write the element-level keys under their own names
|
|
2922
3047
|
* @returns the node index, or -1 when skipped
|
|
2923
3048
|
*/
|
|
2924
3049
|
function pushNestedNode(
|
|
@@ -2928,6 +3053,7 @@ function pushNestedNode(
|
|
|
2928
3053
|
dictKey: string,
|
|
2929
3054
|
structural: ReadonlySet<string>,
|
|
2930
3055
|
element: string,
|
|
3056
|
+
plain = false,
|
|
2931
3057
|
): number {
|
|
2932
3058
|
const index = ctx.pushNode(id, element);
|
|
2933
3059
|
if (index < 0) {
|
|
@@ -2943,7 +3069,7 @@ function pushNestedNode(
|
|
|
2943
3069
|
{ element },
|
|
2944
3070
|
);
|
|
2945
3071
|
}
|
|
2946
|
-
ctx.writeNested(ctx.nodes, index, record, isJsonObject(dict) ? dict : {}, structural,
|
|
3072
|
+
ctx.writeNested(ctx.nodes, index, record, isJsonObject(dict) ? dict : {}, structural, element, { plain });
|
|
2947
3073
|
} catch (err) {
|
|
2948
3074
|
ctx.report.recordError(err, { element: String(id) });
|
|
2949
3075
|
}
|
|
@@ -2990,7 +3116,7 @@ function importJgf(ctx: ImportContext, root: JsonRecord): void {
|
|
|
2990
3116
|
anyHas(nodeRecords, "label"),
|
|
2991
3117
|
);
|
|
2992
3118
|
if (isJsonObject(nodesRaw)) {
|
|
2993
|
-
for (const key of
|
|
3119
|
+
for (const key of jgfNodeKeys(ctx, root, graph, nodesRaw)) {
|
|
2994
3120
|
pushJgfKeyedNode(ctx, key, nodesRaw[key], labelColumn);
|
|
2995
3121
|
}
|
|
2996
3122
|
} else {
|
|
@@ -3127,7 +3253,7 @@ function jgfEdgeWriter(ctx: ImportContext, edges: readonly unknown[], hyperedges
|
|
|
3127
3253
|
element,
|
|
3128
3254
|
});
|
|
3129
3255
|
}
|
|
3130
|
-
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 });
|
|
3131
3257
|
};
|
|
3132
3258
|
}
|
|
3133
3259
|
|
|
@@ -3168,6 +3294,26 @@ function readJgfEdge(
|
|
|
3168
3294
|
}
|
|
3169
3295
|
}
|
|
3170
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
|
+
|
|
3171
3317
|
/**
|
|
3172
3318
|
* The graph object of a JGF document: `graph`, or the graph of `graphs` that graphIndex /
|
|
3173
3319
|
* graphName choose (chooseGraph()).
|
|
@@ -3345,7 +3491,7 @@ function pushJgfNode(
|
|
|
3345
3491
|
element,
|
|
3346
3492
|
});
|
|
3347
3493
|
}
|
|
3348
|
-
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);
|
|
3349
3495
|
} catch (err) {
|
|
3350
3496
|
ctx.report.recordError(err, { element: String(id) });
|
|
3351
3497
|
}
|
|
@@ -3562,69 +3708,63 @@ function distinctMembers(ctx: ImportContext, members: readonly NodeId[], element
|
|
|
3562
3708
|
function importCytoscape(ctx: ImportContext, root: unknown): void {
|
|
3563
3709
|
const { elements, extra } = cytoscapeRoot(ctx, root);
|
|
3564
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
|
+
};
|
|
3565
3720
|
ctx.setHeader(ctx.defaultDirected("cytoscape"));
|
|
3566
3721
|
ctx.reportNodeIdFrom("cytoscape", "data.id");
|
|
3567
3722
|
ctx.sink.reserve(nodes.length, edges.length);
|
|
3568
3723
|
|
|
3569
|
-
const
|
|
3570
|
-
items.some((item) => isJsonObject(item) && isJsonObject(item.data) && item.data[key] !== undefined);
|
|
3571
|
-
const reading: CytoscapeNodeReading = {
|
|
3572
|
-
positionColumn: ctx.nodes.declareIf(
|
|
3573
|
-
{
|
|
3574
|
-
name: POSITION_COLUMN,
|
|
3575
|
-
dtype: "f64",
|
|
3576
|
-
components: 3,
|
|
3577
|
-
role: "position",
|
|
3578
|
-
mutable: true,
|
|
3579
|
-
nullable: true,
|
|
3580
|
-
// 3D layouts written by Cytoscape-compatible tools carry a z
|
|
3581
|
-
extra: { sourceDims: nodes.some(hasPositionZ) ? 3 : 2, units: "file" },
|
|
3582
|
-
origin: { format: "json", namespace: "cytoscape" },
|
|
3583
|
-
},
|
|
3584
|
-
anyHas(nodes, "position"),
|
|
3585
|
-
),
|
|
3586
|
-
classesColumn: ctx.nodes.declareIf(
|
|
3587
|
-
{ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true },
|
|
3588
|
-
anyHas(nodes, "classes"),
|
|
3589
|
-
),
|
|
3590
|
-
parents: [],
|
|
3591
|
-
point: [0, 0, 0],
|
|
3592
|
-
};
|
|
3593
|
-
const parentColumn = ctx.nodes.declareIf(
|
|
3594
|
-
{ name: PARENT_COLUMN, dtype: "u32", role: "parent", refersTo: "node", nullable: true },
|
|
3595
|
-
dataHas(nodes, "parent"),
|
|
3596
|
-
);
|
|
3724
|
+
const columns = cytoscapeNodeColumns(ctx, nodes);
|
|
3597
3725
|
const edgeClassesColumn = ctx.edges.declareIf(
|
|
3598
3726
|
{ name: CLASSES_COLUMN, dtype: "list", itemDtype: "string", role: "classes", nullable: true },
|
|
3599
3727
|
anyHas(edges, "classes"),
|
|
3600
3728
|
);
|
|
3601
|
-
|
|
3729
|
+
const parents: CytoscapeParentRef[] = [];
|
|
3602
3730
|
for (let i = 0; i < nodes.length; i++) {
|
|
3603
|
-
|
|
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
|
+
}
|
|
3604
3740
|
}
|
|
3605
|
-
resolveCytoscapeParents(ctx,
|
|
3741
|
+
resolveCytoscapeParents(ctx, parents, columns.parent);
|
|
3606
3742
|
throwIfAborted(ctx.options.signal);
|
|
3607
3743
|
|
|
3608
3744
|
importCytoscapeEdges(ctx, edges, edgeClassesColumn);
|
|
3609
3745
|
ctx.setMeta({ dialect: "cytoscape", cytoscape: extra }, ctx.weightOriginPatch());
|
|
3610
3746
|
}
|
|
3611
3747
|
|
|
3612
|
-
/**
|
|
3613
|
-
interface
|
|
3614
|
-
readonly
|
|
3615
|
-
readonly
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
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;
|
|
3620
3760
|
}
|
|
3621
3761
|
|
|
3622
3762
|
/**
|
|
3623
|
-
*
|
|
3624
|
-
*
|
|
3763
|
+
* A Cytoscape document's elements, and its other top-level keys (kept in meta); its `data` is written as graph
|
|
3764
|
+
* attributes.
|
|
3625
3765
|
* @param ctx - the context
|
|
3626
|
-
* @param root - the document
|
|
3627
|
-
* @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)
|
|
3628
3768
|
*/
|
|
3629
3769
|
function cytoscapeRoot(ctx: ImportContext, root: unknown): { elements: unknown; extra?: Record<string, unknown> } {
|
|
3630
3770
|
if (Array.isArray(root)) {
|
|
@@ -3636,32 +3776,73 @@ function cytoscapeRoot(ctx: ImportContext, root: unknown): { elements: unknown;
|
|
|
3636
3776
|
`a Cytoscape document must be an object or an array, found ${describe(root)}`,
|
|
3637
3777
|
);
|
|
3638
3778
|
}
|
|
3639
|
-
|
|
3779
|
+
const object = root as JsonRecord;
|
|
3780
|
+
ctx.writeGraphDict(object.data, "data");
|
|
3640
3781
|
const rest: Record<string, unknown> = {};
|
|
3641
|
-
for (const key of Object.keys(
|
|
3642
|
-
if (key !== "elements" && key !== "data") {
|
|
3643
|
-
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];
|
|
3644
3785
|
}
|
|
3645
3786
|
}
|
|
3646
|
-
return Object.keys(rest).length > 0 ? { elements:
|
|
3787
|
+
return Object.keys(rest).length > 0 ? { elements: object.elements, extra: rest } : { elements: object.elements };
|
|
3647
3788
|
}
|
|
3648
3789
|
|
|
3649
3790
|
/**
|
|
3650
|
-
*
|
|
3651
|
-
*
|
|
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.
|
|
3652
3827
|
* @param ctx - the context
|
|
3653
|
-
* @param reading - the read's state
|
|
3654
3828
|
* @param record - the element
|
|
3829
|
+
* @param data - its data object
|
|
3830
|
+
* @param raw - its raw id
|
|
3655
3831
|
* @param element - the element name
|
|
3832
|
+
* @param columns - the node columns
|
|
3833
|
+
* @param parents - receives its parent link
|
|
3656
3834
|
*/
|
|
3657
|
-
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 {
|
|
3658
3844
|
const { report } = ctx;
|
|
3659
|
-
|
|
3660
|
-
ctx.badElement("node", element, "an element with a data object");
|
|
3661
|
-
return;
|
|
3662
|
-
}
|
|
3663
|
-
const { data } = record;
|
|
3664
|
-
const id = ctx.coerceId(hasKey(data, "id") ? data.id : undefined, element);
|
|
3845
|
+
const id = ctx.coerceId(raw, element);
|
|
3665
3846
|
if (id === null) {
|
|
3666
3847
|
ctx.countSkipped("node");
|
|
3667
3848
|
return;
|
|
@@ -3679,9 +3860,18 @@ function readCytoscapeNode(ctx: ImportContext, reading: CytoscapeNodeReading, re
|
|
|
3679
3860
|
);
|
|
3680
3861
|
}
|
|
3681
3862
|
try {
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
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);
|
|
3685
3875
|
writeElementKeys(ctx.nodes, index, record, element);
|
|
3686
3876
|
} catch (err) {
|
|
3687
3877
|
report.recordError(err, { element: String(id) });
|
|
@@ -3689,51 +3879,17 @@ function readCytoscapeNode(ctx: ImportContext, reading: CytoscapeNodeReading, re
|
|
|
3689
3879
|
}
|
|
3690
3880
|
|
|
3691
3881
|
/**
|
|
3692
|
-
* Write a Cytoscape node's
|
|
3693
|
-
*
|
|
3694
|
-
* @param reading - the read's state
|
|
3695
|
-
* @param index - the node index
|
|
3696
|
-
* @param data - the element's data object
|
|
3697
|
-
* @param element - the element name
|
|
3698
|
-
*/
|
|
3699
|
-
function writeCytoscapeData(
|
|
3700
|
-
ctx: ImportContext,
|
|
3701
|
-
reading: CytoscapeNodeReading,
|
|
3702
|
-
index: number,
|
|
3703
|
-
data: JsonRecord,
|
|
3704
|
-
element: string,
|
|
3705
|
-
): void {
|
|
3706
|
-
for (const key of Object.keys(data)) {
|
|
3707
|
-
if (key === "id") {
|
|
3708
|
-
continue;
|
|
3709
|
-
}
|
|
3710
|
-
if (key !== "parent") {
|
|
3711
|
-
ctx.nodes.write(index, key, data[key], SUFFIX.data, element);
|
|
3712
|
-
continue;
|
|
3713
|
-
}
|
|
3714
|
-
const raw = data.parent;
|
|
3715
|
-
if (raw === undefined || raw === null) {
|
|
3716
|
-
continue;
|
|
3717
|
-
}
|
|
3718
|
-
const parent = ctx.coerceId(raw, `${element}.data.parent`);
|
|
3719
|
-
if (parent !== null) {
|
|
3720
|
-
reading.parents.push({ index, parent, element });
|
|
3721
|
-
}
|
|
3722
|
-
}
|
|
3723
|
-
}
|
|
3724
|
-
|
|
3725
|
-
/**
|
|
3726
|
-
* Write a Cytoscape node's `position` ({ x, y, z? }, y flipped to grow upward); any other value is
|
|
3727
|
-
* 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).
|
|
3728
3884
|
* @param ctx - the context
|
|
3729
|
-
* @param
|
|
3730
|
-
* @param index - the node
|
|
3885
|
+
* @param column - the position column
|
|
3886
|
+
* @param index - the node
|
|
3731
3887
|
* @param position - the element's position
|
|
3732
3888
|
* @param element - the element name
|
|
3733
3889
|
*/
|
|
3734
3890
|
function writeCytoscapePosition(
|
|
3735
3891
|
ctx: ImportContext,
|
|
3736
|
-
|
|
3892
|
+
column: ColumnHandle,
|
|
3737
3893
|
index: number,
|
|
3738
3894
|
position: unknown,
|
|
3739
3895
|
element: string,
|
|
@@ -3743,12 +3899,7 @@ function writeCytoscapePosition(
|
|
|
3743
3899
|
}
|
|
3744
3900
|
const z = isJsonObject(position) ? (position.z ?? 0) : 0;
|
|
3745
3901
|
if (isJsonObject(position) && isCoordinate(position.x) && isCoordinate(position.y) && isCoordinate(z)) {
|
|
3746
|
-
|
|
3747
|
-
point[0] = position.x;
|
|
3748
|
-
// Cytoscape's y grows downward; positions are stored y-up (the exporter flips back)
|
|
3749
|
-
point[1] = flipY(position.y);
|
|
3750
|
-
point[2] = z;
|
|
3751
|
-
ctx.nodes.set(reading.positionColumn, index, point);
|
|
3902
|
+
ctx.nodes.set(column, index, [position.x, flipY(position.y), z]);
|
|
3752
3903
|
} else {
|
|
3753
3904
|
ctx.report.error(
|
|
3754
3905
|
"validation-error",
|
|
@@ -3784,7 +3935,11 @@ function resolveCytoscapeParents(
|
|
|
3784
3935
|
// the last link per child (a duplicate node's later parent wins), child -> its link
|
|
3785
3936
|
const linkOf = new Map<number, ParentLink>();
|
|
3786
3937
|
for (let order = 0; order < parents.length; order++) {
|
|
3787
|
-
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
|
+
}
|
|
3788
3943
|
const parentIndex = ctx.sink.indexOf(parent);
|
|
3789
3944
|
if (parentIndex === INVALID_INDEX) {
|
|
3790
3945
|
report.error(
|
|
@@ -3898,8 +4053,8 @@ function importCytoscapeEdge(
|
|
|
3898
4053
|
): void {
|
|
3899
4054
|
const { ids, kind, edgeClassesColumn } = shared;
|
|
3900
4055
|
try {
|
|
3901
|
-
const u = ctx.
|
|
3902
|
-
const v = ctx.
|
|
4056
|
+
const u = ctx.textualId(data.source, `${element}.data.source`);
|
|
4057
|
+
const v = ctx.textualId(data.target, `${element}.data.target`);
|
|
3903
4058
|
if (u === null || v === null) {
|
|
3904
4059
|
ctx.countSkipped("edge");
|
|
3905
4060
|
return;
|
|
@@ -3964,6 +4119,49 @@ function cytoscapeSections(
|
|
|
3964
4119
|
return report.fail(JSON_ISSUE.SHAPE, `elements must be an object or an array, found ${describe(elements)}`);
|
|
3965
4120
|
}
|
|
3966
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
|
+
|
|
3967
4165
|
/**
|
|
3968
4166
|
* Whether a Cytoscape element's position has a numeric z.
|
|
3969
4167
|
* @param element - the element
|
|
@@ -4074,6 +4272,7 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
4074
4272
|
"typedefs",
|
|
4075
4273
|
]),
|
|
4076
4274
|
extensions: Object.freeze([".json"]),
|
|
4275
|
+
positions: jsonPositions,
|
|
4077
4276
|
mimeTypes: Object.freeze(["application/json"]),
|
|
4078
4277
|
|
|
4079
4278
|
/**
|
|
@@ -4113,7 +4312,7 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
4113
4312
|
const report = new ImportReportBuilder("json", resolved.errorLimit);
|
|
4114
4313
|
const text = await readText(input, report, { ...resolved, bomlessUtf16: true });
|
|
4115
4314
|
const { root, dialect } = documentOf(parseDocument(text, report), json, report);
|
|
4116
|
-
readGraph(root, dialect, sink, report, resolved, json, options);
|
|
4315
|
+
readGraph({ root, dialect, text }, sink, report, resolved, json, options);
|
|
4117
4316
|
return report.finish();
|
|
4118
4317
|
},
|
|
4119
4318
|
|
|
@@ -4159,17 +4358,35 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
4159
4358
|
const reports: ImportReport[] = [];
|
|
4160
4359
|
for (let i = 0; i < Math.max(graphs, 1); i++) {
|
|
4161
4360
|
const report = i === 0 ? first : new ImportReportBuilder("json", resolved.errorLimit);
|
|
4162
|
-
readGraph(
|
|
4361
|
+
readGraph(
|
|
4362
|
+
{ root, dialect, text },
|
|
4363
|
+
sinkFor(i),
|
|
4364
|
+
report,
|
|
4365
|
+
resolved,
|
|
4366
|
+
{ ...json, graphIndex: i, all: true },
|
|
4367
|
+
options,
|
|
4368
|
+
);
|
|
4163
4369
|
reports.push(report.finish());
|
|
4164
4370
|
}
|
|
4165
4371
|
return reports;
|
|
4166
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,
|
|
4167
4382
|
});
|
|
4168
4383
|
|
|
4169
4384
|
/**
|
|
4170
4385
|
* Read one graph of a parsed document into a sink.
|
|
4171
|
-
* @param
|
|
4172
|
-
* @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
|
|
4173
4390
|
* @param sink - the sink
|
|
4174
4391
|
* @param report - the graph's report
|
|
4175
4392
|
* @param resolved - the resolved common options
|
|
@@ -4177,19 +4394,21 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
4177
4394
|
* @param options - the caller's options, for the sink and unused-option checks
|
|
4178
4395
|
*/
|
|
4179
4396
|
function readGraph(
|
|
4180
|
-
root: unknown,
|
|
4181
|
-
dialect: JsonImportDialect,
|
|
4397
|
+
doc: { readonly root: unknown; readonly dialect: JsonImportDialect; readonly text: string },
|
|
4182
4398
|
sink: GraphSink,
|
|
4183
4399
|
report: ImportReportBuilder,
|
|
4184
4400
|
resolved: ResolvedImportOptions,
|
|
4185
4401
|
json: ResolvedJsonOptions,
|
|
4186
4402
|
options: (JsonImportOptions & CommonImportOptions) | undefined,
|
|
4187
4403
|
): void {
|
|
4404
|
+
const { root, dialect, text } = doc;
|
|
4188
4405
|
const ctx = new ImportContext(sink, report, resolved, json, options?.defaultDirected !== undefined);
|
|
4406
|
+
ctx.text = text;
|
|
4189
4407
|
// the obographs reader refuses missing endpoints itself, so addMissingNodes false holds on any sink
|
|
4190
4408
|
reportSinkOptions(sink, options, report, dialect === "obographs");
|
|
4191
4409
|
reportUnusedOptions(options, report, USED_OPTIONS);
|
|
4192
4410
|
readDialect(ctx, root, dialect);
|
|
4411
|
+
report.keepForStyles(ctx.styleSource ?? { dialect, block: blockOf(root) });
|
|
4193
4412
|
ctx.reportDangling();
|
|
4194
4413
|
ctx.nodes.reportNullOnly();
|
|
4195
4414
|
ctx.edges.reportNullOnly();
|