@graphty/graph-io 0.3.24 → 0.3.26
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/README.md +49 -414
- package/dist/chunks/children-CL3Cy0ez.js.map +1 -1
- package/dist/chunks/choose-BbJI55dx.js +56 -0
- package/dist/chunks/choose-BbJI55dx.js.map +1 -0
- package/dist/chunks/constants-BCsFsMj4.js +231 -0
- package/dist/chunks/constants-BCsFsMj4.js.map +1 -0
- package/dist/chunks/{escape-B-tkk_Cl.js → escape-D0g6NNUy.js} +382 -92
- package/dist/chunks/escape-D0g6NNUy.js.map +1 -0
- package/dist/chunks/export-BTI4yQgy.js +4333 -0
- package/dist/chunks/export-BTI4yQgy.js.map +1 -0
- package/dist/chunks/exporter-BMqk5oxW.js +3465 -0
- package/dist/chunks/exporter-BMqk5oxW.js.map +1 -0
- package/dist/chunks/{importer-eUREhBGj.js → exporter-Cra2GYcN.js} +4969 -3201
- package/dist/chunks/exporter-Cra2GYcN.js.map +1 -0
- package/dist/chunks/{exporter-BGrsWamJ.js → exporter-Dxh231ZF.js} +38 -229
- package/dist/chunks/exporter-Dxh231ZF.js.map +1 -0
- package/dist/chunks/{exporter-DIeGJXAZ.js → exporter-tne475KK.js} +22 -20
- package/dist/chunks/exporter-tne475KK.js.map +1 -0
- package/dist/chunks/importer-90YWhJoF.js +2811 -0
- package/dist/chunks/importer-90YWhJoF.js.map +1 -0
- package/dist/chunks/{importer-D_e7e7LX.js → importer-Bcl1d4xH.js} +398 -145
- package/dist/chunks/importer-Bcl1d4xH.js.map +1 -0
- package/dist/chunks/{importer-BWY2FFc5.js → importer-BfIsp1f9.js} +255 -77
- package/dist/chunks/importer-BfIsp1f9.js.map +1 -0
- package/dist/chunks/{importer-zGLo8Dg_.js → importer-C_TEA1zJ.js} +484 -157
- package/dist/chunks/importer-C_TEA1zJ.js.map +1 -0
- package/dist/chunks/{importer-DET1tg6s.js → importer-DCjOvO7U.js} +1687 -310
- package/dist/chunks/importer-DCjOvO7U.js.map +1 -0
- package/dist/chunks/{importer-vi3bSdtw.js → importer-DfVu6j59.js} +509 -76
- package/dist/chunks/importer-DfVu6j59.js.map +1 -0
- package/dist/chunks/{importer-DIrbbnAf.js → importer-Dku_VAIY.js} +585 -226
- package/dist/chunks/importer-Dku_VAIY.js.map +1 -0
- package/dist/chunks/json-elements-3tnyt5uZ.js +1225 -0
- package/dist/chunks/json-elements-3tnyt5uZ.js.map +1 -0
- package/dist/chunks/ontology-export-B9Tcf37D.js +357 -0
- package/dist/chunks/ontology-export-B9Tcf37D.js.map +1 -0
- package/dist/chunks/{records-dbRkxwaq.js → records-CQXT__mY.js} +298 -114
- package/dist/chunks/records-CQXT__mY.js.map +1 -0
- package/dist/chunks/{text-Dr0Ifpag.js → text-DmcUs5_Q.js} +29 -11
- package/dist/chunks/text-DmcUs5_Q.js.map +1 -0
- package/dist/chunks/{writer-C7flM-Ih.js → writer-DxjgHwSe.js} +19 -1
- package/dist/chunks/writer-DxjgHwSe.js.map +1 -0
- package/dist/csv.js +951 -261
- package/dist/csv.js.map +1 -1
- package/dist/cx.js +6 -3
- package/dist/cx2.js +2 -2
- package/dist/cys.js +6 -3
- package/dist/dot.js +1 -1
- package/dist/gexf.js +554 -122
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +342 -85
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +1105 -213
- package/dist/graph-io.js.map +1 -1
- package/dist/graphml.js +3 -2
- package/dist/graphml.js.map +1 -1
- package/dist/json.js +3 -2
- package/dist/neo4j.js +564 -157
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.js +6 -3
- package/dist/pajek.js +1 -1
- package/dist/src/children.d.ts +18 -6
- package/dist/src/children.d.ts.map +1 -1
- package/dist/src/children.js +6 -3
- package/dist/src/children.js.map +1 -1
- package/dist/src/common/attributes.d.ts +29 -11
- package/dist/src/common/attributes.d.ts.map +1 -1
- package/dist/src/common/attributes.js +25 -7
- package/dist/src/common/attributes.js.map +1 -1
- package/dist/src/common/cell-budget.d.ts +9 -2
- package/dist/src/common/cell-budget.d.ts.map +1 -1
- package/dist/src/common/cell-budget.js +11 -3
- package/dist/src/common/cell-budget.js.map +1 -1
- package/dist/src/common/choose.d.ts +19 -0
- package/dist/src/common/choose.d.ts.map +1 -0
- package/dist/src/common/choose.js +72 -0
- package/dist/src/common/choose.js.map +1 -0
- package/dist/src/common/codes.d.ts +456 -66
- package/dist/src/common/codes.d.ts.map +1 -1
- package/dist/src/common/codes.js +458 -66
- package/dist/src/common/codes.js.map +1 -1
- package/dist/src/common/compare.d.ts +81 -0
- package/dist/src/common/compare.d.ts.map +1 -0
- package/dist/src/common/compare.js +394 -0
- package/dist/src/common/compare.js.map +1 -0
- package/dist/src/common/cx-export.d.ts +123 -0
- package/dist/src/common/cx-export.d.ts.map +1 -0
- package/dist/src/common/cx-export.js +334 -0
- package/dist/src/common/cx-export.js.map +1 -0
- package/dist/src/common/declared-types.d.ts +22 -2
- package/dist/src/common/declared-types.d.ts.map +1 -1
- package/dist/src/common/declared-types.js +19 -1
- package/dist/src/common/declared-types.js.map +1 -1
- package/dist/src/common/direction.d.ts +74 -50
- package/dist/src/common/direction.d.ts.map +1 -1
- package/dist/src/common/direction.js +47 -27
- package/dist/src/common/direction.js.map +1 -1
- package/dist/src/common/escape.d.ts +21 -9
- package/dist/src/common/escape.d.ts.map +1 -1
- package/dist/src/common/escape.js +33 -17
- package/dist/src/common/escape.js.map +1 -1
- package/dist/src/common/export.d.ts +144 -32
- package/dist/src/common/export.d.ts.map +1 -1
- package/dist/src/common/export.js +216 -42
- package/dist/src/common/export.js.map +1 -1
- package/dist/src/common/format.d.ts +23 -1
- package/dist/src/common/format.d.ts.map +1 -1
- package/dist/src/common/format.js +26 -1
- package/dist/src/common/format.js.map +1 -1
- package/dist/src/common/ids.d.ts +9 -2
- package/dist/src/common/ids.d.ts.map +1 -1
- package/dist/src/common/ids.js +6 -2
- package/dist/src/common/ids.js.map +1 -1
- package/dist/src/common/input.d.ts +122 -28
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +890 -124
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/json-elements.d.ts +144 -20
- package/dist/src/common/json-elements.d.ts.map +1 -1
- package/dist/src/common/json-elements.js +868 -188
- package/dist/src/common/json-elements.js.map +1 -1
- package/dist/src/common/line-format.d.ts +90 -0
- package/dist/src/common/line-format.d.ts.map +1 -0
- package/dist/src/common/line-format.js +185 -0
- package/dist/src/common/line-format.js.map +1 -0
- package/dist/src/common/lists.d.ts +6 -1
- package/dist/src/common/lists.d.ts.map +1 -1
- package/dist/src/common/lists.js +2 -0
- package/dist/src/common/lists.js.map +1 -1
- package/dist/src/common/ontology-export.d.ts +167 -0
- package/dist/src/common/ontology-export.d.ts.map +1 -0
- package/dist/src/common/ontology-export.js +359 -0
- package/dist/src/common/ontology-export.js.map +1 -0
- package/dist/src/common/ontology.d.ts +25 -3
- package/dist/src/common/ontology.d.ts.map +1 -1
- package/dist/src/common/ontology.js +30 -9
- package/dist/src/common/ontology.js.map +1 -1
- package/dist/src/common/options.d.ts +62 -12
- package/dist/src/common/options.d.ts.map +1 -1
- package/dist/src/common/options.js +89 -11
- package/dist/src/common/options.js.map +1 -1
- package/dist/src/common/plural.d.ts +31 -0
- package/dist/src/common/plural.d.ts.map +1 -0
- package/dist/src/common/plural.js +46 -0
- package/dist/src/common/plural.js.map +1 -0
- package/dist/src/common/report.d.ts +72 -14
- package/dist/src/common/report.d.ts.map +1 -1
- package/dist/src/common/report.js +146 -22
- package/dist/src/common/report.js.map +1 -1
- package/dist/src/common/temporal.d.ts +22 -5
- package/dist/src/common/temporal.d.ts.map +1 -1
- package/dist/src/common/temporal.js +13 -2
- package/dist/src/common/temporal.js.map +1 -1
- package/dist/src/common/text.d.ts +39 -23
- package/dist/src/common/text.d.ts.map +1 -1
- package/dist/src/common/text.js +51 -21
- package/dist/src/common/text.js.map +1 -1
- package/dist/src/common/weights.d.ts +37 -4
- package/dist/src/common/weights.d.ts.map +1 -1
- package/dist/src/common/weights.js +61 -2
- package/dist/src/common/weights.js.map +1 -1
- package/dist/src/common/writer.d.ts +19 -3
- package/dist/src/common/writer.d.ts.map +1 -1
- package/dist/src/common/writer.js +30 -2
- package/dist/src/common/writer.js.map +1 -1
- package/dist/src/common/xml.d.ts +125 -9
- package/dist/src/common/xml.d.ts.map +1 -1
- package/dist/src/common/xml.js +400 -83
- package/dist/src/common/xml.js.map +1 -1
- package/dist/src/common/zip.d.ts +44 -4
- package/dist/src/common/zip.d.ts.map +1 -1
- package/dist/src/common/zip.js +134 -5
- package/dist/src/common/zip.js.map +1 -1
- package/dist/src/formats/csv/exporter.d.ts +80 -24
- package/dist/src/formats/csv/exporter.d.ts.map +1 -1
- package/dist/src/formats/csv/exporter.js +145 -32
- package/dist/src/formats/csv/exporter.js.map +1 -1
- package/dist/src/formats/csv/header.d.ts +38 -9
- package/dist/src/formats/csv/header.d.ts.map +1 -1
- package/dist/src/formats/csv/header.js +46 -10
- package/dist/src/formats/csv/header.js.map +1 -1
- package/dist/src/formats/csv/importer.d.ts +187 -37
- package/dist/src/formats/csv/importer.d.ts.map +1 -1
- package/dist/src/formats/csv/importer.js +835 -204
- package/dist/src/formats/csv/importer.js.map +1 -1
- package/dist/src/formats/csv/index.d.ts +100 -19
- package/dist/src/formats/csv/index.d.ts.map +1 -1
- package/dist/src/formats/csv/index.js +94 -21
- package/dist/src/formats/csv/index.js.map +1 -1
- package/dist/src/formats/csv/records.d.ts +95 -10
- package/dist/src/formats/csv/records.d.ts.map +1 -1
- package/dist/src/formats/csv/records.js +333 -122
- package/dist/src/formats/csv/records.js.map +1 -1
- package/dist/src/formats/csv/values.d.ts +1 -0
- package/dist/src/formats/csv/values.d.ts.map +1 -1
- package/dist/src/formats/csv/values.js +1 -0
- package/dist/src/formats/csv/values.js.map +1 -1
- package/dist/src/formats/cx/exporter.d.ts +102 -0
- package/dist/src/formats/cx/exporter.d.ts.map +1 -0
- package/dist/src/formats/cx/exporter.js +1217 -0
- package/dist/src/formats/cx/exporter.js.map +1 -0
- package/dist/src/formats/cx/importer.d.ts +79 -32
- package/dist/src/formats/cx/importer.d.ts.map +1 -1
- package/dist/src/formats/cx/importer.js +680 -285
- package/dist/src/formats/cx/importer.js.map +1 -1
- package/dist/src/formats/cx/index.d.ts +4 -3
- package/dist/src/formats/cx/index.d.ts.map +1 -1
- package/dist/src/formats/cx/index.js +4 -3
- package/dist/src/formats/cx/index.js.map +1 -1
- package/dist/src/formats/cx2/exporter.d.ts +29 -11
- package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
- package/dist/src/formats/cx2/exporter.js +39 -268
- package/dist/src/formats/cx2/exporter.js.map +1 -1
- package/dist/src/formats/cx2/importer.d.ts +78 -28
- package/dist/src/formats/cx2/importer.d.ts.map +1 -1
- package/dist/src/formats/cx2/importer.js +226 -79
- package/dist/src/formats/cx2/importer.js.map +1 -1
- package/dist/src/formats/cx2/index.d.ts +2 -1
- package/dist/src/formats/cx2/index.d.ts.map +1 -1
- package/dist/src/formats/cx2/index.js +2 -1
- package/dist/src/formats/cx2/index.js.map +1 -1
- package/dist/src/formats/cys/constants.d.ts +66 -17
- package/dist/src/formats/cys/constants.d.ts.map +1 -1
- package/dist/src/formats/cys/constants.js +59 -18
- package/dist/src/formats/cys/constants.js.map +1 -1
- package/dist/src/formats/cys/exporter.d.ts +118 -0
- package/dist/src/formats/cys/exporter.d.ts.map +1 -0
- package/dist/src/formats/cys/exporter.js +1072 -0
- package/dist/src/formats/cys/exporter.js.map +1 -0
- package/dist/src/formats/cys/importer.d.ts +22 -8
- package/dist/src/formats/cys/importer.d.ts.map +1 -1
- package/dist/src/formats/cys/importer.js +510 -149
- package/dist/src/formats/cys/importer.js.map +1 -1
- package/dist/src/formats/cys/index.d.ts +4 -2
- package/dist/src/formats/cys/index.d.ts.map +1 -1
- package/dist/src/formats/cys/index.js +4 -2
- package/dist/src/formats/cys/index.js.map +1 -1
- package/dist/src/formats/cys/session.d.ts +46 -7
- package/dist/src/formats/cys/session.d.ts.map +1 -1
- package/dist/src/formats/cys/session.js +98 -24
- package/dist/src/formats/cys/session.js.map +1 -1
- package/dist/src/formats/cys/tables.d.ts +9 -2
- package/dist/src/formats/cys/tables.d.ts.map +1 -1
- package/dist/src/formats/cys/tables.js +29 -7
- package/dist/src/formats/cys/tables.js.map +1 -1
- package/dist/src/formats/dot/exporter.d.ts +42 -14
- package/dist/src/formats/dot/exporter.d.ts.map +1 -1
- package/dist/src/formats/dot/exporter.js +56 -29
- package/dist/src/formats/dot/exporter.js.map +1 -1
- package/dist/src/formats/dot/importer.d.ts +91 -29
- package/dist/src/formats/dot/importer.d.ts.map +1 -1
- package/dist/src/formats/dot/importer.js +190 -39
- package/dist/src/formats/dot/importer.js.map +1 -1
- package/dist/src/formats/dot/index.d.ts +2 -1
- package/dist/src/formats/dot/index.d.ts.map +1 -1
- package/dist/src/formats/dot/index.js +2 -1
- package/dist/src/formats/dot/index.js.map +1 -1
- package/dist/src/formats/dot/names.d.ts +44 -11
- package/dist/src/formats/dot/names.d.ts.map +1 -1
- package/dist/src/formats/dot/names.js +44 -11
- package/dist/src/formats/dot/names.js.map +1 -1
- package/dist/src/formats/dot/tokenizer.d.ts +9 -2
- package/dist/src/formats/dot/tokenizer.d.ts.map +1 -1
- package/dist/src/formats/dot/tokenizer.js +20 -5
- package/dist/src/formats/dot/tokenizer.js.map +1 -1
- package/dist/src/formats/gexf/exporter.d.ts +46 -15
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
- package/dist/src/formats/gexf/exporter.js +45 -15
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts +87 -23
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +471 -90
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gexf/index.d.ts +84 -21
- package/dist/src/formats/gexf/index.d.ts.map +1 -1
- package/dist/src/formats/gexf/index.js +80 -23
- package/dist/src/formats/gexf/index.js.map +1 -1
- package/dist/src/formats/gexf/schema.d.ts +67 -15
- package/dist/src/formats/gexf/schema.d.ts.map +1 -1
- package/dist/src/formats/gexf/schema.js +59 -13
- package/dist/src/formats/gexf/schema.js.map +1 -1
- package/dist/src/formats/gml/exporter.d.ts +69 -21
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +74 -29
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts +116 -25
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +290 -61
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/index.d.ts +87 -24
- package/dist/src/formats/gml/index.d.ts.map +1 -1
- package/dist/src/formats/gml/index.js +79 -26
- package/dist/src/formats/gml/index.js.map +1 -1
- package/dist/src/formats/gml/syntax.d.ts +67 -18
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +108 -26
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/constants.d.ts +174 -44
- package/dist/src/formats/graphml/constants.d.ts.map +1 -1
- package/dist/src/formats/graphml/constants.js +165 -44
- package/dist/src/formats/graphml/constants.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts +17 -9
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +33 -12
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts +19 -7
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +466 -39
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/index.d.ts +2 -1
- package/dist/src/formats/graphml/index.d.ts.map +1 -1
- package/dist/src/formats/graphml/index.js +2 -1
- package/dist/src/formats/graphml/index.js.map +1 -1
- package/dist/src/formats/graphml/tree.d.ts +6 -2
- package/dist/src/formats/graphml/tree.d.ts.map +1 -1
- package/dist/src/formats/graphml/tree.js +6 -2
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts +8 -3
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +36 -15
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/dialect.d.ts +88 -24
- package/dist/src/formats/json/dialect.d.ts.map +1 -1
- package/dist/src/formats/json/dialect.js +109 -19
- package/dist/src/formats/json/dialect.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts +134 -22
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +135 -54
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts +173 -52
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +899 -220
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/index.d.ts +3 -2
- package/dist/src/formats/json/index.d.ts.map +1 -1
- package/dist/src/formats/json/index.js +3 -2
- package/dist/src/formats/json/index.js.map +1 -1
- package/dist/src/formats/json/obographs-export.d.ts +68 -0
- package/dist/src/formats/json/obographs-export.d.ts.map +1 -0
- package/dist/src/formats/json/obographs-export.js +941 -0
- package/dist/src/formats/json/obographs-export.js.map +1 -0
- package/dist/src/formats/json/obographs.d.ts +26 -0
- package/dist/src/formats/json/obographs.d.ts.map +1 -1
- package/dist/src/formats/json/obographs.js +297 -80
- package/dist/src/formats/json/obographs.js.map +1 -1
- package/dist/src/formats/neo4j/exporter.d.ts +109 -21
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +238 -85
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/neo4j/header.d.ts +17 -4
- package/dist/src/formats/neo4j/header.d.ts.map +1 -1
- package/dist/src/formats/neo4j/header.js +36 -4
- package/dist/src/formats/neo4j/header.js.map +1 -1
- package/dist/src/formats/neo4j/importer.d.ts +107 -27
- package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
- package/dist/src/formats/neo4j/importer.js +415 -63
- package/dist/src/formats/neo4j/importer.js.map +1 -1
- package/dist/src/formats/neo4j/index.d.ts +73 -23
- package/dist/src/formats/neo4j/index.d.ts.map +1 -1
- package/dist/src/formats/neo4j/index.js +65 -25
- package/dist/src/formats/neo4j/index.js.map +1 -1
- package/dist/src/formats/obo/exporter.d.ts +150 -0
- package/dist/src/formats/obo/exporter.d.ts.map +1 -0
- package/dist/src/formats/obo/exporter.js +1736 -0
- package/dist/src/formats/obo/exporter.js.map +1 -0
- package/dist/src/formats/obo/importer.d.ts +95 -19
- package/dist/src/formats/obo/importer.d.ts.map +1 -1
- package/dist/src/formats/obo/importer.js +475 -104
- package/dist/src/formats/obo/importer.js.map +1 -1
- package/dist/src/formats/obo/index.d.ts +6 -3
- package/dist/src/formats/obo/index.d.ts.map +1 -1
- package/dist/src/formats/obo/index.js +6 -3
- package/dist/src/formats/obo/index.js.map +1 -1
- package/dist/src/formats/obo/syntax.d.ts +108 -4
- package/dist/src/formats/obo/syntax.d.ts.map +1 -1
- package/dist/src/formats/obo/syntax.js +211 -34
- package/dist/src/formats/obo/syntax.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts +54 -16
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +66 -34
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts +84 -22
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +303 -83
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/index.d.ts +2 -1
- package/dist/src/formats/pajek/index.d.ts.map +1 -1
- package/dist/src/formats/pajek/index.js +2 -1
- package/dist/src/formats/pajek/index.js.map +1 -1
- package/dist/src/formats/pajek/syntax.d.ts +77 -14
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +183 -51
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/columns.d.ts +1 -0
- package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
- package/dist/src/formats/xgmml/columns.js +4 -0
- package/dist/src/formats/xgmml/columns.js.map +1 -1
- package/dist/src/formats/xgmml/constants.d.ts +186 -56
- package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
- package/dist/src/formats/xgmml/constants.js +181 -57
- package/dist/src/formats/xgmml/constants.js.map +1 -1
- package/dist/src/formats/xgmml/document.d.ts +65 -11
- package/dist/src/formats/xgmml/document.d.ts.map +1 -1
- package/dist/src/formats/xgmml/document.js +157 -28
- package/dist/src/formats/xgmml/document.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts +61 -3
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +129 -26
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts +13 -6
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +20 -21
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts +33 -12
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +35 -72
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/formats/xgmml/index.d.ts +1 -0
- package/dist/src/formats/xgmml/index.d.ts.map +1 -1
- package/dist/src/formats/xgmml/index.js +1 -0
- package/dist/src/formats/xgmml/index.js.map +1 -1
- package/dist/src/formats/xgmml/values.d.ts +8 -1
- package/dist/src/formats/xgmml/values.d.ts.map +1 -1
- package/dist/src/formats/xgmml/values.js +7 -2
- package/dist/src/formats/xgmml/values.js.map +1 -1
- package/dist/src/index.d.ts +23 -18
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +28 -16
- package/dist/src/index.js.map +1 -1
- package/dist/src/registry.d.ts +478 -92
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +807 -88
- package/dist/src/registry.js.map +1 -1
- package/dist/src/sniff.d.ts +61 -19
- package/dist/src/sniff.d.ts.map +1 -1
- package/dist/src/sniff.js +121 -29
- package/dist/src/sniff.js.map +1 -1
- package/dist/src/types.d.ts +362 -112
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +28 -7
- package/dist/src/types.js.map +1 -1
- package/dist/xgmml.js +2 -2
- package/docs/samples/README.md +25 -0
- package/docs/samples/got-edges.csv +353 -0
- package/docs/samples/got-network.graphml +1384 -0
- package/docs/samples/got-nodes.csv +108 -0
- package/docs/samples/got.cx +930 -0
- package/docs/samples/got.cx2 +465 -0
- package/docs/samples/got.gexf +471 -0
- package/docs/samples/got.gml +2650 -0
- package/docs/samples/got.json +2546 -0
- package/docs/samples/got.net +461 -0
- package/docs/samples/movies-nodes.csv +9 -0
- package/docs/samples/movies-rels.csv +8 -0
- package/docs/samples/networks.cys +0 -0
- package/docs/samples/proteins.xgmml +9 -0
- package/docs/samples/teams.gv +24 -0
- package/docs/samples/vehicles.obo +25 -0
- package/package.json +9 -4
- package/src/children.ts +18 -6
- package/src/common/attributes.ts +29 -11
- package/src/common/cell-budget.ts +11 -3
- package/src/common/choose.ts +89 -0
- package/src/common/codes.ts +490 -66
- package/src/common/compare.ts +472 -0
- package/src/common/cx-export.ts +432 -0
- package/src/common/declared-types.ts +38 -4
- package/src/common/direction.ts +78 -51
- package/src/common/escape.ts +33 -17
- package/src/common/export.ts +284 -47
- package/src/common/format.ts +30 -1
- package/src/common/ids.ts +14 -3
- package/src/common/input.ts +1057 -134
- package/src/common/json-elements.ts +1034 -198
- package/src/common/line-format.ts +249 -0
- package/src/common/lists.ts +6 -1
- package/src/common/ontology-export.ts +461 -0
- package/src/common/ontology.ts +31 -9
- package/src/common/options.ts +110 -15
- package/src/common/plural.ts +49 -0
- package/src/common/report.ts +168 -25
- package/src/common/temporal.ts +22 -5
- package/src/common/text.ts +62 -23
- package/src/common/weights.ts +74 -4
- package/src/common/writer.ts +37 -3
- package/src/common/xml.ts +477 -88
- package/src/common/zip.ts +169 -7
- package/src/formats/csv/exporter.ts +201 -46
- package/src/formats/csv/header.ts +50 -11
- package/src/formats/csv/importer.ts +1133 -245
- package/src/formats/csv/index.ts +106 -19
- package/src/formats/csv/records.ts +396 -122
- package/src/formats/csv/values.ts +1 -0
- package/src/formats/cx/exporter.ts +1483 -0
- package/src/formats/cx/importer.ts +888 -300
- package/src/formats/cx/index.ts +4 -3
- package/src/formats/cx2/exporter.ts +61 -338
- package/src/formats/cx2/importer.ts +317 -86
- package/src/formats/cx2/index.ts +2 -1
- package/src/formats/cys/constants.ts +62 -17
- package/src/formats/cys/exporter.ts +1368 -0
- package/src/formats/cys/importer.ts +659 -157
- package/src/formats/cys/index.ts +4 -2
- package/src/formats/cys/session.ts +139 -31
- package/src/formats/cys/tables.ts +47 -9
- package/src/formats/dot/exporter.ts +83 -37
- package/src/formats/dot/importer.ts +248 -54
- package/src/formats/dot/index.ts +2 -1
- package/src/formats/dot/names.ts +44 -11
- package/src/formats/dot/tokenizer.ts +25 -6
- package/src/formats/gexf/exporter.ts +57 -18
- package/src/formats/gexf/importer.ts +636 -105
- package/src/formats/gexf/index.ts +90 -21
- package/src/formats/gexf/schema.ts +67 -15
- package/src/formats/gml/exporter.ts +90 -36
- package/src/formats/gml/importer.ts +388 -67
- package/src/formats/gml/index.ts +85 -24
- package/src/formats/gml/syntax.ts +111 -26
- package/src/formats/graphml/constants.ts +178 -44
- package/src/formats/graphml/exporter.ts +52 -18
- package/src/formats/graphml/importer.ts +640 -60
- package/src/formats/graphml/index.ts +2 -1
- package/src/formats/graphml/tree.ts +6 -2
- package/src/formats/graphml/yfiles.ts +37 -15
- package/src/formats/json/dialect.ts +128 -29
- package/src/formats/json/exporter.ts +205 -68
- package/src/formats/json/importer.ts +1163 -253
- package/src/formats/json/index.ts +3 -1
- package/src/formats/json/obographs-export.ts +1197 -0
- package/src/formats/json/obographs.ts +353 -92
- package/src/formats/neo4j/exporter.ts +320 -101
- package/src/formats/neo4j/header.ts +44 -6
- package/src/formats/neo4j/importer.ts +512 -73
- package/src/formats/neo4j/index.ts +75 -23
- package/src/formats/obo/exporter.ts +2210 -0
- package/src/formats/obo/importer.ts +622 -109
- package/src/formats/obo/index.ts +6 -3
- package/src/formats/obo/syntax.ts +232 -37
- package/src/formats/pajek/exporter.ts +89 -41
- package/src/formats/pajek/importer.ts +407 -93
- package/src/formats/pajek/index.ts +2 -1
- package/src/formats/pajek/syntax.ts +213 -52
- package/src/formats/xgmml/columns.ts +10 -0
- package/src/formats/xgmml/constants.ts +183 -56
- package/src/formats/xgmml/document.ts +212 -34
- package/src/formats/xgmml/emit.ts +175 -34
- package/src/formats/xgmml/exporter.ts +35 -27
- package/src/formats/xgmml/importer.ts +64 -86
- package/src/formats/xgmml/index.ts +1 -0
- package/src/formats/xgmml/values.ts +11 -3
- package/src/index.ts +81 -11
- package/src/registry.ts +1121 -135
- package/src/sniff.ts +163 -32
- package/src/types.ts +385 -114
- package/dist/chunks/escape-B-tkk_Cl.js.map +0 -1
- package/dist/chunks/export-Bh60Dv-n.js +0 -582
- package/dist/chunks/export-Bh60Dv-n.js.map +0 -1
- package/dist/chunks/exporter-BGrsWamJ.js.map +0 -1
- package/dist/chunks/exporter-DIeGJXAZ.js.map +0 -1
- package/dist/chunks/importer-BWY2FFc5.js.map +0 -1
- package/dist/chunks/importer-DD-xv9_Y.js +0 -1471
- package/dist/chunks/importer-DD-xv9_Y.js.map +0 -1
- package/dist/chunks/importer-DET1tg6s.js.map +0 -1
- package/dist/chunks/importer-DEcKhsmQ.js +0 -1614
- package/dist/chunks/importer-DEcKhsmQ.js.map +0 -1
- package/dist/chunks/importer-DIrbbnAf.js.map +0 -1
- package/dist/chunks/importer-D_e7e7LX.js.map +0 -1
- package/dist/chunks/importer-eUREhBGj.js.map +0 -1
- package/dist/chunks/importer-vi3bSdtw.js.map +0 -1
- package/dist/chunks/importer-zGLo8Dg_.js.map +0 -1
- package/dist/chunks/json-elements-DDS8N2Dd.js +0 -779
- package/dist/chunks/json-elements-DDS8N2Dd.js.map +0 -1
- package/dist/chunks/ontology-BnrJ4I98.js +0 -113
- package/dist/chunks/ontology-BnrJ4I98.js.map +0 -1
- package/dist/chunks/records-dbRkxwaq.js.map +0 -1
- package/dist/chunks/report-B1z4WT9e.js +0 -2372
- package/dist/chunks/report-B1z4WT9e.js.map +0 -1
- package/dist/chunks/text-Dr0Ifpag.js.map +0 -1
- package/dist/chunks/weights-Dzba96G3.js +0 -176
- package/dist/chunks/weights-Dzba96G3.js.map +0 -1
- package/dist/chunks/writer-C7flM-Ih.js.map +0 -1
|
@@ -0,0 +1,1072 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Cytoscape session exporter: writes a snapshot as a `.cys` file that Cytoscape Desktop 3.x
|
|
3
|
+
* opens as a session holding one network (one collection with one registered subnetwork).
|
|
4
|
+
*
|
|
5
|
+
* The archive holds what Cytoscape's session reader needs and nothing more:
|
|
6
|
+
* - `CytoscapeSession/3.0.0.version`, the empty marker that makes Cytoscape read a 3.x session;
|
|
7
|
+
* - `networks/<root>-<name>.xgmml`, the topology: one root network holding one registered
|
|
8
|
+
* subnetwork, its nodes and edges by SUID, each edge with `cy:directed`;
|
|
9
|
+
* - `tables/<subnetwork>-<name>/LOCAL_ATTRS-org.cytoscape.model.Cy{Node,Edge,Network}-*.cytable`,
|
|
10
|
+
* every column as a CyCSV table keyed by SUID (a session network file carries no attributes:
|
|
11
|
+
* Cytoscape ignores atts on the nodes and edges of a 3.x session), with the label in `name`,
|
|
12
|
+
* which Cytoscape's default style shows; plus the root network's own table with its name;
|
|
13
|
+
* - `views/<subnetwork>-<view>-<name>.xgmml`, the node coordinates (y negated back to screen
|
|
14
|
+
* coordinates), only when the snapshot has positions: graph-io computes no layout.
|
|
15
|
+
*
|
|
16
|
+
* Styles, the network list, properties and thumbnails are not written; Cytoscape gives the view
|
|
17
|
+
* its default style. Entries are stored, not compressed. Every SUID comes from one counter, above
|
|
18
|
+
* the node ids that are kept as SUIDs; Cytoscape renumbers them all on load.
|
|
19
|
+
*/
|
|
20
|
+
import { GraphFormatError } from "@graphty/graph-format";
|
|
21
|
+
import { DictHeuristic } from "../../common/attributes.js";
|
|
22
|
+
import { pairFolding } from "../../common/direction.js";
|
|
23
|
+
import { escapeXmlAttribute, quoteCsvCell } from "../../common/escape.js";
|
|
24
|
+
import { capabilities, checkCapabilities, LOSS } from "../../common/export.js";
|
|
25
|
+
import { formatDecimal, formatF32, formatF64 } from "../../common/format.js";
|
|
26
|
+
import { resolveExportOptions } from "../../common/options.js";
|
|
27
|
+
import { agree, plural } from "../../common/plural.js";
|
|
28
|
+
import { explicitWeights } from "../../common/weights.js";
|
|
29
|
+
import { hasIllegalXmlChar } from "../../common/xml.js";
|
|
30
|
+
import { writeZip } from "../../common/zip.js";
|
|
31
|
+
import { ORIGINAL_ID_ATTRIBUTE } from "../graphml/constants.js";
|
|
32
|
+
import { CY_NAMESPACE, EDGE_ID_COLUMN, GRAPHICS_COLUMN, NESTED_NETWORK_COLUMN, NETWORK_POINTER_COLUMN, NETWORKS_COLUMN, PARENT_COLUMN, PARENTS_COLUMN, POSITION_COLUMN, SUBGRAPH_COLUMN, XGMML_NAMESPACE, XLINK_NAMESPACE, Z_COLUMN, } from "../xgmml/constants.js";
|
|
33
|
+
import { FORMAT } from "./constants.js";
|
|
34
|
+
/**
|
|
35
|
+
* The loss notes the session exporter's check() returns, by name. A key is the code without its
|
|
36
|
+
* severity and format prefixes.
|
|
37
|
+
* @category Built-in formats
|
|
38
|
+
*/
|
|
39
|
+
export const CYS_LOSS = Object.freeze({
|
|
40
|
+
/** CyCSV has no unset text cell: an unset cell of a text column reads back as "". */
|
|
41
|
+
UNSET_AS_EMPTY_STRING: "W_CYS_UNSET_AS_EMPTY_STRING",
|
|
42
|
+
/**
|
|
43
|
+
* List cells CyCSV cannot hold exactly: an unset or empty list of text reads back as [""], an
|
|
44
|
+
* empty list of numbers or booleans reads back unset, trailing empty text items vanish, and an
|
|
45
|
+
* item holding a newline splits in two.
|
|
46
|
+
*/
|
|
47
|
+
LIST_ITEMS: "W_CYS_LIST_ITEMS",
|
|
48
|
+
/** A text cell starting with "=" is a formula to Cytoscape (an error cell there); graph-io reads it back as text. */
|
|
49
|
+
TEXT_AS_EQUATION: "W_CYS_TEXT_AS_EQUATION",
|
|
50
|
+
/** A nested (json) column is written as a text column holding its JSON text. */
|
|
51
|
+
JSON_AS_STRING: "W_CYS_JSON_AS_STRING",
|
|
52
|
+
/** A position that cannot be written as it is: another shape, a non-finite coordinate, a z read back in the z column. */
|
|
53
|
+
POSITION: "W_CYS_POSITION",
|
|
54
|
+
/**
|
|
55
|
+
* An attribute with a role (for example, the label) is written where the format keeps that role, and reads back under the
|
|
56
|
+
* name the format's importer gives it.
|
|
57
|
+
*/
|
|
58
|
+
COLUMN_NAME_CHANGED: LOSS.COLUMN_NAME_CHANGED,
|
|
59
|
+
/**
|
|
60
|
+
* An attribute without a role is written where the format keeps a role (for example, a `name` column as the label), and
|
|
61
|
+
* reads back with that role.
|
|
62
|
+
*/
|
|
63
|
+
ROLE_ASSUMED: LOSS.ROLE_ASSUMED,
|
|
64
|
+
/** A mutual pair is written as two directed edges without its mark. */
|
|
65
|
+
MUTUAL_EXPANDED: LOSS.MUTUAL_EXPANDED,
|
|
66
|
+
/** Node ids that are numbers read back as their text. */
|
|
67
|
+
ID_TEXT_TYPE: LOSS.ID_TEXT_TYPE,
|
|
68
|
+
/**
|
|
69
|
+
* Two node ids would be written as the same text (the number 5 and the text "5"); the save fails with
|
|
70
|
+
* E_INVALID_ID.
|
|
71
|
+
*/
|
|
72
|
+
ID_TEXT_COLLISION: LOSS.ID_TEXT_COLLISION,
|
|
73
|
+
/**
|
|
74
|
+
* Node ids the format cannot write, under `sanitizeIds: "error"`; the save fails with E_INVALID_ID. Pass
|
|
75
|
+
* `sanitizeIds: "mangle"` to rewrite them.
|
|
76
|
+
*/
|
|
77
|
+
ID_CHARSET: LOSS.ID_CHARSET,
|
|
78
|
+
/** Node ids that are not positive integers under sanitizeIds "mangle": renumbered, originals kept. */
|
|
79
|
+
ID_MANGLED: LOSS.ID_MANGLED,
|
|
80
|
+
/** Edges without a usable id get generated SUIDs. */
|
|
81
|
+
EDGE_IDS_GENERATED: LOSS.EDGE_IDS_GENERATED,
|
|
82
|
+
/**
|
|
83
|
+
* An attribute type Cytoscape stores as a wider one (a 32-bit float as Double, a byte as Integer), or a label that
|
|
84
|
+
* is not text and is written as text; it reads back with the Cytoscape type.
|
|
85
|
+
*/
|
|
86
|
+
DTYPE_UNSUPPORTED: LOSS.DTYPE,
|
|
87
|
+
/** A column whose every cell is unset (and which is not text) vanishes. */
|
|
88
|
+
EMPTY_COLUMN_DROPPED: LOSS.EMPTY_COLUMN,
|
|
89
|
+
/**
|
|
90
|
+
* A text attribute reads back as a dictionary attribute, or the reverse, because the importer chooses by how often
|
|
91
|
+
* its values repeat. The values are the same.
|
|
92
|
+
*/
|
|
93
|
+
STORAGE_CLASS_CHANGED: LOSS.STORAGE_CLASS,
|
|
94
|
+
/** A plain `weight` edge column reads back as the edge weight. */
|
|
95
|
+
WEIGHT_KEY_CLASH: LOSS.WEIGHT_KEY_CLASH,
|
|
96
|
+
/** A parent / parents column: groups are not written. */
|
|
97
|
+
HIERARCHY_DROPPED: LOSS.HIERARCHY,
|
|
98
|
+
/** A start / end / timestamp column: sessions have no time. */
|
|
99
|
+
TEMPORAL_DROPPED: LOSS.TEMPORAL,
|
|
100
|
+
/** An attribute with a role the format has no place for is written as a plain attribute; the role is lost. */
|
|
101
|
+
ROLE_DROPPED: LOSS.ROLE,
|
|
102
|
+
/** An extension table the session cannot carry. */
|
|
103
|
+
EXTENSION_TABLE_DROPPED: LOSS.EXTENSION_TABLE,
|
|
104
|
+
});
|
|
105
|
+
/**
|
|
106
|
+
* What a Cytoscape session keeps: mixed direction, parallel edges and self-loops, integer SUIDs
|
|
107
|
+
* for nodes and edges, String / Double / Integer / Long / Boolean columns and lists of them,
|
|
108
|
+
* graph columns and positions. Groups, defaults, styles and time are not written.
|
|
109
|
+
* @category Built-in formats
|
|
110
|
+
*/
|
|
111
|
+
export const CYS_CAPABILITIES = capabilities({
|
|
112
|
+
mixedDirection: true,
|
|
113
|
+
multiEdges: true,
|
|
114
|
+
selfLoops: true,
|
|
115
|
+
edgeIds: "required",
|
|
116
|
+
idCharset: "integer",
|
|
117
|
+
dtypes: ["string", "dict", "f64", "i32", "bool", "list"],
|
|
118
|
+
components: false,
|
|
119
|
+
lists: true,
|
|
120
|
+
json: false,
|
|
121
|
+
defaults: false,
|
|
122
|
+
options: false,
|
|
123
|
+
hierarchy: false,
|
|
124
|
+
temporal: "none",
|
|
125
|
+
graphAttributes: true,
|
|
126
|
+
positions: true,
|
|
127
|
+
viz: false,
|
|
128
|
+
});
|
|
129
|
+
/** The roles with a slot (the label as `name`, the edge id as the SUID, positions in the view) and their read-back names. */
|
|
130
|
+
const SLOT_ROLES = new Set(["label", "id", "position"]);
|
|
131
|
+
const ROLE_NAMES = Object.freeze({
|
|
132
|
+
label: "name",
|
|
133
|
+
id: EDGE_ID_COLUMN,
|
|
134
|
+
position: POSITION_COLUMN,
|
|
135
|
+
});
|
|
136
|
+
/** Roles whose columns are not written as table columns (structure, or reported dropped by checkCapabilities). */
|
|
137
|
+
const SKIPPED_ROLES = new Set([
|
|
138
|
+
"directed",
|
|
139
|
+
"pair",
|
|
140
|
+
"mutual",
|
|
141
|
+
"weight",
|
|
142
|
+
"timeText",
|
|
143
|
+
"parent",
|
|
144
|
+
"parents",
|
|
145
|
+
"start",
|
|
146
|
+
"end",
|
|
147
|
+
"timestamp",
|
|
148
|
+
"timestamps",
|
|
149
|
+
"spells",
|
|
150
|
+
"open",
|
|
151
|
+
"spellsOpen",
|
|
152
|
+
"position",
|
|
153
|
+
"color",
|
|
154
|
+
"size",
|
|
155
|
+
"shape",
|
|
156
|
+
"thickness",
|
|
157
|
+
]);
|
|
158
|
+
/** The Java classes of the columns Cytoscape creates in every table, by lower-case name. */
|
|
159
|
+
const BUILT_IN = {
|
|
160
|
+
node: new Map([
|
|
161
|
+
["name", "java.lang.String"],
|
|
162
|
+
["shared name", "java.lang.String"],
|
|
163
|
+
["selected", "java.lang.Boolean"],
|
|
164
|
+
]),
|
|
165
|
+
edge: new Map([
|
|
166
|
+
["name", "java.lang.String"],
|
|
167
|
+
["shared name", "java.lang.String"],
|
|
168
|
+
["interaction", "java.lang.String"],
|
|
169
|
+
["shared interaction", "java.lang.String"],
|
|
170
|
+
["selected", "java.lang.Boolean"],
|
|
171
|
+
]),
|
|
172
|
+
network: new Map([
|
|
173
|
+
["name", "java.lang.String"],
|
|
174
|
+
["shared name", "java.lang.String"],
|
|
175
|
+
["selected", "java.lang.Boolean"],
|
|
176
|
+
["__annotations", "java.util.List<java.lang.String>"],
|
|
177
|
+
]),
|
|
178
|
+
};
|
|
179
|
+
/** Column names graph-io's session importer gives its own columns; a table column of such a name reads back renamed. */
|
|
180
|
+
const IMPORTER_NAMES = {
|
|
181
|
+
node: new Set([
|
|
182
|
+
POSITION_COLUMN,
|
|
183
|
+
Z_COLUMN,
|
|
184
|
+
GRAPHICS_COLUMN,
|
|
185
|
+
PARENT_COLUMN,
|
|
186
|
+
PARENTS_COLUMN,
|
|
187
|
+
SUBGRAPH_COLUMN,
|
|
188
|
+
NESTED_NETWORK_COLUMN,
|
|
189
|
+
NETWORK_POINTER_COLUMN,
|
|
190
|
+
NETWORKS_COLUMN,
|
|
191
|
+
]),
|
|
192
|
+
edge: new Set([EDGE_ID_COLUMN, GRAPHICS_COLUMN]),
|
|
193
|
+
network: new Set([GRAPHICS_COLUMN]),
|
|
194
|
+
};
|
|
195
|
+
const STRING = "java.lang.String";
|
|
196
|
+
const DOUBLE = "java.lang.Double";
|
|
197
|
+
const INTEGER = "java.lang.Integer";
|
|
198
|
+
const LONG = "java.lang.Long";
|
|
199
|
+
const BOOLEAN = "java.lang.Boolean";
|
|
200
|
+
const I32_MAX = 2147483647;
|
|
201
|
+
/** The folder every entry lives under (Cytoscape needs one; the name is free). */
|
|
202
|
+
const ROOT_FOLDER = "CytoscapeSession/";
|
|
203
|
+
/** The default network name. */
|
|
204
|
+
const DEFAULT_NAME = "Network";
|
|
205
|
+
// ============================================================ ids
|
|
206
|
+
/** Positive integer text a Long holds exactly. */
|
|
207
|
+
const SUID_TEXT = /^[1-9]\d*$/;
|
|
208
|
+
/**
|
|
209
|
+
* The SUID a node id can keep, or null: a positive safe integer, or its canonical digits.
|
|
210
|
+
* @param id - the id
|
|
211
|
+
* @returns the SUID, or null
|
|
212
|
+
*/
|
|
213
|
+
function suidOf(id) {
|
|
214
|
+
if (typeof id === "number") {
|
|
215
|
+
return Number.isSafeInteger(id) && id > 0 ? id : null;
|
|
216
|
+
}
|
|
217
|
+
if (SUID_TEXT.test(id)) {
|
|
218
|
+
const n = Number(id);
|
|
219
|
+
return Number.isSafeInteger(n) ? n : null;
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* The SUIDs node ids keep (the first of two ids naming one SUID keeps it).
|
|
225
|
+
* @param snapshot - the snapshot
|
|
226
|
+
* @returns the kept SUIDs and the nodes without one
|
|
227
|
+
*/
|
|
228
|
+
function keptNodeSuids(snapshot) {
|
|
229
|
+
const kept = [];
|
|
230
|
+
const bad = [];
|
|
231
|
+
const used = new Set();
|
|
232
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
233
|
+
const suid = suidOf(snapshot.ids.idOf(i));
|
|
234
|
+
if (suid === null || used.has(suid)) {
|
|
235
|
+
kept.push(0);
|
|
236
|
+
bad.push(i);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
used.add(suid);
|
|
240
|
+
kept.push(suid);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return { kept, bad };
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The edge SUIDs the id role column keeps: positive integers, distinct, and not node SUIDs.
|
|
247
|
+
* @param snapshot - the snapshot
|
|
248
|
+
* @param nodeSuids - the SUIDs nodes keep
|
|
249
|
+
* @returns the kept SUID of each edge, 0 where one is generated
|
|
250
|
+
*/
|
|
251
|
+
function keptEdgeSuids(snapshot, nodeSuids) {
|
|
252
|
+
const column = snapshot.edges.byRole("id");
|
|
253
|
+
const out = new Array(snapshot.edgeCount).fill(0);
|
|
254
|
+
if (column === null) {
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
const used = new Set();
|
|
258
|
+
for (let e = 0; e < snapshot.edgeCount; e++) {
|
|
259
|
+
const value = column.isSet(e) ? column.value(e) : null;
|
|
260
|
+
const suid = typeof value === "number" || typeof value === "string" ? suidOf(value) : null;
|
|
261
|
+
if (suid !== null && !used.has(suid) && !nodeSuids.has(suid)) {
|
|
262
|
+
used.add(suid);
|
|
263
|
+
out[e] = suid;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
// ============================================================ columns
|
|
269
|
+
/**
|
|
270
|
+
* The Java class of a scalar dtype.
|
|
271
|
+
* @param column - the column (for the long origin and the u32 range)
|
|
272
|
+
* @param dtype - the dtype (a list's item dtype)
|
|
273
|
+
* @returns the class text
|
|
274
|
+
*/
|
|
275
|
+
function scalarClass(column, dtype) {
|
|
276
|
+
switch (dtype) {
|
|
277
|
+
case "bool":
|
|
278
|
+
return BOOLEAN;
|
|
279
|
+
case "i32":
|
|
280
|
+
case "u8":
|
|
281
|
+
return INTEGER;
|
|
282
|
+
case "u32":
|
|
283
|
+
return maxValue(column) > I32_MAX ? LONG : INTEGER;
|
|
284
|
+
case "f32":
|
|
285
|
+
case "f64":
|
|
286
|
+
return /long$/i.test(column.meta.origin?.type ?? "") && allSafeIntegers(column) ? LONG : DOUBLE;
|
|
287
|
+
default:
|
|
288
|
+
return STRING;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* The items of a set cell: a list's items, or the scalar itself.
|
|
293
|
+
* @param column - the column
|
|
294
|
+
* @param row - the row
|
|
295
|
+
* @returns the values
|
|
296
|
+
*/
|
|
297
|
+
function itemsOf(column, row) {
|
|
298
|
+
return column.dtype === "list" ? [...column.sliceOf(row)] : [column.value(row)];
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Whether every set value of a numeric column (or of its list items) is a safe integer.
|
|
302
|
+
* @param column - the column
|
|
303
|
+
* @returns true when Long can carry every value
|
|
304
|
+
*/
|
|
305
|
+
function allSafeIntegers(column) {
|
|
306
|
+
for (let r = 0; r < column.length; r++) {
|
|
307
|
+
if (column.isSet(r) && !itemsOf(column, r).every((v) => typeof v === "number" && Number.isSafeInteger(v))) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* The largest value of a numeric column (or of its list items).
|
|
315
|
+
* @param column - the column
|
|
316
|
+
* @returns the maximum, 0 when empty
|
|
317
|
+
*/
|
|
318
|
+
function maxValue(column) {
|
|
319
|
+
let max = 0;
|
|
320
|
+
for (let r = 0; r < column.length; r++) {
|
|
321
|
+
if (column.isSet(r)) {
|
|
322
|
+
for (const v of itemsOf(column, r)) {
|
|
323
|
+
if (typeof v === "number" && v > max) {
|
|
324
|
+
max = v;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return max;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* The CyCSV text of one value of a class.
|
|
333
|
+
* @param value - the value
|
|
334
|
+
* @param javaClass - the scalar class
|
|
335
|
+
* @param dtype - the dtype it came from
|
|
336
|
+
* @returns the text Cytoscape's `valueOf` reads back
|
|
337
|
+
*/
|
|
338
|
+
function valueText(value, javaClass, dtype) {
|
|
339
|
+
if (javaClass === BOOLEAN) {
|
|
340
|
+
return value === true ? "true" : "false";
|
|
341
|
+
}
|
|
342
|
+
if (javaClass === DOUBLE) {
|
|
343
|
+
return formatDecimal(value, dtype === "f32" ? "f32" : "f64");
|
|
344
|
+
}
|
|
345
|
+
if (javaClass === INTEGER || javaClass === LONG) {
|
|
346
|
+
return String(value);
|
|
347
|
+
}
|
|
348
|
+
return textOf(value);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* A value as text: strings as they are, numbers in their shortest form, anything else as JSON.
|
|
352
|
+
* @param value - the value
|
|
353
|
+
* @returns the text
|
|
354
|
+
*/
|
|
355
|
+
function textOf(value) {
|
|
356
|
+
if (typeof value === "string") {
|
|
357
|
+
return value;
|
|
358
|
+
}
|
|
359
|
+
if (typeof value === "number") {
|
|
360
|
+
return formatF64(value);
|
|
361
|
+
}
|
|
362
|
+
if (typeof value === "boolean") {
|
|
363
|
+
return value ? "true" : "false";
|
|
364
|
+
}
|
|
365
|
+
if (ArrayBuffer.isView(value)) {
|
|
366
|
+
return JSON.stringify(Array.from(value).map(finiteOrText));
|
|
367
|
+
}
|
|
368
|
+
return JSON.stringify(value) ?? "";
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* A number for JSON text (JSON has no NaN or infinities: those are written as their names).
|
|
372
|
+
* @param v - the number
|
|
373
|
+
* @returns the number or its name
|
|
374
|
+
*/
|
|
375
|
+
function finiteOrText(v) {
|
|
376
|
+
return Number.isFinite(v) ? v : String(v);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* A snapshot column as a table candidate: its class and cell texts.
|
|
380
|
+
* @param column - the column
|
|
381
|
+
* @param name - the name it wants
|
|
382
|
+
* @returns the candidate
|
|
383
|
+
*/
|
|
384
|
+
function candidateOf(column, name) {
|
|
385
|
+
const { meta } = column;
|
|
386
|
+
if (column.dtype === "list") {
|
|
387
|
+
const item = meta.itemDtype;
|
|
388
|
+
const itemClass = scalarClass(column, item === "json" ? null : item);
|
|
389
|
+
return {
|
|
390
|
+
name,
|
|
391
|
+
source: column,
|
|
392
|
+
javaClass: `java.util.List<${itemClass}>`,
|
|
393
|
+
cell: (row) => column.isSet(row)
|
|
394
|
+
? [...column.sliceOf(row)].map((v) => valueText(v, itemClass, item)).join("\n")
|
|
395
|
+
: null,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
const javaClass = meta.dtype === "json" || meta.components > 1 ? STRING : scalarClass(column, meta.dtype);
|
|
399
|
+
return {
|
|
400
|
+
name,
|
|
401
|
+
source: column,
|
|
402
|
+
javaClass,
|
|
403
|
+
cell: (row) => (column.isSet(row) ? valueText(column.value(row), javaClass, meta.dtype) : null),
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Settle the names of a table's columns: case-insensitively unique, `SUID` and Cytoscape's own
|
|
408
|
+
* columns of another class renamed `<name>#2`, `#3`...
|
|
409
|
+
* @param domain - the table
|
|
410
|
+
* @param candidates - the columns in order
|
|
411
|
+
* @param note - the recorder
|
|
412
|
+
* @returns the table's columns (the SUID key first)
|
|
413
|
+
*/
|
|
414
|
+
function settleNames(domain, candidates, note) {
|
|
415
|
+
const taken = new Set(["suid"]);
|
|
416
|
+
const out = [];
|
|
417
|
+
for (const c of candidates) {
|
|
418
|
+
const lower = c.name.toLowerCase();
|
|
419
|
+
const builtIn = BUILT_IN[domain].get(lower);
|
|
420
|
+
let { name } = c;
|
|
421
|
+
const clash = builtIn !== undefined && builtIn !== c.javaClass ? builtIn : null;
|
|
422
|
+
if (taken.has(lower) || clash !== null) {
|
|
423
|
+
let k = 2;
|
|
424
|
+
while (taken.has(`${lower}#${k}`)) {
|
|
425
|
+
k++;
|
|
426
|
+
}
|
|
427
|
+
name = `${c.name}#${k}`;
|
|
428
|
+
const why = renameReason(lower, clash);
|
|
429
|
+
const set = c.source === null ? null : c.source.length - c.source.nullCount;
|
|
430
|
+
note(LOSS.COLUMN_NAME_CHANGED, `${domain} column "${c.name}" is written as "${name}": ${why}`, c.name, set);
|
|
431
|
+
}
|
|
432
|
+
else if (c.source !== null && IMPORTER_NAMES[domain].has(c.name)) {
|
|
433
|
+
note(LOSS.COLUMN_NAME_CHANGED, `${domain} column "${c.name}" is written to the session's table and reads back as "${c.name}#2": graph-io's session reader makes a "${c.name}" column of its own`, c.name, c.source.length - c.source.nullCount);
|
|
434
|
+
}
|
|
435
|
+
taken.add(name.toLowerCase());
|
|
436
|
+
out.push({ name, javaClass: c.javaClass, cell: c.cell });
|
|
437
|
+
}
|
|
438
|
+
return out;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Why a column is renamed.
|
|
442
|
+
* @param lower - the column name, lower-cased
|
|
443
|
+
* @param clash - the Java class of Cytoscape's own column of that name when it holds another, else null
|
|
444
|
+
* @returns the reason
|
|
445
|
+
*/
|
|
446
|
+
function renameReason(lower, clash) {
|
|
447
|
+
if (lower === "suid") {
|
|
448
|
+
return "SUID is Cytoscape's key column";
|
|
449
|
+
}
|
|
450
|
+
if (clash !== null) {
|
|
451
|
+
return `Cytoscape's own "${lower}" column holds ${clash}`;
|
|
452
|
+
}
|
|
453
|
+
return "its name is taken (Cytoscape column names ignore case)";
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Java's `String.split("\n")`: trailing empty items are removed.
|
|
457
|
+
* @param text - the cell
|
|
458
|
+
* @returns the items
|
|
459
|
+
*/
|
|
460
|
+
function javaSplit(text) {
|
|
461
|
+
if (!text.includes("\n")) {
|
|
462
|
+
return [text];
|
|
463
|
+
}
|
|
464
|
+
const items = text.split("\n");
|
|
465
|
+
while (items.at(-1) === "") {
|
|
466
|
+
items.pop();
|
|
467
|
+
}
|
|
468
|
+
return items;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* The notes of one written column: what CyCSV cannot hold of its cells.
|
|
472
|
+
* @param domain - the table
|
|
473
|
+
* @param c - the candidate
|
|
474
|
+
* @param rows - the row count
|
|
475
|
+
* @param note - the recorder
|
|
476
|
+
*/
|
|
477
|
+
function cellNotes(domain, c, rows, note) {
|
|
478
|
+
const { source } = c;
|
|
479
|
+
if (source === null) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const label = `${domain} column "${source.meta.name}"`;
|
|
483
|
+
const { name } = source.meta;
|
|
484
|
+
const text = c.javaClass === STRING;
|
|
485
|
+
const { unset, equations, lists, set, heuristic } = cellCounts(c, source, rows);
|
|
486
|
+
if (source.dtype === "json") {
|
|
487
|
+
note(CYS_LOSS.JSON_AS_STRING, `${label} holds nested values; written as their JSON text`, name, set);
|
|
488
|
+
}
|
|
489
|
+
if (set === 0 && (!text || rows === 0)) {
|
|
490
|
+
note(LOSS.EMPTY_COLUMN, `${label} has no value; it is not in the re-import`, name, null);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
if (text && unset > 0) {
|
|
494
|
+
note(CYS_LOSS.UNSET_AS_EMPTY_STRING, `${label}: ${unset} unset cell${plural(unset)} ${agree(unset, "is", "are")} written empty and read back as ""`, name, unset);
|
|
495
|
+
}
|
|
496
|
+
if (lists > 0) {
|
|
497
|
+
note(CYS_LOSS.LIST_ITEMS, `${label}: ${lists} list cell${plural(lists)} ${agree(lists, "reads", "read")} back changed (an unset or empty list of text as [""], an empty list of numbers unset, trailing empty items dropped, items holding a newline split)`, name, lists);
|
|
498
|
+
}
|
|
499
|
+
if (equations > 0) {
|
|
500
|
+
note(CYS_LOSS.TEXT_AS_EQUATION, `${label}: ${equations} cell${plural(equations)} start with "=", which Cytoscape reads as a formula (an error cell); graph-io reads them back as text`, name, equations);
|
|
501
|
+
}
|
|
502
|
+
if (text && (source.dtype === "string" || source.dtype === "dict")) {
|
|
503
|
+
const readsAs = heuristic.decide();
|
|
504
|
+
if (readsAs !== source.dtype) {
|
|
505
|
+
note(LOSS.STORAGE_CLASS, `${label} reads back as ${readsAs} (the cardinality heuristic)`, name, null);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Count what CyCSV changes in a column's cells: unset cells, text read as a formula, lists that
|
|
511
|
+
* read back changed; and feed the text cells to the dict heuristic.
|
|
512
|
+
* @param c - the candidate
|
|
513
|
+
* @param source - its column
|
|
514
|
+
* @param rows - the row count
|
|
515
|
+
* @returns the counts and the heuristic
|
|
516
|
+
*/
|
|
517
|
+
function cellCounts(c, source, rows) {
|
|
518
|
+
const text = c.javaClass === STRING;
|
|
519
|
+
const stringList = c.javaClass === `java.util.List<${STRING}>`;
|
|
520
|
+
const counts = { unset: 0, equations: 0, lists: 0, set: 0, heuristic: new DictHeuristic() };
|
|
521
|
+
for (let r = 0; r < rows; r++) {
|
|
522
|
+
const cell = c.cell(r);
|
|
523
|
+
if (cell === null) {
|
|
524
|
+
counts.unset++;
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
counts.set++;
|
|
528
|
+
}
|
|
529
|
+
const written = cell ?? "";
|
|
530
|
+
if (text && !counts.heuristic.decided) {
|
|
531
|
+
counts.heuristic.observe(written);
|
|
532
|
+
}
|
|
533
|
+
if (written.startsWith("=")) {
|
|
534
|
+
counts.equations++;
|
|
535
|
+
}
|
|
536
|
+
else if (source.dtype === "list" && listChanges(source, r, cell, stringList)) {
|
|
537
|
+
counts.lists++;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return counts;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Whether a list cell reads back changed from the text CyCSV writes for it.
|
|
544
|
+
* @param source - the list column
|
|
545
|
+
* @param r - the row
|
|
546
|
+
* @param cell - the written text, or null when unset
|
|
547
|
+
* @param stringList - whether Cytoscape reads the column as a list of text
|
|
548
|
+
* @returns true when it changes
|
|
549
|
+
*/
|
|
550
|
+
function listChanges(source, r, cell, stringList) {
|
|
551
|
+
const written = cell ?? "";
|
|
552
|
+
let back = null;
|
|
553
|
+
if (stringList) {
|
|
554
|
+
back = written.length === 0 ? [""] : javaSplit(written);
|
|
555
|
+
}
|
|
556
|
+
else if (written.length > 0) {
|
|
557
|
+
back = javaSplit(written).filter((item) => item.length > 0);
|
|
558
|
+
}
|
|
559
|
+
const items = cell === null ? null : [...source.sliceOf(r)].map((v) => valueText(v, "", null));
|
|
560
|
+
if (back === null || items === null) {
|
|
561
|
+
return back !== items;
|
|
562
|
+
}
|
|
563
|
+
return back.length !== items.length || (stringList && back.some((b, i) => b !== items[i]));
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Whether a column is the z slot (a role-less f64 `z` column, as the importer makes it).
|
|
567
|
+
* @param column - the column
|
|
568
|
+
* @returns true for the z column
|
|
569
|
+
*/
|
|
570
|
+
function isZ(column) {
|
|
571
|
+
return column.meta.name === Z_COLUMN && column.meta.dtype === "f64" && column.meta.role === null;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* The candidates of the node or edge table: the label as `name`, then the exporter's own columns,
|
|
575
|
+
* then every other column in order.
|
|
576
|
+
* @param snapshot - the snapshot
|
|
577
|
+
* @param domain - node or edge
|
|
578
|
+
* @param own - the exporter's columns after the label
|
|
579
|
+
* @param skip - columns written elsewhere
|
|
580
|
+
* @param note - the recorder
|
|
581
|
+
* @returns the candidates
|
|
582
|
+
*/
|
|
583
|
+
function elementCandidates(snapshot, domain, own, skip, note) {
|
|
584
|
+
const table = domain === "node" ? snapshot.nodes : snapshot.edges;
|
|
585
|
+
const label = table.byRole("label");
|
|
586
|
+
const out = [];
|
|
587
|
+
if (label !== null) {
|
|
588
|
+
const c = candidateOf(label, "name");
|
|
589
|
+
out.push({ ...c, javaClass: STRING, cell: (row) => (label.isSet(row) ? textOf(label.value(row)) : null) });
|
|
590
|
+
if (label.dtype !== "string" && label.dtype !== "dict") {
|
|
591
|
+
note(LOSS.DTYPE, `${domain} label column "${label.meta.name}" (${label.dtype}) is written as text and reads back as string`, label.meta.name, label.length - label.nullCount);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
out.push(...own);
|
|
595
|
+
for (const column of table) {
|
|
596
|
+
const { role } = column.meta;
|
|
597
|
+
if (column === label || (role !== null && (SKIPPED_ROLES.has(role) || role === "id")) || skip(column)) {
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
out.push(candidateOf(column, column.meta.name));
|
|
601
|
+
if (label === null &&
|
|
602
|
+
role === null &&
|
|
603
|
+
column.meta.name === "name" &&
|
|
604
|
+
scalarClass(column, column.dtype) === STRING) {
|
|
605
|
+
note(LOSS.ROLE_ASSUMED, `${domain} column "name" is Cytoscape's name and reads back as the label`, "name", column.length - column.nullCount);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return out;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Settle a table and record the notes of its cells.
|
|
612
|
+
* @param domain - the table
|
|
613
|
+
* @param candidates - its columns
|
|
614
|
+
* @param rows - its row count
|
|
615
|
+
* @param note - the recorder
|
|
616
|
+
* @returns the columns
|
|
617
|
+
*/
|
|
618
|
+
function planTable(domain, candidates, rows, note) {
|
|
619
|
+
for (const c of candidates) {
|
|
620
|
+
cellNotes(domain, c, rows, note);
|
|
621
|
+
}
|
|
622
|
+
return settleNames(domain, candidates, note);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Note the node ids whose text changes on the way back: numbers read back as text, and the error
|
|
626
|
+
* for two ids with the same text.
|
|
627
|
+
* @param snapshot - the snapshot
|
|
628
|
+
* @param note - the recorder
|
|
629
|
+
* @returns the error the save throws for a collision, or null
|
|
630
|
+
*/
|
|
631
|
+
function idTextNotes(snapshot, note) {
|
|
632
|
+
let collisions = 0;
|
|
633
|
+
let numeric = 0;
|
|
634
|
+
const texts = new Set();
|
|
635
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
636
|
+
const id = snapshot.ids.idOf(i);
|
|
637
|
+
numeric += typeof id === "number" ? 1 : 0;
|
|
638
|
+
const text = String(id);
|
|
639
|
+
if (texts.has(text)) {
|
|
640
|
+
collisions++;
|
|
641
|
+
}
|
|
642
|
+
texts.add(text);
|
|
643
|
+
}
|
|
644
|
+
let fatal = null;
|
|
645
|
+
if (collisions > 0) {
|
|
646
|
+
note(LOSS.ID_TEXT_COLLISION, `${collisions} node id${plural(collisions)} ${agree(collisions, "shares", "share")} their text with another id (a number and a string); the save fails with E_INVALID_ID`, null, collisions);
|
|
647
|
+
fatal = new GraphFormatError("E_INVALID_ID", `${collisions} node id${plural(collisions)} ${agree(collisions, "shares", "share")} their text with another id`, { reason: "collision", count: collisions });
|
|
648
|
+
}
|
|
649
|
+
if (numeric > 0) {
|
|
650
|
+
note(LOSS.ID_TEXT_TYPE, `${numeric} numeric node id${plural(numeric)} ${agree(numeric, "reads", "read")} back as their text`, null, numeric);
|
|
651
|
+
}
|
|
652
|
+
return fatal;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Note the node ids that are not positive integers: renumbered under "mangle", else refused.
|
|
656
|
+
* @param snapshot - the snapshot
|
|
657
|
+
* @param bad - the indices of those ids
|
|
658
|
+
* @param mangle - whether sanitizeIds is "mangle"
|
|
659
|
+
* @param note - the recorder
|
|
660
|
+
* @returns the error the save throws, or null under "mangle"
|
|
661
|
+
*/
|
|
662
|
+
function badIdNotes(snapshot, bad, mangle, note) {
|
|
663
|
+
if (mangle) {
|
|
664
|
+
note(LOSS.ID_MANGLED, `${bad.length} node id${plural(bad.length)} that ${agree(bad.length, "is", "are")} not positive integers ${agree(bad.length, "is", "are")} renumbered, since Cytoscape ids are positive integers; the originals are kept in the "${ORIGINAL_ID_ATTRIBUTE}" column (restored by restoreMangledIds)`, null, bad.length);
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
note(LOSS.ID_CHARSET, `${bad.length} node id${plural(bad.length)} ${agree(bad.length, "is", "are")} not positive integers, which Cytoscape requires; the save fails unless sanitizeIds is "mangle"`, null, bad.length);
|
|
668
|
+
const first = snapshot.ids.idOf(bad[0]);
|
|
669
|
+
return new GraphFormatError("E_INVALID_ID", `${bad.length} node id${plural(bad.length)} cannot be written as Cytoscape ids, which are positive integers (first: ${JSON.stringify(first)} at index ${bad[0]}); pass sanitizeIds: "mangle" to rewrite them`, { reason: "charset", charset: "integer", count: bad.length, index: bad[0] });
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* The SUIDs: node ids that are positive integers keep them, the others get new ones (or refuse
|
|
673
|
+
* under sanitizeIds "error"); edge ids likewise, from the id role column. Every new SUID comes
|
|
674
|
+
* from one counter above the kept ones.
|
|
675
|
+
* @param snapshot - the snapshot
|
|
676
|
+
* @param common - the resolved common options
|
|
677
|
+
* @param note - the recorder
|
|
678
|
+
* @returns the SUIDs
|
|
679
|
+
*/
|
|
680
|
+
function planIds(snapshot, common, note) {
|
|
681
|
+
const { kept, bad } = keptNodeSuids(snapshot);
|
|
682
|
+
const collision = idTextNotes(snapshot, note);
|
|
683
|
+
const charset = bad.length > 0 ? badIdNotes(snapshot, bad, common.sanitizeIds === "mangle", note) : null;
|
|
684
|
+
const fatal = collision ?? charset;
|
|
685
|
+
const nodeSet = new Set(kept.filter((s) => s > 0));
|
|
686
|
+
const edgeKept = keptEdgeSuids(snapshot, nodeSet);
|
|
687
|
+
let next = 1;
|
|
688
|
+
for (const s of [...nodeSet, ...edgeKept]) {
|
|
689
|
+
next = Math.max(next, s + 1);
|
|
690
|
+
}
|
|
691
|
+
const nodeSuids = kept.map((s) => (s > 0 ? s : next++));
|
|
692
|
+
const idColumn = snapshot.edges.byRole("id");
|
|
693
|
+
let generated = 0;
|
|
694
|
+
const edgeSuids = edgeKept.map((s) => {
|
|
695
|
+
if (s > 0) {
|
|
696
|
+
return s;
|
|
697
|
+
}
|
|
698
|
+
generated++;
|
|
699
|
+
return next++;
|
|
700
|
+
});
|
|
701
|
+
if (idColumn !== null && generated > 0) {
|
|
702
|
+
note(LOSS.EDGE_IDS_GENERATED, `${generated} edge${plural(generated)} ${agree(generated, "has", "have")} no distinct positive integer id in "${idColumn.meta.name}"; they are written with new positive integer ids`, idColumn.meta.name, generated);
|
|
703
|
+
}
|
|
704
|
+
if (idColumn !== null && idColumn.dtype !== "string") {
|
|
705
|
+
note(LOSS.DTYPE, `edge id column "${idColumn.meta.name}" is ${idColumn.dtype}; edge ids read back as text`, idColumn.meta.name, snapshot.edgeCount - idColumn.nullCount);
|
|
706
|
+
}
|
|
707
|
+
return { nodeSuids, edgeSuids, next, fatal, mangled: common.sanitizeIds === "mangle" && bad.length > 0 };
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* The columns of the network's own table: its name (from meta.name unless a text graph column
|
|
711
|
+
* holds it), its description, and the graph columns.
|
|
712
|
+
* @param snapshot - the snapshot
|
|
713
|
+
* @param name - the network name
|
|
714
|
+
* @returns the candidates
|
|
715
|
+
*/
|
|
716
|
+
function networkCandidates(snapshot, name) {
|
|
717
|
+
const out = [];
|
|
718
|
+
const graphName = snapshot.graph.get("name");
|
|
719
|
+
if (graphName === null || (graphName.dtype !== "string" && graphName.dtype !== "dict")) {
|
|
720
|
+
out.push({ name: "name", source: null, javaClass: STRING, cell: () => name });
|
|
721
|
+
}
|
|
722
|
+
const { description } = snapshot.meta;
|
|
723
|
+
if (description !== null && snapshot.graph.get("description") === null) {
|
|
724
|
+
out.push({ name: "description", source: null, javaClass: STRING, cell: () => description });
|
|
725
|
+
}
|
|
726
|
+
for (const column of snapshot.graph) {
|
|
727
|
+
out.push(candidateOf(column, column.meta.name));
|
|
728
|
+
}
|
|
729
|
+
return out;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Plan an export: the notes, the fatal condition, the SUIDs and the tables.
|
|
733
|
+
* @param snapshot - the snapshot
|
|
734
|
+
* @param common - the resolved common options
|
|
735
|
+
* @returns the plan
|
|
736
|
+
*/
|
|
737
|
+
function plan(snapshot, common) {
|
|
738
|
+
const notes = [];
|
|
739
|
+
const note = (code, message, column = null, count = null) => {
|
|
740
|
+
notes.push(Object.freeze({ code, message, column, count }));
|
|
741
|
+
};
|
|
742
|
+
for (const gen of checkCapabilities(snapshot, CYS_CAPABILITIES, common, {
|
|
743
|
+
roles: SLOT_ROLES,
|
|
744
|
+
roleNames: ROLE_NAMES,
|
|
745
|
+
positionDtype: "f32",
|
|
746
|
+
})) {
|
|
747
|
+
// json is reported per column below; ids by the SUID rule (positive integers)
|
|
748
|
+
if (gen.code !== LOSS.JSON && gen.code !== LOSS.ID_CHARSET && gen.code !== LOSS.ID_MANGLED) {
|
|
749
|
+
notes.push(gen);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
const ids = planIds(snapshot, common, note);
|
|
753
|
+
const { nodeSuids, edgeSuids, mangled, fatal } = ids;
|
|
754
|
+
let { next } = ids;
|
|
755
|
+
const rootSuid = next++;
|
|
756
|
+
const subSuid = next++;
|
|
757
|
+
// direction
|
|
758
|
+
const folding = pairFolding(snapshot);
|
|
759
|
+
if (folding.mutualCount > 0) {
|
|
760
|
+
note(LOSS.MUTUAL_EXPANDED, `${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, "is", "are")} written as two directed edges; the mutual mark is lost`, null, folding.mutualCount);
|
|
761
|
+
}
|
|
762
|
+
// positions
|
|
763
|
+
const position = snapshot.nodes.byRole("position");
|
|
764
|
+
const z = [...snapshot.nodes].find(isZ) ?? null;
|
|
765
|
+
const positionZ = position !== null && z === null;
|
|
766
|
+
if (position !== null) {
|
|
767
|
+
positionNotes(position, z, note);
|
|
768
|
+
}
|
|
769
|
+
const placed = (position !== null && position.nullCount < position.length) || (z !== null && z.nullCount < z.length);
|
|
770
|
+
const viewSuid = placed ? next++ : null;
|
|
771
|
+
const viewNodeBase = next;
|
|
772
|
+
// node table
|
|
773
|
+
const nodeOwn = mangled
|
|
774
|
+
? [
|
|
775
|
+
{
|
|
776
|
+
name: ORIGINAL_ID_ATTRIBUTE,
|
|
777
|
+
source: null,
|
|
778
|
+
javaClass: STRING,
|
|
779
|
+
// every node carries its original, so an empty id ("") is told apart from an unset cell
|
|
780
|
+
cell: (row) => String(snapshot.ids.idOf(row)),
|
|
781
|
+
},
|
|
782
|
+
]
|
|
783
|
+
: [];
|
|
784
|
+
const nodeTable = planTable("node", elementCandidates(snapshot, "node", nodeOwn, (c) => placed && c === z, note), snapshot.nodeCount, note);
|
|
785
|
+
// edge table
|
|
786
|
+
const weights = explicitWeights(snapshot);
|
|
787
|
+
const plainWeight = snapshot.edges.get("weight");
|
|
788
|
+
if (plainWeight !== null && plainWeight.meta.role === null && !weights.weighted) {
|
|
789
|
+
note(LOSS.WEIGHT_KEY_CLASH, `edge column "weight" reads back as the edge weight`, "weight", null);
|
|
790
|
+
}
|
|
791
|
+
const edgeOwn = weights.weighted
|
|
792
|
+
? [
|
|
793
|
+
{
|
|
794
|
+
name: "weight",
|
|
795
|
+
source: null,
|
|
796
|
+
javaClass: DOUBLE,
|
|
797
|
+
cell: (e) => (weights.isExplicit(e) ? formatDecimal(weights.value(e)) : null),
|
|
798
|
+
},
|
|
799
|
+
]
|
|
800
|
+
: [];
|
|
801
|
+
const edgeRows = snapshot.edgeCount;
|
|
802
|
+
const edgeTable = planTable("edge", elementCandidates(snapshot, "edge", edgeOwn, () => false, note), edgeRows, note);
|
|
803
|
+
const name = snapshot.meta.name ?? DEFAULT_NAME;
|
|
804
|
+
const networkTable = planTable("network", networkCandidates(snapshot, name), 1, note);
|
|
805
|
+
const rootTable = [{ name: "name", javaClass: STRING, cell: () => name }];
|
|
806
|
+
return {
|
|
807
|
+
notes,
|
|
808
|
+
fatal,
|
|
809
|
+
nodeSuids,
|
|
810
|
+
edgeSuids,
|
|
811
|
+
rootSuid,
|
|
812
|
+
subSuid,
|
|
813
|
+
viewSuid,
|
|
814
|
+
viewNodeBase,
|
|
815
|
+
name,
|
|
816
|
+
folding,
|
|
817
|
+
nodeTable,
|
|
818
|
+
edgeTable,
|
|
819
|
+
networkTable,
|
|
820
|
+
rootTable,
|
|
821
|
+
position,
|
|
822
|
+
z: placed ? z : null,
|
|
823
|
+
positionZ,
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* The notes of the position column: its shape, non-finite coordinates, and a z that reads back
|
|
828
|
+
* in the z column.
|
|
829
|
+
* @param position - the position column
|
|
830
|
+
* @param z - the z column, or null
|
|
831
|
+
* @param note - the recorder
|
|
832
|
+
*/
|
|
833
|
+
function positionNotes(position, z, note) {
|
|
834
|
+
const { name, components } = position.meta;
|
|
835
|
+
if (components !== 3) {
|
|
836
|
+
note(CYS_LOSS.POSITION, `position column "${name}" has ${components} component${plural(components)} and reads back with 3`, name, null);
|
|
837
|
+
}
|
|
838
|
+
let nonFinite = 0;
|
|
839
|
+
let depth = 0;
|
|
840
|
+
for (let r = 0; r < position.length; r++) {
|
|
841
|
+
if (!position.isSet(r)) {
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
const xyz = Array.from(position.value(r));
|
|
845
|
+
if (xyz.slice(0, 2).some((v) => !Number.isFinite(v))) {
|
|
846
|
+
nonFinite++;
|
|
847
|
+
}
|
|
848
|
+
else if ((xyz[2] ?? 0) !== 0) {
|
|
849
|
+
depth++;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
if (nonFinite > 0) {
|
|
853
|
+
note(CYS_LOSS.POSITION, `${nonFinite} position${plural(nonFinite)} with a non-finite coordinate ${agree(nonFinite, "is", "are")} not written`, name, nonFinite);
|
|
854
|
+
}
|
|
855
|
+
if (depth > 0) {
|
|
856
|
+
note(CYS_LOSS.POSITION, z === null
|
|
857
|
+
? `${depth} position${plural(depth)} ${agree(depth, "has", "have")} a z; it is written as the view's z and reads back in the z column`
|
|
858
|
+
: `${depth} position${plural(depth)} ${agree(depth, "has", "have")} a z, but the view's z holds the z column; the position z is lost`, name, depth);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
// ============================================================ writing
|
|
862
|
+
/**
|
|
863
|
+
* Cytoscape's `SessionUtil.escape`: Java's `URLEncoder.encode(text, "UTF-8")` (letters, digits
|
|
864
|
+
* and `.-*_` kept, space as `+`, everything else `%XX`), then `-` as `%2D`.
|
|
865
|
+
* @param text - the text
|
|
866
|
+
* @returns the escaped text
|
|
867
|
+
* @category Plugin helpers
|
|
868
|
+
*/
|
|
869
|
+
export function sessionEscape(text) {
|
|
870
|
+
const wellFormed = text.replaceAll(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "�");
|
|
871
|
+
return encodeURIComponent(wellFormed)
|
|
872
|
+
.replaceAll(/[!~'()]/g, (c) => `%${(c.codePointAt(0) ?? 0).toString(16).toUpperCase()}`)
|
|
873
|
+
.replaceAll("%20", "+")
|
|
874
|
+
.replaceAll("-", "%2D");
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* One CyCSV table (schema version 1): the key column SUID, then the columns.
|
|
878
|
+
* @param title - the table title
|
|
879
|
+
* @param columns - the columns
|
|
880
|
+
* @param rows - the SUID of each row, with its row index
|
|
881
|
+
* @yields the table text
|
|
882
|
+
* @returns nothing
|
|
883
|
+
*/
|
|
884
|
+
function* cyTable(title, columns, rows) {
|
|
885
|
+
const line = (cells) => `${cells.map((c) => quoteCsvCell(c)).join(",")}\n`;
|
|
886
|
+
yield line(["CyCSV-Version", "1"]);
|
|
887
|
+
yield line(["SUID", ...columns.map((c) => c.name)]);
|
|
888
|
+
yield line([LONG, ...columns.map((c) => c.javaClass)]);
|
|
889
|
+
yield line(["", ...columns.map(() => "mutable")]);
|
|
890
|
+
yield line([title, ""]);
|
|
891
|
+
for (const [suid, row] of rows) {
|
|
892
|
+
yield line([String(suid), ...columns.map((c) => c.cell(row) ?? "")]);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
/** The XML declaration and the namespaces of a session XGMML document. */
|
|
896
|
+
const XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
|
|
897
|
+
const NAMESPACES = `xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="${XLINK_NAMESPACE}" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cy="${CY_NAMESPACE}" xmlns="${XGMML_NAMESPACE}"`;
|
|
898
|
+
/**
|
|
899
|
+
* The network file: the root network holding the one registered subnetwork, nodes and edges by
|
|
900
|
+
* SUID. No attributes: a 3.x session keeps them in the tables.
|
|
901
|
+
* @param snapshot - the snapshot
|
|
902
|
+
* @param p - the plan
|
|
903
|
+
* @yields the document's text
|
|
904
|
+
* @returns nothing
|
|
905
|
+
*/
|
|
906
|
+
function* networkFile(snapshot, p) {
|
|
907
|
+
const label = !hasIllegalXmlChar(p.name) ? ` label="${escapeXmlAttribute(p.name)}"` : "";
|
|
908
|
+
yield XML_DECLARATION;
|
|
909
|
+
yield `<graph id="${p.rootSuid}"${label} directed="${snapshot.directed ? "1" : "0"}" cy:view="0" cy:registered="0" cy:documentVersion="3.0" ${NAMESPACES}>\n`;
|
|
910
|
+
yield ` <att>\n <graph id="${p.subSuid}"${label} cy:registered="1">\n`;
|
|
911
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
912
|
+
yield ` <node id="${p.nodeSuids[i]}"/>\n`;
|
|
913
|
+
}
|
|
914
|
+
const { src, dst } = snapshot.edgeList();
|
|
915
|
+
for (let e = 0; e < snapshot.edgeCount; e++) {
|
|
916
|
+
if (p.folding.folded(e)) {
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
const directed = snapshot.directed && p.folding.sourceDirected(e);
|
|
920
|
+
yield ` <edge id="${p.edgeSuids[e]}" source="${p.nodeSuids[src[e]]}" target="${p.nodeSuids[dst[e]]}" cy:directed="${directed ? "1" : "0"}"/>\n`;
|
|
921
|
+
}
|
|
922
|
+
yield " </graph>\n </att>\n</graph>\n";
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* The view file: one view node per placed node, x and y (y negated back to screen coordinates)
|
|
926
|
+
* and z.
|
|
927
|
+
* @param snapshot - the snapshot
|
|
928
|
+
* @param p - the plan
|
|
929
|
+
* @param viewSuid - the view's SUID
|
|
930
|
+
* @yields the document's text
|
|
931
|
+
* @returns nothing
|
|
932
|
+
*/
|
|
933
|
+
function* viewFile(snapshot, p, viewSuid) {
|
|
934
|
+
yield XML_DECLARATION;
|
|
935
|
+
yield `<graph id="${viewSuid}" label="${viewSuid}" cy:view="1" cy:networkId="${p.subSuid}" cy:visualStyle="default" cy:rendererId="org.cytoscape.ding" cy:documentVersion="3.0" ${NAMESPACES}>\n`;
|
|
936
|
+
let view = p.viewNodeBase;
|
|
937
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
938
|
+
const coords = viewCoords(p, i);
|
|
939
|
+
if (coords.length > 0) {
|
|
940
|
+
yield ` <node id="${view++}" cy:nodeId="${p.nodeSuids[i]}">\n <graphics ${coords.join(" ")}/>\n </node>\n`;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
yield "</graph>\n";
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* The graphics attributes of one node's view: x and y (y down, as Cytoscape draws), and z.
|
|
947
|
+
* @param p - the plan
|
|
948
|
+
* @param i - the node
|
|
949
|
+
* @returns the attributes, none when the node has no position
|
|
950
|
+
*/
|
|
951
|
+
function viewCoords(p, i) {
|
|
952
|
+
const coords = [];
|
|
953
|
+
if (p.position?.isSet(i) === true) {
|
|
954
|
+
const [x, y, z] = Array.from(p.position.value(i));
|
|
955
|
+
if (Number.isFinite(x) && Number.isFinite(y)) {
|
|
956
|
+
coords.push(`x="${formatF32(x)}"`, `y="${formatF32(y === 0 ? 0 : -y)}"`);
|
|
957
|
+
}
|
|
958
|
+
if (Number.isFinite(x) &&
|
|
959
|
+
Number.isFinite(y) &&
|
|
960
|
+
p.positionZ &&
|
|
961
|
+
z !== undefined &&
|
|
962
|
+
z !== 0 &&
|
|
963
|
+
Number.isFinite(z)) {
|
|
964
|
+
coords.push(`z="${formatF32(z)}"`);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
if (p.z?.isSet(i) === true) {
|
|
968
|
+
coords.push(`z="${formatDecimal(p.z.value(i))}"`);
|
|
969
|
+
}
|
|
970
|
+
return coords;
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* The text of string parts, UTF-8 encoded.
|
|
974
|
+
* @param parts - the parts
|
|
975
|
+
* @returns the bytes
|
|
976
|
+
*/
|
|
977
|
+
function encode(parts) {
|
|
978
|
+
let text = "";
|
|
979
|
+
for (const part of parts) {
|
|
980
|
+
text += part;
|
|
981
|
+
}
|
|
982
|
+
return new TextEncoder().encode(text);
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* The archive's entries, each built when the zip writer asks for it.
|
|
986
|
+
* @param snapshot - the snapshot
|
|
987
|
+
* @param p - the plan
|
|
988
|
+
* @yields the entries
|
|
989
|
+
* @returns nothing
|
|
990
|
+
*/
|
|
991
|
+
function* entries(snapshot, p) {
|
|
992
|
+
if (p.fatal !== null) {
|
|
993
|
+
throw p.fatal;
|
|
994
|
+
}
|
|
995
|
+
const name = sessionEscape(p.name);
|
|
996
|
+
const tables = `${ROOT_FOLDER}tables/${p.subSuid}-${name}/LOCAL_ATTRS-org.cytoscape.model.`;
|
|
997
|
+
const rowsOf = (suids, skip) => suids.flatMap((suid, row) => (skip?.(row) === true ? [] : [[suid, row]]));
|
|
998
|
+
const nodeTable = `${p.name} default node`;
|
|
999
|
+
const edgeTable = `${p.name} default edge`;
|
|
1000
|
+
const networkTable = `${p.name} default network`;
|
|
1001
|
+
yield { name: `${ROOT_FOLDER}3.0.0.version`, data: new Uint8Array(0) };
|
|
1002
|
+
yield { name: `${ROOT_FOLDER}networks/${p.rootSuid}-${name}.xgmml`, data: encode(networkFile(snapshot, p)) };
|
|
1003
|
+
yield {
|
|
1004
|
+
name: `${tables}CyNode-${sessionEscape(nodeTable)}.cytable`,
|
|
1005
|
+
data: encode(cyTable(nodeTable, p.nodeTable, rowsOf(p.nodeSuids))),
|
|
1006
|
+
};
|
|
1007
|
+
yield {
|
|
1008
|
+
name: `${tables}CyEdge-${sessionEscape(edgeTable)}.cytable`,
|
|
1009
|
+
data: encode(cyTable(edgeTable, p.edgeTable, rowsOf(p.edgeSuids, (e) => p.folding.folded(e)))),
|
|
1010
|
+
};
|
|
1011
|
+
yield {
|
|
1012
|
+
name: `${tables}CyNetwork-${sessionEscape(networkTable)}.cytable`,
|
|
1013
|
+
data: encode(cyTable(networkTable, p.networkTable, [[p.subSuid, 0]])),
|
|
1014
|
+
};
|
|
1015
|
+
yield {
|
|
1016
|
+
name: `${ROOT_FOLDER}tables/${p.rootSuid}-${name}/LOCAL_ATTRS-org.cytoscape.model.CyNetwork-${p.rootSuid}+default+network.cytable`,
|
|
1017
|
+
data: encode(cyTable(`${p.rootSuid} default network`, p.rootTable, [[p.rootSuid, 0]])),
|
|
1018
|
+
};
|
|
1019
|
+
if (p.viewSuid !== null) {
|
|
1020
|
+
yield {
|
|
1021
|
+
name: `${ROOT_FOLDER}views/${p.subSuid}-${p.viewSuid}-${name}.xgmml`,
|
|
1022
|
+
data: encode(viewFile(snapshot, p, p.viewSuid)),
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* The archive as byte chunks.
|
|
1028
|
+
* @param snapshot - the snapshot
|
|
1029
|
+
* @param options - the options
|
|
1030
|
+
* @yields the bytes
|
|
1031
|
+
* @returns nothing
|
|
1032
|
+
*/
|
|
1033
|
+
async function* write(snapshot, options) {
|
|
1034
|
+
const p = plan(snapshot, resolveExportOptions(options));
|
|
1035
|
+
await Promise.resolve();
|
|
1036
|
+
yield* writeZip(entries(snapshot, p));
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* The Cytoscape session exporter.
|
|
1040
|
+
* @category Built-in formats
|
|
1041
|
+
*/
|
|
1042
|
+
export const cysExporter = Object.freeze({
|
|
1043
|
+
format: FORMAT,
|
|
1044
|
+
options: Object.freeze([]),
|
|
1045
|
+
capabilities: CYS_CAPABILITIES,
|
|
1046
|
+
/**
|
|
1047
|
+
* Pre-flight: what export() would lose, without writing anything.
|
|
1048
|
+
* @param snapshot - the snapshot
|
|
1049
|
+
* @param options - the common options
|
|
1050
|
+
* @returns the notes, empty when the export is exact
|
|
1051
|
+
*/
|
|
1052
|
+
check(snapshot, options) {
|
|
1053
|
+
return Object.freeze([...plan(snapshot, resolveExportOptions(options)).notes]);
|
|
1054
|
+
},
|
|
1055
|
+
/**
|
|
1056
|
+
* Write the session as zip bytes (stored entries, one at a time).
|
|
1057
|
+
* @param snapshot - the snapshot
|
|
1058
|
+
* @param options - the common options
|
|
1059
|
+
* @returns the bytes
|
|
1060
|
+
*/
|
|
1061
|
+
export(snapshot, options) {
|
|
1062
|
+
return write(snapshot, options);
|
|
1063
|
+
},
|
|
1064
|
+
/**
|
|
1065
|
+
* A session is a zip archive, not text: always rejects with E_UNSUPPORTED (use exportGraphToBytes() or exportGraphToBlob()).
|
|
1066
|
+
* @returns never
|
|
1067
|
+
*/
|
|
1068
|
+
exportToString() {
|
|
1069
|
+
return Promise.reject(new GraphFormatError("E_UNSUPPORTED", "a Cytoscape session is a zip file, not text; use exportGraphToBytes() or exportGraphToBlob()", { reason: "binary" }));
|
|
1070
|
+
},
|
|
1071
|
+
});
|
|
1072
|
+
//# sourceMappingURL=exporter.js.map
|