Skip to content

Commit e6a32cd

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents 4e00f44 + 6ce5a91 commit e6a32cd

File tree

12 files changed

+3074
-1756
lines changed

12 files changed

+3074
-1756
lines changed

site/static/assets/css/sparnatural-history.css

Lines changed: 84 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/static/assets/css/sparnatural.css

Lines changed: 889 additions & 889 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/static/assets/js/main.js

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
document.addEventListener("DOMContentLoaded", () => {
2+
// =============================================================================
3+
// 1. DOM REFERENCES
4+
// =============================================================================
5+
const sparnatural = document.querySelector("spar-natural");
6+
const historyComponent = document.querySelector("sparnatural-history");
7+
const sparnaturalTextQuery = document.querySelector("sparnatural-text-query");
8+
const displayEndpoint =
9+
document.querySelector("#displayEndpoint") ||
10+
document.querySelector("#endpoint");
11+
const selectExamples = document.getElementById("select-examples");
12+
13+
if (!sparnatural) {
14+
console.error("spar-natural not found");
15+
return;
16+
}
17+
18+
// =============================================================================
19+
// 2. STATE & CONFIGURATION
20+
// =============================================================================
21+
let lastQueryJson = null;
22+
const lang =
23+
new URLSearchParams(window.location.search).get("lang") || "fr";
24+
25+
// Set language on components
26+
sparnatural.setAttribute("lang", lang);
27+
historyComponent?.setAttribute("lang", lang);
28+
sparnaturalTextQuery?.setAttribute("lang", lang);
29+
30+
// Display endpoint link
31+
if (displayEndpoint) {
32+
displayEndpoint.href = sparnatural.getAttribute("endpoint");
33+
displayEndpoint.textContent = sparnatural.getAttribute("endpoint");
34+
}
35+
36+
// =============================================================================
37+
// 3. YASQE SETUP
38+
// =============================================================================
39+
const yasqe = new Yasqe(document.getElementById("yasqe"), {
40+
requestConfig: {
41+
endpoint: sparnatural.getAttribute("endpoint"),
42+
method: "GET",
43+
},
44+
copyEndpointOnNewTab: false,
45+
});
46+
47+
// =============================================================================
48+
// 4. YASR SETUP
49+
// =============================================================================
50+
Yasr.registerPlugin("TableX", SparnaturalYasguiPlugins.TableX);
51+
Yasr.registerPlugin("Grid", SparnaturalYasguiPlugins.GridPlugin);
52+
if (SparnaturalYasguiPlugins.MapPlugin) {
53+
Yasr.registerPlugin("Map", SparnaturalYasguiPlugins.MapPlugin);
54+
}
55+
Yasr.registerPlugin("Stats", SparnaturalYasguiPlugins.StatsPlugin);
56+
if (typeof Timeline !== "undefined") {
57+
Yasr.registerPlugin("Timeline", Timeline);
58+
}
59+
delete Yasr.plugins["table"];
60+
61+
// Build plugin order based on what's available
62+
const pluginOrder = ["TableX", "Grid"];
63+
if (SparnaturalYasguiPlugins.MapPlugin) pluginOrder.push("Map");
64+
pluginOrder.push("Stats");
65+
if (typeof Timeline !== "undefined") pluginOrder.push("Timeline");
66+
67+
const yasr = new Yasr(document.getElementById("yasr"), {
68+
pluginOrder,
69+
defaultPlugin: "TableX",
70+
});
71+
72+
// Set lang on YASR plugins
73+
for (const name of ["TableX", "Grid", "Stats"]) {
74+
if (yasr.plugins[name]?.config) {
75+
yasr.plugins[name].config.lang = lang;
76+
}
77+
}
78+
79+
// Link YASQE response to YASR
80+
yasqe.on("queryResponse", (_, response, duration) => {
81+
yasr.setResponse(response, duration);
82+
sparnatural.enablePlayBtn();
83+
});
84+
85+
// =============================================================================
86+
// 5. SPARNATURAL EVENT HANDLERS
87+
// =============================================================================
88+
89+
// --- Init ---
90+
sparnatural.addEventListener("init", () => {
91+
for (const plugin in yasr.plugins) {
92+
yasr.plugins[plugin]?.notifyConfiguration?.(
93+
sparnatural.sparnatural.specProvider,
94+
);
95+
}
96+
historyComponent?.notifyConfiguration(
97+
sparnatural.sparnatural.specProvider,
98+
);
99+
sparnaturalTextQuery?.notifyConfiguration(
100+
sparnatural.sparnatural.specProvider,
101+
);
102+
loadQueryFromUrl();
103+
});
104+
105+
// --- Query Updated ---
106+
sparnatural.addEventListener("queryUpdated", (event) => {
107+
yasqe.setValue(sparnatural.expandSparql(event.detail.queryString));
108+
lastQueryJson = event.detail.queryJson;
109+
110+
for (const plugin in yasr.plugins) {
111+
yasr.plugins[plugin]?.notifyQuery?.(event.detail.queryJson);
112+
}
113+
114+
document.getElementById("query-json").value = JSON.stringify(
115+
event.detail.queryJson,
116+
);
117+
});
118+
119+
// --- Submit ---
120+
sparnatural.addEventListener("submit", () => {
121+
sparnatural.disablePlayBtn();
122+
yasqe.query();
123+
if (lastQueryJson) {
124+
historyComponent?.saveQuery(lastQueryJson);
125+
}
126+
});
127+
128+
// =============================================================================
129+
// 6. UI ACTIONS
130+
// =============================================================================
131+
132+
// --- Toggle SPARQL ---
133+
document.getElementById("sparql-toggle")?.addEventListener("click", (e) => {
134+
e.preventDefault();
135+
const yasqeEl = document.getElementById("yasqe");
136+
const icon = document.getElementById("sparql-toggle-icon");
137+
if (yasqeEl.style.display === "none") {
138+
yasqeEl.style.display = "block";
139+
yasqe.refresh();
140+
if (icon) icon.className = "fad fa-eye-slash fa-fw";
141+
} else {
142+
yasqeEl.style.display = "none";
143+
if (icon) icon.className = "fad fa-eye fa-fw";
144+
}
145+
});
146+
147+
// --- Share ---
148+
document.getElementById("share")?.addEventListener("click", (e) => {
149+
e.preventDefault();
150+
const compressCodec = JsonUrl("lzma");
151+
152+
compressCodec
153+
.compress(document.getElementById("query-json").value)
154+
.then((result) => {
155+
const url = window.location.pathname + "?query=" + result;
156+
const link = document.getElementById("share-link");
157+
link.href = url;
158+
link.textContent =
159+
lang === "fr" ? "Requête Sparnatural" : "Sparnatural Query";
160+
new bootstrap.Modal(document.getElementById("shareModal")).show();
161+
});
162+
});
163+
164+
// --- Export ---
165+
document.getElementById("export")?.addEventListener("click", (e) => {
166+
e.preventDefault();
167+
const jsonString = JSON.stringify(
168+
JSON.parse(document.getElementById("query-json").value),
169+
null,
170+
2,
171+
);
172+
document.getElementById("export-json").value = jsonString;
173+
new bootstrap.Modal(document.getElementById("exportModal")).show();
174+
});
175+
176+
// --- Import ---
177+
document.getElementById("import")?.addEventListener("click", (e) => {
178+
e.preventDefault();
179+
new bootstrap.Modal(document.getElementById("importModal")).show();
180+
});
181+
182+
document.getElementById("importButton")?.addEventListener("click", () => {
183+
const json = JSON.parse(document.getElementById("import-json").value);
184+
sparnatural.loadQuery(json);
185+
bootstrap.Modal.getInstance(document.getElementById("importModal"))?.hide();
186+
});
187+
188+
// =============================================================================
189+
// 7. HISTORY & TEXT QUERY HANDLERS
190+
// =============================================================================
191+
192+
historyComponent?.addEventListener("loadQuery", (event) => {
193+
sparnatural.loadQuery(event.detail.query);
194+
});
195+
196+
document.getElementById("myCustomButton")?.addEventListener("click", () => {
197+
historyComponent?.openHistoryModal();
198+
});
199+
200+
sparnaturalTextQuery?.addEventListener("loadQuery", (event) => {
201+
const query = event.detail.query;
202+
sparnatural.loadQuery(query);
203+
204+
if (!hasNotFound(query)) {
205+
setTimeout(() => sparnatural.dispatchEvent(new Event("submit")), 300);
206+
} else {
207+
console.log("Query contains URI_NOT_FOUND — loaded but not submitted.");
208+
}
209+
});
210+
211+
// =============================================================================
212+
// 8. SAMPLE QUERIES
213+
// =============================================================================
214+
if (selectExamples) {
215+
// Dynamic option creation from queries array (if available)
216+
if (typeof queries !== "undefined" && Array.isArray(queries)) {
217+
queries.forEach((q, index) => {
218+
const label =
219+
lang === "fr"
220+
? q.label_fr || q.label_en
221+
: q.label_en || q.label_fr;
222+
const option = document.createElement("option");
223+
option.value = index;
224+
option.textContent = label;
225+
selectExamples.appendChild(option);
226+
});
227+
}
228+
229+
selectExamples.addEventListener("change", (e) => {
230+
const key = e.target.value;
231+
if (key === "none" || key === "") return;
232+
233+
// Object format (sampleQueries global from sampleQueries.js)
234+
if (typeof sampleQueries !== "undefined" && sampleQueries[key]) {
235+
sparnatural.loadQuery(sampleQueries[key]);
236+
}
237+
// Array format (queries global)
238+
else if (typeof queries !== "undefined" && queries[key]) {
239+
sparnatural.loadQuery(queries[key].query);
240+
}
241+
});
242+
}
243+
244+
// =============================================================================
245+
// 9. ADVANCED / SIMPLE TOGGLE
246+
// =============================================================================
247+
document
248+
.getElementById("advanced-simple")
249+
?.addEventListener("click", (e) => {
250+
e.preventDefault();
251+
const ADVANCED_SRC =
252+
"../configs/RUIM-SHACL-postprocessed.ttl ../configs/RUIM-statistics.ttl";
253+
const SIMPLE_SRC = "../configs/sparnatural-config.ttl";
254+
255+
if (sparnatural.getAttribute("src") === SIMPLE_SRC) {
256+
sparnatural.setAttribute(
257+
"prefixes",
258+
"skos:http://www.w3.org/2004/02/skos/core# dct:http://purl.org/dc/terms/",
259+
);
260+
sparnatural.setAttribute(
261+
"typePredicate",
262+
"<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>",
263+
);
264+
sparnatural.setAttribute("src", ADVANCED_SRC);
265+
e.target.textContent = "Recherche simple";
266+
} else {
267+
sparnatural.setAttribute(
268+
"prefixes",
269+
"med:http://data.esante.gouv.fr/ansm/medicament/ skos:http://www.w3.org/2004/02/skos/core# dct:http://purl.org/dc/terms/",
270+
);
271+
sparnatural.setAttribute(
272+
"typePredicate",
273+
"<http://www.w3.org/2000/01/rdf-schema#subClassOf>",
274+
);
275+
sparnatural.setAttribute("src", SIMPLE_SRC);
276+
e.target.textContent = "Recherche avancée";
277+
}
278+
});
279+
280+
// =============================================================================
281+
// 10. HELPER FUNCTIONS
282+
// =============================================================================
283+
284+
function loadQueryFromUrl() {
285+
const urlParams = new URLSearchParams(window.location.search);
286+
if (!urlParams.has("query")) return;
287+
288+
const compressedJson = urlParams.get("query");
289+
const compressCodec = JsonUrl("lzma");
290+
291+
compressCodec.decompress(compressedJson).then((json) => {
292+
const queryJson = JSON.parse(json);
293+
294+
sparnatural.loadQuery(queryJson);
295+
sparnatural.disablePlayBtn();
296+
297+
yasqe.setValue(
298+
sparnatural.expandSparql(
299+
sparnatural.sparnatural.queryBuilder.buildQuery(queryJson),
300+
),
301+
);
302+
303+
sparnatural.executeSparql(
304+
yasqe.getValue(),
305+
(finalResult) => {
306+
yasr.setResponse(finalResult);
307+
sparnatural.enablePlayBtn();
308+
},
309+
(error) => {
310+
console.error("Error executing SPARQL from shared URL", error);
311+
sparnatural.enablePlayBtn();
312+
},
313+
);
314+
});
315+
}
316+
317+
function hasNotFound(node) {
318+
if (!node) return false;
319+
if (
320+
node.line?.criterias?.some(
321+
(v) =>
322+
v.criteria?.rdfTerm?.value ===
323+
"https://services.sparnatural.eu/api/v1/URI_NOT_FOUND",
324+
)
325+
) {
326+
return true;
327+
}
328+
if (node.branches?.some(hasNotFound)) return true;
329+
if (node.children?.some(hasNotFound)) return true;
330+
return false;
331+
}
332+
});

