graphwise-reactodia 0.0.1-TR9 → 0.0.1-blank-nodes-TR2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/graphwise-reactodia.cjs.entry.js +778 -86
- package/dist/cjs/graphwise-reactodia.cjs.js +2 -2
- package/dist/cjs/{index-CII9aUQC.js → index-D1-Nmh_N.js} +111 -9
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/collection/components/graphwise-reactodia/graphwise-reactodia.css +10 -2
- package/dist/collection/components/graphwise-reactodia/graphwise-reactodia.js +73 -8
- package/dist/collection/components/graphwise-reactodia/providers/service/service.provider.js +5 -5
- package/dist/collection/components/graphwise-reactodia/reactodia-app.js +8 -6
- package/dist/collection/components/graphwise-reactodia/services/diagram-storage/diagram-storage.service.js +23 -0
- package/dist/collection/components/graphwise-reactodia/styles/type-style.resolver.js +53 -0
- package/dist/esm/graphwise-reactodia.entry.js +778 -86
- package/dist/esm/graphwise-reactodia.js +3 -3
- package/dist/esm/{index-Cypw7ydc.js → index-CKnLINf8.js} +112 -9
- package/dist/esm/loader.js +3 -3
- package/dist/graphwise-reactodia/graphwise-reactodia.esm.js +1 -1
- package/dist/graphwise-reactodia/p-CKnLINf8.js +2 -0
- package/dist/graphwise-reactodia/p-b3505db7.entry.js +1 -0
- package/dist/types/components/graphwise-reactodia/graphwise-reactodia.d.ts +33 -3
- package/dist/types/components/graphwise-reactodia/models/reactodia-app-props.d.ts +1 -1
- package/dist/types/components/graphwise-reactodia/providers/service/service.provider.d.ts +2 -2
- package/dist/types/components/graphwise-reactodia/services/diagram-storage/diagram-storage.service.d.ts +19 -0
- package/dist/types/components/graphwise-reactodia/styles/type-style.resolver.d.ts +11 -0
- package/dist/types/components.d.ts +10 -2
- package/package.json +1 -1
- package/dist/graphwise-reactodia/p-5b907998.entry.js +0 -1
- package/dist/graphwise-reactodia/p-Cypw7ydc.js +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index$1 = require('./index-
|
|
3
|
+
var index$1 = require('./index-D1-Nmh_N.js');
|
|
4
4
|
var index = require('./index.cjs.js');
|
|
5
5
|
|
|
6
6
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
@@ -776,7 +776,7 @@ var global$1 = (typeof global !== "undefined" ? global :
|
|
|
776
776
|
typeof self !== "undefined" ? self :
|
|
777
777
|
typeof window !== "undefined" ? window : {});
|
|
778
778
|
|
|
779
|
-
var lookup = [];
|
|
779
|
+
var lookup$1 = [];
|
|
780
780
|
var revLookup = [];
|
|
781
781
|
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
|
|
782
782
|
var inited = false;
|
|
@@ -784,7 +784,7 @@ function init () {
|
|
|
784
784
|
inited = true;
|
|
785
785
|
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
786
786
|
for (var i = 0, len = code.length; i < len; ++i) {
|
|
787
|
-
lookup[i] = code[i];
|
|
787
|
+
lookup$1[i] = code[i];
|
|
788
788
|
revLookup[code.charCodeAt(i)] = i;
|
|
789
789
|
}
|
|
790
790
|
|
|
@@ -838,7 +838,7 @@ function toByteArray (b64) {
|
|
|
838
838
|
}
|
|
839
839
|
|
|
840
840
|
function tripletToBase64 (num) {
|
|
841
|
-
return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
|
|
841
|
+
return lookup$1[num >> 18 & 0x3F] + lookup$1[num >> 12 & 0x3F] + lookup$1[num >> 6 & 0x3F] + lookup$1[num & 0x3F]
|
|
842
842
|
}
|
|
843
843
|
|
|
844
844
|
function encodeChunk (uint8, start, end) {
|
|
@@ -870,14 +870,14 @@ function fromByteArray (uint8) {
|
|
|
870
870
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
871
871
|
if (extraBytes === 1) {
|
|
872
872
|
tmp = uint8[len - 1];
|
|
873
|
-
output += lookup[tmp >> 2];
|
|
874
|
-
output += lookup[(tmp << 4) & 0x3F];
|
|
873
|
+
output += lookup$1[tmp >> 2];
|
|
874
|
+
output += lookup$1[(tmp << 4) & 0x3F];
|
|
875
875
|
output += '==';
|
|
876
876
|
} else if (extraBytes === 2) {
|
|
877
877
|
tmp = (uint8[len - 2] << 8) + (uint8[len - 1]);
|
|
878
|
-
output += lookup[tmp >> 10];
|
|
879
|
-
output += lookup[(tmp >> 4) & 0x3F];
|
|
880
|
-
output += lookup[(tmp << 2) & 0x3F];
|
|
878
|
+
output += lookup$1[tmp >> 10];
|
|
879
|
+
output += lookup$1[(tmp >> 4) & 0x3F];
|
|
880
|
+
output += lookup$1[(tmp << 2) & 0x3F];
|
|
881
881
|
output += '=';
|
|
882
882
|
}
|
|
883
883
|
|
|
@@ -6400,6 +6400,567 @@ function escapeRegexp(token) {
|
|
|
6400
6400
|
return token.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
6401
6401
|
}
|
|
6402
6402
|
//#endregion
|
|
6403
|
+
//#region src/data/sparql/sparqlModels.ts
|
|
6404
|
+
function mapSparqlResponseIntoRdfJs(response, factory) {
|
|
6405
|
+
function mapSparqlBinding(binding) {
|
|
6406
|
+
const mapped = {};
|
|
6407
|
+
for (const key in binding) if (Object.prototype.hasOwnProperty.call(binding, key)) {
|
|
6408
|
+
const term = binding[key];
|
|
6409
|
+
switch (term.type) {
|
|
6410
|
+
case "uri":
|
|
6411
|
+
mapped[key] = factory.namedNode(term.value);
|
|
6412
|
+
break;
|
|
6413
|
+
case "bnode":
|
|
6414
|
+
mapped[key] = factory.blankNode(term.value);
|
|
6415
|
+
break;
|
|
6416
|
+
case "literal":
|
|
6417
|
+
case "typed-literal":
|
|
6418
|
+
mapped[key] = factory.literal(term.value, term["xml:lang"] ? term["xml:lang"] : term.datatype ? factory.namedNode(term.datatype) : void 0);
|
|
6419
|
+
break;
|
|
6420
|
+
default: throw new Error(`Unexpected SPARQL term type: "${term.type}"`);
|
|
6421
|
+
}
|
|
6422
|
+
}
|
|
6423
|
+
return mapped;
|
|
6424
|
+
}
|
|
6425
|
+
return {
|
|
6426
|
+
...response,
|
|
6427
|
+
results: {
|
|
6428
|
+
...response.results,
|
|
6429
|
+
bindings: response.results.bindings.map(mapSparqlBinding)
|
|
6430
|
+
}
|
|
6431
|
+
};
|
|
6432
|
+
}
|
|
6433
|
+
function isRdfIri(term) {
|
|
6434
|
+
return Boolean(term && term.termType === "NamedNode");
|
|
6435
|
+
}
|
|
6436
|
+
function isRdfBlank(term) {
|
|
6437
|
+
return Boolean(term && term.termType === "BlankNode");
|
|
6438
|
+
}
|
|
6439
|
+
function isRdfLiteral(term) {
|
|
6440
|
+
return Boolean(term && term.termType === "Literal");
|
|
6441
|
+
}
|
|
6442
|
+
function isBlankBinding(binding) {
|
|
6443
|
+
const blank = binding;
|
|
6444
|
+
return blank.blankTrgProp !== void 0 || blank.blankTrg !== void 0 || blank.blankSrcProp !== void 0 || blank.blankSrc !== void 0;
|
|
6445
|
+
}
|
|
6446
|
+
/**
|
|
6447
|
+
* IRI prefix for the encoded blank node subtype produced by this module.
|
|
6448
|
+
*
|
|
6449
|
+
* @see {@link isEncodedBlank}
|
|
6450
|
+
*/
|
|
6451
|
+
var ENCODED_PREFIX = "urn:reactodia:blank:sparql:";
|
|
6452
|
+
/**
|
|
6453
|
+
* There is no way to refer to an existing blank node from a SPARQL query, and the labels
|
|
6454
|
+
* an endpoint returns for one are local to the response they came in. Instead of trying to
|
|
6455
|
+
* address a blank node, the whole statement set which describes it is encoded into the
|
|
6456
|
+
* element IRI, which makes the identity content-addressed: the same surrounding graph always
|
|
6457
|
+
* yields the same IRI, and every request about such an element can be answered locally by
|
|
6458
|
+
* decoding it back, without another round-trip to the endpoint.
|
|
6459
|
+
*
|
|
6460
|
+
* The trade-off is size: the IRI grows linearly with the number of statements attached to
|
|
6461
|
+
* the blank node, at roughly 300 characters per statement. {@link MAX_RECURSION_DEEP} bounds
|
|
6462
|
+
* how deep nesting can go, but nothing bounds how wide a single blank node can be, so a long
|
|
6463
|
+
* RDF list produces a correspondingly long IRI (measured against the W3C Organization
|
|
6464
|
+
* ontology: 380-1650 characters for its OWL axioms; a synthetic 100-member list: ~28 KB).
|
|
6465
|
+
*/
|
|
6466
|
+
function isEncodedBlank$1(iri) {
|
|
6467
|
+
return iri.startsWith(ENCODED_PREFIX);
|
|
6468
|
+
}
|
|
6469
|
+
/**
|
|
6470
|
+
* Additional output bindings which {@link BLANK_NODE_QUERY} produces, to be added
|
|
6471
|
+
* to the outer projection of the lookup query.
|
|
6472
|
+
*/
|
|
6473
|
+
var BLANK_NODE_QUERY_PARAMETERS = "?blankType ?blankTrgProp ?blankTrg ?blankSrc ?blankSrcProp";
|
|
6474
|
+
/**
|
|
6475
|
+
* Lookup query addition which describes every blank node in `?inst` by its outgoing
|
|
6476
|
+
* statements and the statement pointing at it.
|
|
6477
|
+
*
|
|
6478
|
+
* The three alternatives handle a plain blank node, an RDF list referenced directly
|
|
6479
|
+
* and a cell in the middle of an RDF list (which is resolved to the list head, so that
|
|
6480
|
+
* a list appears as a single element rather than one element per cell).
|
|
6481
|
+
*/
|
|
6482
|
+
var BLANK_NODE_QUERY = `
|
|
6483
|
+
OPTIONAL {
|
|
6484
|
+
FILTER (ISBLANK(?inst)).
|
|
6485
|
+
{
|
|
6486
|
+
?inst ?blankTrgProp ?blankTrg.
|
|
6487
|
+
?blankSrc ?blankSrcProp ?inst.
|
|
6488
|
+
FILTER NOT EXISTS { ?inst rdf:first _:smth1 }.
|
|
6489
|
+
BIND("blankNode" as ?blankType)
|
|
6490
|
+
} UNION {
|
|
6491
|
+
?inst rdf:rest*/rdf:first ?blankTrg.
|
|
6492
|
+
?blankSrc ?blankSrcProp ?inst.
|
|
6493
|
+
_:smth2 rdf:first ?blankTrg.
|
|
6494
|
+
BIND(?blankSrcProp as ?blankTrgProp)
|
|
6495
|
+
BIND("listHead" as ?blankType)
|
|
6496
|
+
FILTER NOT EXISTS { _:smth3 rdf:rest ?inst }.
|
|
6497
|
+
} UNION {
|
|
6498
|
+
?listHead rdf:rest* ?inst.
|
|
6499
|
+
FILTER NOT EXISTS { _:smth4 rdf:rest ?listHead }.
|
|
6500
|
+
|
|
6501
|
+
?listHead rdf:rest*/rdf:first ?blankTrg.
|
|
6502
|
+
?blankSrc ?blankSrcProp ?listHead.
|
|
6503
|
+
_:smth5 rdf:first ?blankTrg.
|
|
6504
|
+
BIND(?blankSrcProp as ?blankTrgProp)
|
|
6505
|
+
BIND("listHead" as ?blankType)
|
|
6506
|
+
}
|
|
6507
|
+
}
|
|
6508
|
+
`;
|
|
6509
|
+
var XSD_STRING = "http://www.w3.org/2001/XMLSchema#string";
|
|
6510
|
+
function encodeTerm(term) {
|
|
6511
|
+
switch (term.termType) {
|
|
6512
|
+
case "NamedNode": return ["i", term.value];
|
|
6513
|
+
case "BlankNode": return ["b", term.value];
|
|
6514
|
+
case "Literal":
|
|
6515
|
+
if (term.language) return [
|
|
6516
|
+
"l",
|
|
6517
|
+
term.value,
|
|
6518
|
+
term.language
|
|
6519
|
+
];
|
|
6520
|
+
else if (term.datatype && term.datatype.value !== XSD_STRING) return [
|
|
6521
|
+
"d",
|
|
6522
|
+
term.value,
|
|
6523
|
+
term.datatype.value
|
|
6524
|
+
];
|
|
6525
|
+
return ["l", term.value];
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6528
|
+
function decodeTerm(encoded, factory) {
|
|
6529
|
+
switch (encoded[0]) {
|
|
6530
|
+
case "i": return factory.namedNode(encoded[1]);
|
|
6531
|
+
case "b": return factory.blankNode(encoded[1]);
|
|
6532
|
+
case "l": return factory.literal(encoded[1], encoded[2]);
|
|
6533
|
+
case "d": return factory.literal(encoded[1], factory.namedNode(encoded[2]));
|
|
6534
|
+
default: throw new Error(`Unexpected encoded blank node term: ${JSON.stringify(encoded)}`);
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
6537
|
+
/**
|
|
6538
|
+
* Encodes the statement set which describes a single blank node into an element IRI.
|
|
6539
|
+
*
|
|
6540
|
+
* Bindings are deduplicated and sorted, so that the same statement set always produces
|
|
6541
|
+
* the same IRI regardless of the order the endpoint returned it in. The blank node label
|
|
6542
|
+
* itself is deliberately left out: it is local to the response it came from and would make
|
|
6543
|
+
* the IRI change between otherwise identical requests.
|
|
6544
|
+
*/
|
|
6545
|
+
function encodeId(blankBindings, statementLimit) {
|
|
6546
|
+
const bindingSet = /* @__PURE__ */ new Map();
|
|
6547
|
+
for (const binding of blankBindings) {
|
|
6548
|
+
const encoded = {
|
|
6549
|
+
t: binding.blankType.value,
|
|
6550
|
+
p: encodeTerm(binding.blankTrgProp),
|
|
6551
|
+
o: encodeTerm(binding.blankTrg),
|
|
6552
|
+
s: binding.blankSrc ? encodeTerm(binding.blankSrc) : void 0,
|
|
6553
|
+
sp: binding.blankSrcProp ? encodeTerm(binding.blankSrcProp) : void 0,
|
|
6554
|
+
c: binding.class ? encodeTerm(binding.class) : void 0
|
|
6555
|
+
};
|
|
6556
|
+
bindingSet.set(JSON.stringify(encoded), encoded);
|
|
6557
|
+
}
|
|
6558
|
+
const orderedKeys = Array.from(bindingSet.keys()).sort();
|
|
6559
|
+
const normalizedBindings = (typeof statementLimit === "number" ? orderedKeys.slice(0, Math.max(statementLimit, 0)) : orderedKeys).map((key) => bindingSet.get(key));
|
|
6560
|
+
return ENCODED_PREFIX + toBase64Url(JSON.stringify(normalizedBindings));
|
|
6561
|
+
}
|
|
6562
|
+
/**
|
|
6563
|
+
* Encodes the payload as base64url rather than percent-escaping it.
|
|
6564
|
+
*
|
|
6565
|
+
* A nested blank node is embedded into its parent by its own encoded IRI, and base64url
|
|
6566
|
+
* output contains only characters which need no escaping, so nesting costs a flat 4/3 per
|
|
6567
|
+
* level. Percent-escaping would escape the escapes, roughly doubling the payload at every
|
|
6568
|
+
* level instead.
|
|
6569
|
+
*/
|
|
6570
|
+
function toBase64Url(text) {
|
|
6571
|
+
const bytes = new TextEncoder().encode(text);
|
|
6572
|
+
let binary = "";
|
|
6573
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
6574
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
6575
|
+
}
|
|
6576
|
+
function fromBase64Url(encoded) {
|
|
6577
|
+
const binary = atob(encoded.replace(/-/g, "+").replace(/_/g, "/"));
|
|
6578
|
+
const bytes = new Uint8Array(binary.length);
|
|
6579
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
6580
|
+
return new TextDecoder().decode(bytes);
|
|
6581
|
+
}
|
|
6582
|
+
/**
|
|
6583
|
+
* Decodes an element IRI produced by {@link encodeId} back into the statement set it
|
|
6584
|
+
* was made from, or returns `undefined` if the IRI is not an encoded blank node or
|
|
6585
|
+
* cannot be parsed.
|
|
6586
|
+
*/
|
|
6587
|
+
function decodeId(id, factory) {
|
|
6588
|
+
if (!isEncodedBlank$1(id)) return;
|
|
6589
|
+
let parsed;
|
|
6590
|
+
try {
|
|
6591
|
+
const encodedPart = id.substring(27);
|
|
6592
|
+
parsed = JSON.parse(fromBase64Url(encodedPart));
|
|
6593
|
+
if (!Array.isArray(parsed)) return;
|
|
6594
|
+
} catch {
|
|
6595
|
+
return;
|
|
6596
|
+
}
|
|
6597
|
+
try {
|
|
6598
|
+
const inst = factory.namedNode(id);
|
|
6599
|
+
return parsed.map((encoded) => {
|
|
6600
|
+
const binding = {
|
|
6601
|
+
inst,
|
|
6602
|
+
blankType: factory.literal(encoded.t),
|
|
6603
|
+
blankTrgProp: decodeTerm(encoded.p, factory),
|
|
6604
|
+
blankTrg: decodeTerm(encoded.o, factory),
|
|
6605
|
+
blankSrc: encoded.s ? decodeTerm(encoded.s, factory) : void 0,
|
|
6606
|
+
blankSrcProp: encoded.sp ? decodeTerm(encoded.sp, factory) : void 0,
|
|
6607
|
+
class: encoded.c ? decodeTerm(encoded.c, factory) : void 0
|
|
6608
|
+
};
|
|
6609
|
+
binding.label = createLabelForBlankBinding(binding, factory);
|
|
6610
|
+
return binding;
|
|
6611
|
+
});
|
|
6612
|
+
} catch {
|
|
6613
|
+
return;
|
|
6614
|
+
}
|
|
6615
|
+
}
|
|
6616
|
+
function createLabelForBlankBinding(binding, factory) {
|
|
6617
|
+
if (binding.blankType.value === "listHead") return factory.literal("RDFList");
|
|
6618
|
+
const { class: classTerm } = binding;
|
|
6619
|
+
return factory.literal(classTerm ? getLocalName(classTerm.value) ?? classTerm.value : "anonymous");
|
|
6620
|
+
}
|
|
6621
|
+
/**
|
|
6622
|
+
* Deduplicates identical in-flight queries, since the chains being followed
|
|
6623
|
+
* often overlap on their leading statements.
|
|
6624
|
+
*/
|
|
6625
|
+
var QueryExecutor = class {
|
|
6626
|
+
queryFunction;
|
|
6627
|
+
running = /* @__PURE__ */ new Map();
|
|
6628
|
+
constructor(queryFunction) {
|
|
6629
|
+
this.queryFunction = queryFunction;
|
|
6630
|
+
}
|
|
6631
|
+
executeQuery(query) {
|
|
6632
|
+
const existing = this.running.get(query);
|
|
6633
|
+
if (existing) return existing;
|
|
6634
|
+
const execution = this.queryFunction(query).then((response) => {
|
|
6635
|
+
this.running.delete(query);
|
|
6636
|
+
return response;
|
|
6637
|
+
}, (error) => {
|
|
6638
|
+
this.running.delete(query);
|
|
6639
|
+
throw error;
|
|
6640
|
+
});
|
|
6641
|
+
this.running.set(query, execution);
|
|
6642
|
+
return execution;
|
|
6643
|
+
}
|
|
6644
|
+
};
|
|
6645
|
+
/**
|
|
6646
|
+
* Replaces the raw blank node bindings in a lookup response with bindings whose `inst`
|
|
6647
|
+
* is an encoded IRI, resolving nested blank nodes along the way.
|
|
6648
|
+
*/
|
|
6649
|
+
async function updateLookupResults(result, queryFunction, settings, factory) {
|
|
6650
|
+
const completeBindings = [];
|
|
6651
|
+
const blankBindings = [];
|
|
6652
|
+
for (const binding of result.results.bindings) if (isBlankBinding(binding)) blankBindings.push(binding);
|
|
6653
|
+
else completeBindings.push(binding);
|
|
6654
|
+
if (blankBindings.length === 0) return result;
|
|
6655
|
+
const processedBindings = await processBlankBindings(blankBindings, queryFunction, settings, factory);
|
|
6656
|
+
return {
|
|
6657
|
+
...result,
|
|
6658
|
+
results: {
|
|
6659
|
+
...result.results,
|
|
6660
|
+
bindings: completeBindings.concat(processedBindings)
|
|
6661
|
+
}
|
|
6662
|
+
};
|
|
6663
|
+
}
|
|
6664
|
+
/**
|
|
6665
|
+
* Groups raw blank node bindings by the blank node they describe, resolves any nested
|
|
6666
|
+
* blank nodes they point at and rewrites both the instance and the nested targets into
|
|
6667
|
+
* encoded IRIs.
|
|
6668
|
+
*
|
|
6669
|
+
* The bindings are mutated in place and returned.
|
|
6670
|
+
*/
|
|
6671
|
+
async function processBlankBindings(blankBindings, queryFunction, settings, factory) {
|
|
6672
|
+
const bindingGroupsById = /* @__PURE__ */ new Map();
|
|
6673
|
+
for (const binding of blankBindings) {
|
|
6674
|
+
let group = bindingGroupsById.get(binding.inst.value);
|
|
6675
|
+
if (!group) {
|
|
6676
|
+
group = [];
|
|
6677
|
+
bindingGroupsById.set(binding.inst.value, group);
|
|
6678
|
+
}
|
|
6679
|
+
group.push(binding);
|
|
6680
|
+
}
|
|
6681
|
+
const nestedChains = [];
|
|
6682
|
+
for (const binding of blankBindings) if (isRdfBlank(binding.blankTrg)) nestedChains.push([binding]);
|
|
6683
|
+
const loadedGroups = await loadRelatedBlankNodes(nestedChains, new QueryExecutor(queryFunction), settings, factory);
|
|
6684
|
+
const { blankNodeStatementLimit } = settings;
|
|
6685
|
+
const idsMap = getEncodedIdDictionary(loadedGroups, factory, blankNodeStatementLimit);
|
|
6686
|
+
for (const group of bindingGroupsById.values()) {
|
|
6687
|
+
for (const binding of group) {
|
|
6688
|
+
const encodedNestedId = idsMap.get(binding.blankTrg.value);
|
|
6689
|
+
if (encodedNestedId !== void 0) binding.blankTrg = factory.namedNode(encodedNestedId);
|
|
6690
|
+
if (!binding.label) binding.label = createLabelForBlankBinding(binding, factory);
|
|
6691
|
+
}
|
|
6692
|
+
updateGroupIds(group, encodeId(group, blankNodeStatementLimit), factory);
|
|
6693
|
+
}
|
|
6694
|
+
return blankBindings.slice();
|
|
6695
|
+
}
|
|
6696
|
+
function getEncodedIdDictionary(blankBindingGroups, factory, statementLimit) {
|
|
6697
|
+
const idDictionary = /* @__PURE__ */ new Map();
|
|
6698
|
+
for (const [key, group] of blankBindingGroups) {
|
|
6699
|
+
const encodedId = encodeId(group, statementLimit);
|
|
6700
|
+
idDictionary.set(key, encodedId);
|
|
6701
|
+
updateGroupIds(group, encodedId, factory);
|
|
6702
|
+
}
|
|
6703
|
+
return idDictionary;
|
|
6704
|
+
}
|
|
6705
|
+
function updateGroupIds(group, newId, factory) {
|
|
6706
|
+
const inst = factory.namedNode(newId);
|
|
6707
|
+
for (const binding of group) binding.inst = inst;
|
|
6708
|
+
}
|
|
6709
|
+
async function loadRelatedBlankNodes(blankChains, queryExecutor, settings, factory, recursionDeep = 1) {
|
|
6710
|
+
const loadedBlankBindings = /* @__PURE__ */ new Map();
|
|
6711
|
+
if (recursionDeep > 3 || blankChains.length === 0) return loadedBlankBindings;
|
|
6712
|
+
const results = await Promise.all(blankChains.map(async (chain) => ({
|
|
6713
|
+
chain,
|
|
6714
|
+
response: await queryExecutor.executeQuery(getQueryForChain(chain, settings))
|
|
6715
|
+
})));
|
|
6716
|
+
await Promise.all(results.map(async ({ chain, response }) => {
|
|
6717
|
+
const bindings = response.results.bindings;
|
|
6718
|
+
if (bindings.length === 0) return;
|
|
6719
|
+
const nestedChains = [];
|
|
6720
|
+
for (const binding of bindings) if (isRdfBlank(binding.blankTrg)) nestedChains.push(chain.concat([binding]));
|
|
6721
|
+
const idsMap = getEncodedIdDictionary(await loadRelatedBlankNodes(nestedChains, queryExecutor, settings, factory, recursionDeep + 1), factory, settings.blankNodeStatementLimit);
|
|
6722
|
+
for (const binding of bindings) {
|
|
6723
|
+
const encodedNestedId = idsMap.get(binding.blankTrg.value);
|
|
6724
|
+
if (encodedNestedId !== void 0) binding.blankTrg = factory.namedNode(encodedNestedId);
|
|
6725
|
+
binding.label = createLabelForBlankBinding(binding, factory);
|
|
6726
|
+
let group = loadedBlankBindings.get(binding.inst.value);
|
|
6727
|
+
if (!group) {
|
|
6728
|
+
group = [];
|
|
6729
|
+
loadedBlankBindings.set(binding.inst.value, group);
|
|
6730
|
+
}
|
|
6731
|
+
group.push(binding);
|
|
6732
|
+
}
|
|
6733
|
+
}));
|
|
6734
|
+
return loadedBlankBindings;
|
|
6735
|
+
}
|
|
6736
|
+
/**
|
|
6737
|
+
* Builds a query which walks the chain of statements from the named node the chain starts at
|
|
6738
|
+
* down to the blank node at its end, and describes that blank node.
|
|
6739
|
+
*
|
|
6740
|
+
* The chain is expressed entirely in IRIs and predicates, since blank node labels from
|
|
6741
|
+
* a previous response cannot be used to address anything in a new query.
|
|
6742
|
+
*/
|
|
6743
|
+
function getQueryForChain(blankNodes, settings) {
|
|
6744
|
+
function getQueryBlock(blankNode, index, maxIndex) {
|
|
6745
|
+
const trustableTrgProp = index === 0 || blankNode.blankType.value !== "listHead";
|
|
6746
|
+
const sourceId = index > 0 ? `?inst${index - 1}` : formatChainTerm(blankNode.blankSrc, `?blankAnySrc${index}`);
|
|
6747
|
+
const sourcePropId = trustableTrgProp ? index > 0 ? `?blankTrgProp${index - 1}` : formatChainTerm(blankNode.blankSrcProp, `?blankAnySrcProp${index}`) : `?anyType${index}`;
|
|
6748
|
+
const instPostfix = index === maxIndex ? "" : index.toString();
|
|
6749
|
+
const targetPropId = trustableTrgProp ? formatChainTerm(blankNode.blankTrgProp, `?blankAnyTrgProp${index}`) : `?anyType0${index}`;
|
|
6750
|
+
return `
|
|
6751
|
+
# ======================
|
|
6752
|
+
${sourceId} ${sourcePropId} ?blankSrc${index}.
|
|
6753
|
+
${index === 0 && blankNode.blankType.value === "listHead" ? `?blankSrc${index} rdf:rest*/rdf:first ?inst${instPostfix}.` : `?blankSrc${index} ${targetPropId} ?inst${instPostfix}.`}
|
|
6754
|
+
BIND (${formatChainTerm(blankNode.blankTrgProp, `?blankAnyBind${index}`)} as ?blankSrcProp${index}).
|
|
6755
|
+
FILTER (ISBLANK(?inst${instPostfix})).
|
|
6756
|
+
{
|
|
6757
|
+
?inst${instPostfix} ?blankTrgProp${instPostfix} ?blankTrg${instPostfix}.
|
|
6758
|
+
BIND("blankNode" as ?blankType${instPostfix}).
|
|
6759
|
+
FILTER NOT EXISTS { ?inst${instPostfix} rdf:first _:smth1${index} }.
|
|
6760
|
+
} UNION {
|
|
6761
|
+
?inst${instPostfix} rdf:rest*/rdf:first ?blankTrg${instPostfix}.
|
|
6762
|
+
?blankSrc${index} ?blankSrcProp${index} ?inst${instPostfix}.
|
|
6763
|
+
_:smth2${index} rdf:first ?blankTrg${instPostfix}.
|
|
6764
|
+
BIND(?blankSrcProp${index} as ?blankTrgProp${instPostfix})
|
|
6765
|
+
BIND("listHead" as ?blankType${instPostfix})
|
|
6766
|
+
FILTER NOT EXISTS { _:smth3${index} rdf:rest ?inst${instPostfix} }.
|
|
6767
|
+
}
|
|
6768
|
+
OPTIONAL {
|
|
6769
|
+
?inst${instPostfix} rdf:type ?class${instPostfix}.
|
|
6770
|
+
}
|
|
6771
|
+
`;
|
|
6772
|
+
}
|
|
6773
|
+
const body = blankNodes.map((bn, index) => getQueryBlock(bn, index, blankNodes.length - 1)).join("\n");
|
|
6774
|
+
return `${settings.defaultPrefix}
|
|
6775
|
+
SELECT ?inst ?class ?label ?blankTrgProp ?blankTrg ?blankType
|
|
6776
|
+
WHERE {
|
|
6777
|
+
${body}
|
|
6778
|
+
}
|
|
6779
|
+
`;
|
|
6780
|
+
}
|
|
6781
|
+
/**
|
|
6782
|
+
* Formats a chain term as an IRI, falling back to a fresh variable when the term is
|
|
6783
|
+
* missing or is itself a blank node, neither of which can be referenced from a query.
|
|
6784
|
+
*/
|
|
6785
|
+
function formatChainTerm(term, fallbackVariable) {
|
|
6786
|
+
return isRdfIri(term) ? `<${term.value}>` : fallbackVariable;
|
|
6787
|
+
}
|
|
6788
|
+
/**
|
|
6789
|
+
* Answers {@link DataProvider.elements} for encoded blank nodes without querying the endpoint.
|
|
6790
|
+
*
|
|
6791
|
+
* Literal targets of the blank node are surfaced as element properties; targets which are
|
|
6792
|
+
* entities of their own become links instead, see {@link links}.
|
|
6793
|
+
*/
|
|
6794
|
+
function elements(elementIds, factory) {
|
|
6795
|
+
const bindings = [];
|
|
6796
|
+
for (const id of elementIds) {
|
|
6797
|
+
const blankBindings = decodeId(id, factory);
|
|
6798
|
+
if (!blankBindings) continue;
|
|
6799
|
+
for (const binding of blankBindings) if (isRdfLiteral(binding.blankTrg)) bindings.push({
|
|
6800
|
+
...binding,
|
|
6801
|
+
propType: binding.blankTrgProp,
|
|
6802
|
+
propValue: binding.blankTrg
|
|
6803
|
+
});
|
|
6804
|
+
else bindings.push(binding);
|
|
6805
|
+
}
|
|
6806
|
+
return bindings;
|
|
6807
|
+
}
|
|
6808
|
+
/**
|
|
6809
|
+
* Answers {@link DataProvider.links} for encoded blank nodes without querying the endpoint.
|
|
6810
|
+
*
|
|
6811
|
+
* Only links where both ends are among the requested elements are returned, matching
|
|
6812
|
+
* the behavior of the endpoint-backed query.
|
|
6813
|
+
*/
|
|
6814
|
+
function links(elementIds, factory) {
|
|
6815
|
+
const requested = new Set(elementIds);
|
|
6816
|
+
const bindings = [];
|
|
6817
|
+
for (const id of elementIds) {
|
|
6818
|
+
const blankBindings = decodeId(id, factory);
|
|
6819
|
+
if (!blankBindings) continue;
|
|
6820
|
+
for (const binding of blankBindings) {
|
|
6821
|
+
const { inst, blankSrc, blankSrcProp, blankTrg, blankTrgProp } = binding;
|
|
6822
|
+
if (blankSrc && blankSrcProp && requested.has(blankSrc.value)) bindings.push({
|
|
6823
|
+
source: blankSrc,
|
|
6824
|
+
type: blankSrcProp,
|
|
6825
|
+
target: inst
|
|
6826
|
+
});
|
|
6827
|
+
if (!isRdfLiteral(blankTrg) && requested.has(blankTrg.value)) bindings.push({
|
|
6828
|
+
source: inst,
|
|
6829
|
+
type: blankTrgProp,
|
|
6830
|
+
target: blankTrg
|
|
6831
|
+
});
|
|
6832
|
+
}
|
|
6833
|
+
}
|
|
6834
|
+
return bindings;
|
|
6835
|
+
}
|
|
6836
|
+
/**
|
|
6837
|
+
* Answers {@link DataProvider.connectedLinkStats} for an encoded blank node without
|
|
6838
|
+
* querying the endpoint.
|
|
6839
|
+
*/
|
|
6840
|
+
function connectedLinkStats(elementId, factory) {
|
|
6841
|
+
const blankBindings = decodeId(elementId, factory);
|
|
6842
|
+
if (!blankBindings) return [];
|
|
6843
|
+
const stats = /* @__PURE__ */ new Map();
|
|
6844
|
+
function getOrCreate(linkTypeId) {
|
|
6845
|
+
let count = stats.get(linkTypeId);
|
|
6846
|
+
if (!count) {
|
|
6847
|
+
count = {
|
|
6848
|
+
id: linkTypeId,
|
|
6849
|
+
inCount: 0,
|
|
6850
|
+
outCount: 0
|
|
6851
|
+
};
|
|
6852
|
+
stats.set(linkTypeId, count);
|
|
6853
|
+
}
|
|
6854
|
+
return count;
|
|
6855
|
+
}
|
|
6856
|
+
for (const binding of blankBindings) {
|
|
6857
|
+
const { blankSrc, blankSrcProp, blankTrg, blankTrgProp } = binding;
|
|
6858
|
+
if (isRdfLiteral(blankTrg)) continue;
|
|
6859
|
+
if (blankSrc && blankSrcProp) {
|
|
6860
|
+
const incoming = getOrCreate(blankSrcProp.value);
|
|
6861
|
+
incoming.inCount += 1;
|
|
6862
|
+
}
|
|
6863
|
+
const outgoing = getOrCreate(blankTrgProp.value);
|
|
6864
|
+
outgoing.outCount += 1;
|
|
6865
|
+
}
|
|
6866
|
+
return Array.from(stats.values());
|
|
6867
|
+
}
|
|
6868
|
+
/**
|
|
6869
|
+
* Answers {@link DataProvider.lookup} for navigation from an encoded blank node without
|
|
6870
|
+
* querying the endpoint, or returns `undefined` when the lookup is not about one.
|
|
6871
|
+
*/
|
|
6872
|
+
function lookup(params, factory) {
|
|
6873
|
+
let bindings;
|
|
6874
|
+
if (params.elementTypeId) return;
|
|
6875
|
+
else if (params.refElementId && params.refElementLinkId) bindings = getAllRelatedByLinkTypeElements(params.refElementId, params.refElementLinkId, params.linkDirection, factory);
|
|
6876
|
+
else if (params.refElementId) bindings = getAllRelatedElements(params.refElementId, factory);
|
|
6877
|
+
else return;
|
|
6878
|
+
if (bindings.length === 0) return;
|
|
6879
|
+
if (params.text) {
|
|
6880
|
+
const text = params.text.toLowerCase();
|
|
6881
|
+
bindings = bindings.filter((binding) => binding.inst.value.toLowerCase().includes(text));
|
|
6882
|
+
}
|
|
6883
|
+
return {
|
|
6884
|
+
head: { vars: [] },
|
|
6885
|
+
results: { bindings }
|
|
6886
|
+
};
|
|
6887
|
+
}
|
|
6888
|
+
/**
|
|
6889
|
+
* Answers the element type query for encoded blank nodes without querying the endpoint.
|
|
6890
|
+
*/
|
|
6891
|
+
function collectElementTypes$1(elementIds, factory, result) {
|
|
6892
|
+
for (const id of elementIds) {
|
|
6893
|
+
const blankBindings = decodeId(id, factory);
|
|
6894
|
+
if (!blankBindings) continue;
|
|
6895
|
+
for (const binding of blankBindings) if (binding.class) {
|
|
6896
|
+
let types = result.get(id);
|
|
6897
|
+
if (!types) {
|
|
6898
|
+
types = /* @__PURE__ */ new Set();
|
|
6899
|
+
result.set(id, types);
|
|
6900
|
+
}
|
|
6901
|
+
types.add(binding.class.value);
|
|
6902
|
+
}
|
|
6903
|
+
}
|
|
6904
|
+
}
|
|
6905
|
+
/**
|
|
6906
|
+
* Answers navigation from an encoded blank node over a single link type.
|
|
6907
|
+
*
|
|
6908
|
+
* The direction is relative to the blank node: `in` collects the entities which point at it,
|
|
6909
|
+
* `out` the entities it points at.
|
|
6910
|
+
*/
|
|
6911
|
+
function getAllRelatedByLinkTypeElements(refElementId, refElementLinkId, linkDirection, factory) {
|
|
6912
|
+
const blankBindings = decodeId(refElementId, factory);
|
|
6913
|
+
if (!blankBindings) return [];
|
|
6914
|
+
const linkType = factory.namedNode(refElementLinkId);
|
|
6915
|
+
const bindings = [];
|
|
6916
|
+
for (const binding of blankBindings) {
|
|
6917
|
+
const { blankSrc, blankSrcProp, blankTrg, blankTrgProp } = binding;
|
|
6918
|
+
if (linkDirection !== "out" && blankSrc && blankSrcProp && blankSrcProp.value === refElementLinkId) pushRelated(bindings, blankSrc, linkType, "in", factory);
|
|
6919
|
+
if (linkDirection !== "in" && blankTrgProp.value === refElementLinkId && !isRdfLiteral(blankTrg)) pushRelated(bindings, blankTrg, linkType, "out", factory);
|
|
6920
|
+
}
|
|
6921
|
+
return bindings;
|
|
6922
|
+
}
|
|
6923
|
+
/**
|
|
6924
|
+
* Answers navigation from an encoded blank node over every link type at once.
|
|
6925
|
+
*
|
|
6926
|
+
* Literal targets are left out: they are the blank node's own properties, not connections.
|
|
6927
|
+
*/
|
|
6928
|
+
function getAllRelatedElements(refElementId, factory) {
|
|
6929
|
+
const blankBindings = decodeId(refElementId, factory);
|
|
6930
|
+
if (!blankBindings) return [];
|
|
6931
|
+
const bindings = [];
|
|
6932
|
+
for (const binding of blankBindings) {
|
|
6933
|
+
const { blankSrc, blankSrcProp, blankTrg, blankTrgProp } = binding;
|
|
6934
|
+
if (blankSrc && blankSrcProp) pushRelated(bindings, blankSrc, blankSrcProp, "in", factory);
|
|
6935
|
+
if (!isRdfLiteral(blankTrg)) pushRelated(bindings, blankTrg, blankTrgProp, "out", factory);
|
|
6936
|
+
}
|
|
6937
|
+
return bindings;
|
|
6938
|
+
}
|
|
6939
|
+
/**
|
|
6940
|
+
* Adds the element at the far end of a statement to the result: a named node as-is,
|
|
6941
|
+
* an encoded blank node expanded back into the statements which describe it.
|
|
6942
|
+
*
|
|
6943
|
+
* The link type and the direction are carried over so that the result describes the
|
|
6944
|
+
* connection the same way an endpoint-backed lookup does. A named node is returned bare,
|
|
6945
|
+
* without a label or a type: {@link SparqlDataProvider.lookup} fills those in, since only
|
|
6946
|
+
* the endpoint knows them.
|
|
6947
|
+
*/
|
|
6948
|
+
function pushRelated(bindings, term, linkType, direction, factory) {
|
|
6949
|
+
const connection = {
|
|
6950
|
+
link: linkType,
|
|
6951
|
+
direction: factory.literal(direction)
|
|
6952
|
+
};
|
|
6953
|
+
const nested = isRdfIri(term) ? decodeId(term.value, factory) : void 0;
|
|
6954
|
+
if (nested) for (const binding of nested) bindings.push({
|
|
6955
|
+
...binding,
|
|
6956
|
+
...connection
|
|
6957
|
+
});
|
|
6958
|
+
else bindings.push({
|
|
6959
|
+
inst: term,
|
|
6960
|
+
...connection
|
|
6961
|
+
});
|
|
6962
|
+
}
|
|
6963
|
+
//#endregion
|
|
6403
6964
|
//#region src/data/sparql/requestChunking.ts
|
|
6404
6965
|
function* chunkArray(items, measure, maxChunkSize) {
|
|
6405
6966
|
if (!Number.isFinite(maxChunkSize)) {
|
|
@@ -6462,46 +7023,6 @@ function* chunkDirectedCrossProduct(from, to, measure, maxChunkSize, excludedTar
|
|
|
6462
7023
|
}
|
|
6463
7024
|
}
|
|
6464
7025
|
//#endregion
|
|
6465
|
-
//#region src/data/sparql/sparqlModels.ts
|
|
6466
|
-
function mapSparqlResponseIntoRdfJs(response, factory) {
|
|
6467
|
-
function mapSparqlBinding(binding) {
|
|
6468
|
-
const mapped = {};
|
|
6469
|
-
for (const key in binding) if (Object.prototype.hasOwnProperty.call(binding, key)) {
|
|
6470
|
-
const term = binding[key];
|
|
6471
|
-
switch (term.type) {
|
|
6472
|
-
case "uri":
|
|
6473
|
-
mapped[key] = factory.namedNode(term.value);
|
|
6474
|
-
break;
|
|
6475
|
-
case "bnode":
|
|
6476
|
-
mapped[key] = factory.blankNode(term.value);
|
|
6477
|
-
break;
|
|
6478
|
-
case "literal":
|
|
6479
|
-
case "typed-literal":
|
|
6480
|
-
mapped[key] = factory.literal(term.value, term["xml:lang"] ? term["xml:lang"] : term.datatype ? factory.namedNode(term.datatype) : void 0);
|
|
6481
|
-
break;
|
|
6482
|
-
default: throw new Error(`Unexpected SPARQL term type: "${term.type}"`);
|
|
6483
|
-
}
|
|
6484
|
-
}
|
|
6485
|
-
return mapped;
|
|
6486
|
-
}
|
|
6487
|
-
return {
|
|
6488
|
-
...response,
|
|
6489
|
-
results: {
|
|
6490
|
-
...response.results,
|
|
6491
|
-
bindings: response.results.bindings.map(mapSparqlBinding)
|
|
6492
|
-
}
|
|
6493
|
-
};
|
|
6494
|
-
}
|
|
6495
|
-
function isRdfIri(term) {
|
|
6496
|
-
return Boolean(term && term.termType === "NamedNode");
|
|
6497
|
-
}
|
|
6498
|
-
function isRdfBlank(term) {
|
|
6499
|
-
return Boolean(term && term.termType === "BlankNode");
|
|
6500
|
-
}
|
|
6501
|
-
function isRdfLiteral(term) {
|
|
6502
|
-
return Boolean(term && term.termType === "Literal");
|
|
6503
|
-
}
|
|
6504
|
-
//#endregion
|
|
6505
7026
|
//#region src/data/sparql/responseHandler.ts
|
|
6506
7027
|
var TYPE_PREDICATE = "urn:reactodia:sparql:type";
|
|
6507
7028
|
var LABEL_PREDICATE = "urn:reactodia:sparql:label";
|
|
@@ -7036,7 +7557,7 @@ var SparqlDataProvider = class {
|
|
|
7036
7557
|
options;
|
|
7037
7558
|
settings;
|
|
7038
7559
|
queryFunction;
|
|
7039
|
-
acceptBlankNodes
|
|
7560
|
+
acceptBlankNodes;
|
|
7040
7561
|
chunkMeasure;
|
|
7041
7562
|
linkByPredicate = /* @__PURE__ */ new Map();
|
|
7042
7563
|
linkById = /* @__PURE__ */ new Map();
|
|
@@ -7051,6 +7572,7 @@ var SparqlDataProvider = class {
|
|
|
7051
7572
|
this.options = options;
|
|
7052
7573
|
this.settings = settings;
|
|
7053
7574
|
this.queryFunction = queryFunction;
|
|
7575
|
+
this.acceptBlankNodes = Boolean(settings.acceptBlankNodes);
|
|
7054
7576
|
const { chunk, queryMethod = "GET", prepareLabelPredicate, prepareImagePredicate } = this.options;
|
|
7055
7577
|
if (chunk) {
|
|
7056
7578
|
if (!Number.isSafeInteger(chunk.maxSize)) throw new Error("SparqlDataProviderOptions.chunk.maxSize should be a non-negative number");
|
|
@@ -7174,8 +7696,9 @@ var SparqlDataProvider = class {
|
|
|
7174
7696
|
}
|
|
7175
7697
|
async elements(params) {
|
|
7176
7698
|
const { elementIds, signal } = params;
|
|
7699
|
+
const queriedIds = this.acceptBlankNodes ? elementIds.filter((iri) => !isEncodedBlank$1(iri)) : elementIds;
|
|
7177
7700
|
const triples = [];
|
|
7178
|
-
if (
|
|
7701
|
+
if (queriedIds.length > 0) await this.queryChunked(queriedIds, async (batch) => {
|
|
7179
7702
|
const ids = batch.map(escapeIri).map((id) => ` (${id})`).join(" ");
|
|
7180
7703
|
const { defaultPrefix, dataLabelProperty, filterOnlyLanguages, elementInfoQuery } = this.settings;
|
|
7181
7704
|
const query = defaultPrefix + resolveTemplate(elementInfoQuery, {
|
|
@@ -7189,15 +7712,18 @@ var SparqlDataProvider = class {
|
|
|
7189
7712
|
for (const triple of response) triples.push(triple);
|
|
7190
7713
|
});
|
|
7191
7714
|
const types = this.queryManyElementTypes(this.settings.propertyConfigurations.length > 0 ? elementIds : [], signal);
|
|
7192
|
-
const
|
|
7193
|
-
if (this.
|
|
7194
|
-
|
|
7715
|
+
const bindings = triplesToElementBinding(triples);
|
|
7716
|
+
if (this.acceptBlankNodes) bindings.results.bindings.push(...elements(elementIds, this.factory));
|
|
7717
|
+
const elementModels = getElementsInfo(bindings, await types, this.propertyByPredicate, this.labelPredicate, this.openWorldProperties);
|
|
7718
|
+
if (this.options.prepareLabels) await attachProperties(Array.from(elementModels.values()), this.options.prepareLabels, this.labelPredicate, this.acceptBlankNodes, signal);
|
|
7719
|
+
if (this.options.prepareImages) await prepareElementImages(elementModels, this.options.prepareImages, this.imagePredicate, this.factory, this.acceptBlankNodes, signal);
|
|
7195
7720
|
else if (this.options.imagePropertyUris && this.options.imagePropertyUris.length) await this.attachImages(elementModels, this.options.imagePropertyUris, signal);
|
|
7196
7721
|
return elementModels;
|
|
7197
7722
|
}
|
|
7198
7723
|
async attachImages(elements, imagePropertyIris, signal) {
|
|
7199
7724
|
const imageProperties = imagePropertyIris.map(escapeIri).map((id) => ` ( ${id} )`).join(" ");
|
|
7200
|
-
|
|
7725
|
+
const imageableIds = Array.from(elements.keys()).filter((iri) => !(this.acceptBlankNodes && isEncodedBlank$1(iri)));
|
|
7726
|
+
await this.queryChunked(imageableIds, async (batch) => {
|
|
7201
7727
|
const ids = batch.map((id) => ` ( ${escapeIri(id)} )`).join(" ");
|
|
7202
7728
|
const query = this.settings.defaultPrefix + `
|
|
7203
7729
|
SELECT ?inst ?linkType ?image
|
|
@@ -7219,12 +7745,14 @@ var SparqlDataProvider = class {
|
|
|
7219
7745
|
const { filterOnlyLanguages, linksInfoQuery } = this.settings;
|
|
7220
7746
|
const propLanguageFilter = formatLanguageFilter("?propValue", filterOnlyLanguages);
|
|
7221
7747
|
const linkConfigurations = this.formatLinkLinks();
|
|
7748
|
+
const queriedPrimary = this.acceptBlankNodes ? primary.filter((iri) => !isEncodedBlank$1(iri)) : primary;
|
|
7749
|
+
const queriedSecondary = this.acceptBlankNodes ? secondary.filter((iri) => !isEncodedBlank$1(iri)) : secondary;
|
|
7222
7750
|
let bindings;
|
|
7223
|
-
if (
|
|
7751
|
+
if (queriedPrimary.length > 0 && queriedSecondary.length > 0) if (linksInfoQuery.includes("${ids}")) bindings = this.queryUndirectedLinks(queriedPrimary, queriedSecondary, {
|
|
7224
7752
|
propLanguageFilter,
|
|
7225
7753
|
linkConfigurations
|
|
7226
7754
|
}, signal);
|
|
7227
|
-
else bindings = this.queryDirectedLinks(
|
|
7755
|
+
else bindings = this.queryDirectedLinks(queriedPrimary, queriedSecondary, {
|
|
7228
7756
|
propLanguageFilter,
|
|
7229
7757
|
linkConfigurations
|
|
7230
7758
|
}, signal);
|
|
@@ -7235,7 +7763,13 @@ var SparqlDataProvider = class {
|
|
|
7235
7763
|
for (const iri of secondary) elementsForTypes.add(iri);
|
|
7236
7764
|
}
|
|
7237
7765
|
const types = this.queryManyElementTypes(elementsForTypes ? Array.from(elementsForTypes) : [], signal);
|
|
7238
|
-
let
|
|
7766
|
+
let allBindings = await bindings;
|
|
7767
|
+
if (this.acceptBlankNodes) {
|
|
7768
|
+
const connected = new Set(primary);
|
|
7769
|
+
for (const iri of secondary) connected.add(iri);
|
|
7770
|
+
allBindings = [...allBindings, ...links(Array.from(connected), this.factory)];
|
|
7771
|
+
}
|
|
7772
|
+
let linksInfo = getLinksInfo(allBindings, await types, this.linkByPredicate, this.openWorldLinks);
|
|
7239
7773
|
if (linkTypeIds) {
|
|
7240
7774
|
const allowedLinkTypes = new Set(linkTypeIds);
|
|
7241
7775
|
linksInfo = linksInfo.filter((link) => allowedLinkTypes.has(link.linkTypeId));
|
|
@@ -7274,6 +7808,7 @@ var SparqlDataProvider = class {
|
|
|
7274
7808
|
}
|
|
7275
7809
|
async connectedLinkStats(params) {
|
|
7276
7810
|
const { elementId, inexactCount, signal } = params;
|
|
7811
|
+
if (this.acceptBlankNodes && isEncodedBlank$1(elementId)) return connectedLinkStats(elementId, this.factory);
|
|
7277
7812
|
const { defaultPrefix, linkTypesOfQuery, linkTypesStatisticsQuery, filterTypePattern } = this.settings;
|
|
7278
7813
|
const bindDirection = /\?direction\b/.test(linkTypesOfQuery);
|
|
7279
7814
|
const elementIri = escapeIri(elementId);
|
|
@@ -7339,11 +7874,48 @@ var SparqlDataProvider = class {
|
|
|
7339
7874
|
limit: baseParams.limit === void 0 ? 100 : baseParams.limit
|
|
7340
7875
|
};
|
|
7341
7876
|
const types = this.querySingleElementTypes(params.refElementId && this.settings.linkConfigurations.length > 0 ? params.refElementId : void 0, signal);
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
if (this.
|
|
7877
|
+
let bindings;
|
|
7878
|
+
let navigatedFromBlank = false;
|
|
7879
|
+
if (this.acceptBlankNodes) {
|
|
7880
|
+
const fromBlank = lookup(params, this.factory);
|
|
7881
|
+
if (fromBlank) {
|
|
7882
|
+
bindings = fromBlank;
|
|
7883
|
+
navigatedFromBlank = true;
|
|
7884
|
+
} else bindings = await updateLookupResults(await this.executeSparqlSelect(this.createFilterQuery(params), { signal }), (blankQuery) => this.executeSparqlSelect(blankQuery, { signal }), this.settings, this.factory);
|
|
7885
|
+
} else {
|
|
7886
|
+
const filterQuery = this.createFilterQuery(params);
|
|
7887
|
+
bindings = await this.executeSparqlSelect(filterQuery, { signal });
|
|
7888
|
+
}
|
|
7889
|
+
const linkedElements = getFilteredData(bindings, await types, this.linkByPredicate, this.labelPredicate, this.openWorldLinks);
|
|
7890
|
+
if (navigatedFromBlank) return this.attachNeighborInfo(linkedElements, signal);
|
|
7891
|
+
if (this.options.prepareLabels) await attachProperties(linkedElements.map((linked) => linked.element), this.options.prepareLabels, this.labelPredicate, this.acceptBlankNodes, signal);
|
|
7345
7892
|
return linkedElements;
|
|
7346
7893
|
}
|
|
7894
|
+
/**
|
|
7895
|
+
* Fills in the data of the elements found by navigating from a blank node.
|
|
7896
|
+
*
|
|
7897
|
+
* The navigation is answered by decoding the blank node IRI, which knows only the term
|
|
7898
|
+
* at the far end of each statement, so everything else about a named neighbor - its
|
|
7899
|
+
* label, types and properties - still has to be read from the endpoint. Without this
|
|
7900
|
+
* the neighbor would render as its bare IRI.
|
|
7901
|
+
*
|
|
7902
|
+
* {@link elements} applies the {@link SparqlDataProviderOptions.prepareLabels} hook
|
|
7903
|
+
* itself, so the caller must not apply it again.
|
|
7904
|
+
*/
|
|
7905
|
+
async attachNeighborInfo(items, signal) {
|
|
7906
|
+
if (items.length === 0) return [];
|
|
7907
|
+
const elementModels = await this.elements({
|
|
7908
|
+
elementIds: items.map((item) => item.element.id),
|
|
7909
|
+
signal
|
|
7910
|
+
});
|
|
7911
|
+
return items.map((item) => {
|
|
7912
|
+
const element = elementModels.get(item.element.id);
|
|
7913
|
+
return element ? {
|
|
7914
|
+
...item,
|
|
7915
|
+
element
|
|
7916
|
+
} : item;
|
|
7917
|
+
});
|
|
7918
|
+
}
|
|
7347
7919
|
createFilterQuery(params) {
|
|
7348
7920
|
if (!params.refElementId && params.refElementLinkId) throw new Error("Cannot execute refElementLink filter without refElement");
|
|
7349
7921
|
let outerProjection = "?inst ?class ?label";
|
|
@@ -7369,10 +7941,14 @@ var SparqlDataProvider = class {
|
|
|
7369
7941
|
filterByType = this.settings.filterTypePattern.replace(/[?$]class\b/g, elementTypeIri);
|
|
7370
7942
|
}
|
|
7371
7943
|
const { defaultPrefix, dataLabelProperty, lookupQuery, filterOnlyLanguages, filterElementInfoPattern, fullTextSearch } = this.settings;
|
|
7372
|
-
|
|
7944
|
+
let queryElementInfo = resolveTemplate(filterElementInfoPattern, {
|
|
7373
7945
|
dataLabelProperty,
|
|
7374
7946
|
labelLanguageFilter: formatLanguageFilter("?label", filterOnlyLanguages)
|
|
7375
7947
|
});
|
|
7948
|
+
if (this.acceptBlankNodes) {
|
|
7949
|
+
outerProjection += ` ${BLANK_NODE_QUERY_PARAMETERS}`;
|
|
7950
|
+
queryElementInfo += BLANK_NODE_QUERY;
|
|
7951
|
+
}
|
|
7376
7952
|
let filterByText = "";
|
|
7377
7953
|
let orderBy = "";
|
|
7378
7954
|
if (params.text) {
|
|
@@ -7500,7 +8076,12 @@ var SparqlDataProvider = class {
|
|
|
7500
8076
|
if (elements.length === 0) return /* @__PURE__ */ new Map();
|
|
7501
8077
|
const { filterTypePattern } = this.settings;
|
|
7502
8078
|
const elementTypes = /* @__PURE__ */ new Map();
|
|
7503
|
-
|
|
8079
|
+
let queriedElements = elements;
|
|
8080
|
+
if (this.acceptBlankNodes) {
|
|
8081
|
+
queriedElements = elements.filter((iri) => !isEncodedBlank$1(iri));
|
|
8082
|
+
collectElementTypes$1(elements, this.factory, elementTypes);
|
|
8083
|
+
}
|
|
8084
|
+
await this.queryChunked(queriedElements, async (batch) => {
|
|
7504
8085
|
const query = resolveTemplate("SELECT ?inst ?class { VALUES(?inst) { ${ids} } ${filterTypePattern} }", {
|
|
7505
8086
|
ids: batch.map((iri) => `(${escapeIri(iri)})`).join(" "),
|
|
7506
8087
|
filterTypePattern
|
|
@@ -7523,15 +8104,16 @@ async function attachLabels(items, fetchLabels, signal) {
|
|
|
7523
8104
|
if (itemLabels) item.label = itemLabels;
|
|
7524
8105
|
}
|
|
7525
8106
|
}
|
|
7526
|
-
async function attachProperties(items, fetchProperties, propertyIri, signal) {
|
|
7527
|
-
const
|
|
7528
|
-
|
|
8107
|
+
async function attachProperties(items, fetchProperties, propertyIri, acceptBlankNodes, signal) {
|
|
8108
|
+
const resolvable = acceptBlankNodes ? items.filter((item) => !isEncodedBlank(item.id)) : items;
|
|
8109
|
+
const properties = await fetchProperties(new Set(resolvable.map((item) => item.id)), signal);
|
|
8110
|
+
for (const item of resolvable) {
|
|
7529
8111
|
const itemValues = properties.get(item.id);
|
|
7530
8112
|
if (itemValues) item.properties[propertyIri] = itemValues;
|
|
7531
8113
|
}
|
|
7532
8114
|
}
|
|
7533
|
-
function prepareElementImages(elements, fetchImages, imagePropertyIri, factory, signal) {
|
|
7534
|
-
return fetchImages(elements.values(), signal).then((images) => {
|
|
8115
|
+
function prepareElementImages(elements, fetchImages, imagePropertyIri, factory, acceptBlankNodes, signal) {
|
|
8116
|
+
return fetchImages(acceptBlankNodes ? Array.from(elements.values()).filter((element) => !isEncodedBlank(element.id)) : elements.values(), signal).then((images) => {
|
|
7535
8117
|
for (const [iri, image] of images) {
|
|
7536
8118
|
const entity = elements.get(iri);
|
|
7537
8119
|
if (entity) appendProperty(entity.properties, imagePropertyIri, factory.namedNode(image));
|
|
@@ -30655,15 +31237,15 @@ class ServiceProvider {
|
|
|
30655
31237
|
/**
|
|
30656
31238
|
* Returns the instance of the given service type, creating and caching it on first request.
|
|
30657
31239
|
*
|
|
30658
|
-
* @param
|
|
31240
|
+
* @param serviceClass The service type to retrieve.
|
|
30659
31241
|
* @returns The shared instance of the service.
|
|
30660
31242
|
* @template T The type of the service to retrieve.
|
|
30661
31243
|
*/
|
|
30662
|
-
static get(
|
|
30663
|
-
if (!ServiceProvider.SERVICE_INSTANCES.has(
|
|
30664
|
-
ServiceProvider.SERVICE_INSTANCES.set(
|
|
31244
|
+
static get(serviceClass) {
|
|
31245
|
+
if (!ServiceProvider.SERVICE_INSTANCES.has(serviceClass.ID)) {
|
|
31246
|
+
ServiceProvider.SERVICE_INSTANCES.set(serviceClass.ID, new serviceClass());
|
|
30665
31247
|
}
|
|
30666
|
-
return ServiceProvider.SERVICE_INSTANCES.get(
|
|
31248
|
+
return ServiceProvider.SERVICE_INSTANCES.get(serviceClass.ID);
|
|
30667
31249
|
}
|
|
30668
31250
|
}
|
|
30669
31251
|
ServiceProvider.SERVICE_INSTANCES = new Map();
|
|
@@ -30806,6 +31388,28 @@ let DiagramStorageService = DiagramStorageService_1 = class DiagramStorageServic
|
|
|
30806
31388
|
clear() {
|
|
30807
31389
|
this.storage.removeItem(DiagramStorageService_1.STORAGE_KEY);
|
|
30808
31390
|
}
|
|
31391
|
+
/**
|
|
31392
|
+
* Reads back the blank node toggle, defaulting to `false` when nothing is stored.
|
|
31393
|
+
*
|
|
31394
|
+
* The flag has to survive a reload: a restored diagram can hold blank node elements, and
|
|
31395
|
+
* their IRIs can only be resolved while the data provider accepts blank nodes - the endpoint
|
|
31396
|
+
* knows nothing about them.
|
|
31397
|
+
*
|
|
31398
|
+
* TEMPORARY, remove together with the blank node checkbox in `graphwise-reactodia.tsx`.
|
|
31399
|
+
* Only the checkbox needs this. In production the flag arrives on the `providerSettings`
|
|
31400
|
+
* prop, which the host supplies on every mount, so there is nothing to persist.
|
|
31401
|
+
*/
|
|
31402
|
+
loadAcceptBlankNodes() {
|
|
31403
|
+
return this.storage.getItem(DiagramStorageService_1.ACCEPT_BLANK_NODES_KEY) === 'true';
|
|
31404
|
+
}
|
|
31405
|
+
/**
|
|
31406
|
+
* Persists the blank node toggle.
|
|
31407
|
+
*
|
|
31408
|
+
* TEMPORARY, see {@link loadAcceptBlankNodes}.
|
|
31409
|
+
*/
|
|
31410
|
+
saveAcceptBlankNodes(acceptBlankNodes) {
|
|
31411
|
+
this.storage.setItem(DiagramStorageService_1.ACCEPT_BLANK_NODES_KEY, String(acceptBlankNodes));
|
|
31412
|
+
}
|
|
30809
31413
|
/**
|
|
30810
31414
|
* Local storage getter
|
|
30811
31415
|
*/
|
|
@@ -30815,6 +31419,7 @@ let DiagramStorageService = DiagramStorageService_1 = class DiagramStorageServic
|
|
|
30815
31419
|
};
|
|
30816
31420
|
DiagramStorageService.ID = 'DiagramStorageService';
|
|
30817
31421
|
DiagramStorageService.STORAGE_KEY = 'diagram.state';
|
|
31422
|
+
DiagramStorageService.ACCEPT_BLANK_NODES_KEY = 'diagram.accept-blank-nodes';
|
|
30818
31423
|
DiagramStorageService = DiagramStorageService_1 = __decorate$1([
|
|
30819
31424
|
Service
|
|
30820
31425
|
], DiagramStorageService);
|
|
@@ -30882,6 +31487,60 @@ class SubscriptionList {
|
|
|
30882
31487
|
}
|
|
30883
31488
|
}
|
|
30884
31489
|
|
|
31490
|
+
const NODE_COLOR_VARIABLES = [
|
|
31491
|
+
'--gw-foreground-visual-diagram-categorical-01',
|
|
31492
|
+
'--gw-foreground-visual-diagram-categorical-02',
|
|
31493
|
+
'--gw-foreground-visual-diagram-categorical-03',
|
|
31494
|
+
'--gw-foreground-visual-diagram-categorical-04',
|
|
31495
|
+
'--gw-foreground-visual-diagram-categorical-05',
|
|
31496
|
+
'--gw-foreground-visual-diagram-categorical-06',
|
|
31497
|
+
'--gw-foreground-visual-diagram-categorical-07',
|
|
31498
|
+
'--gw-foreground-visual-diagram-categorical-08',
|
|
31499
|
+
'--gw-foreground-visual-diagram-categorical-09',
|
|
31500
|
+
'--gw-foreground-visual-diagram-categorical-10',
|
|
31501
|
+
'--gw-foreground-visual-diagram-categorical-11',
|
|
31502
|
+
'--gw-foreground-visual-diagram-categorical-12',
|
|
31503
|
+
'--gw-foreground-visual-diagram-categorical-13',
|
|
31504
|
+
'--gw-foreground-visual-diagram-categorical-14',
|
|
31505
|
+
'--gw-foreground-visual-diagram-categorical-15',
|
|
31506
|
+
'--gw-foreground-visual-diagram-categorical-16',
|
|
31507
|
+
'--gw-foreground-visual-diagram-categorical-17',
|
|
31508
|
+
'--gw-foreground-visual-diagram-categorical-18',
|
|
31509
|
+
'--gw-foreground-visual-diagram-categorical-19',
|
|
31510
|
+
'--gw-foreground-visual-diagram-categorical-20'
|
|
31511
|
+
];
|
|
31512
|
+
/**
|
|
31513
|
+
* Color slot assigned to each type, in the order the types are first seen.
|
|
31514
|
+
*/
|
|
31515
|
+
const typeToIndex = new Map();
|
|
31516
|
+
function getColor(variable) {
|
|
31517
|
+
return getComputedStyle(document.documentElement).getPropertyValue(variable).trim();
|
|
31518
|
+
}
|
|
31519
|
+
/**
|
|
31520
|
+
* Colors an element by its type: each type takes the next color from the palette, which wraps
|
|
31521
|
+
* around once it runs out
|
|
31522
|
+
*
|
|
31523
|
+
* Returns `undefined` for elements without a type (placeholders and other non-entity elements) and
|
|
31524
|
+
* when the host does not define the palette, leaving Reactodia to fall back to its own hash-based
|
|
31525
|
+
* colors.
|
|
31526
|
+
*/
|
|
31527
|
+
const resolveTypeStyle = (types) => {
|
|
31528
|
+
const type = types[0];
|
|
31529
|
+
if (!type) {
|
|
31530
|
+
return undefined;
|
|
31531
|
+
}
|
|
31532
|
+
let index = typeToIndex.get(type);
|
|
31533
|
+
if (index === undefined) {
|
|
31534
|
+
index = typeToIndex.size;
|
|
31535
|
+
typeToIndex.set(type, index);
|
|
31536
|
+
}
|
|
31537
|
+
const color = getColor(NODE_COLOR_VARIABLES[index % NODE_COLOR_VARIABLES.length]);
|
|
31538
|
+
return color ? { color } : undefined;
|
|
31539
|
+
};
|
|
31540
|
+
const resetColors = () => {
|
|
31541
|
+
typeToIndex.clear();
|
|
31542
|
+
};
|
|
31543
|
+
|
|
30885
31544
|
/**
|
|
30886
31545
|
* Reactodia ships English as its built-in default bundle, so English needs no override.
|
|
30887
31546
|
* Other languages are layered on top as partial bundles; unknown codes fall back to English.
|
|
@@ -30908,7 +31567,7 @@ const subscriptions = new SubscriptionList();
|
|
|
30908
31567
|
/**
|
|
30909
31568
|
* Builds a Reactodia {@link SparqlDataProvider} for the given endpoint using the supplied
|
|
30910
31569
|
* query preset. The host owns the query configuration and passes it in via props; when none
|
|
30911
|
-
* is provided we fall back to Reactodia's generic {@link
|
|
31570
|
+
* is provided we fall back to Reactodia's generic {@link OwlStatsSettings} OWL/RDFS preset.
|
|
30912
31571
|
*/
|
|
30913
31572
|
function createDataProvider(props) {
|
|
30914
31573
|
const { currentRepository, config, providerSettings } = props;
|
|
@@ -30916,7 +31575,7 @@ function createDataProvider(props) {
|
|
|
30916
31575
|
endpointUrl: currentRepository,
|
|
30917
31576
|
queryMethod: 'POST',
|
|
30918
31577
|
queryFunction: config.queryFunction
|
|
30919
|
-
}, providerSettings);
|
|
31578
|
+
}, Object.assign(Object.assign({}, OwlStatsSettings), providerSettings));
|
|
30920
31579
|
}
|
|
30921
31580
|
/**
|
|
30922
31581
|
* Places the seed entities on the canvas. Each IRI is added as a placeholder element, and data is then loaded for the
|
|
@@ -31004,7 +31663,7 @@ function ReactodiaApp(props) {
|
|
|
31004
31663
|
if (isReload) {
|
|
31005
31664
|
// Simply reload without using the current state. This may happen when the user switches the language at runtime.
|
|
31006
31665
|
// since there is no existing mechanism to re-translate the UI at runtime (not for the UI labels at least)
|
|
31007
|
-
await model.importLayout({ dataProvider, diagram: currentDiagram, signal });
|
|
31666
|
+
await model.importLayout({ dataProvider, diagram: currentDiagram, signal, validateLinks: true });
|
|
31008
31667
|
}
|
|
31009
31668
|
else if ((_a = config.seedIris) === null || _a === void 0 ? void 0 : _a.length) {
|
|
31010
31669
|
await seedIrisToCanvas(context, dataProvider, config.seedIris, signal);
|
|
@@ -31015,7 +31674,7 @@ function ReactodiaApp(props) {
|
|
|
31015
31674
|
else {
|
|
31016
31675
|
// Restore the previously saved layout when present, otherwise start empty. Either way this binds the
|
|
31017
31676
|
// data provider (savedDiagram is undefined -> empty diagram), so the unified search/lookup works.
|
|
31018
|
-
await model.importLayout({ dataProvider, diagram: savedDiagram, signal });
|
|
31677
|
+
await model.importLayout({ dataProvider, diagram: savedDiagram, signal, validateLinks: true });
|
|
31019
31678
|
}
|
|
31020
31679
|
}, [language]);
|
|
31021
31680
|
/**
|
|
@@ -31032,7 +31691,8 @@ function ReactodiaApp(props) {
|
|
|
31032
31691
|
ref: onMount,
|
|
31033
31692
|
defaultLayout: blockingDefaultLayout,
|
|
31034
31693
|
defaultLanguage: language,
|
|
31035
|
-
translations: translationsForLanguage(language)
|
|
31694
|
+
translations: translationsForLanguage(language),
|
|
31695
|
+
typeStyleResolver: resolveTypeStyle
|
|
31036
31696
|
}, reactExports.createElement(DefaultWorkspace, {}));
|
|
31037
31697
|
}
|
|
31038
31698
|
/**
|
|
@@ -31070,7 +31730,7 @@ function unmountReactodia(root) {
|
|
|
31070
31730
|
root.unmount();
|
|
31071
31731
|
}
|
|
31072
31732
|
|
|
31073
|
-
const graphwiseReactodiaCss = "graphwise-reactodia{display:
|
|
31733
|
+
const graphwiseReactodiaCss = "graphwise-reactodia{display:flex;flex-direction:column;width:100%;height:75vh}graphwise-reactodia .blank-nodes-toggle{display:flex;align-items:center;gap:4px;padding:4px 8px}graphwise-reactodia .reactodia-container{position:relative;flex:1;min-height:0;width:100%}";
|
|
31074
31734
|
|
|
31075
31735
|
const GraphwiseReactodia = class {
|
|
31076
31736
|
constructor(hostRef) {
|
|
@@ -31079,6 +31739,19 @@ const GraphwiseReactodia = class {
|
|
|
31079
31739
|
* UI language code (e.g. `en`, `fr`) for the Reactodia interface. Defaults to English.
|
|
31080
31740
|
*/
|
|
31081
31741
|
this.language = LanguageKey.EN;
|
|
31742
|
+
/**
|
|
31743
|
+
* Runtime toggle for the data provider's `acceptBlankNodes` flag, for testing blank node
|
|
31744
|
+
* support against a live endpoint. Internal, not part of the public API - in production the
|
|
31745
|
+
* host sets `acceptBlankNodes` through the {@link providerSettings} prop instead.
|
|
31746
|
+
*
|
|
31747
|
+
* TEMPORARY. Removing the checkbox means removing, in this file: this field, the
|
|
31748
|
+
* `blank-nodes-toggle` label in {@link render}, {@link onAcceptBlankNodesChange}, the
|
|
31749
|
+
* `acceptBlankNodes` override handed to `mountReactodia`, and the {@link graphContainer} /
|
|
31750
|
+
* {@link componentDidLoad} pair - the graph mounted straight into the host element before
|
|
31751
|
+
* the toggle needed a sibling. Then `.blank-nodes-toggle` in the stylesheet, and
|
|
31752
|
+
* `loadAcceptBlankNodes` / `saveAcceptBlankNodes` on {@link DiagramStorageService}.
|
|
31753
|
+
*/
|
|
31754
|
+
this.acceptBlankNodes = service(DiagramStorageService).loadAcceptBlankNodes();
|
|
31082
31755
|
}
|
|
31083
31756
|
onProviderSettingsChange() {
|
|
31084
31757
|
this.renderGraph();
|
|
@@ -31088,7 +31761,12 @@ const GraphwiseReactodia = class {
|
|
|
31088
31761
|
// to re-translate the UI is to re-create the graph.
|
|
31089
31762
|
this.renderGraph(true);
|
|
31090
31763
|
}
|
|
31091
|
-
|
|
31764
|
+
onThemeChange() {
|
|
31765
|
+
resetColors();
|
|
31766
|
+
this.renderGraph(true);
|
|
31767
|
+
}
|
|
31768
|
+
// The graph container is only available after the first render, so the initial mount happens here.
|
|
31769
|
+
componentDidLoad() {
|
|
31092
31770
|
if (this.config) {
|
|
31093
31771
|
this.renderGraph();
|
|
31094
31772
|
}
|
|
@@ -31100,7 +31778,20 @@ const GraphwiseReactodia = class {
|
|
|
31100
31778
|
}
|
|
31101
31779
|
}
|
|
31102
31780
|
render() {
|
|
31103
|
-
return (index$1.h(index$1.Host, { key: '
|
|
31781
|
+
return (index$1.h(index$1.Host, { key: 'dbb9526355787bc4a7952d7faff2fc23666b6cfd' }, index$1.h("label", { key: '755311e40eb8b043a3077553389ee11e1d322782', class: "blank-nodes-toggle" }, index$1.h("input", { key: '42581ef35034da73b28b3e3e369888f8fa2b92a0', type: "checkbox", "data-test": "accept-blank-nodes", checked: this.acceptBlankNodes, onChange: (event) => this.onAcceptBlankNodesChange(event) }), "Accept blank nodes"), index$1.h("div", { key: '67ca64a44b0f4692a0ed05a3984babc0e0cd646b', class: "reactodia-container", ref: (element) => this.graphContainer = element })));
|
|
31782
|
+
}
|
|
31783
|
+
/**
|
|
31784
|
+
* The flag is read when the provider is constructed, so the toggle rebuilds the provider,
|
|
31785
|
+
* which resets the canvas. That is the same path the host takes when it changes
|
|
31786
|
+
* {@link providerSettings}.
|
|
31787
|
+
*
|
|
31788
|
+
* It is also persisted, so that a diagram restored from the local store is loaded by a
|
|
31789
|
+
* provider which can still resolve the blank nodes it holds.
|
|
31790
|
+
*/
|
|
31791
|
+
onAcceptBlankNodesChange(event) {
|
|
31792
|
+
this.acceptBlankNodes = event.target.checked;
|
|
31793
|
+
service(DiagramStorageService).saveAcceptBlankNodes(this.acceptBlankNodes);
|
|
31794
|
+
this.renderGraph();
|
|
31104
31795
|
}
|
|
31105
31796
|
renderGraph(isReloading) {
|
|
31106
31797
|
var _a;
|
|
@@ -31110,7 +31801,7 @@ const GraphwiseReactodia = class {
|
|
|
31110
31801
|
if (!((_a = this.config) === null || _a === void 0 ? void 0 : _a.queryFunction)) {
|
|
31111
31802
|
throw new Error('config.queryFunction is required');
|
|
31112
31803
|
}
|
|
31113
|
-
if (!this.
|
|
31804
|
+
if (!this.graphContainer) {
|
|
31114
31805
|
return;
|
|
31115
31806
|
}
|
|
31116
31807
|
const props = {
|
|
@@ -31118,20 +31809,21 @@ const GraphwiseReactodia = class {
|
|
|
31118
31809
|
currentRepository: this.currentRepository,
|
|
31119
31810
|
config: this.config,
|
|
31120
31811
|
language: this.language,
|
|
31121
|
-
|
|
31812
|
+
// The toggle wins over the host setting, so it stays applied when the provider is rebuilt.
|
|
31813
|
+
providerSettings: Object.assign(Object.assign({}, this.providerSettings), { acceptBlankNodes: this.acceptBlankNodes }),
|
|
31122
31814
|
};
|
|
31123
31815
|
if (this.reactRoot && !isReloading) {
|
|
31124
31816
|
void updateReactodia(props);
|
|
31125
31817
|
}
|
|
31126
31818
|
else {
|
|
31127
|
-
this.reactRoot = mountReactodia(this.
|
|
31819
|
+
this.reactRoot = mountReactodia(this.graphContainer, props);
|
|
31128
31820
|
}
|
|
31129
31821
|
}
|
|
31130
|
-
get hostElement() { return index$1.getElement(this); }
|
|
31131
31822
|
static get watchers() { return {
|
|
31132
31823
|
"currentRepository": ["onProviderSettingsChange"],
|
|
31133
31824
|
"providerSettings": ["onProviderSettingsChange"],
|
|
31134
|
-
"language": ["onLanguageChange"]
|
|
31825
|
+
"language": ["onLanguageChange"],
|
|
31826
|
+
"theme": ["onThemeChange"]
|
|
31135
31827
|
}; }
|
|
31136
31828
|
};
|
|
31137
31829
|
GraphwiseReactodia.style = graphwiseReactodiaCss;
|