graphwise-reactodia 0.0.1-TR10 → 0.0.1-TR12
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/COPYING +674 -0
- package/COPYING.LESSER +165 -0
- package/NOTICE +41 -0
- package/README.md +12 -0
- package/dist/cjs/graphwise-reactodia.cjs.entry.js +820 -81
- package/dist/collection/components/graphwise-reactodia/i18n/translations.js +4 -1
- package/dist/collection/components/graphwise-reactodia/menu/graphwise-menu.js +37 -0
- package/dist/collection/components/graphwise-reactodia/reactodia-app.js +6 -4
- package/dist/collection/components/graphwise-reactodia/services/diagram/diagram.service.js +14 -0
- package/dist/collection/components/graphwise-reactodia/services/diagram-file/diagram-file.service.js +68 -0
- package/dist/esm/graphwise-reactodia.entry.js +820 -81
- package/dist/graphwise-reactodia/graphwise-reactodia.esm.js +1 -1
- package/dist/graphwise-reactodia/p-52bd1b0e.entry.js +1 -0
- package/dist/types/components/graphwise-reactodia/menu/graphwise-menu.d.ts +9 -0
- package/dist/types/components/graphwise-reactodia/services/diagram/diagram.service.d.ts +11 -1
- package/dist/types/components/graphwise-reactodia/services/diagram-file/diagram-file.service.d.ts +24 -0
- package/package.json +6 -3
- package/dist/graphwise-reactodia/p-b56c58a0.entry.js +0 -1
|
@@ -774,7 +774,7 @@ var global$1 = (typeof global !== "undefined" ? global :
|
|
|
774
774
|
typeof self !== "undefined" ? self :
|
|
775
775
|
typeof window !== "undefined" ? window : {});
|
|
776
776
|
|
|
777
|
-
var lookup = [];
|
|
777
|
+
var lookup$1 = [];
|
|
778
778
|
var revLookup = [];
|
|
779
779
|
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
|
|
780
780
|
var inited = false;
|
|
@@ -782,7 +782,7 @@ function init () {
|
|
|
782
782
|
inited = true;
|
|
783
783
|
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
784
784
|
for (var i = 0, len = code.length; i < len; ++i) {
|
|
785
|
-
lookup[i] = code[i];
|
|
785
|
+
lookup$1[i] = code[i];
|
|
786
786
|
revLookup[code.charCodeAt(i)] = i;
|
|
787
787
|
}
|
|
788
788
|
|
|
@@ -836,7 +836,7 @@ function toByteArray (b64) {
|
|
|
836
836
|
}
|
|
837
837
|
|
|
838
838
|
function tripletToBase64 (num) {
|
|
839
|
-
return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
|
|
839
|
+
return lookup$1[num >> 18 & 0x3F] + lookup$1[num >> 12 & 0x3F] + lookup$1[num >> 6 & 0x3F] + lookup$1[num & 0x3F]
|
|
840
840
|
}
|
|
841
841
|
|
|
842
842
|
function encodeChunk (uint8, start, end) {
|
|
@@ -868,14 +868,14 @@ function fromByteArray (uint8) {
|
|
|
868
868
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
869
869
|
if (extraBytes === 1) {
|
|
870
870
|
tmp = uint8[len - 1];
|
|
871
|
-
output += lookup[tmp >> 2];
|
|
872
|
-
output += lookup[(tmp << 4) & 0x3F];
|
|
871
|
+
output += lookup$1[tmp >> 2];
|
|
872
|
+
output += lookup$1[(tmp << 4) & 0x3F];
|
|
873
873
|
output += '==';
|
|
874
874
|
} else if (extraBytes === 2) {
|
|
875
875
|
tmp = (uint8[len - 2] << 8) + (uint8[len - 1]);
|
|
876
|
-
output += lookup[tmp >> 10];
|
|
877
|
-
output += lookup[(tmp >> 4) & 0x3F];
|
|
878
|
-
output += lookup[(tmp << 2) & 0x3F];
|
|
876
|
+
output += lookup$1[tmp >> 10];
|
|
877
|
+
output += lookup$1[(tmp >> 4) & 0x3F];
|
|
878
|
+
output += lookup$1[(tmp << 2) & 0x3F];
|
|
879
879
|
output += '=';
|
|
880
880
|
}
|
|
881
881
|
|
|
@@ -6398,6 +6398,567 @@ function escapeRegexp(token) {
|
|
|
6398
6398
|
return token.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
6399
6399
|
}
|
|
6400
6400
|
//#endregion
|
|
6401
|
+
//#region src/data/sparql/sparqlModels.ts
|
|
6402
|
+
function mapSparqlResponseIntoRdfJs(response, factory) {
|
|
6403
|
+
function mapSparqlBinding(binding) {
|
|
6404
|
+
const mapped = {};
|
|
6405
|
+
for (const key in binding) if (Object.prototype.hasOwnProperty.call(binding, key)) {
|
|
6406
|
+
const term = binding[key];
|
|
6407
|
+
switch (term.type) {
|
|
6408
|
+
case "uri":
|
|
6409
|
+
mapped[key] = factory.namedNode(term.value);
|
|
6410
|
+
break;
|
|
6411
|
+
case "bnode":
|
|
6412
|
+
mapped[key] = factory.blankNode(term.value);
|
|
6413
|
+
break;
|
|
6414
|
+
case "literal":
|
|
6415
|
+
case "typed-literal":
|
|
6416
|
+
mapped[key] = factory.literal(term.value, term["xml:lang"] ? term["xml:lang"] : term.datatype ? factory.namedNode(term.datatype) : void 0);
|
|
6417
|
+
break;
|
|
6418
|
+
default: throw new Error(`Unexpected SPARQL term type: "${term.type}"`);
|
|
6419
|
+
}
|
|
6420
|
+
}
|
|
6421
|
+
return mapped;
|
|
6422
|
+
}
|
|
6423
|
+
return {
|
|
6424
|
+
...response,
|
|
6425
|
+
results: {
|
|
6426
|
+
...response.results,
|
|
6427
|
+
bindings: response.results.bindings.map(mapSparqlBinding)
|
|
6428
|
+
}
|
|
6429
|
+
};
|
|
6430
|
+
}
|
|
6431
|
+
function isRdfIri(term) {
|
|
6432
|
+
return Boolean(term && term.termType === "NamedNode");
|
|
6433
|
+
}
|
|
6434
|
+
function isRdfBlank(term) {
|
|
6435
|
+
return Boolean(term && term.termType === "BlankNode");
|
|
6436
|
+
}
|
|
6437
|
+
function isRdfLiteral(term) {
|
|
6438
|
+
return Boolean(term && term.termType === "Literal");
|
|
6439
|
+
}
|
|
6440
|
+
function isBlankBinding(binding) {
|
|
6441
|
+
const blank = binding;
|
|
6442
|
+
return blank.blankTrgProp !== void 0 || blank.blankTrg !== void 0 || blank.blankSrcProp !== void 0 || blank.blankSrc !== void 0;
|
|
6443
|
+
}
|
|
6444
|
+
/**
|
|
6445
|
+
* IRI prefix for the encoded blank node subtype produced by this module.
|
|
6446
|
+
*
|
|
6447
|
+
* @see {@link isEncodedBlank}
|
|
6448
|
+
*/
|
|
6449
|
+
var ENCODED_PREFIX = "urn:reactodia:blank:sparql:";
|
|
6450
|
+
/**
|
|
6451
|
+
* There is no way to refer to an existing blank node from a SPARQL query, and the labels
|
|
6452
|
+
* an endpoint returns for one are local to the response they came in. Instead of trying to
|
|
6453
|
+
* address a blank node, the whole statement set which describes it is encoded into the
|
|
6454
|
+
* element IRI, which makes the identity content-addressed: the same surrounding graph always
|
|
6455
|
+
* yields the same IRI, and every request about such an element can be answered locally by
|
|
6456
|
+
* decoding it back, without another round-trip to the endpoint.
|
|
6457
|
+
*
|
|
6458
|
+
* The trade-off is size: the IRI grows linearly with the number of statements attached to
|
|
6459
|
+
* the blank node, at roughly 300 characters per statement. {@link MAX_RECURSION_DEEP} bounds
|
|
6460
|
+
* how deep nesting can go, but nothing bounds how wide a single blank node can be, so a long
|
|
6461
|
+
* RDF list produces a correspondingly long IRI (measured against the W3C Organization
|
|
6462
|
+
* ontology: 380-1650 characters for its OWL axioms; a synthetic 100-member list: ~28 KB).
|
|
6463
|
+
*/
|
|
6464
|
+
function isEncodedBlank$1(iri) {
|
|
6465
|
+
return iri.startsWith(ENCODED_PREFIX);
|
|
6466
|
+
}
|
|
6467
|
+
/**
|
|
6468
|
+
* Additional output bindings which {@link BLANK_NODE_QUERY} produces, to be added
|
|
6469
|
+
* to the outer projection of the lookup query.
|
|
6470
|
+
*/
|
|
6471
|
+
var BLANK_NODE_QUERY_PARAMETERS = "?blankType ?blankTrgProp ?blankTrg ?blankSrc ?blankSrcProp";
|
|
6472
|
+
/**
|
|
6473
|
+
* Lookup query addition which describes every blank node in `?inst` by its outgoing
|
|
6474
|
+
* statements and the statement pointing at it.
|
|
6475
|
+
*
|
|
6476
|
+
* The three alternatives handle a plain blank node, an RDF list referenced directly
|
|
6477
|
+
* and a cell in the middle of an RDF list (which is resolved to the list head, so that
|
|
6478
|
+
* a list appears as a single element rather than one element per cell).
|
|
6479
|
+
*/
|
|
6480
|
+
var BLANK_NODE_QUERY = `
|
|
6481
|
+
OPTIONAL {
|
|
6482
|
+
FILTER (ISBLANK(?inst)).
|
|
6483
|
+
{
|
|
6484
|
+
?inst ?blankTrgProp ?blankTrg.
|
|
6485
|
+
?blankSrc ?blankSrcProp ?inst.
|
|
6486
|
+
FILTER NOT EXISTS { ?inst rdf:first _:smth1 }.
|
|
6487
|
+
BIND("blankNode" as ?blankType)
|
|
6488
|
+
} UNION {
|
|
6489
|
+
?inst rdf:rest*/rdf:first ?blankTrg.
|
|
6490
|
+
?blankSrc ?blankSrcProp ?inst.
|
|
6491
|
+
_:smth2 rdf:first ?blankTrg.
|
|
6492
|
+
BIND(?blankSrcProp as ?blankTrgProp)
|
|
6493
|
+
BIND("listHead" as ?blankType)
|
|
6494
|
+
FILTER NOT EXISTS { _:smth3 rdf:rest ?inst }.
|
|
6495
|
+
} UNION {
|
|
6496
|
+
?listHead rdf:rest* ?inst.
|
|
6497
|
+
FILTER NOT EXISTS { _:smth4 rdf:rest ?listHead }.
|
|
6498
|
+
|
|
6499
|
+
?listHead rdf:rest*/rdf:first ?blankTrg.
|
|
6500
|
+
?blankSrc ?blankSrcProp ?listHead.
|
|
6501
|
+
_:smth5 rdf:first ?blankTrg.
|
|
6502
|
+
BIND(?blankSrcProp as ?blankTrgProp)
|
|
6503
|
+
BIND("listHead" as ?blankType)
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6506
|
+
`;
|
|
6507
|
+
var XSD_STRING = "http://www.w3.org/2001/XMLSchema#string";
|
|
6508
|
+
function encodeTerm(term) {
|
|
6509
|
+
switch (term.termType) {
|
|
6510
|
+
case "NamedNode": return ["i", term.value];
|
|
6511
|
+
case "BlankNode": return ["b", term.value];
|
|
6512
|
+
case "Literal":
|
|
6513
|
+
if (term.language) return [
|
|
6514
|
+
"l",
|
|
6515
|
+
term.value,
|
|
6516
|
+
term.language
|
|
6517
|
+
];
|
|
6518
|
+
else if (term.datatype && term.datatype.value !== XSD_STRING) return [
|
|
6519
|
+
"d",
|
|
6520
|
+
term.value,
|
|
6521
|
+
term.datatype.value
|
|
6522
|
+
];
|
|
6523
|
+
return ["l", term.value];
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
function decodeTerm(encoded, factory) {
|
|
6527
|
+
switch (encoded[0]) {
|
|
6528
|
+
case "i": return factory.namedNode(encoded[1]);
|
|
6529
|
+
case "b": return factory.blankNode(encoded[1]);
|
|
6530
|
+
case "l": return factory.literal(encoded[1], encoded[2]);
|
|
6531
|
+
case "d": return factory.literal(encoded[1], factory.namedNode(encoded[2]));
|
|
6532
|
+
default: throw new Error(`Unexpected encoded blank node term: ${JSON.stringify(encoded)}`);
|
|
6533
|
+
}
|
|
6534
|
+
}
|
|
6535
|
+
/**
|
|
6536
|
+
* Encodes the statement set which describes a single blank node into an element IRI.
|
|
6537
|
+
*
|
|
6538
|
+
* Bindings are deduplicated and sorted, so that the same statement set always produces
|
|
6539
|
+
* the same IRI regardless of the order the endpoint returned it in. The blank node label
|
|
6540
|
+
* itself is deliberately left out: it is local to the response it came from and would make
|
|
6541
|
+
* the IRI change between otherwise identical requests.
|
|
6542
|
+
*/
|
|
6543
|
+
function encodeId(blankBindings, statementLimit) {
|
|
6544
|
+
const bindingSet = /* @__PURE__ */ new Map();
|
|
6545
|
+
for (const binding of blankBindings) {
|
|
6546
|
+
const encoded = {
|
|
6547
|
+
t: binding.blankType.value,
|
|
6548
|
+
p: encodeTerm(binding.blankTrgProp),
|
|
6549
|
+
o: encodeTerm(binding.blankTrg),
|
|
6550
|
+
s: binding.blankSrc ? encodeTerm(binding.blankSrc) : void 0,
|
|
6551
|
+
sp: binding.blankSrcProp ? encodeTerm(binding.blankSrcProp) : void 0,
|
|
6552
|
+
c: binding.class ? encodeTerm(binding.class) : void 0
|
|
6553
|
+
};
|
|
6554
|
+
bindingSet.set(JSON.stringify(encoded), encoded);
|
|
6555
|
+
}
|
|
6556
|
+
const orderedKeys = Array.from(bindingSet.keys()).sort();
|
|
6557
|
+
const normalizedBindings = (typeof statementLimit === "number" ? orderedKeys.slice(0, Math.max(statementLimit, 0)) : orderedKeys).map((key) => bindingSet.get(key));
|
|
6558
|
+
return ENCODED_PREFIX + toBase64Url(JSON.stringify(normalizedBindings));
|
|
6559
|
+
}
|
|
6560
|
+
/**
|
|
6561
|
+
* Encodes the payload as base64url rather than percent-escaping it.
|
|
6562
|
+
*
|
|
6563
|
+
* A nested blank node is embedded into its parent by its own encoded IRI, and base64url
|
|
6564
|
+
* output contains only characters which need no escaping, so nesting costs a flat 4/3 per
|
|
6565
|
+
* level. Percent-escaping would escape the escapes, roughly doubling the payload at every
|
|
6566
|
+
* level instead.
|
|
6567
|
+
*/
|
|
6568
|
+
function toBase64Url(text) {
|
|
6569
|
+
const bytes = new TextEncoder().encode(text);
|
|
6570
|
+
let binary = "";
|
|
6571
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
6572
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
6573
|
+
}
|
|
6574
|
+
function fromBase64Url(encoded) {
|
|
6575
|
+
const binary = atob(encoded.replace(/-/g, "+").replace(/_/g, "/"));
|
|
6576
|
+
const bytes = new Uint8Array(binary.length);
|
|
6577
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
6578
|
+
return new TextDecoder().decode(bytes);
|
|
6579
|
+
}
|
|
6580
|
+
/**
|
|
6581
|
+
* Decodes an element IRI produced by {@link encodeId} back into the statement set it
|
|
6582
|
+
* was made from, or returns `undefined` if the IRI is not an encoded blank node or
|
|
6583
|
+
* cannot be parsed.
|
|
6584
|
+
*/
|
|
6585
|
+
function decodeId(id, factory) {
|
|
6586
|
+
if (!isEncodedBlank$1(id)) return;
|
|
6587
|
+
let parsed;
|
|
6588
|
+
try {
|
|
6589
|
+
const encodedPart = id.substring(27);
|
|
6590
|
+
parsed = JSON.parse(fromBase64Url(encodedPart));
|
|
6591
|
+
if (!Array.isArray(parsed)) return;
|
|
6592
|
+
} catch {
|
|
6593
|
+
return;
|
|
6594
|
+
}
|
|
6595
|
+
try {
|
|
6596
|
+
const inst = factory.namedNode(id);
|
|
6597
|
+
return parsed.map((encoded) => {
|
|
6598
|
+
const binding = {
|
|
6599
|
+
inst,
|
|
6600
|
+
blankType: factory.literal(encoded.t),
|
|
6601
|
+
blankTrgProp: decodeTerm(encoded.p, factory),
|
|
6602
|
+
blankTrg: decodeTerm(encoded.o, factory),
|
|
6603
|
+
blankSrc: encoded.s ? decodeTerm(encoded.s, factory) : void 0,
|
|
6604
|
+
blankSrcProp: encoded.sp ? decodeTerm(encoded.sp, factory) : void 0,
|
|
6605
|
+
class: encoded.c ? decodeTerm(encoded.c, factory) : void 0
|
|
6606
|
+
};
|
|
6607
|
+
binding.label = createLabelForBlankBinding(binding, factory);
|
|
6608
|
+
return binding;
|
|
6609
|
+
});
|
|
6610
|
+
} catch {
|
|
6611
|
+
return;
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
function createLabelForBlankBinding(binding, factory) {
|
|
6615
|
+
if (binding.blankType.value === "listHead") return factory.literal("RDFList");
|
|
6616
|
+
const { class: classTerm } = binding;
|
|
6617
|
+
return factory.literal(classTerm ? getLocalName(classTerm.value) ?? classTerm.value : "anonymous");
|
|
6618
|
+
}
|
|
6619
|
+
/**
|
|
6620
|
+
* Deduplicates identical in-flight queries, since the chains being followed
|
|
6621
|
+
* often overlap on their leading statements.
|
|
6622
|
+
*/
|
|
6623
|
+
var QueryExecutor = class {
|
|
6624
|
+
queryFunction;
|
|
6625
|
+
running = /* @__PURE__ */ new Map();
|
|
6626
|
+
constructor(queryFunction) {
|
|
6627
|
+
this.queryFunction = queryFunction;
|
|
6628
|
+
}
|
|
6629
|
+
executeQuery(query) {
|
|
6630
|
+
const existing = this.running.get(query);
|
|
6631
|
+
if (existing) return existing;
|
|
6632
|
+
const execution = this.queryFunction(query).then((response) => {
|
|
6633
|
+
this.running.delete(query);
|
|
6634
|
+
return response;
|
|
6635
|
+
}, (error) => {
|
|
6636
|
+
this.running.delete(query);
|
|
6637
|
+
throw error;
|
|
6638
|
+
});
|
|
6639
|
+
this.running.set(query, execution);
|
|
6640
|
+
return execution;
|
|
6641
|
+
}
|
|
6642
|
+
};
|
|
6643
|
+
/**
|
|
6644
|
+
* Replaces the raw blank node bindings in a lookup response with bindings whose `inst`
|
|
6645
|
+
* is an encoded IRI, resolving nested blank nodes along the way.
|
|
6646
|
+
*/
|
|
6647
|
+
async function updateLookupResults(result, queryFunction, settings, factory) {
|
|
6648
|
+
const completeBindings = [];
|
|
6649
|
+
const blankBindings = [];
|
|
6650
|
+
for (const binding of result.results.bindings) if (isBlankBinding(binding)) blankBindings.push(binding);
|
|
6651
|
+
else completeBindings.push(binding);
|
|
6652
|
+
if (blankBindings.length === 0) return result;
|
|
6653
|
+
const processedBindings = await processBlankBindings(blankBindings, queryFunction, settings, factory);
|
|
6654
|
+
return {
|
|
6655
|
+
...result,
|
|
6656
|
+
results: {
|
|
6657
|
+
...result.results,
|
|
6658
|
+
bindings: completeBindings.concat(processedBindings)
|
|
6659
|
+
}
|
|
6660
|
+
};
|
|
6661
|
+
}
|
|
6662
|
+
/**
|
|
6663
|
+
* Groups raw blank node bindings by the blank node they describe, resolves any nested
|
|
6664
|
+
* blank nodes they point at and rewrites both the instance and the nested targets into
|
|
6665
|
+
* encoded IRIs.
|
|
6666
|
+
*
|
|
6667
|
+
* The bindings are mutated in place and returned.
|
|
6668
|
+
*/
|
|
6669
|
+
async function processBlankBindings(blankBindings, queryFunction, settings, factory) {
|
|
6670
|
+
const bindingGroupsById = /* @__PURE__ */ new Map();
|
|
6671
|
+
for (const binding of blankBindings) {
|
|
6672
|
+
let group = bindingGroupsById.get(binding.inst.value);
|
|
6673
|
+
if (!group) {
|
|
6674
|
+
group = [];
|
|
6675
|
+
bindingGroupsById.set(binding.inst.value, group);
|
|
6676
|
+
}
|
|
6677
|
+
group.push(binding);
|
|
6678
|
+
}
|
|
6679
|
+
const nestedChains = [];
|
|
6680
|
+
for (const binding of blankBindings) if (isRdfBlank(binding.blankTrg)) nestedChains.push([binding]);
|
|
6681
|
+
const loadedGroups = await loadRelatedBlankNodes(nestedChains, new QueryExecutor(queryFunction), settings, factory);
|
|
6682
|
+
const { blankNodeStatementLimit } = settings;
|
|
6683
|
+
const idsMap = getEncodedIdDictionary(loadedGroups, factory, blankNodeStatementLimit);
|
|
6684
|
+
for (const group of bindingGroupsById.values()) {
|
|
6685
|
+
for (const binding of group) {
|
|
6686
|
+
const encodedNestedId = idsMap.get(binding.blankTrg.value);
|
|
6687
|
+
if (encodedNestedId !== void 0) binding.blankTrg = factory.namedNode(encodedNestedId);
|
|
6688
|
+
if (!binding.label) binding.label = createLabelForBlankBinding(binding, factory);
|
|
6689
|
+
}
|
|
6690
|
+
updateGroupIds(group, encodeId(group, blankNodeStatementLimit), factory);
|
|
6691
|
+
}
|
|
6692
|
+
return blankBindings.slice();
|
|
6693
|
+
}
|
|
6694
|
+
function getEncodedIdDictionary(blankBindingGroups, factory, statementLimit) {
|
|
6695
|
+
const idDictionary = /* @__PURE__ */ new Map();
|
|
6696
|
+
for (const [key, group] of blankBindingGroups) {
|
|
6697
|
+
const encodedId = encodeId(group, statementLimit);
|
|
6698
|
+
idDictionary.set(key, encodedId);
|
|
6699
|
+
updateGroupIds(group, encodedId, factory);
|
|
6700
|
+
}
|
|
6701
|
+
return idDictionary;
|
|
6702
|
+
}
|
|
6703
|
+
function updateGroupIds(group, newId, factory) {
|
|
6704
|
+
const inst = factory.namedNode(newId);
|
|
6705
|
+
for (const binding of group) binding.inst = inst;
|
|
6706
|
+
}
|
|
6707
|
+
async function loadRelatedBlankNodes(blankChains, queryExecutor, settings, factory, recursionDeep = 1) {
|
|
6708
|
+
const loadedBlankBindings = /* @__PURE__ */ new Map();
|
|
6709
|
+
if (recursionDeep > 3 || blankChains.length === 0) return loadedBlankBindings;
|
|
6710
|
+
const results = await Promise.all(blankChains.map(async (chain) => ({
|
|
6711
|
+
chain,
|
|
6712
|
+
response: await queryExecutor.executeQuery(getQueryForChain(chain, settings))
|
|
6713
|
+
})));
|
|
6714
|
+
await Promise.all(results.map(async ({ chain, response }) => {
|
|
6715
|
+
const bindings = response.results.bindings;
|
|
6716
|
+
if (bindings.length === 0) return;
|
|
6717
|
+
const nestedChains = [];
|
|
6718
|
+
for (const binding of bindings) if (isRdfBlank(binding.blankTrg)) nestedChains.push(chain.concat([binding]));
|
|
6719
|
+
const idsMap = getEncodedIdDictionary(await loadRelatedBlankNodes(nestedChains, queryExecutor, settings, factory, recursionDeep + 1), factory, settings.blankNodeStatementLimit);
|
|
6720
|
+
for (const binding of bindings) {
|
|
6721
|
+
const encodedNestedId = idsMap.get(binding.blankTrg.value);
|
|
6722
|
+
if (encodedNestedId !== void 0) binding.blankTrg = factory.namedNode(encodedNestedId);
|
|
6723
|
+
binding.label = createLabelForBlankBinding(binding, factory);
|
|
6724
|
+
let group = loadedBlankBindings.get(binding.inst.value);
|
|
6725
|
+
if (!group) {
|
|
6726
|
+
group = [];
|
|
6727
|
+
loadedBlankBindings.set(binding.inst.value, group);
|
|
6728
|
+
}
|
|
6729
|
+
group.push(binding);
|
|
6730
|
+
}
|
|
6731
|
+
}));
|
|
6732
|
+
return loadedBlankBindings;
|
|
6733
|
+
}
|
|
6734
|
+
/**
|
|
6735
|
+
* Builds a query which walks the chain of statements from the named node the chain starts at
|
|
6736
|
+
* down to the blank node at its end, and describes that blank node.
|
|
6737
|
+
*
|
|
6738
|
+
* The chain is expressed entirely in IRIs and predicates, since blank node labels from
|
|
6739
|
+
* a previous response cannot be used to address anything in a new query.
|
|
6740
|
+
*/
|
|
6741
|
+
function getQueryForChain(blankNodes, settings) {
|
|
6742
|
+
function getQueryBlock(blankNode, index, maxIndex) {
|
|
6743
|
+
const trustableTrgProp = index === 0 || blankNode.blankType.value !== "listHead";
|
|
6744
|
+
const sourceId = index > 0 ? `?inst${index - 1}` : formatChainTerm(blankNode.blankSrc, `?blankAnySrc${index}`);
|
|
6745
|
+
const sourcePropId = trustableTrgProp ? index > 0 ? `?blankTrgProp${index - 1}` : formatChainTerm(blankNode.blankSrcProp, `?blankAnySrcProp${index}`) : `?anyType${index}`;
|
|
6746
|
+
const instPostfix = index === maxIndex ? "" : index.toString();
|
|
6747
|
+
const targetPropId = trustableTrgProp ? formatChainTerm(blankNode.blankTrgProp, `?blankAnyTrgProp${index}`) : `?anyType0${index}`;
|
|
6748
|
+
return `
|
|
6749
|
+
# ======================
|
|
6750
|
+
${sourceId} ${sourcePropId} ?blankSrc${index}.
|
|
6751
|
+
${index === 0 && blankNode.blankType.value === "listHead" ? `?blankSrc${index} rdf:rest*/rdf:first ?inst${instPostfix}.` : `?blankSrc${index} ${targetPropId} ?inst${instPostfix}.`}
|
|
6752
|
+
BIND (${formatChainTerm(blankNode.blankTrgProp, `?blankAnyBind${index}`)} as ?blankSrcProp${index}).
|
|
6753
|
+
FILTER (ISBLANK(?inst${instPostfix})).
|
|
6754
|
+
{
|
|
6755
|
+
?inst${instPostfix} ?blankTrgProp${instPostfix} ?blankTrg${instPostfix}.
|
|
6756
|
+
BIND("blankNode" as ?blankType${instPostfix}).
|
|
6757
|
+
FILTER NOT EXISTS { ?inst${instPostfix} rdf:first _:smth1${index} }.
|
|
6758
|
+
} UNION {
|
|
6759
|
+
?inst${instPostfix} rdf:rest*/rdf:first ?blankTrg${instPostfix}.
|
|
6760
|
+
?blankSrc${index} ?blankSrcProp${index} ?inst${instPostfix}.
|
|
6761
|
+
_:smth2${index} rdf:first ?blankTrg${instPostfix}.
|
|
6762
|
+
BIND(?blankSrcProp${index} as ?blankTrgProp${instPostfix})
|
|
6763
|
+
BIND("listHead" as ?blankType${instPostfix})
|
|
6764
|
+
FILTER NOT EXISTS { _:smth3${index} rdf:rest ?inst${instPostfix} }.
|
|
6765
|
+
}
|
|
6766
|
+
OPTIONAL {
|
|
6767
|
+
?inst${instPostfix} rdf:type ?class${instPostfix}.
|
|
6768
|
+
}
|
|
6769
|
+
`;
|
|
6770
|
+
}
|
|
6771
|
+
const body = blankNodes.map((bn, index) => getQueryBlock(bn, index, blankNodes.length - 1)).join("\n");
|
|
6772
|
+
return `${settings.defaultPrefix}
|
|
6773
|
+
SELECT ?inst ?class ?label ?blankTrgProp ?blankTrg ?blankType
|
|
6774
|
+
WHERE {
|
|
6775
|
+
${body}
|
|
6776
|
+
}
|
|
6777
|
+
`;
|
|
6778
|
+
}
|
|
6779
|
+
/**
|
|
6780
|
+
* Formats a chain term as an IRI, falling back to a fresh variable when the term is
|
|
6781
|
+
* missing or is itself a blank node, neither of which can be referenced from a query.
|
|
6782
|
+
*/
|
|
6783
|
+
function formatChainTerm(term, fallbackVariable) {
|
|
6784
|
+
return isRdfIri(term) ? `<${term.value}>` : fallbackVariable;
|
|
6785
|
+
}
|
|
6786
|
+
/**
|
|
6787
|
+
* Answers {@link DataProvider.elements} for encoded blank nodes without querying the endpoint.
|
|
6788
|
+
*
|
|
6789
|
+
* Literal targets of the blank node are surfaced as element properties; targets which are
|
|
6790
|
+
* entities of their own become links instead, see {@link links}.
|
|
6791
|
+
*/
|
|
6792
|
+
function elements(elementIds, factory) {
|
|
6793
|
+
const bindings = [];
|
|
6794
|
+
for (const id of elementIds) {
|
|
6795
|
+
const blankBindings = decodeId(id, factory);
|
|
6796
|
+
if (!blankBindings) continue;
|
|
6797
|
+
for (const binding of blankBindings) if (isRdfLiteral(binding.blankTrg)) bindings.push({
|
|
6798
|
+
...binding,
|
|
6799
|
+
propType: binding.blankTrgProp,
|
|
6800
|
+
propValue: binding.blankTrg
|
|
6801
|
+
});
|
|
6802
|
+
else bindings.push(binding);
|
|
6803
|
+
}
|
|
6804
|
+
return bindings;
|
|
6805
|
+
}
|
|
6806
|
+
/**
|
|
6807
|
+
* Answers {@link DataProvider.links} for encoded blank nodes without querying the endpoint.
|
|
6808
|
+
*
|
|
6809
|
+
* Only links where both ends are among the requested elements are returned, matching
|
|
6810
|
+
* the behavior of the endpoint-backed query.
|
|
6811
|
+
*/
|
|
6812
|
+
function links(elementIds, factory) {
|
|
6813
|
+
const requested = new Set(elementIds);
|
|
6814
|
+
const bindings = [];
|
|
6815
|
+
for (const id of elementIds) {
|
|
6816
|
+
const blankBindings = decodeId(id, factory);
|
|
6817
|
+
if (!blankBindings) continue;
|
|
6818
|
+
for (const binding of blankBindings) {
|
|
6819
|
+
const { inst, blankSrc, blankSrcProp, blankTrg, blankTrgProp } = binding;
|
|
6820
|
+
if (blankSrc && blankSrcProp && requested.has(blankSrc.value)) bindings.push({
|
|
6821
|
+
source: blankSrc,
|
|
6822
|
+
type: blankSrcProp,
|
|
6823
|
+
target: inst
|
|
6824
|
+
});
|
|
6825
|
+
if (!isRdfLiteral(blankTrg) && requested.has(blankTrg.value)) bindings.push({
|
|
6826
|
+
source: inst,
|
|
6827
|
+
type: blankTrgProp,
|
|
6828
|
+
target: blankTrg
|
|
6829
|
+
});
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
return bindings;
|
|
6833
|
+
}
|
|
6834
|
+
/**
|
|
6835
|
+
* Answers {@link DataProvider.connectedLinkStats} for an encoded blank node without
|
|
6836
|
+
* querying the endpoint.
|
|
6837
|
+
*/
|
|
6838
|
+
function connectedLinkStats(elementId, factory) {
|
|
6839
|
+
const blankBindings = decodeId(elementId, factory);
|
|
6840
|
+
if (!blankBindings) return [];
|
|
6841
|
+
const stats = /* @__PURE__ */ new Map();
|
|
6842
|
+
function getOrCreate(linkTypeId) {
|
|
6843
|
+
let count = stats.get(linkTypeId);
|
|
6844
|
+
if (!count) {
|
|
6845
|
+
count = {
|
|
6846
|
+
id: linkTypeId,
|
|
6847
|
+
inCount: 0,
|
|
6848
|
+
outCount: 0
|
|
6849
|
+
};
|
|
6850
|
+
stats.set(linkTypeId, count);
|
|
6851
|
+
}
|
|
6852
|
+
return count;
|
|
6853
|
+
}
|
|
6854
|
+
for (const binding of blankBindings) {
|
|
6855
|
+
const { blankSrc, blankSrcProp, blankTrg, blankTrgProp } = binding;
|
|
6856
|
+
if (isRdfLiteral(blankTrg)) continue;
|
|
6857
|
+
if (blankSrc && blankSrcProp) {
|
|
6858
|
+
const incoming = getOrCreate(blankSrcProp.value);
|
|
6859
|
+
incoming.inCount += 1;
|
|
6860
|
+
}
|
|
6861
|
+
const outgoing = getOrCreate(blankTrgProp.value);
|
|
6862
|
+
outgoing.outCount += 1;
|
|
6863
|
+
}
|
|
6864
|
+
return Array.from(stats.values());
|
|
6865
|
+
}
|
|
6866
|
+
/**
|
|
6867
|
+
* Answers {@link DataProvider.lookup} for navigation from an encoded blank node without
|
|
6868
|
+
* querying the endpoint, or returns `undefined` when the lookup is not about one.
|
|
6869
|
+
*/
|
|
6870
|
+
function lookup(params, factory) {
|
|
6871
|
+
let bindings;
|
|
6872
|
+
if (params.elementTypeId) return;
|
|
6873
|
+
else if (params.refElementId && params.refElementLinkId) bindings = getAllRelatedByLinkTypeElements(params.refElementId, params.refElementLinkId, params.linkDirection, factory);
|
|
6874
|
+
else if (params.refElementId) bindings = getAllRelatedElements(params.refElementId, factory);
|
|
6875
|
+
else return;
|
|
6876
|
+
if (bindings.length === 0) return;
|
|
6877
|
+
if (params.text) {
|
|
6878
|
+
const text = params.text.toLowerCase();
|
|
6879
|
+
bindings = bindings.filter((binding) => binding.inst.value.toLowerCase().includes(text));
|
|
6880
|
+
}
|
|
6881
|
+
return {
|
|
6882
|
+
head: { vars: [] },
|
|
6883
|
+
results: { bindings }
|
|
6884
|
+
};
|
|
6885
|
+
}
|
|
6886
|
+
/**
|
|
6887
|
+
* Answers the element type query for encoded blank nodes without querying the endpoint.
|
|
6888
|
+
*/
|
|
6889
|
+
function collectElementTypes$1(elementIds, factory, result) {
|
|
6890
|
+
for (const id of elementIds) {
|
|
6891
|
+
const blankBindings = decodeId(id, factory);
|
|
6892
|
+
if (!blankBindings) continue;
|
|
6893
|
+
for (const binding of blankBindings) if (binding.class) {
|
|
6894
|
+
let types = result.get(id);
|
|
6895
|
+
if (!types) {
|
|
6896
|
+
types = /* @__PURE__ */ new Set();
|
|
6897
|
+
result.set(id, types);
|
|
6898
|
+
}
|
|
6899
|
+
types.add(binding.class.value);
|
|
6900
|
+
}
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
/**
|
|
6904
|
+
* Answers navigation from an encoded blank node over a single link type.
|
|
6905
|
+
*
|
|
6906
|
+
* The direction is relative to the blank node: `in` collects the entities which point at it,
|
|
6907
|
+
* `out` the entities it points at.
|
|
6908
|
+
*/
|
|
6909
|
+
function getAllRelatedByLinkTypeElements(refElementId, refElementLinkId, linkDirection, factory) {
|
|
6910
|
+
const blankBindings = decodeId(refElementId, factory);
|
|
6911
|
+
if (!blankBindings) return [];
|
|
6912
|
+
const linkType = factory.namedNode(refElementLinkId);
|
|
6913
|
+
const bindings = [];
|
|
6914
|
+
for (const binding of blankBindings) {
|
|
6915
|
+
const { blankSrc, blankSrcProp, blankTrg, blankTrgProp } = binding;
|
|
6916
|
+
if (linkDirection !== "out" && blankSrc && blankSrcProp && blankSrcProp.value === refElementLinkId) pushRelated(bindings, blankSrc, linkType, "in", factory);
|
|
6917
|
+
if (linkDirection !== "in" && blankTrgProp.value === refElementLinkId && !isRdfLiteral(blankTrg)) pushRelated(bindings, blankTrg, linkType, "out", factory);
|
|
6918
|
+
}
|
|
6919
|
+
return bindings;
|
|
6920
|
+
}
|
|
6921
|
+
/**
|
|
6922
|
+
* Answers navigation from an encoded blank node over every link type at once.
|
|
6923
|
+
*
|
|
6924
|
+
* Literal targets are left out: they are the blank node's own properties, not connections.
|
|
6925
|
+
*/
|
|
6926
|
+
function getAllRelatedElements(refElementId, factory) {
|
|
6927
|
+
const blankBindings = decodeId(refElementId, factory);
|
|
6928
|
+
if (!blankBindings) return [];
|
|
6929
|
+
const bindings = [];
|
|
6930
|
+
for (const binding of blankBindings) {
|
|
6931
|
+
const { blankSrc, blankSrcProp, blankTrg, blankTrgProp } = binding;
|
|
6932
|
+
if (blankSrc && blankSrcProp) pushRelated(bindings, blankSrc, blankSrcProp, "in", factory);
|
|
6933
|
+
if (!isRdfLiteral(blankTrg)) pushRelated(bindings, blankTrg, blankTrgProp, "out", factory);
|
|
6934
|
+
}
|
|
6935
|
+
return bindings;
|
|
6936
|
+
}
|
|
6937
|
+
/**
|
|
6938
|
+
* Adds the element at the far end of a statement to the result: a named node as-is,
|
|
6939
|
+
* an encoded blank node expanded back into the statements which describe it.
|
|
6940
|
+
*
|
|
6941
|
+
* The link type and the direction are carried over so that the result describes the
|
|
6942
|
+
* connection the same way an endpoint-backed lookup does. A named node is returned bare,
|
|
6943
|
+
* without a label or a type: {@link SparqlDataProvider.lookup} fills those in, since only
|
|
6944
|
+
* the endpoint knows them.
|
|
6945
|
+
*/
|
|
6946
|
+
function pushRelated(bindings, term, linkType, direction, factory) {
|
|
6947
|
+
const connection = {
|
|
6948
|
+
link: linkType,
|
|
6949
|
+
direction: factory.literal(direction)
|
|
6950
|
+
};
|
|
6951
|
+
const nested = isRdfIri(term) ? decodeId(term.value, factory) : void 0;
|
|
6952
|
+
if (nested) for (const binding of nested) bindings.push({
|
|
6953
|
+
...binding,
|
|
6954
|
+
...connection
|
|
6955
|
+
});
|
|
6956
|
+
else bindings.push({
|
|
6957
|
+
inst: term,
|
|
6958
|
+
...connection
|
|
6959
|
+
});
|
|
6960
|
+
}
|
|
6961
|
+
//#endregion
|
|
6401
6962
|
//#region src/data/sparql/requestChunking.ts
|
|
6402
6963
|
function* chunkArray(items, measure, maxChunkSize) {
|
|
6403
6964
|
if (!Number.isFinite(maxChunkSize)) {
|
|
@@ -6460,46 +7021,6 @@ function* chunkDirectedCrossProduct(from, to, measure, maxChunkSize, excludedTar
|
|
|
6460
7021
|
}
|
|
6461
7022
|
}
|
|
6462
7023
|
//#endregion
|
|
6463
|
-
//#region src/data/sparql/sparqlModels.ts
|
|
6464
|
-
function mapSparqlResponseIntoRdfJs(response, factory) {
|
|
6465
|
-
function mapSparqlBinding(binding) {
|
|
6466
|
-
const mapped = {};
|
|
6467
|
-
for (const key in binding) if (Object.prototype.hasOwnProperty.call(binding, key)) {
|
|
6468
|
-
const term = binding[key];
|
|
6469
|
-
switch (term.type) {
|
|
6470
|
-
case "uri":
|
|
6471
|
-
mapped[key] = factory.namedNode(term.value);
|
|
6472
|
-
break;
|
|
6473
|
-
case "bnode":
|
|
6474
|
-
mapped[key] = factory.blankNode(term.value);
|
|
6475
|
-
break;
|
|
6476
|
-
case "literal":
|
|
6477
|
-
case "typed-literal":
|
|
6478
|
-
mapped[key] = factory.literal(term.value, term["xml:lang"] ? term["xml:lang"] : term.datatype ? factory.namedNode(term.datatype) : void 0);
|
|
6479
|
-
break;
|
|
6480
|
-
default: throw new Error(`Unexpected SPARQL term type: "${term.type}"`);
|
|
6481
|
-
}
|
|
6482
|
-
}
|
|
6483
|
-
return mapped;
|
|
6484
|
-
}
|
|
6485
|
-
return {
|
|
6486
|
-
...response,
|
|
6487
|
-
results: {
|
|
6488
|
-
...response.results,
|
|
6489
|
-
bindings: response.results.bindings.map(mapSparqlBinding)
|
|
6490
|
-
}
|
|
6491
|
-
};
|
|
6492
|
-
}
|
|
6493
|
-
function isRdfIri(term) {
|
|
6494
|
-
return Boolean(term && term.termType === "NamedNode");
|
|
6495
|
-
}
|
|
6496
|
-
function isRdfBlank(term) {
|
|
6497
|
-
return Boolean(term && term.termType === "BlankNode");
|
|
6498
|
-
}
|
|
6499
|
-
function isRdfLiteral(term) {
|
|
6500
|
-
return Boolean(term && term.termType === "Literal");
|
|
6501
|
-
}
|
|
6502
|
-
//#endregion
|
|
6503
7024
|
//#region src/data/sparql/responseHandler.ts
|
|
6504
7025
|
var TYPE_PREDICATE = "urn:reactodia:sparql:type";
|
|
6505
7026
|
var LABEL_PREDICATE = "urn:reactodia:sparql:label";
|
|
@@ -6778,7 +7299,7 @@ function typeMatchesDomain(config, types) {
|
|
|
6778
7299
|
function enrichElement(element, binding, labelPredicate) {
|
|
6779
7300
|
if (!element) return;
|
|
6780
7301
|
if (binding.label) appendProperty(element.properties, labelPredicate, binding.label);
|
|
6781
|
-
if (binding.class && element.types.indexOf(binding.class.value) < 0) element.types.push(binding.class.value);
|
|
7302
|
+
if (isRdfIri(binding.class) && element.types.indexOf(binding.class.value) < 0) element.types.push(binding.class.value);
|
|
6782
7303
|
if (binding.propType && binding.propValue && binding.propType.value !== LABEL_PREDICATE) appendProperty(element.properties, binding.propType.value, binding.propValue);
|
|
6783
7304
|
}
|
|
6784
7305
|
function appendLabel(container, newLabel) {
|
|
@@ -7034,7 +7555,7 @@ var SparqlDataProvider = class {
|
|
|
7034
7555
|
options;
|
|
7035
7556
|
settings;
|
|
7036
7557
|
queryFunction;
|
|
7037
|
-
acceptBlankNodes
|
|
7558
|
+
acceptBlankNodes;
|
|
7038
7559
|
chunkMeasure;
|
|
7039
7560
|
linkByPredicate = /* @__PURE__ */ new Map();
|
|
7040
7561
|
linkById = /* @__PURE__ */ new Map();
|
|
@@ -7049,6 +7570,7 @@ var SparqlDataProvider = class {
|
|
|
7049
7570
|
this.options = options;
|
|
7050
7571
|
this.settings = settings;
|
|
7051
7572
|
this.queryFunction = queryFunction;
|
|
7573
|
+
this.acceptBlankNodes = Boolean(settings.acceptBlankNodes);
|
|
7052
7574
|
const { chunk, queryMethod = "GET", prepareLabelPredicate, prepareImagePredicate } = this.options;
|
|
7053
7575
|
if (chunk) {
|
|
7054
7576
|
if (!Number.isSafeInteger(chunk.maxSize)) throw new Error("SparqlDataProviderOptions.chunk.maxSize should be a non-negative number");
|
|
@@ -7172,8 +7694,9 @@ var SparqlDataProvider = class {
|
|
|
7172
7694
|
}
|
|
7173
7695
|
async elements(params) {
|
|
7174
7696
|
const { elementIds, signal } = params;
|
|
7697
|
+
const queriedIds = this.acceptBlankNodes ? elementIds.filter((iri) => !isEncodedBlank$1(iri)) : elementIds;
|
|
7175
7698
|
const triples = [];
|
|
7176
|
-
if (
|
|
7699
|
+
if (queriedIds.length > 0) await this.queryChunked(queriedIds, async (batch) => {
|
|
7177
7700
|
const ids = batch.map(escapeIri).map((id) => ` (${id})`).join(" ");
|
|
7178
7701
|
const { defaultPrefix, dataLabelProperty, filterOnlyLanguages, elementInfoQuery } = this.settings;
|
|
7179
7702
|
const query = defaultPrefix + resolveTemplate(elementInfoQuery, {
|
|
@@ -7187,15 +7710,18 @@ var SparqlDataProvider = class {
|
|
|
7187
7710
|
for (const triple of response) triples.push(triple);
|
|
7188
7711
|
});
|
|
7189
7712
|
const types = this.queryManyElementTypes(this.settings.propertyConfigurations.length > 0 ? elementIds : [], signal);
|
|
7190
|
-
const
|
|
7191
|
-
if (this.
|
|
7192
|
-
|
|
7713
|
+
const bindings = triplesToElementBinding(triples);
|
|
7714
|
+
if (this.acceptBlankNodes) bindings.results.bindings.push(...elements(elementIds, this.factory));
|
|
7715
|
+
const elementModels = getElementsInfo(bindings, await types, this.propertyByPredicate, this.labelPredicate, this.openWorldProperties);
|
|
7716
|
+
if (this.options.prepareLabels) await attachProperties(Array.from(elementModels.values()), this.options.prepareLabels, this.labelPredicate, this.acceptBlankNodes, signal);
|
|
7717
|
+
if (this.options.prepareImages) await prepareElementImages(elementModels, this.options.prepareImages, this.imagePredicate, this.factory, this.acceptBlankNodes, signal);
|
|
7193
7718
|
else if (this.options.imagePropertyUris && this.options.imagePropertyUris.length) await this.attachImages(elementModels, this.options.imagePropertyUris, signal);
|
|
7194
7719
|
return elementModels;
|
|
7195
7720
|
}
|
|
7196
7721
|
async attachImages(elements, imagePropertyIris, signal) {
|
|
7197
7722
|
const imageProperties = imagePropertyIris.map(escapeIri).map((id) => ` ( ${id} )`).join(" ");
|
|
7198
|
-
|
|
7723
|
+
const imageableIds = Array.from(elements.keys()).filter((iri) => !(this.acceptBlankNodes && isEncodedBlank$1(iri)));
|
|
7724
|
+
await this.queryChunked(imageableIds, async (batch) => {
|
|
7199
7725
|
const ids = batch.map((id) => ` ( ${escapeIri(id)} )`).join(" ");
|
|
7200
7726
|
const query = this.settings.defaultPrefix + `
|
|
7201
7727
|
SELECT ?inst ?linkType ?image
|
|
@@ -7217,12 +7743,14 @@ var SparqlDataProvider = class {
|
|
|
7217
7743
|
const { filterOnlyLanguages, linksInfoQuery } = this.settings;
|
|
7218
7744
|
const propLanguageFilter = formatLanguageFilter("?propValue", filterOnlyLanguages);
|
|
7219
7745
|
const linkConfigurations = this.formatLinkLinks();
|
|
7746
|
+
const queriedPrimary = this.acceptBlankNodes ? primary.filter((iri) => !isEncodedBlank$1(iri)) : primary;
|
|
7747
|
+
const queriedSecondary = this.acceptBlankNodes ? secondary.filter((iri) => !isEncodedBlank$1(iri)) : secondary;
|
|
7220
7748
|
let bindings;
|
|
7221
|
-
if (
|
|
7749
|
+
if (queriedPrimary.length > 0 && queriedSecondary.length > 0) if (linksInfoQuery.includes("${ids}")) bindings = this.queryUndirectedLinks(queriedPrimary, queriedSecondary, {
|
|
7222
7750
|
propLanguageFilter,
|
|
7223
7751
|
linkConfigurations
|
|
7224
7752
|
}, signal);
|
|
7225
|
-
else bindings = this.queryDirectedLinks(
|
|
7753
|
+
else bindings = this.queryDirectedLinks(queriedPrimary, queriedSecondary, {
|
|
7226
7754
|
propLanguageFilter,
|
|
7227
7755
|
linkConfigurations
|
|
7228
7756
|
}, signal);
|
|
@@ -7233,7 +7761,13 @@ var SparqlDataProvider = class {
|
|
|
7233
7761
|
for (const iri of secondary) elementsForTypes.add(iri);
|
|
7234
7762
|
}
|
|
7235
7763
|
const types = this.queryManyElementTypes(elementsForTypes ? Array.from(elementsForTypes) : [], signal);
|
|
7236
|
-
let
|
|
7764
|
+
let allBindings = await bindings;
|
|
7765
|
+
if (this.acceptBlankNodes) {
|
|
7766
|
+
const connected = new Set(primary);
|
|
7767
|
+
for (const iri of secondary) connected.add(iri);
|
|
7768
|
+
allBindings = [...allBindings, ...links(Array.from(connected), this.factory)];
|
|
7769
|
+
}
|
|
7770
|
+
let linksInfo = getLinksInfo(allBindings, await types, this.linkByPredicate, this.openWorldLinks);
|
|
7237
7771
|
if (linkTypeIds) {
|
|
7238
7772
|
const allowedLinkTypes = new Set(linkTypeIds);
|
|
7239
7773
|
linksInfo = linksInfo.filter((link) => allowedLinkTypes.has(link.linkTypeId));
|
|
@@ -7272,6 +7806,7 @@ var SparqlDataProvider = class {
|
|
|
7272
7806
|
}
|
|
7273
7807
|
async connectedLinkStats(params) {
|
|
7274
7808
|
const { elementId, inexactCount, signal } = params;
|
|
7809
|
+
if (this.acceptBlankNodes && isEncodedBlank$1(elementId)) return connectedLinkStats(elementId, this.factory);
|
|
7275
7810
|
const { defaultPrefix, linkTypesOfQuery, linkTypesStatisticsQuery, filterTypePattern } = this.settings;
|
|
7276
7811
|
const bindDirection = /\?direction\b/.test(linkTypesOfQuery);
|
|
7277
7812
|
const elementIri = escapeIri(elementId);
|
|
@@ -7337,11 +7872,48 @@ var SparqlDataProvider = class {
|
|
|
7337
7872
|
limit: baseParams.limit === void 0 ? 100 : baseParams.limit
|
|
7338
7873
|
};
|
|
7339
7874
|
const types = this.querySingleElementTypes(params.refElementId && this.settings.linkConfigurations.length > 0 ? params.refElementId : void 0, signal);
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
if (this.
|
|
7875
|
+
let bindings;
|
|
7876
|
+
let navigatedFromBlank = false;
|
|
7877
|
+
if (this.acceptBlankNodes) {
|
|
7878
|
+
const fromBlank = lookup(params, this.factory);
|
|
7879
|
+
if (fromBlank) {
|
|
7880
|
+
bindings = fromBlank;
|
|
7881
|
+
navigatedFromBlank = true;
|
|
7882
|
+
} else bindings = await updateLookupResults(await this.executeSparqlSelect(this.createFilterQuery(params), { signal }), (blankQuery) => this.executeSparqlSelect(blankQuery, { signal }), this.settings, this.factory);
|
|
7883
|
+
} else {
|
|
7884
|
+
const filterQuery = this.createFilterQuery(params);
|
|
7885
|
+
bindings = await this.executeSparqlSelect(filterQuery, { signal });
|
|
7886
|
+
}
|
|
7887
|
+
const linkedElements = getFilteredData(bindings, await types, this.linkByPredicate, this.labelPredicate, this.openWorldLinks);
|
|
7888
|
+
if (navigatedFromBlank) return this.attachNeighborInfo(linkedElements, signal);
|
|
7889
|
+
if (this.options.prepareLabels) await attachProperties(linkedElements.map((linked) => linked.element), this.options.prepareLabels, this.labelPredicate, this.acceptBlankNodes, signal);
|
|
7343
7890
|
return linkedElements;
|
|
7344
7891
|
}
|
|
7892
|
+
/**
|
|
7893
|
+
* Fills in the data of the elements found by navigating from a blank node.
|
|
7894
|
+
*
|
|
7895
|
+
* The navigation is answered by decoding the blank node IRI, which knows only the term
|
|
7896
|
+
* at the far end of each statement, so everything else about a named neighbor - its
|
|
7897
|
+
* label, types and properties - still has to be read from the endpoint. Without this
|
|
7898
|
+
* the neighbor would render as its bare IRI.
|
|
7899
|
+
*
|
|
7900
|
+
* {@link elements} applies the {@link SparqlDataProviderOptions.prepareLabels} hook
|
|
7901
|
+
* itself, so the caller must not apply it again.
|
|
7902
|
+
*/
|
|
7903
|
+
async attachNeighborInfo(items, signal) {
|
|
7904
|
+
if (items.length === 0) return [];
|
|
7905
|
+
const elementModels = await this.elements({
|
|
7906
|
+
elementIds: items.map((item) => item.element.id),
|
|
7907
|
+
signal
|
|
7908
|
+
});
|
|
7909
|
+
return items.map((item) => {
|
|
7910
|
+
const element = elementModels.get(item.element.id);
|
|
7911
|
+
return element ? {
|
|
7912
|
+
...item,
|
|
7913
|
+
element
|
|
7914
|
+
} : item;
|
|
7915
|
+
});
|
|
7916
|
+
}
|
|
7345
7917
|
createFilterQuery(params) {
|
|
7346
7918
|
if (!params.refElementId && params.refElementLinkId) throw new Error("Cannot execute refElementLink filter without refElement");
|
|
7347
7919
|
let outerProjection = "?inst ?class ?label";
|
|
@@ -7367,10 +7939,14 @@ var SparqlDataProvider = class {
|
|
|
7367
7939
|
filterByType = this.settings.filterTypePattern.replace(/[?$]class\b/g, elementTypeIri);
|
|
7368
7940
|
}
|
|
7369
7941
|
const { defaultPrefix, dataLabelProperty, lookupQuery, filterOnlyLanguages, filterElementInfoPattern, fullTextSearch } = this.settings;
|
|
7370
|
-
|
|
7942
|
+
let queryElementInfo = resolveTemplate(filterElementInfoPattern, {
|
|
7371
7943
|
dataLabelProperty,
|
|
7372
7944
|
labelLanguageFilter: formatLanguageFilter("?label", filterOnlyLanguages)
|
|
7373
7945
|
});
|
|
7946
|
+
if (this.acceptBlankNodes) {
|
|
7947
|
+
outerProjection += ` ${BLANK_NODE_QUERY_PARAMETERS}`;
|
|
7948
|
+
queryElementInfo += BLANK_NODE_QUERY;
|
|
7949
|
+
}
|
|
7374
7950
|
let filterByText = "";
|
|
7375
7951
|
let orderBy = "";
|
|
7376
7952
|
if (params.text) {
|
|
@@ -7498,7 +8074,12 @@ var SparqlDataProvider = class {
|
|
|
7498
8074
|
if (elements.length === 0) return /* @__PURE__ */ new Map();
|
|
7499
8075
|
const { filterTypePattern } = this.settings;
|
|
7500
8076
|
const elementTypes = /* @__PURE__ */ new Map();
|
|
7501
|
-
|
|
8077
|
+
let queriedElements = elements;
|
|
8078
|
+
if (this.acceptBlankNodes) {
|
|
8079
|
+
queriedElements = elements.filter((iri) => !isEncodedBlank$1(iri));
|
|
8080
|
+
collectElementTypes$1(elements, this.factory, elementTypes);
|
|
8081
|
+
}
|
|
8082
|
+
await this.queryChunked(queriedElements, async (batch) => {
|
|
7502
8083
|
const query = resolveTemplate("SELECT ?inst ?class { VALUES(?inst) { ${ids} } ${filterTypePattern} }", {
|
|
7503
8084
|
ids: batch.map((iri) => `(${escapeIri(iri)})`).join(" "),
|
|
7504
8085
|
filterTypePattern
|
|
@@ -7521,15 +8102,16 @@ async function attachLabels(items, fetchLabels, signal) {
|
|
|
7521
8102
|
if (itemLabels) item.label = itemLabels;
|
|
7522
8103
|
}
|
|
7523
8104
|
}
|
|
7524
|
-
async function attachProperties(items, fetchProperties, propertyIri, signal) {
|
|
7525
|
-
const
|
|
7526
|
-
|
|
8105
|
+
async function attachProperties(items, fetchProperties, propertyIri, acceptBlankNodes, signal) {
|
|
8106
|
+
const resolvable = acceptBlankNodes ? items.filter((item) => !isEncodedBlank(item.id)) : items;
|
|
8107
|
+
const properties = await fetchProperties(new Set(resolvable.map((item) => item.id)), signal);
|
|
8108
|
+
for (const item of resolvable) {
|
|
7527
8109
|
const itemValues = properties.get(item.id);
|
|
7528
8110
|
if (itemValues) item.properties[propertyIri] = itemValues;
|
|
7529
8111
|
}
|
|
7530
8112
|
}
|
|
7531
|
-
function prepareElementImages(elements, fetchImages, imagePropertyIri, factory, signal) {
|
|
7532
|
-
return fetchImages(elements.values(), signal).then((images) => {
|
|
8113
|
+
function prepareElementImages(elements, fetchImages, imagePropertyIri, factory, acceptBlankNodes, signal) {
|
|
8114
|
+
return fetchImages(acceptBlankNodes ? Array.from(elements.values()).filter((element) => !isEncodedBlank(element.id)) : elements.values(), signal).then((images) => {
|
|
7533
8115
|
for (const [iri, image] of images) {
|
|
7534
8116
|
const entity = elements.get(iri);
|
|
7535
8117
|
if (entity) appendProperty(entity.properties, imagePropertyIri, factory.namedNode(image));
|
|
@@ -23284,6 +23866,35 @@ function ToolbarAction(props) {
|
|
|
23284
23866
|
});
|
|
23285
23867
|
}
|
|
23286
23868
|
/**
|
|
23869
|
+
* Toolbar action component to open a file selection dialog.
|
|
23870
|
+
*
|
|
23871
|
+
* @category Components
|
|
23872
|
+
*/
|
|
23873
|
+
function ToolbarActionOpen(props) {
|
|
23874
|
+
const { className, fileAccept, onSelect, children, ...otherProps } = props;
|
|
23875
|
+
const inputRef = reactExports.useRef(null);
|
|
23876
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [/* @__PURE__ */ jsxRuntimeExports.jsx(ToolbarAction, {
|
|
23877
|
+
...otherProps,
|
|
23878
|
+
className: clsx(className, `${CLASS_NAME$8}__open`),
|
|
23879
|
+
onSelect: () => {
|
|
23880
|
+
inputRef.current?.click();
|
|
23881
|
+
},
|
|
23882
|
+
children
|
|
23883
|
+
}), /* @__PURE__ */ jsxRuntimeExports.jsx("input", {
|
|
23884
|
+
ref: inputRef,
|
|
23885
|
+
role: "none",
|
|
23886
|
+
type: "file",
|
|
23887
|
+
className: `${CLASS_NAME$8}__open-input`,
|
|
23888
|
+
accept: fileAccept,
|
|
23889
|
+
onChange: (e) => {
|
|
23890
|
+
if (e.currentTarget.files) {
|
|
23891
|
+
const file = e.currentTarget.files[0];
|
|
23892
|
+
onSelect(file);
|
|
23893
|
+
}
|
|
23894
|
+
}
|
|
23895
|
+
})] });
|
|
23896
|
+
}
|
|
23897
|
+
/**
|
|
23287
23898
|
* Toolbar action component to clear diagram content.
|
|
23288
23899
|
*
|
|
23289
23900
|
* Clearing the diagram adds a command to the command history.
|
|
@@ -30262,6 +30873,16 @@ function getContentFittingBoxForLayout(state) {
|
|
|
30262
30873
|
};
|
|
30263
30874
|
}
|
|
30264
30875
|
|
|
30876
|
+
const graphwise_toolbar_action$1 = {
|
|
30877
|
+
"download_diagram.label": "Export to JSON",
|
|
30878
|
+
"download_diagram.title": "Export the diagram to a JSON file",
|
|
30879
|
+
"upload_diagram.label": "Import from JSON",
|
|
30880
|
+
"upload_diagram.title": "Replace the diagram with one from a JSON file"
|
|
30881
|
+
};
|
|
30882
|
+
var enTranslation = {
|
|
30883
|
+
graphwise_toolbar_action: graphwise_toolbar_action$1
|
|
30884
|
+
};
|
|
30885
|
+
|
|
30265
30886
|
const authoring_state = {
|
|
30266
30887
|
"entity_action_delete.label": "supprimer",
|
|
30267
30888
|
"entity_action_delete.title": "Supprimer l'entité",
|
|
@@ -30516,6 +31137,12 @@ const standard_link = {
|
|
|
30516
31137
|
const toolbar = {
|
|
30517
31138
|
"menu_toggle.title": "Ouvrir le menu"
|
|
30518
31139
|
};
|
|
31140
|
+
const graphwise_toolbar_action = {
|
|
31141
|
+
"download_diagram.label": "Exporter en JSON",
|
|
31142
|
+
"download_diagram.title": "Exporter le diagramme en fichier JSON",
|
|
31143
|
+
"upload_diagram.label": "Importer depuis JSON",
|
|
31144
|
+
"upload_diagram.title": "Remplacer le diagramme par celui d'un fichier JSON"
|
|
31145
|
+
};
|
|
30519
31146
|
const toolbar_action = {
|
|
30520
31147
|
"clear_all.command": "Tout effacer",
|
|
30521
31148
|
"clear_all.title": "Supprimer tous les éléments et liens du diagramme",
|
|
@@ -30626,6 +31253,7 @@ var frTranslation = {
|
|
|
30626
31253
|
standard_element: standard_element,
|
|
30627
31254
|
standard_link: standard_link,
|
|
30628
31255
|
toolbar: toolbar,
|
|
31256
|
+
graphwise_toolbar_action: graphwise_toolbar_action,
|
|
30629
31257
|
toolbar_action: toolbar_action,
|
|
30630
31258
|
unified_search: unified_search,
|
|
30631
31259
|
visual_authoring: visual_authoring,
|
|
@@ -30640,8 +31268,10 @@ var LanguageKey;
|
|
|
30640
31268
|
LanguageKey["FR"] = "fr";
|
|
30641
31269
|
})(LanguageKey || (LanguageKey = {}));
|
|
30642
31270
|
|
|
30643
|
-
// English is provided by default from reactodia so
|
|
31271
|
+
// English is provided by default from reactodia, so the English bundle holds only our own keys and overrides. If
|
|
31272
|
+
// A key is missing, reactodia defaults to its own translation.
|
|
30644
31273
|
const TRANSLATIONS = {
|
|
31274
|
+
[LanguageKey.EN]: enTranslation,
|
|
30645
31275
|
[LanguageKey.FR]: frTranslation
|
|
30646
31276
|
};
|
|
30647
31277
|
|
|
@@ -30701,7 +31331,7 @@ function Service(_serviceClass) {
|
|
|
30701
31331
|
// no implementation needed. We only use the type checking of the parameter
|
|
30702
31332
|
}
|
|
30703
31333
|
|
|
30704
|
-
var __decorate$
|
|
31334
|
+
var __decorate$4 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
30705
31335
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
30706
31336
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
30707
31337
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -30712,6 +31342,20 @@ var DiagramService_1;
|
|
|
30712
31342
|
* Service for diagram related operations and subscriptions
|
|
30713
31343
|
*/
|
|
30714
31344
|
let DiagramService = DiagramService_1 = class DiagramService {
|
|
31345
|
+
/**
|
|
31346
|
+
* Replaces the diagram content with the given serialization. Element data is requested from the data provider and
|
|
31347
|
+
* the links between the imported elements are validated against it.
|
|
31348
|
+
*
|
|
31349
|
+
* @param model The diagram model to import into.
|
|
31350
|
+
* @param dataProvider The data provider to bind to the diagram.
|
|
31351
|
+
* @param diagram The serialization to import. When `undefined`, the diagram is cleared.
|
|
31352
|
+
* @param signal Optional signal to abort the import.
|
|
31353
|
+
*/
|
|
31354
|
+
importDiagram(model, dataProvider, diagram, signal) {
|
|
31355
|
+
// selection is not cleared on import, so we clear it manually to avoid painting the previous selection
|
|
31356
|
+
model.setSelection([]);
|
|
31357
|
+
return model.importLayout({ dataProvider, diagram, signal, validateLinks: true });
|
|
31358
|
+
}
|
|
30715
31359
|
/**
|
|
30716
31360
|
* Subscribes to diagram changes. On every debounced change the current layout is
|
|
30717
31361
|
* exported and passed to the callback.
|
|
@@ -30746,11 +31390,11 @@ let DiagramService = DiagramService_1 = class DiagramService {
|
|
|
30746
31390
|
DiagramService.ID = 'DiagramService';
|
|
30747
31391
|
DiagramService.NOTIFY_DEBOUNCE_MS = 300;
|
|
30748
31392
|
DiagramService.HISTORY_CHANGED_EVENT = 'historyChanged';
|
|
30749
|
-
DiagramService = DiagramService_1 = __decorate$
|
|
31393
|
+
DiagramService = DiagramService_1 = __decorate$4([
|
|
30750
31394
|
Service
|
|
30751
31395
|
], DiagramService);
|
|
30752
31396
|
|
|
30753
|
-
var __decorate$
|
|
31397
|
+
var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
30754
31398
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
30755
31399
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
30756
31400
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -30765,11 +31409,11 @@ let WindowService = class WindowService {
|
|
|
30765
31409
|
}
|
|
30766
31410
|
};
|
|
30767
31411
|
WindowService.ID = 'WindowService';
|
|
30768
|
-
WindowService = __decorate$
|
|
31412
|
+
WindowService = __decorate$3([
|
|
30769
31413
|
Service
|
|
30770
31414
|
], WindowService);
|
|
30771
31415
|
|
|
30772
|
-
var __decorate$
|
|
31416
|
+
var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
30773
31417
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
30774
31418
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
30775
31419
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -30813,11 +31457,11 @@ let DiagramStorageService = DiagramStorageService_1 = class DiagramStorageServic
|
|
|
30813
31457
|
};
|
|
30814
31458
|
DiagramStorageService.ID = 'DiagramStorageService';
|
|
30815
31459
|
DiagramStorageService.STORAGE_KEY = 'diagram.state';
|
|
30816
|
-
DiagramStorageService = DiagramStorageService_1 = __decorate$
|
|
31460
|
+
DiagramStorageService = DiagramStorageService_1 = __decorate$2([
|
|
30817
31461
|
Service
|
|
30818
31462
|
], DiagramStorageService);
|
|
30819
31463
|
|
|
30820
|
-
var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
31464
|
+
var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
30821
31465
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
30822
31466
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
30823
31467
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
@@ -30840,7 +31484,7 @@ let EventService = class EventService {
|
|
|
30840
31484
|
}
|
|
30841
31485
|
};
|
|
30842
31486
|
EventService.ID = 'EventService';
|
|
30843
|
-
EventService = __decorate([
|
|
31487
|
+
EventService = __decorate$1([
|
|
30844
31488
|
Service
|
|
30845
31489
|
], EventService);
|
|
30846
31490
|
|
|
@@ -30934,8 +31578,102 @@ const resetColors = () => {
|
|
|
30934
31578
|
typeToIndex.clear();
|
|
30935
31579
|
};
|
|
30936
31580
|
|
|
31581
|
+
var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
31582
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
31583
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
31584
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
31585
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
31586
|
+
};
|
|
31587
|
+
var DiagramFileService_1;
|
|
31588
|
+
/**
|
|
31589
|
+
* Service for downloading and uploading the diagram as a file. The file content is the same
|
|
31590
|
+
* {@link SerializedDiagram} that is persisted to the local store.
|
|
31591
|
+
*/
|
|
31592
|
+
let DiagramFileService = DiagramFileService_1 = class DiagramFileService {
|
|
31593
|
+
/**
|
|
31594
|
+
* Downloads the given diagram serialization as a file with the given name.
|
|
31595
|
+
*/
|
|
31596
|
+
download(diagram, fileName) {
|
|
31597
|
+
const window = service(WindowService).getWindow();
|
|
31598
|
+
const blob = new Blob([JSON.stringify(diagram)], { type: DiagramFileService_1.MIME_TYPE });
|
|
31599
|
+
const url = window.URL.createObjectURL(blob);
|
|
31600
|
+
const link = window.document.createElement('a');
|
|
31601
|
+
link.href = url;
|
|
31602
|
+
link.download = fileName;
|
|
31603
|
+
link.click();
|
|
31604
|
+
window.URL.revokeObjectURL(url);
|
|
31605
|
+
}
|
|
31606
|
+
/**
|
|
31607
|
+
* Reads a diagram serialization from the given file.
|
|
31608
|
+
*
|
|
31609
|
+
* @throws Error when the file is not valid JSON or is not a serialized diagram.
|
|
31610
|
+
*/
|
|
31611
|
+
async read(file) {
|
|
31612
|
+
let diagram;
|
|
31613
|
+
try {
|
|
31614
|
+
diagram = JSON.parse(await file.text());
|
|
31615
|
+
}
|
|
31616
|
+
catch (error) {
|
|
31617
|
+
throw new Error(`File "${file.name}" is not valid JSON: ${error.message}`);
|
|
31618
|
+
}
|
|
31619
|
+
if (!this.isSerializedDiagram(diagram)) {
|
|
31620
|
+
throw new Error(`File "${file.name}" is not a serialized diagram`);
|
|
31621
|
+
}
|
|
31622
|
+
return diagram;
|
|
31623
|
+
}
|
|
31624
|
+
/**
|
|
31625
|
+
* Checks if the provided value is a serialized diagram
|
|
31626
|
+
*/
|
|
31627
|
+
isSerializedDiagram(value) {
|
|
31628
|
+
var _a, _b;
|
|
31629
|
+
const diagram = value;
|
|
31630
|
+
return (diagram === null || diagram === void 0 ? void 0 : diagram['@type']) === 'Diagram'
|
|
31631
|
+
&& Array.isArray((_a = diagram.layoutData) === null || _a === void 0 ? void 0 : _a.elements)
|
|
31632
|
+
&& Array.isArray((_b = diagram.layoutData) === null || _b === void 0 ? void 0 : _b.links);
|
|
31633
|
+
}
|
|
31634
|
+
};
|
|
31635
|
+
DiagramFileService.ID = 'DiagramFileService';
|
|
31636
|
+
DiagramFileService.FILE_EXTENSION = '.json';
|
|
31637
|
+
DiagramFileService.MIME_TYPE = 'application/json';
|
|
31638
|
+
DiagramFileService = DiagramFileService_1 = __decorate([
|
|
31639
|
+
Service
|
|
31640
|
+
], DiagramFileService);
|
|
31641
|
+
|
|
31642
|
+
/**
|
|
31643
|
+
* The workspace main menu: Reactodia's default actions, followed by the diagram download and upload.
|
|
31644
|
+
*
|
|
31645
|
+
* Passing a `menu` to the `DefaultWorkspace` replaces its default actions, so they are listed here again.
|
|
31646
|
+
* Rendered inside the workspace, so the actions can use the workspace hooks.
|
|
31647
|
+
*/
|
|
31648
|
+
function GraphwiseMenu() {
|
|
31649
|
+
const { model } = useWorkspace();
|
|
31650
|
+
const t = useTranslation();
|
|
31651
|
+
const diagramFileService = service(DiagramFileService);
|
|
31652
|
+
const diagramService = service(DiagramService);
|
|
31653
|
+
const downloadDiagram = () => {
|
|
31654
|
+
const timestamp = new Date().toISOString().replace(/[-:]|\.\d+Z$/g, '');
|
|
31655
|
+
diagramFileService.download(model.exportLayout(), `diagram-${timestamp}${DiagramFileService.FILE_EXTENSION}`);
|
|
31656
|
+
};
|
|
31657
|
+
const uploadDiagram = async (file) => {
|
|
31658
|
+
try {
|
|
31659
|
+
const diagram = await diagramFileService.read(file);
|
|
31660
|
+
await diagramService.importDiagram(model, model.dataProvider, diagram);
|
|
31661
|
+
}
|
|
31662
|
+
catch (error) {
|
|
31663
|
+
console.error('Failed to upload the diagram', error);
|
|
31664
|
+
}
|
|
31665
|
+
};
|
|
31666
|
+
return reactExports.createElement(reactExports.Fragment, null, reactExports.createElement(ToolbarActionClearAll, {}), reactExports.createElement(ToolbarActionExport, { kind: 'exportRaster' }), reactExports.createElement(ToolbarActionExport, { kind: 'exportSvg' }), reactExports.createElement(ToolbarActionExport, { kind: 'print' }), reactExports.createElement(ToolbarAction,
|
|
31667
|
+
// The same icon as ToolbarActionSave. We dont use that action, since it disables saving on refresh
|
|
31668
|
+
{ className: 'reactodia-toolbar-action__save', title: t.text('graphwise_toolbar_action.download_diagram.title'), onSelect: downloadDiagram }, t.text('graphwise_toolbar_action.download_diagram.label')), reactExports.createElement(ToolbarActionOpen, {
|
|
31669
|
+
title: t.text('graphwise_toolbar_action.upload_diagram.title'),
|
|
31670
|
+
fileAccept: DiagramFileService.FILE_EXTENSION,
|
|
31671
|
+
onSelect: (file) => void uploadDiagram(file)
|
|
31672
|
+
}, t.text('graphwise_toolbar_action.upload_diagram.label')));
|
|
31673
|
+
}
|
|
31674
|
+
|
|
30937
31675
|
/**
|
|
30938
|
-
* Reactodia ships English as its built-in default bundle, so English
|
|
31676
|
+
* Reactodia ships English as its built-in default bundle, so the English bundle only adds our own keys.
|
|
30939
31677
|
* Other languages are layered on top as partial bundles; unknown codes fall back to English.
|
|
30940
31678
|
*
|
|
30941
31679
|
* Reactodia never re-reads the translations, so the component must be remounted
|
|
@@ -31053,10 +31791,11 @@ function ReactodiaApp(props) {
|
|
|
31053
31791
|
const dataProvider = createDataProvider(props);
|
|
31054
31792
|
const isReload = props.isReload;
|
|
31055
31793
|
const savedDiagram = service(DiagramStorageService).load();
|
|
31794
|
+
const diagramService = service(DiagramService);
|
|
31056
31795
|
if (isReload) {
|
|
31057
31796
|
// Simply reload without using the current state. This may happen when the user switches the language at runtime.
|
|
31058
31797
|
// since there is no existing mechanism to re-translate the UI at runtime (not for the UI labels at least)
|
|
31059
|
-
await
|
|
31798
|
+
await diagramService.importDiagram(model, dataProvider, currentDiagram, signal);
|
|
31060
31799
|
}
|
|
31061
31800
|
else if ((_a = config.seedIris) === null || _a === void 0 ? void 0 : _a.length) {
|
|
31062
31801
|
await seedIrisToCanvas(context, dataProvider, config.seedIris, signal);
|
|
@@ -31067,7 +31806,7 @@ function ReactodiaApp(props) {
|
|
|
31067
31806
|
else {
|
|
31068
31807
|
// Restore the previously saved layout when present, otherwise start empty. Either way this binds the
|
|
31069
31808
|
// data provider (savedDiagram is undefined -> empty diagram), so the unified search/lookup works.
|
|
31070
|
-
await
|
|
31809
|
+
await diagramService.importDiagram(model, dataProvider, savedDiagram, signal);
|
|
31071
31810
|
}
|
|
31072
31811
|
}, [language]);
|
|
31073
31812
|
/**
|
|
@@ -31086,7 +31825,7 @@ function ReactodiaApp(props) {
|
|
|
31086
31825
|
defaultLanguage: language,
|
|
31087
31826
|
translations: translationsForLanguage(language),
|
|
31088
31827
|
typeStyleResolver: resolveTypeStyle
|
|
31089
|
-
}, reactExports.createElement(DefaultWorkspace, {}));
|
|
31828
|
+
}, reactExports.createElement(DefaultWorkspace, { menu: reactExports.createElement(GraphwiseMenu) }));
|
|
31090
31829
|
}
|
|
31091
31830
|
/**
|
|
31092
31831
|
* Mounts the Reactodia application into the given container and returns the React root.
|