site/static/assets/js/sparnatural-bindings.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ bindSparnaturalWithYasrPlugins = function(sparnatural, yasr) {
1313
for (const plugin in yasr.plugins) {
1414
if (yasr.plugins[plugin].notifyConfiguration) {
1515
yasr.plugins[plugin].notifyConfiguration(
16-
sparnatural.sparnatural.specProvider
16+
event.detail.config
1717
);
1818
}
1919
}
@@ -69,6 +69,40 @@ bindSparnaturalWithYasqe = function(sparnatural, yasqe, yasr) {
6969
}
7070

7171

72+
/**
73+
* Binds Sparnatural with the SparnaturalHistoryComponent.
74+
*
75+
* - On sparnatural `init` event : injects the config into sparnatural-history
76+
* - On sparnatural `queryUpdated` event : stores the latest query in a global var
77+
* - On sparnatural `submit` event : saves the latest query in the history
78+
* - On sparnatural-history `loadQuery` event : loads the query from the history in Sparnatural
79+
*/
80+
bindSparnaturalWithHistory = function (sparnatural, sparnaturalHistory) {
81+
let lastquery = null;
82+
83+
sparnatural.addEventListener("init", (event) => {
84+
const config = event.detail.config;
85+
sparnaturalHistory.notifyConfiguration(config);
86+
});
87+
88+
// stores the latest query
89+
sparnatural.addEventListener("queryUpdated", (event) => {
90+
lastquery = event.detail.queryJson;
91+
});
92+
93+
sparnatural.addEventListener("submit", () => {
94+
// use saveQuery method from history component
95+
sparnaturalHistory.saveQuery(lastquery);
96+
});
97+
98+
// 🔁 Écouteur pour charger une requête depuis l'historique
99+
sparnaturalHistory.addEventListener("loadQuery", (event) => {
100+
const query = event.detail.query;
101+
sparnatural.loadQuery(query);
102+
});
103+
};
104+
105+
72106
/**
73107
* Binds Sparnatural with a query executed by Sparnatural itself, using yasqe as a read-only query editor.
74108
*

site/static/assets/js/sparnatural-history.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/static/assets/js/sparnatural-services.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/static/assets/js/sparnatural-text-query.js

Lines changed: 211 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/static/assets/js/sparnatural-yasgui-plugins.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/static/assets/js/sparnatural.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/static/assets/js/sparnatural.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)